lib/prawn/images.rb in prawn-1.0.0.rc2 vs lib/prawn/images.rb in prawn-1.0.0
- old
+ new
@@ -4,46 +4,43 @@
# Copyright April 2008, James Healy, Gregory Brown. All Rights Reserved.
#
# This is free software. Please see the LICENSE and COPYING files for details.
require 'digest/sha1'
+require 'pathname'
module Prawn
module Images
+ # @group Stable API
# Add the image at filename to the current page. Currently only
- # JPG and PNG files are supported.
+ # JPG and PNG files are supported. (Note that processing PNG
+ # images with alpha channels can be processor and memory intensive.)
#
- # NOTE: Prawn is very slow at rendering PNGs with alpha channels, and this
- # uses a lot of RAM. The workaround for those who don't mind installing
- # RMagick is to use:
- #
- # http://github.com/amberbit/prawn-fast-png
- #
# Arguments:
- # <tt>file</tt>:: path to file or an object that responds to #read
+ # <tt>file</tt>:: path to file or an object that responds to #read and #rewind
#
# Options:
# <tt>:at</tt>:: an array [x,y] with the location of the top left corner of the image.
# <tt>:position</tt>:: One of (:left, :center, :right) or an x-offset
- # <tt>:vposition</tt>:: One of (:top, :center, :center) or an y-offset
+ # <tt>:vposition</tt>:: One of (:top, :center, :center) or an y-offset
# <tt>:height</tt>:: the height of the image [actual height of the image]
# <tt>:width</tt>:: the width of the image [actual width of the image]
# <tt>:scale</tt>:: scale the dimensions of the image proportionally
# <tt>:fit</tt>:: scale the dimensions of the image proportionally to fit inside [width,height]
- #
- # Prawn::Document.generate("image2.pdf", :page_layout => :landscape) do
- # pigs = "#{Prawn::DATADIR}/images/pigs.jpg"
- # image pigs, :at => [50,450], :width => 450
#
+ # Prawn::Document.generate("image2.pdf", :page_layout => :landscape) do
+ # pigs = "#{Prawn::DATADIR}/images/pigs.jpg"
+ # image pigs, :at => [50,450], :width => 450
+ #
# dice = "#{Prawn::DATADIR}/images/dice.png"
- # image dice, :at => [50, 450], :scale => 0.75
- # end
+ # image dice, :at => [50, 450], :scale => 0.75
+ # end
#
# If only one of :width / :height are provided, the image will be scaled
- # proportionally. When both are provided, the image will be stretched to
+ # proportionally. When both are provided, the image will be stretched to
# fit the dimensions without maintaining the aspect ratio.
#
#
# If :at is provided, the image will be place in the current page but
# the text position will not be changed.
@@ -53,58 +50,45 @@
# passed as +file+, you can embed images from IO objects and things
# that act like them (including Tempfiles and open-uri objects).
#
# require "open-uri"
#
- # Prawn::Document.generate("remote_images.pdf") do
+ # Prawn::Document.generate("remote_images.pdf") do
# image open("http://prawn.majesticseacreature.com/media/prawn_logo.png")
# end
#
# This method returns an image info object which can be used to check the
- # dimensions of an image object if needed.
+ # dimensions of an image object if needed.
# (See also: Prawn::Images::PNG , Prawn::Images::JPG)
- #
+ #
def image(file, options={})
- Prawn.verify_options [:at, :position, :vposition, :height,
+ Prawn.verify_options [:at, :position, :vposition, :height,
:width, :scale, :fit], options
pdf_obj, info = build_image_object(file)
embed_image(pdf_obj, info, options)
info
end
+
# Builds an info object (Prawn::Images::*) and a PDF reference representing
# the given image. Return a pair: [pdf_obj, info].
#
+ # @private
def build_image_object(file)
- # Rewind if the object we're passed is an IO, so that multiple embeds of
- # the same IO object will work
- file.rewind if file.respond_to?(:rewind)
- # read the file as binary so the size is calculated correctly
- file.binmode if file.respond_to?(:binmode)
-
- if file.respond_to?(:read)
- image_content = file.read
- else
- raise ArgumentError, "#{file} not found" unless File.file?(file)
- image_content = File.binread(file)
- end
-
+ io = verify_and_open_image(file)
+ image_content = io.read
image_sha1 = Digest::SHA1.hexdigest(image_content)
# if this image has already been embedded, just reuse it
if image_registry[image_sha1]
info = image_registry[image_sha1][:info]
image_obj = image_registry[image_sha1][:obj]
else
# Build the image object
- klass = case Image.detect_image_format(image_content)
- when :jpg then Prawn::Images::JPG
- when :png then Prawn::Images::PNG
- end
- info = klass.new(image_content)
+ info = Prawn.image_handler.find(image_content).new(image_content)
# Bump PDF version if the image requires it
min_version(info.min_pdf_version) if info.respond_to?(:min_pdf_version)
# Add the image to the PDF and register it in case we see it again.
@@ -118,19 +102,20 @@
# Given a PDF image resource <tt>pdf_obj</tt> that has been added to the
# page's resources and an <tt>info</tt> object (the pair returned from
# build_image_object), embed the image according to the <tt>options</tt>
# given.
#
+ # @private
def embed_image(pdf_obj, info, options)
- # find where the image will be placed and how big it will be
+ # find where the image will be placed and how big it will be
w,h = info.calc_image_dimensions(options)
- if options[:at]
- x,y = map_to_absolute(options[:at])
- else
- x,y = image_position(w,h,options)
- move_text_position h
+ if options[:at]
+ x,y = map_to_absolute(options[:at])
+ else
+ x,y = image_position(w,h,options)
+ move_text_position h
end
# add a reference to the image object to the current page
# resource list and give it a label
label = "I#{next_image_id}"
@@ -138,16 +123,35 @@
# add the image to the current page
instruct = "\nq\n%.3f 0 0 %.3f %.3f %.3f cm\n/%s Do\nQ"
add_content instruct % [ w, h, x, y - h, label ]
end
-
- private
+ private
+
+ def verify_and_open_image(io_or_path)
+ # File or IO
+ if io_or_path.respond_to?(:rewind)
+ io = io_or_path
+ # Rewind if the object we're passed is an IO, so that multiple embeds of
+ # the same IO object will work
+ io.rewind
+ # read the file as binary so the size is calculated correctly
+ # guard binmode because some objects acting io-like don't implement it
+ io.binmode if io.respond_to?(:binmode)
+ return io
+ end
+ # String or Pathname
+ io_or_path = Pathname.new(io_or_path)
+ raise ArgumentError, "#{io_or_path} not found" unless io_or_path.file?
+ io = io_or_path.open('rb')
+ io
+ end
+
def image_position(w,h,options)
options[:position] ||= :left
-
+
y = case options[:vposition]
when :top
bounds.absolute_top
when :center
bounds.absolute_top - (bounds.height - h) / 2.0
@@ -157,32 +161,32 @@
bounds.absolute_top - options[:vposition]
else
determine_y_with_page_flow(h)
end
- x = case options[:position]
+ x = case options[:position]
when :left
bounds.left_side
when :center
- bounds.left_side + (bounds.width - w) / 2.0
+ bounds.left_side + (bounds.width - w) / 2.0
when :right
bounds.right_side - w
when Numeric
options[:position] + bounds.left_side
end
return [x,y]
- end
-
+ end
+
def determine_y_with_page_flow(h)
if overruns_page?(h)
bounds.move_past_bottom
end
self.y
- end
-
+ end
+
def overruns_page?(h)
- (self.y - h) < reference_bounds.absolute_bottom
+ (self.y - h) < reference_bounds.absolute_bottom
end
def image_registry
@image_registry ||= {}
end