How to create a file in carrierwave - ruby-on-rails

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

Related

Shared derivatives configuration for multiple Shrine uploaders

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

Why is MiniMagick's crop method returning an empty string?

My screenshot model has a shot image attached via CarrierWave:
class Screenshot < ActiveRecord::Base
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
mount_uploader :shot, ShotUploader
process_in_background :shot unless Rails.env.test?
end
I have a form that gathers cropping information for the #screenshot#process_crop action:
def process_crop
#survey = Survey.find(params[:survey_id])
#review = #survey.review
#screenshot = #survey.screenshots.find(params[:id])
#competitor = #survey.competitor
screenshot = MiniMagick::Image.open(#screenshot.shot.url)
if screenshot_params["crop_x"].present?
x = screenshot_params["crop_x"]
y = screenshot_params["crop_y"]
w = screenshot_params["crop_w"]
h = screenshot_params["crop_h"]
cropped_image = screenshot.crop("#{w}x#{h}+#{x}+#{y}")
uploader = ShotUploader.new
uploader.store!(cropped_image)
end
redirect_to survey_screenshot_path(#survey, #screenshot)
end
I have confirmed that x, y etc are sensible values given the dimensions of the image.
MiniMagick::Image.open(#screenshot.shot.url) returns a MiniMagick image object as you'd expect.
But why does screenshot.crop("#{w}x#{h}+#{x}+#{y}") return an empty string?
Clearly I need it to return a cropped image in a format that will make sense to uploader.store!.
Minimagick tends to operate inplace, so
screenshot.crop("#{w}x#{h}+#{x}+#{y}")
modifies screenshot rather than returning a new cropped image

Paperclip processor generate image from other style

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

Carrierwave image dimension

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

Is there an easy way to automatic vertical/horizontal align images with paperclip?

I always want square images of 100x100 but i dont want to scale.
If the it is an image of 100X80 i want it vertical aligned with 10 white pixels on top and 10 white pixels on bottm.
Is there an easy way to handle this?
Using Rails 2.1 with paperclip 2.3.0
Might not be appropriate, however you can use CSS to easily acchieve this:
html:
<div class="paperclip-holder">
<%= your_paperclip_image_tag %>
</div>
css:
.paperclip-holder{
width: 100px;
height: 100px;
background: #ffffff;
vertical-align: center;
text-align: center;
}
Ok i finally managed it.
i used this script to crop the image the way i want.
http://www.fmwconcepts.com/imagemagick/squareup/index.php
I made a new Paperclip processor and named CropFile. I copy pasted the Thumbnail processor from paper clip and added some lines to run the squareup script.
in my model:
has_attached_file :widget_image, :styles => { :square_60 => ["60x60", :png], :square_100 => ["100x100", :png] }, :processors => [:crop_file]
I added paperclip_postprocess.rb in initializers
#paperclip_postprocess.rb
module Paperclip
# Handles thumbnailing images that are uploaded.
class CropFile < Processor
attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options, :source_file_options
# Creates a Thumbnail object set to work on the +file+ given. It
# will attempt to transform the image into one defined by +target_geometry+
# which is a "WxH"-style string. +format+ will be inferred from the +file+
# unless specified. Thumbnail creation will raise no errors unless
# +whiny+ is true (which it is, by default. If +convert_options+ is
# set, the options will be appended to the convert command upon image conversion
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
#source_file_options = options[:source_file_options]
#convert_options = options[:convert_options]
#whiny = options[:whiny].nil? ? true : options[:whiny]
#format = options[:format]
#path = options[:path]
#source_file_options = #source_file_options.split(/\s+/) if #source_file_options.respond_to?(:split)
#convert_options = #convert_options.split(/\s+/) if #convert_options.respond_to?(:split)
#current_format = File.extname(#file.path)
#basename = File.basename(#file.path, #current_format)
# reg exp to get the with and the height
geometry.match(/(\d+)(#|>|<)?x/)
#width = $1
#height = $3
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?
!#convert_options.nil? && !#convert_options.empty?
end
# Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile
# that contains the new image.
def make
src = #file
dst = Tempfile.new(['bla', ".png"])
dst.binmode
begin
parameters = []
parameters << source_file_options
parameters << ":source"
parameters << transformation_command
parameters << convert_options
parameters << ":dest"
parameters = parameters.flatten.compact.join(" ").strip.squeeze(" ")
Paperclip.run("convert", parameters, :source => "#{File.expand_path(src.path)}[0]", :dest => File.expand_path(dst.path))
# Cropping it with vertical and horizontal aligning
`#{Rails.root}/public/scripts/squareup.sh -c trans -s #{#width} #{File.expand_path(src.path)} #{File.expand_path(dst.path)}`
rescue PaperclipCommandLineError => e
raise PaperclipError, "There was an error processing the thumbnail for #{#basename}" if #whiny
end
dst
end
# Returns the command ImageMagick's +convert+ needs to transform the image
# into the thumbnail.
def transformation_command
scale, crop = #current_geometry.transformation_to(#target_geometry, crop?)
trans = []
trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
trans << "-crop" << %["#{crop}"] << "+repage" if crop
trans
end
end
end
I added the following line to crop the images after paperclip has converted them
# Cropping it with vertical and horizontal aligning
`#{Rails.root}/public/scripts/squareup.sh -c trans -s #{#width} #{File.expand_path(src.path)} #{File.expand_path(dst.path)}`
If somebody gets a better idea, please give it to me :)
Thanks

Resources