Rails Paperclip how to use filter options of ImageMagick? - ruby-on-rails

I recently implemented Paperclip with Rails and want to try out some of the filter options from ImageMagick such as blur. I've not been able to find any examples of how to do this. Does it get passed through :style as another option?
:styles => { :medium => "300x300#", :thumb => "100x100#" }
#plang's answer was correct but I wanted to give the exact solution to the blur, just in case someone was looking and found this question:
:convert_options => { :all => "-blur 0x8" }
// -blur {radius}x{sigma}
Which changed this:
To this:

I did not test this, but you should be able to use the "convert_options" parameter, like this:
:convert_options => { :all => ‘-colorspace Gray’ }
Have a look at https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/thumbnail.rb
I personnaly use my own processor.
In Model:
has_attached_file :logo,
:url => PaperclipAssetsController.config_url,
:path => PaperclipAssetsController.config_path,
:styles => {
:grayscale => { :processors => [:grayscale] }
}
In lib:
module Paperclip
# Handles grayscale conversion of images that are uploaded.
class Grayscale < Processor
def initialize file, options = {}, attachment = nil
super
#format = File.extname(#file.path)
#basename = File.basename(#file.path, #format)
end
def make
src = #file
dst = Tempfile.new([#basename, #format])
dst.binmode
begin
parameters = []
parameters << ":source"
parameters << "-colorspace Gray"
parameters << ":dest"
parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
success = Paperclip.run("convert", parameters, :source => "#{File.expand_path(src.path)}[0]", :dest => File.expand_path(dst.path))
rescue PaperclipCommandLineError => e
raise PaperclipError, "There was an error during the grayscale conversion for #{#basename}" if #whiny
end
dst
end
end
end
This might not be 100% necessary for a simple grayscale conversion, but it works!

Rails 5, Paperclip 5 update
Instead of having to add a library now, you can just call out to ImageMagick's convert command on the system to use its grayscale option. You can do the same for blur or any of the other ImageMagick options, but I needed to do this for conversion to grayscale.
In your model (client that has a logo):
class Client < ApplicationRecord
has_attached_file :logo,
styles: { thumb: "243x243#", grayscale: "243x243#" }
# ensure it's an image
validates_attachment_content_type :logo, content_type: /\Aimage\/.*\z/
# optional, just for name and url to be required
validates :name, presence: true
validates :url, presence: true
after_save :convert_grayscale
def convert_grayscale
system "convert #{self.logo.path(:thumb)} -grayscale Rec709Luminance #{self.logo.path(:grayscale)}"
end
def logo_attached?
self.logo.file?
end
end
Then just use in the view like this (per Paperclips github docs).
In your view:
<%= image_tag(client.logo.url(:grayscale), class: 'thumbnail', alt: client.name, title: client.name) %>
or with a link if you prefer:
<%= link_to(image_tag(client.logo.url(:grayscale), class: 'thumbnail', alt: client.name, title: client.name), client.url ) %>

Related

Why image_tag doesn't show image while link_to shows?

I want to display image in my cart via paperclip gem but it won't works with image_tag while with link_to work fine
This works:
<% simple_current_order.line_items.all.each do |line_item| %>
<%= link_to mini_image(line_item.variant)), product_path(line_item) %>
<% end %>
While this not
<% simple_current_order.line_items.all.each do |line_item| %>
<%= image_tag mini_image(line_item.variant) %>
<% end %>
image.rb model
module Spree
class Image < Asset
validate :no_attachment_errors
has_attached_file :attachment,
styles: { mini: '48x48>', small: '100x100>', product: '240x240>', large: '600x600>' },
default_style: :product,
url: '/spree/products/:id/:style/:basename.:extension',
path: ':rails_root/public/spree/products/:id/:style/:basename.:extension',
convert_options: { all: '-strip -auto-orient -colorspace sRGB' }
validates_attachment :attachment,
:presence => true,
:content_type => { :content_type => %w(image/jpeg image/jpg image/png image/gif) }
# save the w,h of the original image (from which others can be calculated)
# we need to look at the write-queue for images which have not been saved yet
after_post_process :find_dimensions
#used by admin products autocomplete
def mini_url
attachment.url(:mini, false)
end
def find_dimensions
temporary = attachment.queued_for_write[:original]
filename = temporary.path unless temporary.nil?
filename = attachment.path if filename.blank?
geometry = Paperclip::Geometry.from_file(filename)
self.attachment_width = geometry.width
self.attachment_height = geometry.height
end
# if there are errors from the plugin, then add a more meaningful message
def no_attachment_errors
unless attachment.errors.empty?
# uncomment this to get rid of the less-than-useful interim messages
# errors.clear
errors.add :attachment, "Paperclip returned errors for file '#{attachment_file_name}' - check ImageMagick installation or image source file."
false
end
end
end
end
It can't find image with image tag, anyone know why?

Generate Thumbnail From pdf in rails paperclip

How can I generate the first page of a pdf as a thumbnail in paperclip?
I tried a lot but it's not working
has_attached_file :book_url, :styles => {
:thumb => "100x100#",
:small => "150x150>",
:medium => "200x200" }
This is giving the name of the pdf as a link but it's not giving the first page of the pdf
<%= link_to 'My PDF', #book.book_url.url %>
Tadas' answer is right, but for those who need more context, you can do something like this: The model below only creates thumbnails for certain kinds of files (e.g. it doesn't make thumbnails of audio files), but does make thumbnails for pdfs, image files, and video files:
class Record < ActiveRecord::Base
print self # for logging on heroku
belongs_to :user
# Ensure user has provided the required fields
validates :title, presence: true
validates :file_upload, presence: true
validates :description, presence: true
# Use the has_attached_file method to add a file_upload property to the Record
# class.
has_attached_file :file_upload,
# In order to determine the styles of the image we want to save
# e.g. a small style copy of the image, plus a large style copy
# of the image, call the check_file_type method
styles: lambda { |a| a.instance.check_file_type },
processors: lambda {
|a| a.is_video? ? [ :ffmpeg ] : [ :thumbnail ]
}
# Validate that we accept the type of file the user is uploading
# by explicitly listing the mimetypes we are willing to accept
validates_attachment_content_type :file_upload,
:content_type => [
"video/mp4",
"video/quicktime",
"image/jpg",
"image/jpeg",
"image/png",
"image/gif",
"application/pdf",
"audio/mpeg",
"audio/x-mpeg",
"audio/mp3",
"audio/x-mp3",
"file/txt",
"text/plain",
"application/doc",
"application/msword",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint"
],
:message => "Sorry! We do not accept the attached file type"
# Before applying the Imagemagick post processing to this record
# check to see if we indeed wish to process the file. In the case
# of audio files, we don't want to apply post processing
before_post_process :apply_post_processing?
# Helper method that uses the =~ regex method to see if
# the current file_upload has a content_type
# attribute that contains the string "image" / "video", or "audio"
def is_image?
self.file_upload.content_type =~ %r(image)
end
def is_video?
self.file_upload.content_type =~ %r(video)
end
def is_audio?
self.file_upload.content_type =~ /\Aaudio\/.*\Z/
end
def is_plain_text?
self.file_upload_file_name =~ %r{\.(txt)$}i
end
def is_excel?
self.file_upload_file_name =~ %r{\.(xls|xlt|xla|xlsx|xlsm|xltx|xltm|xlsb|xlam|csv|tsv)$}i
end
def is_word_document?
self.file_upload_file_name =~ %r{\.(docx|doc|dotx|docm|dotm)$}i
end
def is_powerpoint?
self.file_upload_file_name =~ %r{\.(pptx|ppt|potx|pot|ppsx|pps|pptm|potm|ppsm|ppam)$}i
end
def is_pdf?
self.file_upload_file_name =~ %r{\.(pdf)$}i
end
def has_default_image?
is_audio?
is_plain_text?
is_excel?
is_word_document?
end
# If the uploaded content type is an audio file,
# return false so that we'll skip audio post processing
def apply_post_processing?
if self.has_default_image?
return false
else
return true
end
end
# Method to be called in order to determine what styles we should
# save of a file.
def check_file_type
if self.is_image?
{
:thumb => "200x200>",
:medium => "500x500>"
}
elsif self.is_pdf?
{
:thumb => ["200x200>", :png],
:medium => ["500x500>", :png]
}
elsif self.is_video?
{
:thumb => {
:geometry => "200x200>",
:format => 'jpg',
:time => 0
},
:medium => {
:geometry => "500x500>",
:format => 'jpg',
:time => 0
}
}
elsif self.is_audio?
{
:audio => {
:format => "mp3"
}
}
else
{}
end
end
end
I think I once got it working by enforcing a file type, e.g.
:thumb => ["100x100#", :png]
of course it's not ideal, because it enforces this filetype for every upload
Thanks a million to #duhaime for his beautiful answer.
Since this is the most complete source of information I found to have PDF attached, I'd like to document it further:
Requirements:
imagemagick
ghostscript (I forgot about this one)
(optional) ffmpeg if you want to handle video files
Also I replaced has_default_image? and apply_post_processing? with the single:
def can_thumbnail?
self.check_file_type.try(:{], :thumb).present?
end
Finally I created a method for the not-thumbable attachments:
def thumb
return self.file.url(:thumb) if self.can_thumbnail?
if self.is_video?
'/thumb/video.png'
else
'/thumb/default.png'
end
end
But thanks again #duhaime

How do I get Paperclip to recognize custom processors instead of just pushing styles through to thumbnail?

I am not having any problem getting the custom processor to load, however when I try to call it from has_attached_file, paperclip ignores it, and instead just runs thumbnail.
model
has_attached_file :file,
:styles => { :web => "some input" },
:processors => [ :custom ],
:url => ":class/:id/:style/:basename.:extension",
:path => ":class/:id/:style/:basename.:extension"
:storage => :s3
As simple a processor as can be made just to show that the processor has been run
processor.rb
module Paperclip
class Custom < Processor
attr_accessor :input
def initialize(file, options = {}, attachment = nil)
super
#basename = File.basename(file.path, File.extname(file.path))
end
def make
dst = Tempfile.new([ #basename, 'jpg' ].compact.join("."))
dst
end
end
end
But instead when I check the saved record it returns instance variables from thumbnail
>record.file.styles
{:web=>
#<Paperclip::Style:0x00000102f185d0
#attachment=
http://s3.amazonaws.com/bucket/model/id/base_name/file_name.jpg,
#format=nil,
#geometry="some_input",
#name=:web,
#other_args={}>}
I must be missing something in either writing the processor or calling it. Any idea what is going on here?
Have you tried something like this?
has_attached_file :file,
:styles => {
:my_super_style => {:geometry => "100x100#", :foo => "bar", :processors => [:custom]}
},
Have you put in the right place?
lib/paperclip_processors/custom.rb
:styles => { :web => "some input" },
:processors => [ :custom ],
should be:
:styles => {
:web => {:geometry => "some input", :processors => [:custom]},

Paperclip sharpening processor causes resizing styles not to work

I'm trying to sharpen images uploaded through paperclip. The sharpen code is working but it causes the styles to not work. The code is like this:
has_attached_file :photo,
:styles => {
:thumb => {:geometry => "100x100>"},
:medium => {:geometry => "300x300>"},
:original => {:geometry => "1024x1024>"}
},
:processors => [:sharpen],
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:path => "/:style/:id/:filename"
Now if I remove the processors option, the uploaded images are resized as specified. However if I include the processors option, all the resulting images are of original size.
My sharpen processor looks like this:
module Paperclip
class Sharpen < Paperclip::Processor
def initialize file, options = {}, attachment = nil
super
#file = file
#current_format = File.extname(#file.path)
#basename = File.basename(#file.path, #current_format)
end
def make
dst = Tempfile.new(#basename)
dst.binmode
command = "#{File.expand_path(#file.path)} -unsharp 1.5×1.0+1.5+0.02 #{File.expand_path(dst.path)}"
begin
success = Paperclip.run("convert", command)
rescue PaperclipCommandLineError
raise PaperclipError, "There was an error converting sharpening the image for #{#basename}"
end
dst
end
end
end
Any thoughts?
Try adding :thumbnail to processors list:
:processors => [:thumbnail, :sharpen]
By default :thumbnail is there but now you are overriding that setting.
"Multiple processors can be specified, and they will be invoked in the order they are defined in the :processors array. Each successive processor will be given the result of the previous processor's execution. All processors will receive the same parameters, which are what you define in the :styles hash."
https://github.com/thoughtbot/paperclip

multiple paperclip attachements with watermark processor

I'm having a ton of trouble with uploading multiple attachments with paperclip and processing them with a watermark.
I have 2 models, Ad and Photo.
The Ad has_many :photos and the Photo belongs_to :ad.
To be more exact in /models/ad.rb I have:
class Ad < ActiveRecord::Base
has_many :photos, :dependent => :destroy
accepts_nested_attributes_for :photos, :allow_destroy => true
end
and my photo.rb file looks like this:
class Photo < ActiveRecord::Base
belongs_to :ad
has_attached_file :data,
:styles => {
:thumb => "100x100#",
:first => {
:processors => [:watermark],
:geometry => '300x250#',
:watermark_path => ':rails_root/public/images/watermark.png',
:position => 'SouthEast' },
:large => {
:processors => [:watermark],
:geometry => '640x480#',
:watermark_path => ':rails_root/public/images/watermark.png',
:position => 'SouthEast' }
}
end
In my view I use this to add the file fields
<% f.fields_for :photos do |p| %>
<%= p.label :data, 'Poza:' %> <%= p.file_field :data %>
<% end %>
In my controller, in the edit action i use 4.times {#ad.photos.build} to generate the file fields.
It all works fine and dandy if I don't use the watermark processor, if I use a normal has_attached_file declaration, like this:
has_attached_file :data,
:styles => {
:thumb => "100x100#",
:first => '300x250#',
:large => '640x480#'
}
But when I use the watermark processor I always get this error:
NoMethodError in PublicController#update_ad
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
..............................
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/nested_attributes.rb:350:in `assign_nested_attributes_for_collection_association'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/nested_attributes.rb:345:in `each'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/nested_attributes.rb:345:in `assign_nested_attributes_for_collection_association'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/nested_attributes.rb:243:in `photos_attributes='
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2746:in `send'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2746:in `attributes='
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2742:in `each'
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2742:in `attributes='
/usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2628:in `update_attributes'
/home/alexg/Sites/vandmasina/app/controllers/public_controller.rb:217
/home/alexg/Sites/vandmasina/app/controllers/public_controller.rb:216:in `update_ad'
The parameters are ok, as far as I can say
Parameters:
{"commit"=>"Salveaza modificarile",
"ad"=>{"price"=>"6000",
"oras"=>"9",
"photos_attributes"=>{"0"=>{"data"=>#<File:/tmp/RackMultipart20100928-5130-b42noe-0>},
"1"=>{"data"=>#<File:/tmp/RackMultipart20100928-5130-r0ukcr-0>},
"2"=>{"data"=>#<File:/tmp/RackMultipart20100928-5130-mb23ei-0>},
"3"=>{"data"=>#<File:/tmp/RackMultipart20100928-5130-1bpkm3b-0>}},
The Watermark processor /lib/paperclip_processors/watermark.rb looks like this:
module Paperclip
class Watermark < Processor
class InstanceNotGiven < ArgumentError;
end
def initialize(file, options = {},attachment = nil)
super
#file = file
#current_format = File.extname(#file.path)
#basename = File.basename(#file.path, #current_format)
#watermark = ':rails_root/public/images/watermark.png'
#current_geometry = Geometry.from_file file # This is pretty slow
#watermark_geometry = watermark_dimensions
end
def watermark_dimensions
return #watermark_dimensions if #watermark_dimensions
#watermark_dimensions = Geometry.from_file #watermark
end
def make
dst = Tempfile.new([#basename, #format].compact.join("."))
watermark = " \\( #{#watermark} -extract #{#current_geometry.width.to_i}x#{#current_geometry.height.to_i}+#{#watermark_geometry.height.to_i /
2}+#{#watermark_geometry.width.to_i / 2} \\) "
command = "-gravity center " + watermark + File.expand_path(#file.path) + " " +File.expand_path(dst.path)
begin
success = Paperclip.run("composite", command.gsub(/\s+/, " "))
rescue PaperclipCommandLineError
raise PaperclipError, "There was an error processing the watermark for #{#basename}" if #whiny_thumbnails
end
dst
end
end
end
I have tried the processor in a normal app, without multiple attachments and it works perfect. It doesn't work with nested_attributes as far as I can tell.
The app is rails 2.3.5 with ruby 1.8.7 and paperclip 2.3.11
If you can provide any help it would be appreciated a lot, since I've been trying to figure this out for 2 days now :)
If you use rails 3 and paperclip > 2.3.3, try https://gist.github.com/843418 source.
Oh, man, that was a tough one!
You have few errors in your code and none is related to nested models. My explanation is for Rails 3 & Paperclip 2.3.3
a) the :rails_root thing doesn't work. This interpolation is used only in url/path and not on custom options. So you should replace it with Rails.root.join("public/images...")
b) you simply ignore the :watermark_path option and you use only hardcoded path (in initialization method). So it doesn't matter what you have in your :styles as it always go for .../images/watermark.png. The :rails_root thingy there again so it cannot work.
c) when you pass a parameter to Paperclip.run("composite", "foo bar there") it actually executes this command:
composite 'foo bar there'
can you see the single quotes? Because of that the composite command see your parameters as one huge parameter and doesn't understand it at all. If you pass it as an array, then every item is enclosed in the quotes, not the array as a whole.
So here is the improved version of watermark processor:
module Paperclip
class Watermark < Processor
class InstanceNotGiven < ArgumentError;
end
def initialize(file, options = {},attachment = nil)
super
#file = file
#current_format = File.extname(#file.path)
#basename = File.basename(#file.path, #current_format)
# PAWIEN: use default value only if option is not specified
#watermark = options[:watermark_path] || Rails.root.join('public/images/watermark.png')
#current_geometry = Geometry.from_file file # This is pretty slow
#watermark_geometry = watermark_dimensions
end
def watermark_dimensions
return #watermark_dimensions if #watermark_dimensions
#watermark_dimensions = Geometry.from_file #watermark
end
def make
dst = Tempfile.new([#basename, #format].compact.join("."))
dst.binmode
begin
# PAWIEN: change original "stringy" approach to arrayish approach
# inspired by the thumbnail processor
options = [
"-gravity",
"center",
"#{#watermark}",
"-extract",
"#{#current_geometry.width.to_i}x#{#current_geometry.height.to_i}+#{#watermark_geometry.height.to_i / 2}+#{#watermark_geometry.width.to_i / 2}",
File.expand_path(#file.path),
File.expand_path(dst.path)
].flatten.compact.join(" ").strip.squeeze(" ")
success = Paperclip.run("composite", options)
rescue PaperclipCommandLineError
raise PaperclipError, "There was an error processing the watermark for #{#basename}" if #whiny_thumbnails
end
dst
end
end
end
Hope that helped you!
UPDATE: You need to use latest paperclip gem from github
gem 'paperclip', '>= 2.3.3', :git => "http://github.com/thoughtbot/paperclip.git"
In this version the formats of #run were changed once again so I've updated the code. It really should work as I've created test application and it's doing what supposed.
UPDATE 2: Repo with working example:
git://repo.or.cz/paperclip-mass-example.git
Just from a quick glance it looks like watermark_path should be "#{Rails.root}/..." though it looks like you have a lot going on here.
Also, I don't see your form as in form_for. Make sure you have {:multipart => true}

Resources