class Thingfish::Processor::Image

Image processor plugin for Thingfish

Constants

IGNORED_MIMETYPES

An array of mediatypes to ignore, even though ImageMagick claims it groks them

REVISION

Version control revision

VERSION

Package version

Attributes

generated_types[R]

The mediatypes the plugin can generated in a response, as ThingFish::AcceptParam objects

handled_types[R]

The mediatypes the plugin accepts in a request, as ThingFish::AcceptParam objects

supported_formats[R]

The Hash of formats and the operations they support.

Public Instance Methods

handled_type?( type ) click to toggle source

Returns true if the given media type is one the processor handles. Overridden so the types can be used by the instance.

# File lib/thingfish/processor/image.rb, line 80
def handled_type?( type )
        self.log.debug "Looking for handled type for: %p" % [ type ]
        result = self.handled_types.find {|handled_type| type =~ handled_type }
        self.log.debug "  found: %p" % [ result ]
        return result
end
Also aliased as: is_handled_type?
inspect() click to toggle source

Return a human-readable representation of the receiving object suitable for debugging.

# File lib/thingfish/processor/image.rb, line 129
def inspect
        return "#<%p:%#x %d supported image formats, %d readable, %d writable>" % [
                self.class,
                self.object_id * 2,
                self.supported_formats.size,
                self.handled_types.size,
                self.generated_types.size,
        ]
end
is_handled_type?( type )
Alias for: handled_type?
on_request( request ) click to toggle source

Synchronous processor API – extract metadata from uploaded images.

# File lib/thingfish/processor/image.rb, line 90
def on_request( request )
        self.log.debug "Image-processing %p" % [ request ]
        image = case request.body
                        when StringIO
                                self.log.debug "  making a single image from a StringIO"
                                Magick::Image.from_blob( request.body.read )
                        else
                                path = request.body.path
                                self.log.debug "  making a flattened image out of %p" % [ path ]
                                path = path.to_s + '[0]' # ImageMagick, read a single frame
                                list = Magick::ImageList.new( path )
                                list.flatten_images
                        end

        image = image.first if image.respond_to?( :first )
        self.log.debug "  image is: %p" % [ image ]
        metadata = self.extract_image_metadata( image )
        self.log.debug "  extracted image metadata: %p" % [ metadata ]
        request.add_metadata( metadata )

        self.log.debug "  going to generate a thumbnail..."
        self.generate_thumbnail( image, metadata['title'] ) do |thumbio, thumb_metadata|
                self.log.debug "  generated: %p (%p)" % [ thumbio, thumb_metadata ]
                request.add_related_resource( thumbio, thumb_metadata )
        end

        self.log.debug "  destroying the image to free up memory"

rescue Magick::ImageMagickError => err
        self.log.error "Problem while processing file %p: %s" % [ err.class, err.message ]
        self.log.debug { err.backtrace.join( "\n  " ) }

ensure
        image.destroy! if image
end

Protected Instance Methods

extract_image_metadata( image ) click to toggle source

Extract metadata from the given image (a Magick::Image object) and return it in a Hash.

# File lib/thingfish/processor/image.rb, line 146
def extract_image_metadata( image )
        metadata = {}

        metadata.merge!( self.get_regular_metadata(image) )
        metadata.merge!( self.get_exif_metadata(image) )

        return metadata
end
find_generated_types( supported_formats ) click to toggle source

Return an Array of Strelka::HTTPRequest::MediaType objects for the formats that the processor is capable of writing.

# File lib/thingfish/processor/image.rb, line 307
def find_generated_types( supported_formats )
        return supported_formats.
                select {|type, op| op.can_write? }.
                collect {|type, op| Strelka::HTTPRequest::MediaType.parse(type) }
end
find_handled_types( supported_formats ) click to toggle source

Return Strelka::HTTPRequest::MediaType objects for the formats that the processor is capable of reading.

# File lib/thingfish/processor/image.rb, line 298
def find_handled_types( supported_formats )
        return supported_formats.
                select {|type, op| op.can_read? }.
                collect {|type, op| Strelka::HTTPRequest::MediaType.parse(type) }
end
find_supported_formats() click to toggle source

Transform the installed ImageMagick's list of formats into AcceptParams for easy comparison later.

# File lib/thingfish/processor/image.rb, line 265
def find_supported_formats
        formats = {}
        raise "Config database doesn't have any mimetypes" if
                Mongrel2::Config.mimetypes.empty?

        # A hash of image formats and their properties. Each key in the returned
        # hash is the name of a supported image format. Each value is a string in
        # the form "BRWA". The details are in this table:
        #   B   is "*" if the format has native blob support, and "-" otherwise.
        #   R   is "r" if ×Magick can read the format, and "-" otherwise.
        #   W   is "w" if ×Magick can write the format, and "-" otherwise.
        #   A   is "+" if the format supports multi-image files, and "-" otherwise.
        Magick.formats.each do |ext,support|
                ext = ".#{ext.downcase}"
                self.log.debug "Looking for mediatype for ext: %s (%p)" % [ ext, support ]

                mimetype = Mongrel2::Config.mimetypes[ ext ] or next
                self.log.debug "  mimetype is: %p" % [ mimetype ]
                next if IGNORED_MIMETYPES.include?( mimetype )

                operations = MagickOperations.new( ext, support )
                self.log.debug "  registering image format %s (%s)" % [ mimetype, operations ]
                formats[ mimetype ] = operations
        end

        self.log.debug "Registered mimetype mapping for %d of %d supported image types" %
                [ formats.keys.length, Magick.formats.length ]
        return formats
end
generate_thumbnail( image, title ) { |imgdata, metadata| ... } click to toggle source

Create a thumbnail from the given image and return it in a string along with any associated metadata.

# File lib/thingfish/processor/image.rb, line 183
def generate_thumbnail( image, title )
        dimensions = self.class.thumbnail_dimensions
        self.log.debug "Making thumbnail of max dimensions: [%d X %d]" % dimensions
        thumb = image.resize_to_fit( *dimensions )
        imgdata = StringIO.new
        imgdata << thumb.to_blob {|img| img.format = 'JPG' }
        imgdata.rewind

        metadata = self.extract_image_metadata( thumb )
        metadata.merge!({
                format:       thumb.mime_type,
                relationship: 'thumbnail',
                title:        "Thumbnail of %s" % [ title || image.inspect ],
                extent:       imgdata.size,
        })

        self.log.debug "  made thumbnail for %p" % [ image ]
        yield( imgdata, metadata )

        thumb.destroy!
end
get_exif_metadata( image ) click to toggle source

Fetch exif metadata and return it as a Hash.

# File lib/thingfish/processor/image.rb, line 170
def get_exif_metadata( image )
        exif_pairs = image.get_exif_by_entry
        exif_pairs.reject! {|name, val| name == 'unknown' || val.nil? }
        exif_pairs.collect! do |name, val|
                newname = name.gsub(/\B([A-Z])(?=[a-z])/) { '_' + $1 }.downcase
                [ "exif:#{newname}", val ]
        end
        return Hash[ exif_pairs ]
end
get_regular_metadata( image ) click to toggle source

Fetch regular image metadata as a Hash.

# File lib/thingfish/processor/image.rb, line 157
def get_regular_metadata( image )
        return {
                'image:height'       => image.rows,
                'image:width'        => image.columns,
                'image:depth'        => image.depth,
                'image:density'      => image.density,
                'image:gamma'        => image.gamma,
                'image:bounding_box' => image.bounding_box.to_s,
        }
end
initialize( * ) click to toggle source

Set up a new Filter object

Calls superclass method
# File lib/thingfish/processor/image.rb, line 53
def initialize( * ) # :notnew:
        super

        @supported_formats = find_supported_formats()
        @handled_types     = find_handled_types( @supported_formats )
        @generated_types   = find_generated_types( @supported_formats )
end