I have uploaders for different types of images. Each has the same set of derivatives (large, medium, thumbnail), but different resolutions. But they also share some configuration. For example, each uploader converts the original to jpeg, changes quality and strips metadata.
class BannerUploader < Shrine
Attacher.derivatives do |original|
magick = ImageProcessing::MiniMagick.source(original)
.convert('jpg')
.saver(quality: 85)
.strip
{
large: magick.resize_to_limit!(1600, 400),
medium: magick.resize_to_limit!(800, 200),
thumbnail: magick.resize_to_limit!(400, 100)
}
end
end
This one has the same processing rules:
class PhotoUploader < Shrine
Attacher.derivatives do |original|
magick = ImageProcessing::MiniMagick.source(original)
.convert('jpg')
.saver(quality: 85)
.strip
{
large: magick.resize_to_limit!(1200, 1200),
medium: magick.resize_to_limit!(600, 600),
thumbnail: magick.resize_to_limit!(300, 300)
}
end
end
Is it possible to extract and share some of the configuration (like .convert('jpg').saver(quality: 85).strip) between those uploaders? Something similar to validations inheritance or a helper.
There isn't anything for sharing processing logic out-of-the-box, but you can create a service object, for example:
class BannerUploader < Shrine
Attacher.derivatives do |original|
Thumbnails.call(original, {
large: [1600, 400],
medium: [800, 200],
thumbnail: [400, 100],
})
end
end
class PhotoUploader < Shrine
Attacher.derivatives do |original|
Thumbnails.call(original, {
large: [1200, 1200],
medium: [600, 600],
thumbnail: [300, 300],
})
end
end
class Thumbnails
def self.call(original, sizes)
magick = ImageProcessing::MiniMagick.source(original)
.convert('jpg')
.saver(quality: 85)
.strip
thumbnails = {}
sizes.each do |name, (width, height)|
thumbnails[name] = magick.resize_to_limit!(width, height)
end
thumbnails
end
end
Related
I'm trying to take user hidden field parameters from Jcrop and crop an image before putting it on the database. I've found a few tutorials and questions but none address my exact problem.
This comes the closest. It looks like the poster was trying to use a single parameter from a selection. I have 4 integers. I didn't think that would matter so I assume it's the 'has_attached_file' part:
Supplying a variable string to model for Paperclip Imagemagik resizing
That combined with this:
https://github.com/thoughtbot/paperclip#dynamic-configuration
Here is what I have,
MODEL:
class Muse < ActiveRecord::Base
attr_accessor :w, :h, :x, :y
def get_coor
#turn parameters into class variables
#w = self.w
#h = self.h
#x = self.x
#y = self.y
#build string for options
cropper = "-crop #{#w.to_i}x#{#h.to_i}+#{#x.to_i}+#{#y.to_i} -resize 200x200"
#return string of the convert options I want
#get_coor = cropper
end
belongs_to :user
default_scope -> { order(created_at: :desc) }
has_attached_file :muse_img, styles: lambda { |attachment| { original: (attachment.instance.get_coor) } }
validates_attachment :muse_img, presence: true,
content_type: { content_type: ["image/jpeg"] },
size: { in: 0..500.kilobytes }
end
I don't get errors. But it's acting like the get_coor method is calling nothing.
Before this I tried string interpolations directly on the convert_options, but it kept coming out as nil for all the coordinates. I'm new to Rails and Ruby so I'm still a little hazy on lambdas. Hopefully it's just a syntax error. Thanks for any help!
The solution seemed to be the order I put the params in params method. I had to put the coordinates first and then the image second. I found it here:
https://github.com/thoughtbot/paperclip/issues/1533
I also changed the has_attached_file section but I don't know if that had anything to do with the problem. But here it is anyway.
has_attached_file :muse_img,
:styles => lambda { |attachment|
crop_convert_options = attachment.instance.get_coor
{
original: {
convert_options: crop_convert_options
}
}
}
I try find way write Paperclip processor which generate image based on other style.
I need generate style with size "54x54" and then generate another style with size 120x120 based on "54x54" style (background + small image).
Model:
class Medal < ActiveRecord::Base
has_attached_file :icon,
styles: {
:'48' => ['48', :png],
:'54' => ['54', :png],
:'120' => ['120', :png],
:'fb' => { geometry: '120', format: :png, processors: [ :fb_medal_icon ] },
}
end
Processor:
module Paperclip
class FbMedalIcon < Processor
def initialize(file, options = {}, attachment = nil)
super
#whiny = options[:whiny].nil? ? true : options[:whiny]
#format = File.extname(#file.path)
#basename = File.basename(#file.path, #format)
end
def make
src = #file
dst = Tempfile.new([#basename, #format].compact.join("."))
dst.binmode
begin
arguments = '-gravity center :icon :background :dest'
options = {
icon: icon_path,
background: "#{File.expand_path(src.path)}[0]",
dest: File.expand_path(dst.path)
}
composite(arguments, options)
rescue Cocaine::ExitStatusError
raise Paperclip::Error, "There was an error processing the icon for #{#basename}" if #whiny
rescue Cocaine::CommandNotFoundError
raise Paperclip::Errors::CommandNotFoundError.new("Could not run the `composite` command. Please install ImageMagick.")
end
dst
end
def composite(arguments = "", local_options = {})
Paperclip.run('composite', arguments, local_options)
end
private
def icon_path
style = :'54'
icon = #attachment.instance.icon
if icon.queued_for_write[style].present?
icon.queued_for_write[style].path
else
Paperclip.io_adapters.for(icon.styles[style]).path
end
end
end
end
But I get error "NoMethodError: undefined method `path' for nil:NilClass"
from ruby-1.9.3-p484/gems/paperclip-4.1.1/lib/paperclip/attachment.rb:174:in `staged_path'
from ruby-1.9.3-p484/gems/paperclip-4.1.1/lib/paperclip/io_adapters/attachment_adapter.rb:25:in `copy_to_tempfile'
from ruby-1.9.3-p484/gems/paperclip-4.1.1/lib/paperclip/io_adapters/attachment_adapter.rb:19:in `cache_current_values'
from ruby-1.9.3-p484/gems/paperclip-4.1.1/lib/paperclip/io_adapters/attachment_adapter.rb:11:in `initialize'
from ruby-1.9.3-p484/gems/paperclip-4.1.1/lib/paperclip/io_adapters/registry.rb:31:in `new'
from ruby-1.9.3-p484/gems/paperclip-4.1.1/lib/paperclip/io_adapters/registry.rb:31:in `for'
from /my_projects/MyApp/lib/paperclip_processors/fb_medal_icon.rb:63:in `icon_path'
from /my_projects/MyApp/lib/paperclip_processors/fb_medal_icon.rb:24:in `make'
Any idea how right handle this case?
You've not mentioned why you've used your own processor, but I'd highly recommend using ImageMagick to handle the styling
If you install ImageMagick (it can be a problem on Windows), you'll be able to get your styles without using the custom processor. I have code if you'd like to see
I am uploading an image that I am making many versions of, and I want to be able to put each version in a different folder. For example:
class ItunesArtworkUploader < CarrierWave::Uploader::Base
DIMENSIONS = [1024, 1024]
{
:iphone_small_29x29 => { size: [29, 29], filename: "icon-small.png" },
:iphone_57x57 => { size: [57, 57], filename: "icon.png" }
}
# resize
def filename
"iTunesArtwork.png"
end
end
The original ItunesArtwork.png is being put into uploads/foo/itunes_artwork/iTunesArtwork.png. Now for each version I want them to be in their own folders underneath "foo" like this:
uploads/foo/itunes_artwork/iTunesArtwork.png
uploads/foo/itunes_artwork/icon-small.png
uploads/foo/itunes_artwork/icon.png
I've tried
:iphone_small_29x29 => { size: [29, 29], filename: "/icon-small/icon-small.png"},
but that gives me this as a resulting filepath:
uploads/foo/itunes_artwork/icon-small/icon-small.png
Any suggestions would be great, thanks!
I'm working on an app that allows users to create custom watermarks for documents. I'd like to store the watermark images using carrierwave.
So far, the image is being created, but the model.image attribute is not getting created/saved with carrierwave. Help?
class Watermark < ActiveRecord::Base
mount_uploader :image, MarkImageUploader
before_save :textToImage
def textToImage
img = MiniMagick::Image.open("#{Rails.root}/public/images/blank.png")
img.combine_options do |c|
c.gravity 'Center'
c.draw "rotate 315 text 50,-375 '#{self.text}'"
c.font '-*-helvetica-*-r-*-*-125-*-*-*-*-*-*-2'
c.fill("#dddddd")
end
self.image = img
end
end
Turns out you can assign the file to the carrierwave column in the controller:
#watermark.image = File.open("#{Rails.root}/public/images/blank.png")
and then handle the addition of text in the uploader:
process :addText
def addText
t = model.text
manipulate! do |img|
img.combine_options do |cmd|
cmd.gravity 'Center'
cmd.fill("lightgray")
cmd.draw "rotate 325 text 85,-30 '#{t.upcase}'"
cmd.font "#{Rails.root}/public/images/fonts/Vera.ttf"
cmd.pointsize '60'
end
result = img
end
end
How can I get the width and height of the current instance of carrierwave?
Something like this:
car_images.each do | image|
image_tag( image.photo_url, :width => image.photo_width, :height => image.photo_height)
end
Unfortunately image.photo_width and image.photo_height are not working.
I need to specify the width and height of the images, it is required on the jquery plugin I'm using.
Combine https://github.com/jnicklas/carrierwave/wiki/How-to:-Get-version-image-dimensions and https://github.com/jnicklas/carrierwave/wiki/How-to:-Store-the-uploaded-file-size-and-content-type and you get:
class Image
before_save :update_image_attributes
private
def update_image_attributes
if image.present?
self.content_type = image.file.content_type
self.file_size = image.file.size
self.width, self.height = `identify -format "%wx%h" #{image.file.path}`.split(/x/)
# if you also need to store the original filename:
# self.original_filename = image.file.filename
end
end
end
You can save the height and width as attributes with your model quite easily if using Rmagick. In the Carrierwave uploader:
class ArtworkUploader < CarrierWave::Uploader::Base
def geometry
#geometry ||= get_geometry
end
def get_geometry
if #file
img = ::Magick::Image::read(#file.file).first
geometry = { width: img.columns, height: img.rows }
end
end
end
And in your model:
class Artwork < ActiveRecord::Base
mount_uploader :image, ArtworkUploader
before_save :save_image_dimensions
private
def save_image_dimensions
if image_changed?
self.image_width = image.geometry[:width]
self.image_height = image.geometry[:height]
end
end
end
Or just use FastImage. This makes it much easier to measure attachments retroactively.
#JamieD's answer worked for me, with one exception. I was using MiniMagick.
So I added something like this to my uploader.
def geometry
#geometry ||= get_geometry
end
def get_geometry
if #file
img = ::Magick::Image::read(#file.file).first
geometry = { width: img.columns, height: img.rows }
end
end