I've been attempting to add watermarks to my images, following the answer listed in watermark with paperclip :
Watermark.rb:
module Paperclip
class Watermark < Processor
# Handles watermarking of images that are uploaded.
attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options, :watermark_path, :watermark_offset, :overlay, :position
def initialize file, options = {}, attachment = nil
super
geometry = options[:geometry]
@file = file
@crop = geometry[-1,1] == '#'
@target_geometry = Geometry.parse geometry
@current_geometry = Geometry.from_file @file
@convert_options = options[:convert_options]
@whiny = options[:whiny].nil? ? true : options[:whiny]
@format = options[:format]
@watermark_path = options[:watermark_path]
@position = options[:position].nil? ? "SouthEast" : options[:position]
@watermark_offset = options[:watermark_offset]
@overlay = options[:overlay].nil? ? true : false
@current_format = File.extname(@file.path)
@basename = File.basename(@file.path, @current_format)
end
# TODO: extend watermark
# Returns true if the +target_geometry+ is meant to crop.
def crop?
@crop
end
# Returns true if the image is meant to make use of additional convert options.
def convert_options?
not @convert_options.blank?
end
# Performs the conversion of the +file+ into a watermark. Returns the Tempfile
# that contains the new image.
def make
dst = Tempfile.new([@basename, @format].compact.join("."))
dst.binmode
if watermark_path
command = "composite"
params = %W[-gravity #{@position}]
params += %W[-geometry '#{@watermark_offset}'] if @watermark_offset
params += %W['#{watermark_path}' '#{fromfile}']
params += transformation_command
params << "'#{tofile(dst)}'"
else
command = "convert"
params = ["'#{fromfile}'"]
params += transformation_command
params << "'#{tofile(dst)}'"
end
begin
Paperclip.run(command, params.join(' '))
rescue ArgumentError, Cocaine::CommandLineError
raise PaperclipError, "There was an error processing the watermark for #{@basename}" if @whiny
end
dst
end
def fromfile
File.expand_path(@file.path)
end
def tofile(destination)
File.expand_path(destination.path)
end
def transformation_command
scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
trans = %W[-resize '#{scale}']
trans += %W[-crop '#{crop}' +repage] if crop
trans << convert_options if convert_options?
trans
end
end
end
and model code:
has_attached_file :image,
:processors => [:watermark],
:styles => {
:large => "640x480",
:thumb => "100x100",
:medium => "300x300",
:content => {
:geometry => '150x153',
:watermark_path => Rails.root.join('app/assets/images/watermark.jpg'),
:position => 'SouthWest'
},
},
dependent: :allow_destroy
I've attempted to update this to work with Rails 4 (moving attr_accessor to params in the model), but I get the error:
uninitialized constant Paperclip::Watermark::PaperclipError
Any tips on how to get implement watermarks in a rails 4 app?
UPDATE: I was able to get past the uninitialized constant error with Graeme's suggestion below of changing:
raise PaperclipError, "There was an error processing the watermark for #{@basename}" if @whiny
to:
raise Paperclip::Error.new("There was an error processing the watermark for #{@basename}") if @whiny
I also had to remove the following from the model in order for the upload to process:
:url => "/images/:style/:id_:style.:extension",
:path => ":rails_root/app/assets/images/:style/:id_:style.:extension"
I don't understand what the purpose of :url and :path are in this scenario, when users are uploading images?
The problem is, even though the images now get uploaded, no watermark is being displayed. Thoughts?
Update 2: In order to get the watermark displaying correctly, I had to change the model to:
has_attached_file :image,
:processors => [:watermark],
:url => "/system/:class/:attachment/:id_partition/:style/:filename",
:path => ":rails_root/public/system/:class/:attachment/:id_partition/:style/:filename",
:styles => {
:large => "640x480",
:thumb => "100x100",
:medium => {
:processors => [:watermark],
:geometry => '300x300',
:watermark_path => Rails.root.join('app/assets/images/icon.gif'),
:position => 'SouthWest'
},
},
dependent: :allow_destroy
The key piece was removing :content => . Only remaining issue is that the watermark is scaling up to fit the entire width of the image. Any recommendations on how to stop the watermark-scaling?
The problem with the watermark being stretched is the Imagemagick command which is combining the two images together and then resizing the result.
Effectively the command being run will be (I've abbreviated the actual filenames for clarity):
composite -gravity SouthWest icon.gif uploaded_image.gif -resize 300x300 output_image.gif
As you see the images are joined and then resized.
The command I believe you need is:
convert uploaded_image.gif -resize 300x300 icon.gif -gravity SouthWest -composite output_image.gif
i.e. resize the uploaded image and then add on the watermark.
I've tested this using composite
and convert
on the command line and it does what I believe you are looking for.
To achieve it in the code, you need to change if watermark_path
statement in the make
method:
def make
dst = Tempfile.new([@basename, @format].compact.join("."))
dst.binmode
if watermark_path
# -- original code --
# command = "composite"
# params = %W[-gravity #{@position}]
# params += %W[-geometry '#{@watermark_offset}'] if @watermark_offset
# params += %W['#{watermark_path}' '#{fromfile}']
# params += transformation_command
# params << "'#{tofile(dst)}'"
# -- new code --
command = "convert"
params = %W['#{fromfile}']
params += transformation_command
params += %W['#{watermark_path}' -gravity #{@position} -composite]
params << "'#{tofile(dst)}'"
else
command = "convert"
params = ["'#{fromfile}'"]
params += transformation_command
params << "'#{tofile(dst)}'"
end
begin
Paperclip.run(command, params.join(' '))
rescue ArgumentError, Cocaine::CommandLineError
raise PaperclipError, "There was an error processing the watermark for #{@basename}" if @whiny
end
dst
end
Disclaimer: I have not actually tested this so please forgive any errors.