Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

PaperclipError doesn't exist.

Try 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

Since you are now asking a different question I'll make a new answer.

:path is used to define where Paperclip will store the uploaded image. By default this will be :rails_root/public/system/:class/:attachment/:id_partition/:style/:filename.

:url is used to access the image later. By default this will be /system/:class/:attachment/:id_partition/:style/:filename.

(Actually, to save having to duplicate the url part, :path is really defined as :rails_root/public:url by default.)

By specifying them in the model you are changing the save location. I wouldn't recommend putting them in the assets directory as assets are really part of your application and you don't want user uploaded files to be going there.

As to why you are not seeing the watermark with the uploaded image, I guess the Imagemagick composite command is not being called correctly. Try running it on the command line, or adding the parameter -debug to see why it is failing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top