Namespace

Prawn::Images

Public Instance Methods

image(file, options={}) click to toggle source

Add the image at filename to the current page. Currently only JPG and PNG files are supported.

Arguments:

file

path to file or an object that responds to #

Options:

:at

an array [x,y] with the location of the top left corner of the image.

:position

One of (:left, :center, :right) or an x-offset

:vposition

One of (:top, :center, :center) or an y-offset

:height

the height of the image [actual height of the image]

:width

the width of the image [actual width of the image]

:scale

scale the dimensions of the image proportionally

:fit

scale the dimensions of the image proportionally to fit inside [width,height]

  Prawn::Document.generate("image2.pdf", :page_layout => :landscape) do     
    pigs = "#{Prawn::BASEDIR}/data/images/pigs.jpg" 
    image pigs, :at => [50,450], :width => 450                                      

    dice = "#{Prawn::BASEDIR}/data/images/dice.png"
    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 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.

If instead of an explicit filename, an object with a read method is 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 
    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. (See also: Prawn::Images::PNG , Prawn::Images::JPG)

     # File lib/prawn/images.rb, line 60
 60:     def image(file, options={})
 61:       Prawn.verify_options [:at, :position, :vposition, :height, 
 62:                             :width, :scale, :fit], options
 63: 
 64:       if file.respond_to?(:read)
 65:         image_content = file.read
 66:       else      
 67:         raise ArgumentError, "#{file} not found" unless File.file?(file)  
 68:         image_content =  File.binread(file)
 69:       end
 70:       
 71:       image_sha1 = Digest::SHA1.hexdigest(image_content)
 72: 
 73:       # if this image has already been embedded, just reuse it
 74:       if image_registry[image_sha1]
 75:         info = image_registry[image_sha1][:info]
 76:         image_obj = image_registry[image_sha1][:obj]
 77:       else
 78:         # build the image object and embed the raw data
 79:         image_obj = case detect_image_format(image_content)
 80:         when :jpg then
 81:           info = Prawn::Images::JPG.new(image_content)
 82:           build_jpg_object(image_content, info)
 83:         when :png then
 84:           info = Prawn::Images::PNG.new(image_content)
 85:           build_png_object(image_content, info)
 86:         end
 87:         image_registry[image_sha1] = {:obj => image_obj, :info => info}
 88:       end
 89: 
 90:       # find where the image will be placed and how big it will be  
 91:       w,h = calc_image_dimensions(info, options)
 92: 
 93:       if options[:at]     
 94:         x,y = map_to_absolute(options[:at]) 
 95:       else                  
 96:         x,y = image_position(w,h,options) 
 97:         move_text_position h   
 98:       end
 99: 
100:       # add a reference to the image object to the current page
101:       # resource list and give it a label
102:       label = "I#{next_image_id}"
103:       page.xobjects.merge!( label => image_obj )
104: 
105:       # add the image to the current page
106:       instruct = "\nq\n%.3f 0 0 %.3f %.3f %.3f cm\n/%s Do\nQ"
107:       add_content instruct % [ w, h, x, y - h, label ]
108:       
109:       return info
110:     end

Private Instance Methods

build_jpg_object(data, jpg) click to toggle source
     # File lib/prawn/images.rb, line 143
143:     def build_jpg_object(data, jpg) 
144:       color_space = case jpg.channels
145:       when 1
146:         :DeviceGray
147:       when 3
148:         :DeviceRGB
149:       when 4
150:         :DeviceCMYK
151:       else
152:         raise ArgumentError, 'JPG uses an unsupported number of channels'
153:       end
154:       obj = ref!(:Type       => :XObject,
155:           :Subtype          => :Image,
156:           :Filter           => :DCTDecode,
157:           :ColorSpace       => color_space,
158:           :BitsPerComponent => jpg.bits,
159:           :Width            => jpg.width,
160:           :Height           => jpg.height,
161:           :Length           => data.size ) 
162: 
163:       # add extra decode params for CMYK images. By swapping the
164:       # min and max values from the default, we invert the colours. See
165:       # section 4.8.4 of the spec.
166:       if color_space == :DeviceCMYK
167:         obj.data[:Decode] = [ 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0 ]
168:       end
169: 
170:       obj << data
171:       return obj
172:     end
build_png_object(data, png) click to toggle source
     # File lib/prawn/images.rb, line 174
174:     def build_png_object(data, png)
175: 
176:       if png.compression_method != 0
177:         raise Errors::UnsupportedImageType, 'PNG uses an unsupported compression method'
178:       end
179: 
180:       if png.filter_method != 0
181:         raise Errors::UnsupportedImageType, 'PNG uses an unsupported filter method'
182:       end
183: 
184:       if png.interlace_method != 0
185:         raise Errors::UnsupportedImageType, 'PNG uses unsupported interlace method'
186:       end
187: 
188:       # some PNG types store the colour and alpha channel data together,
189:       # which the PDF spec doesn't like, so split it out.
190:       png.split_alpha_channel!
191: 
192:       case png.colors
193:       when 1
194:         color = :DeviceGray
195:       when 3
196:         color = :DeviceRGB
197:       else
198:         raise Errors::UnsupportedImageType, "PNG uses an unsupported number of colors (#{png.colors})"
199:       end
200: 
201:       # build the image dict
202:       obj = ref!(:Type             => :XObject,
203:                 :Subtype          => :Image,
204:                 :Height           => png.height,
205:                 :Width            => png.width,
206:                 :BitsPerComponent => png.bits,
207:                 :Length           => png.img_data.size,
208:                 :Filter           => :FlateDecode
209:                )
210: 
211:       unless png.alpha_channel
212:         obj.data[:DecodeParms] = {:Predictor => 15,
213:                                   :Colors    => png.colors,
214:                                   :BitsPerComponent => png.bits,
215:                                   :Columns   => png.width}
216:       end
217: 
218:       # append the actual image data to the object as a stream
219:       obj << png.img_data
220:       
221:       # sort out the colours of the image
222:       if png.palette.empty?
223:         obj.data[:ColorSpace] = color
224:       else
225:         # embed the colour palette in the PDF as a object stream
226:         palette_obj = ref!(:Length => png.palette.size)
227:         palette_obj << png.palette
228: 
229:         # build the color space array for the image
230:         obj.data[:ColorSpace] = [:Indexed, 
231:                                  :DeviceRGB,
232:                                  (png.palette.size / 3) -1,
233:                                  palette_obj]
234:       end
235: 
236:       # *************************************
237:       # add transparency data if necessary
238:       # *************************************
239: 
240:       # For PNG color types 0, 2 and 3, the transparency data is stored in
241:       # a dedicated PNG chunk, and is exposed via the transparency attribute
242:       # of the PNG class.
243:       if png.transparency[:grayscale]
244:         # Use Color Key Masking (spec section 4.8.5)
245:         # - An array with N elements, where N is two times the number of color
246:         #   components.
247:         val = png.transparency[:grayscale]
248:         obj.data[:Mask] = [val, val]
249:       elsif png.transparency[:rgb]
250:         # Use Color Key Masking (spec section 4.8.5)
251:         # - An array with N elements, where N is two times the number of color
252:         #   components.
253:         rgb = png.transparency[:rgb]
254:         obj.data[:Mask] = rgb.collect { |x| [x,x] }.flatten
255:       elsif png.transparency[:indexed]
256:         # TODO: broken. I was attempting to us Color Key Masking, but I think
257:         #       we need to construct an SMask i think. Maybe do it inside
258:         #       the PNG class, and store it in alpha_channel
259:         #obj.data[:Mask] = png.transparency[:indexed]
260:       end
261: 
262:       # For PNG color types 4 and 6, the transparency data is stored as a alpha
263:       # channel mixed in with the main image data. The PNG class seperates
264:       # it out for us and makes it available via the alpha_channel attribute
265:       if png.alpha_channel
266:         min_version 1.4
267:         smask_obj = ref!(:Type             => :XObject,
268:                         :Subtype          => :Image,
269:                         :Height           => png.height,
270:                         :Width            => png.width,
271:                         :BitsPerComponent => png.bits,
272:                         :Length           => png.alpha_channel.size,
273:                         :Filter           => :FlateDecode,
274:                         :ColorSpace       => :DeviceGray,
275:                         :Decode           => [0, 1]
276:                        )
277:         smask_obj << png.alpha_channel
278:         obj.data[:SMask] = smask_obj
279:       end
280: 
281:       return obj
282:     end
calc_image_dimensions(info, options) click to toggle source
     # File lib/prawn/images.rb, line 284
284:     def calc_image_dimensions(info, options)
285:       w = options[:width] || info.width
286:       h = options[:height] || info.height
287: 
288:       if options[:width] && !options[:height]
289:         wp = w / info.width.to_f 
290:         w = info.width * wp
291:         h = info.height * wp
292:       elsif options[:height] && !options[:width]         
293:         hp = h / info.height.to_f
294:         w = info.width * hp
295:         h = info.height * hp   
296:       elsif options[:scale] 
297:         w = info.width * options[:scale]
298:         h = info.height * options[:scale]
299:       elsif options[:fit] 
300:         bw, bh = options[:fit]
301:         bp = bw / bh.to_f
302:         ip = info.width / info.height.to_f
303:         if ip > bp
304:           w = bw
305:           h = bw / ip
306:         else
307:           h = bh
308:           w = bh * ip
309:         end
310:       end
311:       info.scaled_width = w
312:       info.scaled_height = h
313:       [w,h]
314:     end
detect_image_format(content) click to toggle source
     # File lib/prawn/images.rb, line 316
316:     def detect_image_format(content)
317:       top = content[0,128]                       
318: 
319:       if top[0, 3] == "\xff\xd8\xff"
320:         return :jpg
321:       elsif top[0, 8]  == "\x89PNG\x0d\x0a\x1a\x0a"
322:         return :png
323:       else
324:         raise Errors::UnsupportedImageType, "image file is an unrecognised format"
325:       end
326:     end
image_position(w,h,options) click to toggle source
     # File lib/prawn/images.rb, line 114
114:     def image_position(w,h,options)
115:       options[:position] ||= :left
116:       
117:       x = case options[:position] 
118:       when :left
119:         bounds.absolute_left
120:       when :center
121:         bounds.absolute_left + (bounds.width - w) / 2.0 
122:       when :right
123:         bounds.absolute_right - w
124:       when Numeric
125:         options[:position] + bounds.absolute_left
126:       end
127: 
128:       y = case options[:vposition]
129:       when :top
130:         bounds.absolute_top
131:       when :center
132:         bounds.absolute_top - (bounds.height - h) / 2.0
133:       when :bottom
134:         bounds.absolute_bottom + h
135:       when Numeric
136:         bounds.absolute_top - options[:vposition]
137:       else
138:         self.y
139:       end
140:       return [x,y]
141:     end
image_registry() click to toggle source
     # File lib/prawn/images.rb, line 328
328:     def image_registry
329:       @image_registry ||= {}
330:     end
next_image_id() click to toggle source
     # File lib/prawn/images.rb, line 332
332:     def next_image_id
333:       @image_counter ||= 0
334:       @image_counter += 1
335:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.