Change order of Paperclip image processors - ruby-on-rails

I am using Paperclip to create a thumbnail and apply a watermark to an uploaded image. The problem is that the thumbnail is created from a watermarked image instead of the original.
How can I make a thumbnail from the original file?
Model
has_attached_file :image,processors:[:thumbnail, :watermark], styles:{thumb:"240x240#", original: {watermark_path: "#Rails.root}/public/images/watermark.png"}}
Watermark Processor
module Paperclip
class Watermark < Processor
attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options, :watermark_path, :overlay, :position
def initialize file, options = {}, attachment = nil
super
geometry = options[:geometry]
#file = file
if geometry.present?
#crop = geometry[-1, 1] == '#'
end
#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]
#overlay = options[:overlay].nil? ? true : false
#current_format = File.extname(#file.path)
#basename = File.basename(#file.path, #current_format)
end
# 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
return #file unless watermark_path
dst = Tempfile.new([#basename, #format].compact.join("."))
dst.binmode
command = "convert"
params = [fromfile]
params += transformation_command
params << tofile(dst)
begin
success = Paperclip.run(command, params.flatten.compact.collect { |e| "'#{e}'" }.join(" "))
rescue Paperclip::Errors::CommandNotFoundError
raise Paperclip::Errors::CommandNotFoundError, "There was an error resizing and cropping #{#basename}" if #whiny
end
if watermark_path
command = "composite"
params = %W[-gravity #{#position} #{watermark_path} #{tofile(dst)}]
params << tofile(dst)
begin
success = Paperclip.run(command, params.flatten.compact.collect { |e| "'#{e}'" }.join(" "))
rescue Paperclip::Errors::CommandNotFoundError
raise Paperclip::Errors::CommandNotFoundError, "There was an error processing the watermark for #{#basename}" if #whiny
end
end
dst
end
def fromfile
File.expand_path(#file.path)
end
def tofile(destination)
File.expand_path(destination.path)
end
def transformation_command
if #target_geometry.present?
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
else
scale, crop = #current_geometry.transformation_to(#current_geometry, crop?)
trans = %W[-resize #{scale}]
trans += %W[-crop #{crop} +repage] if crop
trans << convert_options if convert_options?
trans
end
end
end
end

Related

Paperclip watermark processor

I'm having issues to execute command in my processor. The same command works in console. Basically, I need to use two commands in one - convert and composite with tile so that watermark is repeated all over the image.
The command I ran in console is this:
run_string = "convert utah.jpg -resize 1200x1200 miff:- | composite -tile Watermark-Photo.png - utah2.jpg"=> "convert utah.jpg -resize 1200x1200 miff:- | composite -tile Watermark-Photo.png - utah2.jpg"
Paperclip.run run_string
And the result was perfect. However, when I put it into my processor:
module Paperclip
class Watermark < Processor
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)
Rails.logger.info "watermark initialized"
end
# 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
Rails.logger.info "watermark make method"
src = #file
dst = Tempfile.new([#basename].compact.join("."))
dst.binmode
if #watermark_path.present?
# params = ["convert"]
# params += %W[#{fromfile}]
# params += transformation_command
# params += %W[| composite -tile #{#watermark_path} -]
# params << "#{tofile(dst)}"
run_string = "convert '#{fromfile}' #{transformation_command.join(' ')} | composite -tile '#{#watermark_path}' - '#{tofile(dst)}'"
else
# params = ["convert"]
# params += ["'#{fromfile}'"]
# params += transformation_command
# params << "'#{tofile(dst)}'"
run_string = "convert '#{fromfile}' #{transformation_command.join(' ')} '#{tofile(dst)}'"
end
Rails.logger.info 'params:' + run_string
begin
# Paperclip.run(params.join(' '))
Paperclip.run(run_string)
rescue ArgumentError, Cocaine::CommandLineError
raise Paperclip::Error.new("There was an error processing the watermark for #{#basename}") if #whiny
end
dst
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 += %W[miff:- ]
trans << #convert_options if #convert_options.present?
trans
end
def fromfile
File.expand_path(#file.path)
end
def tofile(destination)
File.expand_path(destination.path)
end
end
end
I can see from log that it executes the command:
Command :: convert '/tmp/7179f28e8260099624b8c2e7e8fe8fb420170902-1-1ud7pah.jpg' -resize 600x600> miff:- | composite -tile '/storycraftstock/app/assets/images/Watermark-Photo.png' - '/tmp/7179f28e8260099624b8c2e7e8fe8fb420170902-1-1ud7pah20170902-1-1kbmamu'
Gives this unspecific error:
exception while processing Spree::Image ID 11:
There was an error processing the watermark for 32a2a05e7393f7ddc78f0134bcf32e4a20170902-1-19oyq1j
As you can see I've commented a lot of things in the code just to make it run, but still no luck. Any ideal what am I doing wrong?

Clean Image Attachment for OCR in Ruby-On-Rails

How can I use a script such as Textcleaner within a Ruby-On-Rails application? Currently I'm using a custom paperclip processor where I use parameters similar to the script. Here is the has_attached_file line in my ActiveRecord:
has_attached_file :file, :style=> { :processors => [:text_cleaner] } }
Here is the Paperclip Processor:
module Paperclip
# Handles grayscale conversion of images that are uploaded.
class TextCleaner< 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 << "-auto-orient"
parameters << "-colorspace Gray"
#parameters << "-sharpen 0x1"
#parameters << "-type grayscale"
#parameters << "-contrast-stretch 0"
#parameters << "-clone 0"
#parameters << "-deskew 40%"
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
Using paperclip I was able to add a Text Cleaner processor. I added it as a style in model via:
has_attached_file :file, :styles => { :clean => { :processors => [:text_cleaner] } }
And in /lib/paperclip_processors/text_cleaner.rb I have:
module Paperclip
# Handles grayscale conversion of images that are uploaded.
class TextCleaner < 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 = '-respect-parenthesis \( :source -colorspace gray -type grayscale -contrast-stretch 0 \) \( -clone 0 -colorspace gray -negate -lat 15x15+5% -contrast-stretch 0 \) -compose copy_opacity -composite -fill "white" -opaque none +matte -deskew 40% -auto-orient -sharpen 0x1 :dest'
success = Paperclip.run('convert', parameters, :source => File.expand_path(#file.path), :dest => File.expand_path(dst.path))
rescue PaperclipCommandLineError => e
raise PaperclipError, "There was an error during the textclean conversion for #{#basename}" if #whiny
end
dst
end
end
end

Rails + Paperclip + AWS: After watermark is added, new picture is not sent to Amazon s3

I added watermark processor for one size of image. But, all other versions of picture are uploaded to S3, except this one. I believe I had to notify AWS that I changed this size but I do not know how or where to do that...
Here is my code:
lib/paperclip_processors/watermark.rb
module Paperclip
class Watermark < Processor
attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options, :watermark_path, :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]
#format_output = options[:format_output].nil? ? "jpg" : options[:format_output]
#watermark_path = options[:watermark_path]
#position = options[:position].nil? ? "SouthEast" : options[:position]
#overlay = options[:overlay].nil? ? true : false
#current_format = File.extname(#file.path)
#basename = File.basename(#file.path, #current_format)
end
def crop?
#crop
end
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)}.#{#format_output}\""
else
command = "convert"
params = [fromfile]
params += transformation_command
params << tofile(dst)
end
puts command
puts "Params: " + params.to_s
begin
success = Paperclip.run(command, params.join(" "))
rescue ArgumentError, Cocaine::CommandLineError
raise Paperclip::Error, "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:
require 'paperclip_processors/watermark'
class Price < ActiveRecord::Base
has_attached_file :picture,
:processors => [:watermark],
styles: {
thumb: '100x100#',
small: '320x200#',
medium: '640x400#',
large: {
:geometry => "920x575>",
:watermark_path => "#{Rails.root}/public/img/watermark.png"
}
}
end

How to crop image on upload with Rails, Carrierwave and Minimagick?

I have read:
Undefined Method crop! Using Carrierwave with MiniMagick on rails 3.1.3
Carrierwave Cropping
http://pastebin.com/ue4mVbC8
And so I tried:
# encoding: utf-8
class ProjectPictureUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :thumb do
process :cropper
# process :crop
process resize_to_fit: [200, 200]
end
def cropper
manipulate! do |img|
# if model.crop_x.present?
image = MiniMagick::Image.open(current_path)
crop_w = (image[:width] * 0.8).to_i
crop_y = (image[:height] * 0.8).to_i
crop_x = (image[:width] * 0.1).to_i
crop_y = (image[:height] * 0.1).to_i
# end
img = img.crop "#{crop_x}x#{crop_y}+#{crop_w}+#{crop_h}"
img
end
end
def crop
if model.crop_x.present?
resize_to_limit(700, 700)
manipulate! do |img|
x = model.crop_x
y = model.crop_y
w = model.crop_w
h = model.crop_h
w << 'x' << h << '+' << x << '+' << y
img.crop(w)
img
end
end
end
end
Then I used cropper: undefined local variable or method `crop_h' for /uploads/tmp/20121006-2227-4220-9621/thumb_Koala.jpg:#
Then crop: undefined method `crop_x' for #
Model:
class Project < ActiveRecord::Base
...
mount_uploader :picture, ProjectPictureUploader
end
Rails 3.2, Win7,
convert -version
Version: ImageMagick 6.7.9-4 2012-09-08 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2012 ImageMagick Studio LLC
Features: OpenMP
I have understood.
version :thumb do
process resize_to_fit: [300, nil]
process crop: '300x150+0+0'
#process resize_and_crop: 200
end
private
# Simplest way
def crop(geometry)
manipulate! do |img|
img.crop(geometry)
img
end
end
# Resize and crop square from Center
def resize_and_crop(size)
manipulate! do |image|
if image[:width] < image[:height]
remove = ((image[:height] - image[:width])/2).round
image.shave("0x#{remove}")
elsif image[:width] > image[:height]
remove = ((image[:width] - image[:height])/2).round
image.shave("#{remove}x0")
end
image.resize("#{size}x#{size}")
image
end
end
resize_and_crop from here:
http://blog.aclarke.eu/crop-and-resize-an-image-using-minimagick/
In #cropper your crop_h is not initialized(you double initialize crop_y instead).
In #crop error is exactly what error message said - you don't have defined crop_x for Project class.

archiloque-rest-client: change content-type to multipart/related when posing a file

Using
http://github.com/archiloque/rest-client
When posting a file using this line, the content type is set as
Content-Type: multipart/form-data; boundary=301405
in the header, by default.
RestClient.post '/data', :myfile => File.new("/path/to/image.jpg", 'rb')
I tried this and it still sets the header multipart/form-data.
RestClient.post '/data', :myfile => File.new("/path/to/image.jpg", 'rb'), :content_type => 'multipart/related'
Has anybody tried setting multipart/related?
Or you can use rather new-ish multipart_body gem:
http://steve.dynedge.co.uk/2010/09/19/multipart-body-a-gem-for-working-with-multipart-data/
Ok. I could not find a better alternative, so ended up with the following app.
It is based on this code here.
http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
Usage:
files = { File.new("myfile1","rb"), File.new("myfile2","rb")}
mpost = Mutipart("main", "<my xml main part content which refers to file names>",files )
mpost.post("our_url","post")
Note: This code is not production ready. It is just functional.
---------------- multipart.rb -------------
require 'net/http'
require 'uri'
require 'pp'
require 'mime/types'
class Multipart
def initialize( main_part_id, main_part_content, file_names )
#file_names = file_names
#main_part_id = main_part_id
#main_part_content = main_part_content
end
def post( to_url, method = :post )
boundary = "###-------#{Time.new}-----####"
parts = []
streams = []
# write main part first
parts << StringPart.new( "--" + boundary + "\r\n")
parts << StringPart.new("Content-Disposition: name=\"#{#main_part_id}\";\"\r\n" +
"Content-ID: #{#main_part_id}\r\n\r\n"+
"Content-Type: application/xml\r\n\r\n" +
#main_part_content + "\r\n\r\n")
parts << StringPart.new( "\r\n--" + boundary + "\r\n")
#file_names.each do |param_name, filestream|
raise 'mutlipartsend: empty file object' if filestream.blank?
filename= filestream.respond_to?(:original_path) ? filestream.original_path : filestream.path
ctype = filestream.respond_to?(:content_type) ? filestream.content_type: nil
fsize = filestream.respond_to?(:lstat) ? filestream.lstat.size : filestream.size
if !ctype
begin
pos = filename.rindex('/') # if filename is a path
fname = filename[pos + 1, filename.length - pos]
mm = MIME::Types.type_for(fname)
ctype = mm.first.content_type if !mm.blank?
rescue Exception => e
p e.message
end
end
if !ctype
ctype= 'application/binary'
p "mutlipartsend: failed to determine contenttype for #{filename}. using application/binary"
end
parts << StringPart.new("Content-Disposition: name=\"" + param_name.to_s + "\"; filename=\"" + filename + "\"\r\n" +
"Content-Type: #{ctype}\r\n\r\n")
#"Content-Type: application/binary\r\n\r\n")
begin
stream = File.open(filestream.path,"rb")
streams << stream
parts << StreamPart.new(stream, fsize)
parts << StringPart.new( "\r\n--" + boundary + "\r\n" )
rescue Exception => e
p 'failed to load filestream '+ filestream.path
p e.message
raise 'failed to load filestream ' + e.message
end
end
post_stream = MultipartStream.new( parts )
url = URI.parse( to_url )
req = method == :post ? Net::HTTP::Post.new(url.path) : Net::HTTP::Put.new(url.path)
req.content_length = post_stream.size
req.content_type = 'multipart/mixed; boundary=' + boundary
req["myheader1"] = 'header1'
req["myheader2"] = 'header2'
req.body_stream = post_stream
res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
streams.each do |stream|
stream.close();
end
res
end
end
class StreamPart
def initialize( stream, size )
#stream, #size = stream, size
end
def size
#size
end
def read( offset, how_much )
#stream.read( how_much )
end
end
class StringPart
def initialize ( str )
#str = str
end
def size
#str.length
end
def read ( offset, how_much )
#str[offset, how_much]
end
end
class MultipartStream
def initialize( parts )
#parts = parts
#part_no = 0;
#part_offset = 0;
end
def size
total = 0
#parts.each do |part|
total += part.size
end
total
end
def read ( how_much )
if #part_no >= #parts.size
return nil;
end
how_much_current_part = #parts[#part_no].size - #part_offset
how_much_current_part = if how_much_current_part > how_much
how_much
else
how_much_current_part
end
how_much_next_part = how_much - how_much_current_part
current_part = #parts[#part_no].read(#part_offset, how_much_current_part )
if how_much_next_part > 0
#part_no += 1
#part_changed=true
#part_offset = 0
next_part = read( how_much_next_part )
current_part + if next_part
next_part
else
''
end
else
#part_offset += how_much_current_part
current_part
end
end
end

Resources