lib/pdf/wrapper.rb in pdf-wrapper-0.1.0 vs lib/pdf/wrapper.rb in pdf-wrapper-0.1.3
- old
+ new
@@ -1,31 +1,21 @@
# coding: utf-8
require 'stringio'
require 'pdf/core'
+require 'pdf/errors'
require File.dirname(__FILE__) + "/wrapper/graphics"
require File.dirname(__FILE__) + "/wrapper/images"
require File.dirname(__FILE__) + "/wrapper/loading"
require File.dirname(__FILE__) + "/wrapper/table"
require File.dirname(__FILE__) + "/wrapper/text"
+require File.dirname(__FILE__) + "/wrapper/page"
-# try to load cairo from the standard places, but don't worry if it fails,
-# we'll try to find it via rubygems
-begin
- require 'cairo'
-rescue LoadError
- begin
- require 'rubygems'
- gem 'cairo', '>=1.5'
- require 'cairo'
- rescue Gem::LoadError
- raise LoadError, "Could not find the ruby cairo bindings in the standard locations or via rubygems. Check to ensure they're installed correctly"
- rescue LoadError
- raise LoadError, "Could not load rubygems"
- end
-end
+require 'rubygems'
+gem 'cairo', '>=1.5'
+require 'cairo'
module PDF
# Create PDF files by using the cairo and pango libraries.
#
# Rendering to a file:
@@ -287,41 +277,15 @@
# captioned_image("hobbit.svg", "Hobbit", 100, 400)
# captioned_image("elf.svg", "Elf", 100, 400)
def translate(x, y, &block)
@context.save do
@context.translate(x, y)
+ move_to(0,0)
yield
end
end
- # all code wrapped in the block passed to this function will have co-ordinates
- # and distances (width/height) multiplied by these values before being used
- #
- # Divide everything by 2
- #
- # pdf.scale(0.5, 0.5) do
- # ...
- # end
- #
- # Make the page 1.0 wide and 1.0 tall, so co-ordinates and distances
- # can be specified as percentages (0.5 == 50%, etc)
- #
- # pdf.scale(pdf.page_width.to_f, pdf.page_height.to_f) do
- # ...
- # end
- #
- def scale(w, h, &block)
- @context.save do
- @context.scale(w, h)
-
- # set the line width again so that it's set relative to the current
- # scale factor
- line_width @line_width
- yield
- end
- end
-
# change the default colour used to draw on the canvas
#
# Parameters:
# <tt>c</tt>:: either a colour symbol recognised by rcairo (:red, :blue, :black, etc) or
# an array with 3-4 integer elements. The first 3 numbers are red, green and
@@ -341,20 +305,23 @@
# render the PDF and return it as a string
def render
# finalise the document, then convert the StringIO object it was rendered to
# into a string
- @context.show_page
- @context.target.finish
+ finish
return @output.string
end
+ def render_to_file(filename) #nodoc
+ # TODO: remove this at some point
+ warn "WARNING: render_to_file() is deprecated, please use render_file()"
+ render_file filename
+ end
+
# save the rendered PDF to a file
- def render_to_file(filename)
- # finalise the document
- @context.show_page
- @context.target.finish
+ def render_file(filename)
+ finish
# write each line from the StringIO object it was rendered to into the
# requested file
File.open(filename, "w") do |of|
@output.rewind
@@ -364,41 +331,155 @@
#####################################################
# Misc Functions
#####################################################
- def pad(n)
+ # move down the canvas by n points
+ # returns the new y position
+ #
+ def move_down(n)
x, y = current_point
- move_to(x, y + n)
- y + n
+ newy = y + n
+ move_to(x, newy)
+ newy
end
+ # move up the canvas by n points
+ # returns the new y position
+ #
+ def move_up(n)
+ x, y = current_point
+ newy = y - n
+ move_to(x, newy)
+ newy
+ end
+
+ # move left across the canvas by n points
+ # returns the new x position
+ #
+ def move_left(n)
+ x, y = current_point
+ newx = x - n
+ move_to(newx, y)
+ newx
+ end
+
+ # move right across the canvas by n points
+ # returns the new x position
+ #
+ def move_right(n)
+ x, y = current_point
+ newx = x + n
+ move_to(newx, y)
+ newx
+ end
+
+ # Moves down the document and then executes a block.
+ #
+ # pdf.text "some text"
+ # pdf.pad_top(100) do
+ # pdf.text "This is 100 points below the previous line of text"
+ # end
+ # pdf.text "This text appears right below the previous line of text"
+ #
+ def pad_top(n)
+ move_down n
+ yield
+ end
+
+ # Executes a block then moves down the document
+ #
+ # pdf.text "some text"
+ # pdf.pad_bottom(100) do
+ # pdf.text "This text appears right below the previous line of text"
+ # end
+ # pdf.text "This is 100 points below the previous line of text"
+ #
+ def pad_bottom(n)
+ yield
+ move_down n
+ end
+
+ # Moves down the document by y, executes a block, then moves down the
+ # document by y again.
+ #
+ # pdf.text "some text"
+ # pdf.pad(100) do
+ # pdf.text "This is 100 points below the previous line of text"
+ # end
+ # pdf.text "This is 100 points below the previous line of text"
+ #
+ def pad(n)
+ if block_given?
+ move_down n
+ yield
+ move_down n
+ else
+ move_down n
+ end
+ end
+
+ # Moves right across the document by n, executes a block, then moves back
+ # left by the same amount
+ #
+ # pdf.text "some text"
+ # pdf.indent(50) do
+ # pdf.text "This starts 50 points right the previous line of text"
+ # end
+ # pdf.text "This starts in line with the first line of text"
+ #
+ # If no block is provided, operates just like move_right.
+ #
+ def indent(n)
+ if block_given?
+ move_right n
+ yield
+ move_left n
+ else
+ move_right n
+ end
+ end
+
# move the cursor to an arbitary position on the current page
def move_to(x,y)
@context.move_to(x,y)
end
# reset the cursor by moving it to the top left of the useable section of the page
def reset_cursor
@context.move_to(margin_left,margin_top)
end
+ # returns true if the PDF has already been rendered, false if it hasn't.
+ # Due to limitations of the underlying libraries, content cannot be
+ # added to a PDF once it has been rendered.
+ #
+ def finished?
+ @output.seek(@output.size - 6)
+ bytes = @output.read(6)
+ bytes == "%%EOF\n" ? true : false
+ end
+
# add the same elements to multiple pages. Useful for adding items like headers, footers and
# watermarks.
#
+ # There is a single block parameter that is a proxy to the current PDF::Wrapper object that
+ # disallows start_new_page calls. Every other method from PDF::Wrapper is considered valid.
+ #
# arguments:
# <tt>spec</tt>:: Which pages to add the items to. :all, :odd, :even, a range, an Array of numbers or an number
#
# To add text to every page that mentions the page number
- # pdf.repeating_element(:all) do
- # pdf.text("Page #{pdf.page}!", :left => pdf.margin_left, :top => pdf.margin_top, :font_size => 18)
+ # pdf.repeating_element(:all) do |page|
+ # page.text("Page #{page.page}!", :left => page.margin_left, :top => page.margin_top, :font_size => 18)
# end
#
# To add a circle to the middle of every page
- # pdf.repeating_element(:all) do
- # pdf.circle(pdf.absolute_x_middle, pdf.absolute_y_middle, 100)
+ # pdf.repeating_element(:all) do |page|
+ # page.circle(page.absolute_x_middle, page.absolute_y_middle, 100)
# end
+ #
def repeating_element(spec = :all, &block)
call_repeating_element(spec, block)
# store it so we can add it to future pages
@repeating << {:spec => spec, :block => block}
@@ -407,10 +488,11 @@
# move to the next page
#
# 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
@@ -438,24 +520,31 @@
end
end
private
+ def finish
+ # finalise the document
+ @context.show_page
+ @context.target.finish
+ rescue Cairo::SurfaceFinishedError
+ # do nothing, we're happy that the surfaced has been finished
+ end
+
# runs the code in block, passing it a hash of options that might be
# required
def call_repeating_element(spec, block)
- # TODO: disallow start_new_page when adding a repeating element
if spec == :all ||
(spec == :even && (page % 2) == 0) ||
(spec == :odd && (page % 2) == 1) ||
(spec.class == Range && spec.include?(page)) ||
(spec.class == Array && spec.include?(page)) ||
(spec.respond_to?(:to_i) && spec.to_i == page)
@context.save do
# add it to the current page
- block.call
+ block.call PDF::Wrapper::Page.new(self)
end
end
end
def default_positioning_options
@@ -489,23 +578,15 @@
# into a 4 item array. This is normally handled within cairo itself, however when
# Cairo and Poppler are both loaded, it breaks.
Cairo::Color.parse(c).to_rgb.to_a
end
- def user_to_device_dist(x,y)
- @context.user_to_device_distance(x, y)
- end
-
def user_x_to_device_x(x)
@context.user_to_device(x, 0).first.abs
end
def user_y_to_device_y(y)
@context.user_to_device(0, y).last.abs
- end
-
- def device_to_user_dist(x, y)
- @context.device_to_user_distance(x, y)
end
def device_x_to_user_x(x)
@context.device_to_user(x, 0).first.abs
end