diff --git a/README.rdoc b/README.rdoc
index eab90e9..5b3bb3a 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -1,14 +1,14 @@
== Installation
Gem: (Recommended)
-
+
gem install fleximage
-
+
# in config/environment.rb
config.gem 'fleximage'
Plugin:
-
+
./script/plugin install git://github.com/Squeegy/fleximage.git
@@ -49,21 +49,21 @@ Fleximage uses a simple templating engine that allows you to re-render images ex
== 1. Installation
Gem: (Recommended)
-
+
gem install fleximage
-
+
# in config/environment.rb
config.gem 'fleximage'
Plugin:
-
+
./script/plugin install git://github.com/Squeegy/fleximage.git
== 2. Activating your model
You need to let your model know it should be Fleximage-friendly. Lets say you have a model for photos.
-
+
# app/models/photo.rb
class Photo < ActiveRecord::Base
acts_as_fleximage :image_directory => 'public/images/uploaded_photos'
@@ -77,9 +77,9 @@ There are many other options for your model. Refer to the Fleximage::Model:
== 3. The upload form
Your users need a way to upload their images into your site. Here is how we might render a form to create a photo record.
-
+
# app/views/photos/new.html.erb
-
+
<% form_for @photo, :html => { :multipart => true } do |f| %>
Name
@@ -101,7 +101,7 @@ Your users need a way to upload their images into your site. Here is how we mig
<%= f.submit "Create" %>
<% end %>
-
+
*NOTE*: The ":html => { :multipart => true }" is *VERY* *IMPORTANT*. Without this snippet your browser will not send binary data to the server. If things aren't working, check to make sure you have this in your form declaration.
The relevant bit of our form code is:
@@ -119,7 +119,7 @@ Right along side the upload field is a simple text field, which maps to the "ima
You can have just one of these fields, or both. The model will know how to do the right thing either way.
When the user submits the form, all you have to do is assign the form contents to your object in standard Rails fashion, and the image is uploaded and saved for you. Creating a new photo may look like this in your controller:
-
+
# app/controllers/photos_controller.rb
def create
@photo = Photo.new(params[:photo])
@@ -137,10 +137,10 @@ When the user submits the form, all you have to do is assign the form contents t
Rails 2 has amazing support for format driven responses. Given a photo object, by default it would have an HTML view that describes information about that photo. With Fleximage, the JPG or (GIF or PNG) view can be the image data itself.
A photo HTML view may look like this:
-
+
# app/views/photos/show.html.erb
# http://mysite.com/photos/123
-
+
@@ -169,7 +169,7 @@ The filename of the template should look like this: action_name.jpg.flexi '20x20'
image.border :size => 10, :color => 'green'
image.text 'I like Cheese'
- image.unsharp_mask
+ image.unsharp_mask
image.shadow
end
@@ -221,7 +221,7 @@ You don't want to render JPGs? That's fine. Just link to the format you want (
# app/views/photos/show.html.erb
<%= image_tag photo_path(@photo, :gif) %>
-
+
# app/views/photos/show.gif.flexi
@photo.operate do |image|
@photo.resize '150x150', :crop => true
@@ -232,7 +232,7 @@ The Fleximage template engine will automatically detect the format that is being
== Converting/Upgrading your master image store
Are you upgrading your live app to the new file store creation date based format? Did you start out with PNG image storage, and later realize you need to store with the more space economic JPG instead? Not problem, Fleximage provides some rake tasks to help you out.
-
+
Each conversion rake task requires that you tell it the class for which that you are changing the file store. For example, if you want to change to the new creation date based storage structure, for the class +Photo+, you can run a rake task like this:
rake fleximage:convert:to_nested FLEXIMAGE_CLASS=Photo
diff --git a/lib/fleximage/aviary_controller.rb b/lib/fleximage/aviary_controller.rb
index 532c2f0..aa745a2 100644
--- a/lib/fleximage/aviary_controller.rb
+++ b/lib/fleximage/aviary_controller.rb
@@ -1,75 +1,75 @@
module Fleximage
-
+
module AviaryController
def self.api_key(value = nil)
value ? @api_key = value : @api_key
end
-
+
def self.api_key=(value = nil)
api_key value
end
-
+
# Include acts_as_fleximage class method
def self.included(base) #:nodoc:
base.extend(ClassMethods)
end
-
+
module ClassMethods
-
+
# Invoke this method to enable this controller to allow editing of images via Aviary
def editable_in_aviary(model_class, options = {})
unless options.has_key?(:secret)
raise ArgumentError, ":secret key in options is required.\nExample: editable_in_aviary(Photo, :secret => \"My-deep-dark-secret\")"
end
-
+
# Don't verify authenticity for aviary callback
- protect_from_forgery :except => :aviary_image_update
-
+ protect_from_forgery :except => :aviary_image_update
+
# Include the necesary instance methods
include Fleximage::AviaryController::InstanceMethods
-
+
# Add before_filter to secure aviary actions
before_filter :aviary_image_security, :only => [:aviary_image, :aviary_image_update]
-
+
# Allow the view access to the image hash generation method
helper_method :aviary_image_hash
-
+
# Save the Fleximage model class
model_class = model_class.constantize if model_class.is_a?(String)
dsl_accessor :aviary_model_class, :default => model_class
dsl_accessor :aviary_secret, :default => options[:secret]
end
-
+
end
-
+
module InstanceMethods
-
+
# Deliver the master image to aviary
def aviary_image
render :text => @model.load_image.to_blob,
:content_type => Mime::Type.lookup_by_extension(self.class.aviary_model_class.image_storage_format.to_s)
end
-
+
# Aviary posts the edited image back to the controller here
def aviary_image_update
@model.image_file_url = params[:imageurl]
@model.save
render :text => 'Image Updated From Aviary'
end
-
+
protected
def aviary_image_hash(model)
Digest::SHA1.hexdigest("fleximage-aviary-#{model.id}-#{model.created_at}-#{self.class.aviary_secret}")
end
-
+
def aviary_image_security
@model = self.class.aviary_model_class.find(params[:id])
unless aviary_image_hash(@model) == params[:key]
render :text => '
403 Not Authorized
', :status => '403'
end
end
-
+
end
end
-
+
end
\ No newline at end of file
diff --git a/lib/fleximage/blank.rb b/lib/fleximage/blank.rb
index 1be44f5..0e432a2 100644
--- a/lib/fleximage/blank.rb
+++ b/lib/fleximage/blank.rb
@@ -1,5 +1,5 @@
module Fleximage
-
+
# The +Blank+ class allows easy creation of dynamic images for views which depends models that
# do not store images. For example, perhaps you want a rendering of a text label, or a comment,
# or some other type of data that is not inherently image based.
@@ -8,27 +8,27 @@ module Fleximage
# a new Fleximage::Blank object right in your view.
#
# Usage:
- #
+ #
# Fleximage::Blank.new(size, options = {}).operate { |image| ... }
#
# Use the following keys in the +options+ hash:
#
# * color: the color the image will be. Can be a named color or a Magick::Pixel object.
- #
+ #
# Example:
#
# # app/views/comments/show.png.flexi
# Fleximage::Blank.new('400x150')).operate do |image|
# # Start with a chat bubble image as the background
# image.image_overlay('public/images/comment_bubble.png')
- #
+ #
# # Assuming that the user model acts_as_fleximage, this will draw the users image.
# image.image_overlay(@comment.user.file_path,
# :size => '50x50',
# :alignment => :top_left,
# :offset => '10x10'
# )
- #
+ #
# # Add the author name text
# image.text(@comment.author,
# :alignment => :top_left,
@@ -40,9 +40,9 @@ module Fleximage
# :opacity => 0.5,
# }
# )
- #
+ #
# # Add the comment body text
- # image.text(@comment.body,
+ # image.text(@comment.body,
# :alignment => :top_left,
# :offset => '10x90',
# :color => color(128, 128, 128),
@@ -52,10 +52,10 @@ module Fleximage
class Blank
include Fleximage::Model
acts_as_fleximage
-
+
def initialize(size, options = {})
width, height = Fleximage::Operator::Base.size_to_xy(size)
-
+
@uploaded_image = Magick::Image.new(width, height) do
self.colorspace = Magick::RGBColorspace
self.depth = 8
@@ -63,7 +63,7 @@ def initialize(size, options = {})
self.format = 'PNG'
self.background_color = options[:color] || 'none'
end
-
+
@output_image = @uploaded_image
end
end
diff --git a/lib/fleximage/helper.rb b/lib/fleximage/helper.rb
index 8672692..8241395 100644
--- a/lib/fleximage/helper.rb
+++ b/lib/fleximage/helper.rb
@@ -1,6 +1,6 @@
module Fleximage
module Helper
-
+
# Creates an image tag that links directly to image data. Recommended for displays of a
# temporary upload that is not saved to a record in the databse yet.
def embedded_image_tag(model, options = {})
@@ -9,20 +9,20 @@ def embedded_image_tag(model, options = {})
mime = Mime::Type.lookup_by_extension(format.to_s).to_s
image = model.output_image(:format => format)
data = Base64.encode64(image)
-
+
options = { :alt => model.class.to_s }.merge(options)
-
+
result = image_tag("data:#{mime};base64,#{data}", options)
result.gsub(%r{src=".*/images/data:}, 'src="data:')
-
+
rescue Fleximage::Model::MasterImageNotFound => e
nil
end
-
+
# Creates a link that opens an image for editing in Aviary.
#
# Options:
- #
+ #
# * image_url: url to the master image used by Aviary for editing. Defauls to url_for(:action => 'aviary_image', :id => model, :only_path => false)
# * post_url: url where Aviary will post the updated image. Defauls to url_for(:action => 'aviary_image_update', :id => model, :only_path => false)
#
@@ -33,9 +33,9 @@ def link_to_edit_in_aviary(text, model, options = {})
post_url = options.delete(:image_update_url) || url_for(:action => 'aviary_image_update', :id => model, :only_path => false, :key => key)
api_key = Fleximage::AviaryController.api_key
url = "http://aviary.com/flash/aviary/index.aspx?tid=1&phoenix&apil=#{api_key}&loadurl=#{CGI.escape image_url}&posturl=#{CGI.escape post_url}"
-
+
link_to text, url, { :target => 'aviary' }.merge(options)
end
-
+
end
end
diff --git a/lib/fleximage/image_proxy.rb b/lib/fleximage/image_proxy.rb
index 2721ce6..ff00837 100644
--- a/lib/fleximage/image_proxy.rb
+++ b/lib/fleximage/image_proxy.rb
@@ -1,5 +1,5 @@
module Fleximage
-
+
# An instance of this class is yielded when Model#operate is called. It enables image operators
# to be called to transform the image. You should never need to directly deal with this class.
# You simply call image operators on this object when inside an Model#operate block
@@ -10,13 +10,13 @@ module Fleximage
#
# In this example, +image+ is an instance of ImageProxy
class ImageProxy
-
+
class OperatorNotFound < NameError #:nodoc:
end
-
+
# The image to be manipulated by operators.
attr_accessor :image
-
+
# Create a new image operator proxy.
def initialize(image, model_obj)
@image = image
@@ -32,24 +32,24 @@ def width
def height
@image.rows
end
-
+
# A call to an unknown method will look for an Operator by that method's name.
# If it finds one, it will execute that operator.
def method_missing(method_name, *args)
# Find the operator class
class_name = method_name.to_s.camelcase
operator_class = "Fleximage::Operator::#{class_name}".constantize
-
+
# Define a method for this operator so future calls to this operation are faster
self.class.module_eval <<-EOF
def #{method_name}(*args)
@image = execute_operator(#{operator_class}, *args)
end
EOF
-
+
# Call the method that was just defined to perform its functionality.
send(method_name, *args)
-
+
rescue NameError => e
if e.to_s =~ /uninitialized constant Fleximage::Operator::#{class_name}/
raise OperatorNotFound, "No operator Fleximage::Operator::#{class_name} found for the method \"#{method_name}\""
@@ -57,13 +57,13 @@ def #{method_name}(*args)
raise e
end
end
-
+
private
# Instantiate and execute the requested image Operator.
def execute_operator(operator_class, *args)
operator_class.new(self, @image, @model).execute(*args)
end
-
+
end
-
+
end
\ No newline at end of file
diff --git a/lib/fleximage/legacy_view.rb b/lib/fleximage/legacy_view.rb
index 1a2a7aa..4c36611 100644
--- a/lib/fleximage/legacy_view.rb
+++ b/lib/fleximage/legacy_view.rb
@@ -1,18 +1,18 @@
module Fleximage
-
+
# Renders a .flexi template
class LegacyView #:nodoc:
class TemplateDidNotReturnImage < RuntimeError #:nodoc:
end
-
+
def initialize(view)
@view = view
end
-
+
def render(template, local_assigns = {})
# process the view
result = @view.instance_eval do
-
+
# Shorthand color creation
def color(*args)
if args.size == 1 && args.first.is_a?(String)
@@ -21,40 +21,40 @@ def color(*args)
Magick::Pixel.new(*args)
end
end
-
+
# inject assigns into instance variables
assigns.each do |key, value|
instance_variable_set "@#{key}", value
end
-
+
# inject local assigns into reader methods
local_assigns.each do |key, value|
class << self; self; end.send(:define_method, key) { value }
end
-
+
#execute the template
eval(template)
end
-
+
# Raise an error if object returned from template is not an image record
unless result.class.include?(Fleximage::Model::InstanceMethods)
raise TemplateDidNotReturnImage, ".flexi template was expected to return a model instance that acts_as_fleximage, but got an instance of <#{result.class}> instead."
end
-
+
# Figure out the proper format
requested_format = (@view.params[:format] || :jpg).to_sym
raise 'Image must be requested with an image type format. jpg, gif and png only are supported.' unless [:jpg, :gif, :png].include?(requested_format)
-
+
# Set proper content type
@view.controller.headers["Content-Type"] = Mime::Type.lookup_by_extension(requested_format.to_s).to_s
-
+
# get rendered result
rendered_image = result.output_image(:format => requested_format)
-
+
# Return image data
return rendered_image
ensure
-
+
# ensure garbage collection happens after every flex image render
rendered_image.dispose!
GC.start
diff --git a/lib/fleximage/model.rb b/lib/fleximage/model.rb
index a1c96e7..8527a9c 100644
--- a/lib/fleximage/model.rb
+++ b/lib/fleximage/model.rb
@@ -1,40 +1,40 @@
module Fleximage
-
+
# Container for Fleximage model method inclusion modules
module Model
-
+
class MasterImageNotFound < RuntimeError #:nodoc:
end
-
+
# Include acts_as_fleximage class method
def self.included(base) #:nodoc:
base.extend(ClassMethods)
end
-
+
# Provides class methods for Fleximage for use in model classes. The only class method is
# acts_as_fleximage which integrates Fleximage functionality into a model class.
#
# The following class level accessors also get inserted.
#
- # * +image_directory+: (String, no default) Where the master images are stored, directory path relative to your
+ # * +image_directory+: (String, no default) Where the master images are stored, directory path relative to your
# app root.
- # * s3_bucket: Name of the bucket on Amazon S3 where your master images are stored. To use this you must
+ # * s3_bucket: Name of the bucket on Amazon S3 where your master images are stored. To use this you must
# call establish_connection! on the aws/s3 gem form your app's initilization to authenticate with your
# S3 account.
# * +use_creation_date_based_directories+: (Boolean, default +true+) If true, master images will be stored in
# directories based on creation date. For example: "#{image_directory}/2007/11/24/123.png" for an
# image with an id of 123 and a creation date of November 24, 2007. Turing this off would cause the path
- # to be "#{image_directory}/123.png" instead. This helps keep the OS from having directories that are too
+ # to be "#{image_directory}/123.png" instead. This helps keep the OS from having directories that are too
# full.
- # * +image_storage_format+: (:png or :jpg, default :png) The format of your master images. Using :png will give
- # you the best quality, since the master images as stored as lossless version of the original upload. :jpg
- # will apply lossy compression, but the master image file sizes will be much smaller. If storage space is a
+ # * +image_storage_format+: (:png or :jpg, default :png) The format of your master images. Using :png will give
+ # you the best quality, since the master images as stored as lossless version of the original upload. :jpg
+ # will apply lossy compression, but the master image file sizes will be much smaller. If storage space is a
# concern, us :jpg.
# * +require_image+: (Boolean, default +true+) The model will raise a validation error if no image is uploaded
# with the record. Setting to false allows record to be saved with no images.
- # * +missing_image_message+: (String, default "is required") Validation message to display when no image was uploaded for
+ # * +missing_image_message+: (String, default "is required") Validation message to display when no image was uploaded for
# a record.
- # * +invalid_image_message+: (String default "was not a readable image") Validation message when an image is uploaded, but is not an
+ # * +invalid_image_message+: (String default "was not a readable image") Validation message when an image is uploaded, but is not an
# image format that can be read by RMagick.
# * +output_image_jpg_quality+: (Integer, default 85) When rendering JPGs, this represents the amount of
# compression. Valid values are 0-100, where 0 is very small and very ugly, and 100 is near lossless but
@@ -45,13 +45,13 @@ def self.included(base) #:nodoc:
# :color => :transparent, where :size defines the dimensions of the default image, and :color
# defines the fill. :color can be a named color as a string ('red'), :transparent, or a Magick::Pixel object.
# * +preprocess_image+: (Block, no default) Call this class method just like you would call +operate+ in a view.
- # The image transoformation in the provided block will be run on every uploaded image before its saved as the
+ # The image transoformation in the provided block will be run on every uploaded image before its saved as the
# master image.
#
# Example:
#
# class Photo < ActiveRecord::Base
- # acts_as_fleximage do
+ # acts_as_fleximage do
# image_directory 'public/images/uploaded'
# use_creation_date_based_directories true
# image_storage_format :png
@@ -61,32 +61,32 @@ def self.included(base) #:nodoc:
# default_image_path 'public/images/no_photo_yet.png'
# default_image nil
# output_image_jpg_quality 85
- #
+ #
# preprocess_image do |image|
# image.resize '1024x768'
# end
# end
- #
+ #
# # normal model methods...
# end
module ClassMethods
-
- # Use this method to include Fleximage functionality in your model. It takes an
- # options hash with a single required key, :+image_directory+. This key should
+
+ # Use this method to include Fleximage functionality in your model. It takes an
+ # options hash with a single required key, :+image_directory+. This key should
# point to the directory you want your images stored on your server. Or
# configure with a nice looking block.
def acts_as_fleximage(options = {})
-
+
# Include the necesary instance methods
include Fleximage::Model::InstanceMethods
-
+
# Call this class method just like you would call +operate+ in a view.
- # The image transoformation in the provided block will be run on every uploaded image before its saved as the
+ # The image transoformation in the provided block will be run on every uploaded image before its saved as the
# master image.
def self.preprocess_image(&block)
preprocess_image_operation(block)
end
-
+
# Internal method to ask this class if it stores image in the DB.
def self.db_store?
return false if s3_store?
@@ -98,52 +98,52 @@ def self.db_store?
false
end
end
-
+
def self.s3_store?
!!s3_bucket
end
-
+
def self.file_store?
!db_store? && !s3_store?
end
-
+
def self.has_store?
respond_to?(:columns) && (db_store? || s3_store? || image_directory)
end
-
+
# validation callback
validate :validate_image if respond_to?(:validate)
-
+
# The filename of the temp image. Used for storing of good images when validation fails
# and the form needs to be redisplayed.
attr_reader :image_file_temp
-
+
# Setter for jpg compression quality at the instance level
attr_accessor :jpg_compression_quality
-
+
# Where images get stored
dsl_accessor :image_directory
-
+
# Amazon S3 bucket where the master images are stored
dsl_accessor :s3_bucket
-
+
# Put uploads from different days into different subdirectories
dsl_accessor :use_creation_date_based_directories, :default => true
-
+
# The format are master images are stored in
dsl_accessor :image_storage_format, :default => Proc.new { :png }
-
+
# Require a valid image. Defaults to true. Set to false if its ok to have no image for
dsl_accessor :require_image, :default => true
-
-
+
+
def self.translate_error_message(name, fallback, options = {})
translation = I18n.translate "activerecord.errors.models.#{self.model_name.underscore}.#{name}", options
if translation.match /translation missing:/
I18n.translate "activerecord.errors.messages.#{name}", options.merge({ :default => fallback })
end
end
-
+
# Missing image message
#dsl_accessor :missing_image_message, :default => 'is required'
def self.missing_image_message(str = nil)
@@ -153,13 +153,13 @@ def self.missing_image_message(str = nil)
else
translate_error_message("missing_image", "is required")
end
-
+
else
@missing_image_message = str
end
end
-
-
+
+
# Invalid image message
#dsl_accessor :invalid_image_message, :default => 'was not a readable image'
def self.invalid_image_message(str = nil)
@@ -173,7 +173,7 @@ def self.invalid_image_message(str = nil)
@invalid_image_message = str
end
end
-
+
# Image too small message
# Should include {{minimum}}
def self.image_too_small_message(str = nil)
@@ -189,36 +189,36 @@ def self.image_too_small_message(str = nil)
@image_too_small_message = str
end
end
-
+
# Sets the quality of rendered JPGs
dsl_accessor :output_image_jpg_quality, :default => 85
-
+
# Set a default image to use when no image has been assigned to this record
dsl_accessor :default_image_path
-
+
# Set a default image based on a a size and fill
dsl_accessor :default_image
-
+
# A block that processes an image before it gets saved as the master image of a record.
# Can be helpful to resize potentially huge images to something more manageable. Set via
# the "preprocess_image { |image| ... }" class method.
dsl_accessor :preprocess_image_operation
-
+
# Set a minimum size ([x, y] e.g. 200, '800x600', [800, 600])
# Set '0x600' to just enforce y size or
# '800x0' to just validate x size.
dsl_accessor :validates_image_size
-
+
# Image related save and destroy callbacks
if respond_to?(:before_save)
after_destroy :delete_image_file
before_save :pre_save
after_save :post_save
end
-
+
# execute configuration block
yield if block_given?
-
+
# Create S3 bucket if it's not present
if s3_bucket
begin
@@ -227,10 +227,10 @@ def self.image_too_small_message(str = nil)
AWS::S3::Bucket.create(s3_bucket)
end
end
-
+
# set the image directory from passed options
image_directory options[:image_directory] if options[:image_directory]
-
+
# Require the declaration of a master image storage directory
if respond_to?(:validate) && !image_directory && !db_store? && !s3_store? && !default_image && !default_image_path
raise "No place to put images! Declare this via the :image_directory => 'path/to/directory' option\n"+
@@ -239,31 +239,31 @@ def self.image_too_small_message(str = nil)
"Or set a default image to show with :default_image or :default_image_path"
end
end
-
+
def image_file_exists(file)
# File must be a valid object
return false if file.nil?
-
+
# Get the size of the file. file.size works for form-uploaded images, file.stat.size works
# for file object created by File.open('foo.jpg', 'rb'). It must have a size > 0.
return false if (file.respond_to?(:size) ? file.size : file.stat.size) <= 0
-
+
# object must respond to the read method to fetch its contents.
return false if !file.respond_to?(:read)
-
+
# file validation passed, return true
true
end
end
-
+
# Provides methods that every model instance that acts_as_fleximage needs.
module InstanceMethods
-
+
# Returns the path to the master image file for this record.
- #
+ #
# @some_image.directory_path #=> /var/www/myapp/uploaded_images
#
- # If this model has a created_at field, it will use a directory
+ # If this model has a created_at field, it will use a directory
# structure based on the creation date, to prevent hitting the OS imposed
# limit on the number files in a directory.
#
@@ -271,21 +271,21 @@ module InstanceMethods
def directory_path
directory = self.class.image_directory
raise 'No image directory was defined, cannot generate path' unless directory
-
+
# base directory
directory = "#{RAILS_ROOT}/#{directory}" unless /^\// =~ directory
-
+
# specific creation date based directory suffix.
creation = self[:created_at] || self[:created_on]
- if self.class.use_creation_date_based_directories && creation
+ if self.class.use_creation_date_based_directories && creation
"#{directory}/#{creation.year}/#{creation.month}/#{creation.day}"
else
directory
end
end
-
+
# Returns the path to the master image file for this record.
- #
+ #
# @some_image.file_path #=> /var/www/myapp/uploaded_images/123.png
def file_path
"#{directory_path}/#{id}.#{extension}"
@@ -305,16 +305,16 @@ def extension
self.class.image_storage_format
end
end
-
+
def url_format
extension.to_sym
end
- # Sets the image file for this record to an uploaded file. This can
- # be called directly, or passively like from an ActiveRecord mass
+ # Sets the image file for this record to an uploaded file. This can
+ # be called directly, or passively like from an ActiveRecord mass
# assignment.
- #
- # Rails will automatically call this method for you, in most of the
+ #
+ # Rails will automatically call this method for you, in most of the
# situations you would expect it to.
#
# # via mass assignment, the most common form you'll probably use
@@ -324,31 +324,31 @@ def url_format
# # via explicit assignment hash
# Photo.new(:image_file => params[:photo][:image_file])
# Photo.create(:image_file => params[:photo][:image_file])
- #
+ #
# # Direct Assignment, usually not needed
# photo = Photo.new
# photo.image_file = params[:photo][:image_file]
- #
+ #
# # via an association proxy
# p = Product.find(1)
# p.images.create(params[:photo])
def image_file=(file)
if self.class.image_file_exists(file)
-
+
# Create RMagick Image object from uploaded file
if file.path
@uploaded_image = Magick::Image.read(file.path).first
else
@uploaded_image = Magick::Image.from_blob(file.read).first
end
-
+
# Sanitize image data
@uploaded_image.colorspace = Magick::RGBColorspace
@uploaded_image.density = '72'
-
+
# Save meta data to database
set_magic_attributes(file)
-
+
# Success, make sure everything is valid
@invalid_image = false
save_temp_image(file) unless @dont_save_temp
@@ -370,10 +370,10 @@ def image_file=(file)
def image_file
has_image?
end
-
+
# Assign the image via a URL, which will make the plugin go
# and fetch the image at the provided URL. The image will be stored
- # locally as a master image for that record from then on. This is
+ # locally as a master image for that record from then on. This is
# intended to be used along side the image upload to allow people the
# choice to upload from their local machine, or pull from the internet.
#
@@ -382,25 +382,25 @@ def image_file_url=(file_url)
@image_file_url = file_url
if file_url =~ %r{^(https?|ftp)://}
file = open(file_url)
-
+
# Force a URL based file to have an original_filename
eval <<-CODE
def file.original_filename
"#{file_url}"
end
CODE
-
+
self.image_file = file
-
+
elsif file_url.empty?
# Nothing to process, move along
-
+
else
# invalid URL, raise invalid image validation error
@invalid_image = true
end
end
-
+
# Set the image for this record by reading in file data as a string.
#
# data = File.read('my_image_file.jpg')
@@ -428,7 +428,7 @@ def image_file_temp=(file_name)
if !@uploaded_image && file_name && file_name.present? && file_name !~ %r{\.\./}
@image_file_temp = file_name
file_path = "#{RAILS_ROOT}/tmp/fleximage/#{file_name}"
-
+
@dont_save_temp = true
if File.exists?(file_path)
File.open(file_path, 'rb') do |f|
@@ -444,12 +444,12 @@ def image_file_temp=(file_name)
def image_file_url
@image_file_url
end
-
+
# Return true if this record has an image.
def has_image?
@uploaded_image || @output_image || has_saved_image?
end
-
+
def has_saved_image?
if self.class.db_store?
!!image_file_data
@@ -459,11 +459,11 @@ def has_saved_image?
File.exists?(file_path)
end
end
-
- # Call from a .flexi view template. This enables the rendering of operators
+
+ # Call from a .flexi view template. This enables the rendering of operators
# so that you can transform your image. This is the method that is the foundation
# of .flexi views. Every view should consist of image manipulation code inside a
- # block passed to this method.
+ # block passed to this method.
#
# # app/views/photos/thumb.jpg.flexi
# @photo.operate do |image|
@@ -485,42 +485,42 @@ def operate!(&block)
save
end
- # Load the image from disk/DB, or return the cached and potentially
+ # Load the image from disk/DB, or return the cached and potentially
# processed output image.
def load_image #:nodoc:
@output_image ||= @uploaded_image
-
+
# Return the current image if we have loaded it already
return @output_image if @output_image
-
+
# Load the image from disk
if self.class.db_store?
# Load the image from the database column
if image_file_data && image_file_data.present?
@output_image = Magick::Image.from_blob(image_file_data).first
end
-
+
elsif self.class.s3_store?
# Load image from S3
filename = "#{id}.#{self.class.image_storage_format}"
bucket = self.class.s3_bucket
-
+
if AWS::S3::S3Object.exists?(filename, bucket)
@output_image = Magick::Image.from_blob(AWS::S3::S3Object.value(filename, bucket)).first
end
-
+
else
# Load the image from the disk
@output_image = Magick::Image.read(file_path).first
-
+
end
-
+
if @output_image
@output_image
else
master_image_not_found
end
-
+
rescue Magick::ImageMagickError => e
if e.to_s =~ /unable to open (file|image)/
master_image_not_found
@@ -528,7 +528,7 @@ def load_image #:nodoc:
raise e
end
end
-
+
# Convert the current output image to a jpg, and return it in binary form. options support a
# :format key that can be :jpg, :gif or :png
def output_image(options = {}) #:nodoc:
@@ -544,12 +544,12 @@ def output_image(options = {}) #:nodoc:
ensure
GC.start
end
-
- # Delete the image file for this record. This is automatically ran after this record gets
+
+ # Delete the image file for this record. This is automatically ran after this record gets
# destroyed, but you can call it manually if you want to remove the image from the record.
def delete_image_file
return unless self.class.has_store?
-
+
if self.class.db_store?
update_attribute :image_file_data, nil unless frozen?
elsif self.class.s3_store?
@@ -557,69 +557,69 @@ def delete_image_file
else
File.delete(file_path) if File.exists?(file_path)
end
-
+
clear_magic_attributes
-
+
self
end
-
+
# Execute image presence and validity validations.
def validate_image #:nodoc:
field_name = (@image_file_url && @image_file_url.present?) ? :image_file_url : :image_file
-
+
# Could not read the file as an image
if @invalid_image
errors.add field_name, self.class.invalid_image_message
-
+
# no image uploaded and one is required
elsif self.class.require_image && !has_image?
errors.add field_name, self.class.missing_image_message
-
+
# Image does not meet minimum size
elsif self.class.validates_image_size && !@uploaded_image.nil?
x, y = Fleximage::Operator::Base.size_to_xy(self.class.validates_image_size)
-
+
if @uploaded_image.columns < x || @uploaded_image.rows < y
errors.add field_name, self.class.image_too_small_message
end
-
+
end
end
-
+
private
# Perform pre save tasks. Preprocess the image, and write it to DB.
def pre_save
if @uploaded_image
# perform preprocessing
perform_preprocess_operation
-
+
# Convert to storage format
@uploaded_image.format = self.class.image_storage_format.to_s.upcase unless respond_to?(:image_format)
-
+
# Write image data to the DB field
if self.class.db_store?
self.image_file_data = @uploaded_image.to_blob
end
end
end
-
+
# Write image to file system/S3 and cleanup garbage.
def post_save
if @uploaded_image
if self.class.file_store?
# Make sure target directory exists
FileUtils.mkdir_p(directory_path)
-
+
# Write master image file
@uploaded_image.write(file_path)
-
+
elsif self.class.s3_store?
blob = StringIO.new(@uploaded_image.to_blob)
AWS::S3::S3Object.store("#{id}.#{self.class.image_storage_format}", blob, self.class.s3_bucket)
-
+
end
end
-
+
# Cleanup temp files
delete_temp_image
@@ -628,7 +628,7 @@ def post_save
GC.start
end
end
-
+
# Preprocess this image before saving
def perform_preprocess_operation
if self.class.preprocess_image_operation
@@ -637,7 +637,7 @@ def perform_preprocess_operation
@uploaded_image = @output_image
end
end
-
+
def clear_magic_attributes
unless frozen?
self.image_filename = nil if respond_to?(:image_filename=)
@@ -646,7 +646,7 @@ def clear_magic_attributes
self.image_format = nil if respond_to?(:image_format=)
end
end
-
+
# If any magic column names exists fill them with image meta data.
def set_magic_attributes(file = nil)
if file && self.respond_to?(:image_filename=)
@@ -658,7 +658,7 @@ def set_magic_attributes(file = nil)
self.image_height = @uploaded_image.rows if self.respond_to?(:image_height=)
self.image_format = @uploaded_image.format if self.respond_to?(:image_format=)
end
-
+
# Save the image in the rails tmp directory
def save_temp_image(file)
file_name = file.respond_to?(:original_filename) ? file.original_filename : file.path
@@ -670,42 +670,42 @@ def save_temp_image(file)
f.write file.read
end
end
-
+
# Delete the temp image after its no longer needed
def delete_temp_image
FileUtils.rm_rf "#{RAILS_ROOT}/tmp/fleximage/#{@image_file_temp}"
end
-
+
# Load the default image, or raise an expection
def master_image_not_found
# Load the default image from a path
if self.class.default_image_path
@output_image = Magick::Image.read("#{RAILS_ROOT}/#{self.class.default_image_path}").first
-
+
# Or create a default image
elsif self.class.default_image
x, y = Fleximage::Operator::Base.size_to_xy(self.class.default_image[:size])
color = self.class.default_image[:color]
-
+
@output_image = Magick::Image.new(x, y) do
self.background_color = color if color && color != :transparent
end
-
+
# No default, not master image, so raise exception
else
message = "Master image was not found for this record"
-
+
if !self.class.db_store?
message << "\nExpected image to be at:"
message << "\n #{file_path}"
end
-
+
raise MasterImageNotFound, message
end
ensure
GC.start
end
end
-
+
end
end
diff --git a/lib/fleximage/operator/background.rb b/lib/fleximage/operator/background.rb
index 445e3d8..8aa1ed0 100644
--- a/lib/fleximage/operator/background.rb
+++ b/lib/fleximage/operator/background.rb
@@ -8,28 +8,28 @@ module Operator
# * +color+: the color of the background image.
# Use an RMagick named color or use the +color+ method in Fleximage::Controller, or a
# Magick::Pixel object.
- #
- # * +size+: The size of the background image, in Fleximage *size* format.
+ #
+ # * +size+: The size of the background image, in Fleximage *size* format.
# By default the background image is the same size as the foreground image
- #
- # * +alignment+: A symbol that tells Fleximage where to put the foreground image on
+ #
+ # * +alignment+: A symbol that tells Fleximage where to put the foreground image on
# top of the background image. Can be any of the following:
# :center, :top, :top_right, :right, :bottom_right, :bottom, :bottom_left, :left, :top_left.
# Default is :+center+
- #
- # * +offset+: the number of pixels to offset the foreground image from it's :+alignment+ anchor, in FlexImage
+ #
+ # * +offset+: the number of pixels to offset the foreground image from it's :+alignment+ anchor, in FlexImage
# *size* format. Useful to give a bit a space between your image and the edge of the background, for instance.
# *NOTE:* Due to some unexpected (buggy?) RMagick behaviour :+offset+ will work strangely
# if :+alignment+ is set to a corner non-corner value, such as :+top+ or :+center+. Using :+offset+ in
# these cases will force the overlay into a corner anyway.
- #
- # * +blending+: The blending mode governs how the foreground image gets composited onto the background. You can
+ #
+ # * +blending+: The blending mode governs how the foreground image gets composited onto the background. You can
# get some funky effects with modes like :+copy_cyan+ or :+screen+. For a full list of blending
# modes checkout the RMagick documentation (http://www.simplesystems.org/RMagick/doc/constants.html#CompositeOperator).
# To use a blend mode remove the +CompositeOp+ form the name and "unserscorize" the rest. For instance,
# +MultiplyCompositeOp+ becomes :+multiply+, and +CopyBlackCompositeOp+ becomes :+copy_black+.
- class Background < Operator::Base
+ class Background < Operator::Base
def operate(options = {})
options = options.symbolize_keys
@@ -40,7 +40,7 @@ def operate(options = {})
width, height = options.key?(:size) ? size_to_xy(options[:size]) : [@image.columns, @image.rows]
#create the background image onto which we will composite the foreground image
- bg = Magick::Image.new(width, height) do
+ bg = Magick::Image.new(width, height) do
self.background_color = color
self.format = 'PNG'
end
diff --git a/lib/fleximage/operator/base.rb b/lib/fleximage/operator/base.rb
index 7ae904f..970cb61 100644
--- a/lib/fleximage/operator/base.rb
+++ b/lib/fleximage/operator/base.rb
@@ -1,21 +1,21 @@
module Fleximage
module Operator
-
+
class BadOperatorResult < Exception #:nodoc:
end
-
+
class OperationNotImplemented < Exception #:nodoc:
end
-
+
# The Operator::Base class is what all other Operator classes inherit from.
- # To write your own Operator class, simply inherit from this class, and
- # implement your own operate methods, with your own arguments. Just
+ # To write your own Operator class, simply inherit from this class, and
+ # implement your own operate methods, with your own arguments. Just
# return a new RMagick image object that represents the new image, and
# the model will be updated automatically.
#
# You have access to a few instance variables in the operate method:
#
- # * @image : The current image from the model. Use this is a starting
+ # * @image : The current image from the model. Use this is a starting
# point for all transformations.
# * @model : The model instance that this image transformation is happenining
# in. Use it to get data out of your model for display in your image.
@@ -26,47 +26,47 @@ def initialize(proxy, image, model_obj) #:nodoc:
@image = image
@model = model_obj
end
-
+
# Start the operation
def execute(*args) #:nodoc:
# Get the result of the Operators #operate method
result = operate(*args)
-
+
# Ensure that the result is an RMagick:Image object
unless result.is_a?(Magick::Image)
raise BadOperatorResult, "expected #{self.class}#operate to return an instance of Magick::Image. \n"+
"Got instance of #{result.class} instead."
end
-
+
# Save the result to the operator proxy
@proxy.image = result
end
-
+
# Perform the operation. Override this method in your Operator::Base subclasses
# in order to write your own image operators.
def operate(*args)
raise OperationNotImplemented, "Override this method in your own subclass."
end
-
+
# ---
# - SUPPORT METHODS
# ---
-
+
# Allows access to size conversion globally. See size_to_xy for a more detailed explanation
def self.size_to_xy(size)
- case
+ case
when size.is_a?(Array) && size.size == 2 # [320, 240]
size
-
+
when size.to_s.include?('x') # "320x240"
size.split('x').collect(&:to_i)
-
+
else # Anything else, convert the object to an integer and assume square dimensions
[size.to_i, size.to_i]
-
+
end
end
-
+
# This method will return a valid color Magick::Pixel object. It will also auto adjust
# for the bit depth of your ImageMagick configuration.
#
@@ -83,7 +83,7 @@ def self.color(*args)
if args.size == 1 && args.first.is_a?(String)
args.first
else
-
+
# adjust color to proper bit depth
if Magick::QuantumDepth != 8
max = case Magick::QuantumDepth
@@ -92,23 +92,23 @@ def self.color(*args)
when 32
4_294_967_295
end
-
+
args.map! do |value|
(value.to_f/255 * max).to_i
end
end
-
+
# create the pixel
Magick::Pixel.new(*args)
end
end
-
+
def color(*args)
self.class.color(*args)
end
-
+
# Converts a size object to an [x,y] array. Acceptible formats are:
- #
+ #
# * 10
# * "10"
# * "10x20"
@@ -120,8 +120,8 @@ def color(*args)
def size_to_xy(size)
self.class.size_to_xy size
end
-
- # Scale the image, respecting aspect ratio.
+
+ # Scale the image, respecting aspect ratio.
# Operation will happen in the main @image unless you supply the +img+ argument
# to operate on instead.
def scale(size, img = @image)
@@ -131,24 +131,24 @@ def scale(size, img = @image)
_img.resize!(cols, rows)
end
end
-
+
# Scale to the desired size and crop edges off to get the exact dimensions needed.
# Operation will happen in the main @image unless you supply the +img+ argument
# to operate on instead.
def scale_and_crop(size, img = @image)
img.crop_resized!(*size_to_xy(size))
end
-
- # Resize the image, with no respect to aspect ratio.
+
+ # Resize the image, with no respect to aspect ratio.
# Operation will happen in the main @image unless you supply the +img+ argument
# to operate on instead.
def stretch(size, img = @image)
img.resize!(*size_to_xy(size))
end
-
+
# Convert a symbol to an RMagick blending mode.
- #
- # The blending mode governs how the overlay gets composited onto the image. You can
+ #
+ # The blending mode governs how the overlay gets composited onto the image. You can
# get some funky effects with modes like :+copy_cyan+ or :+screen+. For a full list of blending
# modes checkout the RMagick documentation (http://www.simplesystems.org/RMagick/doc/constants.html#CompositeOperator).
# To use a blend mode remove the +CompositeOp+ form the name and "unserscorize" the rest. For instance,
@@ -158,20 +158,20 @@ def symbol_to_blending_mode(mode)
rescue NameError
raise ArgumentError, ":#{mode} is not a valid blending mode."
end
-
+
def symbol_to_gravity(gravity_name)
gravity = GRAVITIES[gravity_name]
-
+
if gravity
gravity
else
raise ArgumentError, ":#{gravity_name} is not a valid gravity name.\n\nValid names are :center, :top, :top_right, :right, :bottom_right, :bottom, :bottom_left, :left, :top_left"
end
end
-
-
+
+
end # Base
-
+
# Conversion table for mapping alignment symbols to their equivalent RMagick gravity constants.
GRAVITIES = {
:center => Magick::CenterGravity,
@@ -184,6 +184,6 @@ def symbol_to_gravity(gravity_name)
:left => Magick::WestGravity,
:top_left => Magick::NorthWestGravity,
} unless defined?(GRAVITIES)
-
+
end # Operator
end # Fleximage
\ No newline at end of file
diff --git a/lib/fleximage/operator/border.rb b/lib/fleximage/operator/border.rb
index 80a6680..75163f3 100644
--- a/lib/fleximage/operator/border.rb
+++ b/lib/fleximage/operator/border.rb
@@ -1,28 +1,28 @@
module Fleximage
module Operator
-
+
# Add a border to the outside of the image
#
# image.border(options = {})
- #
+ #
# Use the following keys in the +options+ hash:
#
# * +size+: Width of the border on each side. You can use a 2 dimensional value ('5x10') if you want
- # different widths for the sides and top borders, but a single integer will apply the same border on
+ # different widths for the sides and top borders, but a single integer will apply the same border on
# all sides.
- #
- # * +color+: the color of the border. Use an RMagick named color or use the +color+ method in
+ #
+ # * +color+: the color of the border. Use an RMagick named color or use the +color+ method in
# FlexImage::Controller, or a Magick::Pixel object.
- #
+ #
# Example:
- #
+ #
# @photo.operate do |image|
# # Defaults
# image.border(
# :size => 10,
# :color => 'white' # or color(255, 255, 255)
# )
- #
+ #
# # Big, pink and wide
# image.border(
# :size => '200x100',
@@ -37,7 +37,7 @@ def operate(options = {})
:color => 'white'
}
options = options.is_a?(Hash) ? defaults.update(options) : defaults
-
+
# Get border size
options[:size] = size_to_xy(options[:size])
@@ -45,6 +45,6 @@ def operate(options = {})
@image.border!(options[:size][0], options[:size][1], options[:color])
end
end
-
+
end
end
\ No newline at end of file
diff --git a/lib/fleximage/operator/crop.rb b/lib/fleximage/operator/crop.rb
index f55377a..3ff44aa 100644
--- a/lib/fleximage/operator/crop.rb
+++ b/lib/fleximage/operator/crop.rb
@@ -1,9 +1,9 @@
module Fleximage
module Operator
-
+
# Crops the image without doing any resizing first. The operation crops from the :+from+ coordinate,
# and returns an image of size :+size+ down and right from there.
- #
+ #
# image.crop(options = {})
#
# Use the following keys in the +options+ hash:
@@ -12,7 +12,7 @@ module Operator
# Choose from GRAVITITES constant defined in base.rb.
# * +from+: coorinates for the upper left corner of resulting image.
# * +size+: The size of the resulting image, going down and to the right of the :+from+ coordinate.
- #
+ #
# size and from options are *required*.
#
# Example:
@@ -53,6 +53,6 @@ def operate(options = {})
@image.crop!(symbol_to_gravity(options[:gravity]), options[:from][0], options[:from][1], options[:size][0], options[:size][1], true)
end
end
-
+
end
end
\ No newline at end of file
diff --git a/lib/fleximage/operator/image_overlay.rb b/lib/fleximage/operator/image_overlay.rb
index 058b40d..5186060 100644
--- a/lib/fleximage/operator/image_overlay.rb
+++ b/lib/fleximage/operator/image_overlay.rb
@@ -1,46 +1,46 @@
module Fleximage
module Operator
-
+
# Adds an overlay to the base image. It's useful for things like attaching a logo,
# watermark, or even a border to the image. It will work best with a 24-bit PNG with
# alpha channel since it will properly deal with partial transparency.
- #
+ #
# image.resize(image_overlay_path, options = {})
- #
+ #
# +image_overlay_path+ is the path, relative to +RAILS_ROOT+ where the image you want superimposed
# can be found.
- #
+ #
# Use the following keys in the +options+ hash:
- #
- # * +size+: The size of the overlayed image, as '123x456' or [123, 456].
+ #
+ # * +size+: The size of the overlayed image, as '123x456' or [123, 456].
# By default the overlay is not resized before compositing.
# Use this options if you want to resize the overlay, perhaps to have a small
# logo on thumbnails and a big logo on full size images. Other than just numerical dimensions, the
- # size parameter takes 2 special values :+scale_to_fit+ and :+stretch_to_fit+. :+scale_to_fit+ will
- # make the overlay fit as
+ # size parameter takes 2 special values :+scale_to_fit+ and :+stretch_to_fit+. :+scale_to_fit+ will
+ # make the overlay fit as
# much as it can inside the image without changing the aspect ratio. :+stretch_to_fit+
# will make the overlay the exact same size as the image but with a distorted aspect ratio to make
# it fit. :+stretch_to_fit+ is designed to add border to images.
- #
+ #
# * +alignment+: A symbol that tells Fleximage where to put the overlay. Can be any of the following:
# :center, :top, :top_right, :right, :bottom_right, :bottom, :bottom_left, :left, :top_left.
# Default is :+center+
- #
- # * +offset+: the number of pixels to offset the overlay from it's :+alignment+ anchor, in
- # '123x456' or [123, 456] format. Useful to give a bit a space between your logo
+ #
+ # * +offset+: the number of pixels to offset the overlay from it's :+alignment+ anchor, in
+ # '123x456' or [123, 456] format. Useful to give a bit a space between your logo
# and the edge of the image, for instance.
# *NOTE:* Due to some unexpected (buggy?) RMagick behaviour :+offset+ will work strangely
# if :+alignment+ is set to a non-corner value, such as :+top+ or :+center+. Using :+offset+ in
# these cases will force the overlay into a corner anyway.
- #
- # * +blending+: The blending mode governs how the overlay gets composited onto the image. You can
+ #
+ # * +blending+: The blending mode governs how the overlay gets composited onto the image. You can
# get some funky effects with modes like :+copy_cyan+ or :+screen+. For a full list of blending
# modes checkout the RMagick documentation (http://www.simplesystems.org/RMagick/doc/constants.html#CompositeOperator).
# To use a blend mode remove the +CompositeOp+ form the name and "unserscorize" the rest. For instance,
# +MultiplyCompositeOp+ becomes :+multiply+, and +CopyBlackCompositeOp+ becomes :+copy_black+.
#
# Example:
- #
+ #
# @photo.operate do |image|
# image.image_overlay('images/my_logo_with_alpha.png',
# :size => '25x25',
@@ -51,10 +51,10 @@ module Operator
class ImageOverlay < Operator::Base
def operate(image_overlay_path, options = {})
options = options.symbolize_keys
-
+
#load overlay
overlay = Magick::Image.read(image_overlay_path).first
-
+
#resize overlay
if options[:size]
if options[:size] == :scale_to_fit || options[:size] == :stretch_to_fit
@@ -62,24 +62,24 @@ def operate(image_overlay_path, options = {})
else
x, y = size_to_xy(options[:size])
end
-
+
method = options[:size] == :stretch_to_fit ? :stretch : :scale
send(method, [x, y], overlay)
end
-
+
#prepare arguments for composite!
args = []
args << overlay #overlay image
args << symbol_to_gravity(options[:alignment] || :center) #gravity
args += size_to_xy(options[:offset]) if options[:offset] #offset
args << symbol_to_blending_mode(options[:blending] || :over) #compositing mode
-
+
#composite
@image.composite!(*args)
-
+
return @image
end
end
-
+
end
end
\ No newline at end of file
diff --git a/lib/fleximage/operator/resize.rb b/lib/fleximage/operator/resize.rb
index 6040eac..bac85e3 100644
--- a/lib/fleximage/operator/resize.rb
+++ b/lib/fleximage/operator/resize.rb
@@ -1,44 +1,44 @@
module Fleximage
module Operator
-
+
# Resize this image, constraining proportions. Options allow cropping, stretching, upsampling and
# padding.
- #
+ #
# image.resize(size, options = {})
- #
+ #
# +size+ is size of the output image after the resize operation. Accepts either '123x456'
# format or [123, 456] format.
#
# Use the following keys in the +options+ hash:
- #
- # * +crop+: pass true to this option to make the ouput image exactly
+ #
+ # * +crop+: pass true to this option to make the ouput image exactly
# the same dimensions as +size+. The default behaviour will resize the image without
# cropping any part meaning the image will be no bigger than the +size+. When :crop
- # is true the final image is resized to fit as much as possible in the frame, and then crops it
+ # is true the final image is resized to fit as much as possible in the frame, and then crops it
# to make it exactly the dimensions declared by the +size+ argument.
- #
- # * +upsample+: By default the image will never display larger than its original dimensions,
+ #
+ # * +upsample+: By default the image will never display larger than its original dimensions,
# no matter how large the +size+ argument is. Pass +true+ to use this option to allow
# upsampling, disabling the default behaviour.
#
# * +padding+: This option will pad the space around your image with a solid color to make it exactly the requested
- # size. Pass +true+, for the default of +white+, or give it a text or pixel color like "red" or
- # color(255, 127, 0). This is like the opposite of the +crop+ option. Instead of trimming the
- # image to make it exactly the requested size, it will make sure the entire image is visible, but adds space
+ # size. Pass +true+, for the default of +white+, or give it a text or pixel color like "red" or
+ # color(255, 127, 0). This is like the opposite of the +crop+ option. Instead of trimming the
+ # image to make it exactly the requested size, it will make sure the entire image is visible, but adds space
# around the edges to make it the right dimensions.
#
# * +stretch+: Set this option to true and the image will not preserve its aspect ratio. The final image will
# stretch to fit the requested +size+. The resulting image is exactly the size you ask for.
- #
+ #
# Example:
- #
+ #
# @photo.operate do |image|
# image.resize '200x200', :crop => true
# end
class Resize < Operator::Base
def operate(size, options = {})
options = options.symbolize_keys
-
+
# Find dimensions
x, y = size_to_xy(size)
@@ -69,7 +69,7 @@ def operate(size, options = {})
# get color
padding_color = 'white' if padding_color == true
- # get original x and y. This makes it play nice if the requested size is larger
+ # get original x and y. This makes it play nice if the requested size is larger
# than the image and upsampling is not allowed.
x, y = size_to_xy(size)
@@ -83,10 +83,10 @@ def operate(size, options = {})
# crop to remove possible extra pixel
@image.crop!(0, 0, x, y, true)
end
-
+
return @image
end
end
-
+
end
end
\ No newline at end of file
diff --git a/lib/fleximage/operator/shadow.rb b/lib/fleximage/operator/shadow.rb
index deca029..7c69745 100644
--- a/lib/fleximage/operator/shadow.rb
+++ b/lib/fleximage/operator/shadow.rb
@@ -1,30 +1,30 @@
module Fleximage
module Operator
-
+
# Add a drop shadow to the image.
#
# image.shadow(options = {})
- #
+ #
# Use the following keys in the +options+ hash:
#
# * +offset+: distance of the dropsahdow form the image, in FlexImage *size* format. Positive
# number move it down and right, negative numbers move it up and left.
- #
+ #
# * +blur+: how blurry the shadow is. Roughly corresponds to distance in pixels of the blur.
- #
- # * +background+: a color for the background of the image. What the shadow fades into.
+ #
+ # * +background+: a color for the background of the image. What the shadow fades into.
# Use an RMagick named color or use the +color+ method in FlexImage::Controller, or a
# Magick::Pixel object.
- #
+ #
# * +color+: color of the shadow itself.
# Use an RMagick named color or use the +color+ method in FlexImage::Controller, or a
# Magick::Pixel object.
- #
+ #
# * +opacity+: opacity of the shadow. A value between 0.0 and 1.0, where 1 is opaque and 0 is
# transparent.
- #
+ #
# Example:
- #
+ #
# @photo.operate do |image|
# # Default settings
# image.shadow(
@@ -32,9 +32,9 @@ module Operator
# :background => 'white', # or color(255, 255, 255)
# :blur => 8,
# :offset => '2x2',
- # :opacity => 0.75
+ # :opacity => 0.75
# )
- #
+ #
# # Huge, red shadow
# image.shadow(
# :color => color(255, 0, 0),
@@ -73,15 +73,15 @@ def operate(options = {})
# apply shadow
@image = shadow.composite(
- @image,
- symbol_to_gravity(:top_left),
- (shadow.columns - @image.columns) / 2 - options[:offset][0],
- (shadow.rows - @image.rows) / 2 - options[:offset][1],
+ @image,
+ symbol_to_gravity(:top_left),
+ (shadow.columns - @image.columns) / 2 - options[:offset][0],
+ (shadow.rows - @image.rows) / 2 - options[:offset][1],
symbol_to_blending_mode(:over)
)
@image.trim!
end
end
-
+
end
end
\ No newline at end of file
diff --git a/lib/fleximage/operator/text.rb b/lib/fleximage/operator/text.rb
index 26ba496..70dd601 100644
--- a/lib/fleximage/operator/text.rb
+++ b/lib/fleximage/operator/text.rb
@@ -1,12 +1,12 @@
module Fleximage
module Operator
-
+
# Draw text on the image. Customize size, position, color, dropshadow, and font.
- #
+ #
# image.text(string_to_write, options = {})
#
# Use the following keys in the +options+ hash:
- #
+ #
# * alignment: symbol like in ImageOverlay
# * offset: size string
# * antialias: true or false
@@ -66,7 +66,7 @@ def operate(string_to_write, options = {})
text.pointsize = options[:font_size].to_i
text.rotation = options[:rotate]
text.font_weight = options[:font_weight] if options[:font_weight]
-
+
if options[:stroke][:width] > 0
text.stroke_width = options[:stroke][:width]
text.stroke = options[:stroke][:color]
@@ -99,6 +99,6 @@ def operate(string_to_write, options = {})
@image.composite!(temp_image, 0, 0, symbol_to_blending_mode(:over))
end
end
-
+
end
end
\ No newline at end of file
diff --git a/lib/fleximage/operator/trim.rb b/lib/fleximage/operator/trim.rb
index 857a320..6def64e 100644
--- a/lib/fleximage/operator/trim.rb
+++ b/lib/fleximage/operator/trim.rb
@@ -1,6 +1,6 @@
module Fleximage
module Operator
-
+
# Trim off all the pixels around the image border that have the same color.
#
# image.trim
@@ -9,6 +9,6 @@ def operate()
@image.trim!(true)
end
end
-
+
end
end
\ No newline at end of file
diff --git a/lib/fleximage/operator/unsharp_mask.rb b/lib/fleximage/operator/unsharp_mask.rb
index 5aa8c80..e9c539e 100644
--- a/lib/fleximage/operator/unsharp_mask.rb
+++ b/lib/fleximage/operator/unsharp_mask.rb
@@ -1,8 +1,8 @@
module Fleximage
module Operator
-
+
# Sharpen an image using an unsharp mask filter.
- #
+ #
# image.unsharp_mask(options = {})
#
# Use the following keys in the +options+ hash:
@@ -31,6 +31,6 @@ def operate(options = {})
@image = @image.unsharp_mask(options[:radius], options[:sigma], options[:amount], options[:threshold])
end
end
-
+
end
end
\ No newline at end of file
diff --git a/lib/fleximage/view.rb b/lib/fleximage/view.rb
index 6a86f82..4ee8f1b 100644
--- a/lib/fleximage/view.rb
+++ b/lib/fleximage/view.rb
@@ -1,10 +1,10 @@
module Fleximage
-
+
# Renders a .flexi template
class View < ActionView::TemplateHandler #:nodoc:
class TemplateDidNotReturnImage < RuntimeError #:nodoc:
end
-
+
def self.call(template)
"Fleximage::View.new(self).render(template)"
end
@@ -12,44 +12,44 @@ def self.call(template)
def initialize(action_view)
@view = action_view
end
-
+
def render(template)
# process the view
result = @view.instance_eval do
-
+
# Shorthand color creation
def color(*args)
Fleximage::Operator::Base.color(*args)
end
-
+
#execute the template
eval(template.source)
end
-
+
# Raise an error if object returned from template is not an image record
unless result.class.include?(Fleximage::Model::InstanceMethods)
- raise TemplateDidNotReturnImage,
+ raise TemplateDidNotReturnImage,
".flexi template was expected to return a model instance that acts_as_fleximage, but got an instance of <#{result.class}> instead."
end
-
+
# Figure out the proper format
requested_format = (@view.params[:format] || :jpg).to_sym
unless [:jpg, :gif, :png].include?(requested_format)
raise 'Image must be requested with an image type format. jpg, gif and png only are supported.'
end
-
+
# Set proper content type
@view.controller.response.content_type = Mime::Type.lookup_by_extension(requested_format.to_s).to_s
-
+
# Set proper caching headers
if defined?(Rails) && Rails.env == 'production'
@view.controller.response.headers['Cache-Control'] = 'public, max-age=86400'
end
-
+
# return rendered result
return result.output_image(:format => requested_format)
ensure
-
+
# ensure garbage collection happens after every flex image render
GC.start
end
diff --git a/tasks/fleximage_tasks.rake b/tasks/fleximage_tasks.rake
index a88ea2c..4945d28 100644
--- a/tasks/fleximage_tasks.rake
+++ b/tasks/fleximage_tasks.rake
@@ -1,11 +1,11 @@
namespace :fleximage do
-
+
# Find the model class
def model_class
raise 'You must specify a FLEXIMAGE_CLASS=MyClass' unless ENV['FLEXIMAGE_CLASS']
@model_class ||= ENV['FLEXIMAGE_CLASS'].camelcase.constantize
end
-
+
desc "Populate width and height magic columns from the current image store. Useful when migrating from on old installation."
task :dimensions => :environment do
model_class.find(:all).each do |obj|
@@ -16,19 +16,19 @@ namespace :fleximage do
end
end
end
-
+
namespace :convert do
-
+
def convert_directory_format(to_format)
model_class.find(:all).each do |obj|
-
+
# Get the creation date
creation = obj[:created_at] || obj[:created_on]
-
+
# Generate both types of file paths
flat_path = "#{RAILS_ROOT}/#{model_class.image_directory}/#{obj.id}.#{model_class.image_storage_format}"
- nested_path = "#{RAILS_ROOT}/#{model_class.image_directory}/#{creation.year}/#{creation.month}/#{creation.day}/#{obj.id}.#{model_class.image_storage_format}"
-
+ nested_path = "#{RAILS_ROOT}/#{model_class.image_directory}/#{creation.year}/#{creation.month}/#{creation.day}/#{obj.id}.#{model_class.image_storage_format}"
+
# Assign old path and new path based on desired directory format
if to_format == :nested
old_path = flat_path
@@ -37,7 +37,7 @@ namespace :fleximage do
old_path = nested_path
new_path = flat_path
end
-
+
# Move the files
if old_path != new_path && File.exists?(old_path)
FileUtils.mkdir_p(File.dirname(new_path))
@@ -46,10 +46,10 @@ namespace :fleximage do
end
end
end
-
+
def convert_image_format(to_format)
model_class.find(:all).each do |obj|
-
+
# convert DB stored images
if model_class.db_store?
if obj.image_file_data && obj.image_file_data.any?
@@ -62,16 +62,16 @@ namespace :fleximage do
puts "Could not convert image for #{model_class} with id #{obj.id}\n #{e.class} #{e}\n"
end
end
-
+
# Convert file system stored images
- else
+ else
# Generate both types of file paths
png_path = obj.file_path.gsub(/\.jpg$/, '.png')
jpg_path = obj.file_path.gsub(/\.png$/, '.jpg')
-
+
# Output stub
output = (to_format == :jpg) ? 'PNG -> JPG' : 'JPG -> PNG'
-
+
# Assign old path and new path based on desired image format
if to_format == :jpg
old_path = png_path
@@ -80,47 +80,47 @@ namespace :fleximage do
old_path = jpg_path
new_path = png_path
end
-
+
# Perform conversion
if File.exists?(old_path)
image = Magick::Image.read(old_path).first
image.format = to_format.to_s.upcase
image.write(new_path)
File.delete(old_path)
-
+
puts "#{output} : Image #{obj.id}"
end
end
end
end
-
+
def ensure_db_store
col = model_class.columns.find {|c| c.name == 'image_file_data'}
unless col && col.type == :binary
raise "No image_file_data field of type :binary for this model!"
end
end
-
+
desc "Convert a flat images/123.png style image store to a images/2007/11/12/123.png style. Requires FLEXIMAGE_CLASS=ModelName"
task :to_nested => :environment do
convert_directory_format :nested
end
-
+
desc "Convert a nested images/2007/11/12/123.png style image store to a images/123.png style. Requires FLEXIMAGE_CLASS=ModelName"
task :to_flat => :environment do
convert_directory_format :flat
end
-
+
desc "Convert master images stored as JPGs to PNGs"
task :to_png => :environment do
convert_image_format :png
end
-
+
desc "Convert master images stored as PNGs to JPGs"
task :to_jpg => :environment do
convert_image_format :jpg
end
-
+
desc "Convert master image storage to use the database. Loads all file-stored images into the database."
task :to_db => :environment do
ensure_db_store
@@ -132,10 +132,10 @@ namespace :fleximage do
end
end
end
-
+
puts "--- All images successfully moved to the database. Check to make sure the transfer worked cleanly before deleting your file system image store."
end
-
+
desc "Convert master image storage to use the file system. Loads all database images into files."
task :to_filestore => :environment do
ensure_db_store
@@ -146,9 +146,9 @@ namespace :fleximage do
end
end
end
-
+
puts "--- All images successfully moved to the file system. Remember to remove your image_file_data field from your models database table."
end
-
+
end
end
diff --git a/test/mock_file.rb b/test/mock_file.rb
index 17dea65..6ef212b 100644
--- a/test/mock_file.rb
+++ b/test/mock_file.rb
@@ -4,18 +4,18 @@ class MockFile
def initialize(path)
@path = path
end
-
+
def size
1
end
-
+
def original_filename
@path.split('/').last
end
-
+
def read
File.open(@path) { |f| f.read }
end
-
+
def rewind; end
end
\ No newline at end of file
diff --git a/test/rails_root/app/models/abstract.rb b/test/rails_root/app/models/abstract.rb
index 00360b1..ea5f807 100644
--- a/test/rails_root/app/models/abstract.rb
+++ b/test/rails_root/app/models/abstract.rb
@@ -1,6 +1,6 @@
class Abstract < ActiveRecord::Base
set_table_name :photo_dbs
-
+
acts_as_fleximage do
require_image false
default_image :size => '320x240', :color => 'red'
diff --git a/test/rails_root/config/environment.rb b/test/rails_root/config/environment.rb
index 166187f..9bed49d 100644
--- a/test/rails_root/config/environment.rb
+++ b/test/rails_root/config/environment.rb
@@ -20,7 +20,7 @@
# To use Rails without a database, you must remove the Active Record framework
# config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
- # Only load the plugins named here, in the order given. By default, all plugins
+ # Only load the plugins named here, in the order given. By default, all plugins
# in vendor/plugins are loaded in alphabetical order.
# :all can be used as a placeholder for all plugins not explicitly named
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
@@ -34,7 +34,7 @@
# Your secret key for verifying cookie session data integrity.
# If you change this key, all old sessions will become invalid!
- # Make sure the secret is at least 30 characters and all random,
+ # Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
config.action_controller.session = {
:session_key => '_rails_root_session',
@@ -56,7 +56,7 @@
# Make Active Record use UTC-base instead of local time
# config.active_record.default_timezone = :utc
-
+
config.log_level = :debug
config.cache_classes = false
config.whiny_nils = true
diff --git a/test/rails_root/config/initializers/inflections.rb b/test/rails_root/config/initializers/inflections.rb
index 09158b8..5261cd1 100644
--- a/test/rails_root/config/initializers/inflections.rb
+++ b/test/rails_root/config/initializers/inflections.rb
@@ -1,6 +1,6 @@
# Be sure to restart your server when you modify this file.
-# Add new inflection rules using the following format
+# Add new inflection rules using the following format
# (all these examples are active by default):
# Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
diff --git a/test/rails_root/public/.htaccess b/test/rails_root/public/.htaccess
index d9d211c..1d37bd9 100644
--- a/test/rails_root/public/.htaccess
+++ b/test/rails_root/public/.htaccess
@@ -5,14 +5,14 @@ Options +FollowSymLinks +ExecCGI
# If you don't want Rails to look in certain directories,
# use the following rewrite rules so that Apache won't rewrite certain requests
-#
+#
# Example:
# RewriteCond %{REQUEST_URI} ^/notrails.*
# RewriteRule .* - [L]
# Redirect all requests not available on the filesystem to Rails
# By default the cgi dispatcher is used which is very slow
-#
+#
# For better performance replace the dispatcher with the fastcgi one
#
# Example:
@@ -33,7 +33,7 @@ RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
# In case Rails experiences terminal errors
# Instead of displaying this message you can supply a file here which will be rendered instead
-#
+#
# Example:
# ErrorDocument 500 /500.html
diff --git a/test/rails_root/public/index.html b/test/rails_root/public/index.html
index 84b7b57..be32234 100644
--- a/test/rails_root/public/index.html
+++ b/test/rails_root/public/index.html
@@ -14,20 +14,20 @@
font-size: 13px;
color: #333;
}
-
+
h1 {
font-size: 28px;
color: #000;
}
-
+
a {color: #03c}
a:hover {
background-color: #03c;
color: white;
text-decoration: none;
}
-
-
+
+
#page {
background-color: #f0f0f0;
width: 750px;
@@ -35,7 +35,7 @@
margin-left: auto;
margin-right: auto;
}
-
+
#content {
float: left;
background-color: white;
@@ -44,7 +44,7 @@
padding: 25px;
width: 500px;
}
-
+
#sidebar {
float: right;
width: 175px;
@@ -53,7 +53,7 @@
#footer {
clear: both;
}
-
+
#header, #about, #getting-started {
padding-left: 75px;
@@ -73,14 +73,14 @@
font-weight: normal;
font-size: 16px;
}
-
-
+
+
#about h3 {
margin: 0;
margin-bottom: 10px;
font-size: 14px;
}
-
+
#about-content {
background-color: #ffd;
border: 1px solid #fc0;
@@ -99,7 +99,7 @@
}
#about-content td.name {color: #555}
#about-content td.value {color: #000}
-
+
#about-content.failure {
background-color: #fcc;
border: 1px solid #f00;
@@ -108,8 +108,8 @@
margin: 0;
padding: 10px;
}
-
-
+
+
#getting-started {
border-top: 1px solid #ccc;
margin-top: 25px;
@@ -145,8 +145,8 @@
color: #555;
font-size: 13px;
}
-
-
+
+
#search {
margin: 0;
padding-top: 10px;
@@ -158,8 +158,8 @@
margin: 2px;
}
#search-text {width: 170px}
-
-
+
+
#sidebar ul {
margin-left: 0;
padding-left: 0;
@@ -176,7 +176,7 @@
#sidebar ul.links li {
margin-bottom: 5px;
}
-
+
@@ -189,11 +189,11 @@
onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
});
} else {
- new Effect[Element.visible('about-content') ?
+ new Effect[Element.visible('about-content') ?
'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
}
}
-
+
window.onload = function() {
$('search-text').value = '';
$('search').onsubmit = function() {
@@ -213,7 +213,7 @@
the Rails site
-
+