CarrierWave - Set width and max height of images - ruby-on-rails

I use CarrierWave and I want to resize the images to a width of 220px and a max-height of 220px.
If I use process :resize_to_fit => [220,220] it could be that the width is not 220px. What can I do?

If I interpret the question correctly:
for a portrait image (say 480px wide, 640px high) you would want to scale it down to 220px wide, then crop it down to 220px high, resulting in a square image.
for a landscape image, you would want to scale it down to 220px wide (the height would therefore be less than 220px).
If that's right, you want a two-step process:
Resize to 220px wide, preserving the aspect ratio
Crop to 220px high (if portrait)
You can do so by writing your own processor using the manipulate! command (see CarrierWave's own for some inspiration).
I think this is roughly what you're after
process :resize => [220, 220]
protected
def resize(width, height, gravity = 'Center')
manipulate! do |img|
img.combine_options do |cmd|
cmd.resize width.to_s
if img[:width] < img[:height]
cmd.gravity gravity
cmd.background "rgba(255,255,255,0.0)"
cmd.extent "#{width}x#{height}"
end
end
img = yield(img) if block_given?
img
end
end

An improvment of Andy H's answer:
process :resize => [220, 220]
protected
def resize(width, height, gravity = 'Center')
manipulate! do |img|
img.combine_options do |cmd|
cmd.resize "#{width}"
if img[:width] < img[:height]
cmd.gravity gravity
cmd.background "rgba(255,255,255,0.0)"
cmd.extent "#{width}x#{height}"
end
end
img = yield(img) if block_given?
img
end
end

Related

path name contains null byte for image url while existing

I'm using the following code to get the width of image using Dimensions Gem.
file_path = "http://localhost:3000/uploads/resize/avatar/25/ch05.jpg"
width = Dimensions.width(open(file_path).read)
When I put the image url in url bar it renders the image in browser. what I'm trying to do is to get the width of image. so can anyone know what I'm doing wrong?
So your issue is that Dimensions requires a file path to determine the width of the image. open will return a StringIO and open(...).read will return a String both will fail when using File.open.
Dimensions#width
def width(path)
io_for(path).width
end
Dimensions#io_for
def io_for(path)
Dimensions(File.open(path, "rb")).tap do |io|
io.read
io.close
end
end
To work around this you can download the image to a Tempfile and then use that path to pass to Dimensions.width like so
path = "http://localhost:3000/uploads/resize/avatar/25/ch05.jpg"
t = Tempfile.new # you could add a name but it doesn't matter
t.write(open(path).read) # write the image to the Tempfile
t.close # must close the file before reading it
width = Dimensions.width(t.path) # pass the Tempfile path to Dimensions
t.unlink # deletes the Tempfile
We can make this look a little cleaner like so:
def get_width_of_url_image(url)
t = Tempfile.new.tap do |f|
f.write(open(url).read)
f.close
end
width = Dimensions.width(t.path)
t.unlink and width
end
get_width_of_url_image("https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png")
#=> 272

How to manage quality of an image with gem MiniMagik

I'm working on an App which gets shipment labels from Express Carriers in Ruby on Rails 4. At the moment I´m working with the UPS API and its label which is GIF image.
To be able to generate the PDF containing the GIF label I'm using Prawn which not support GIF. So the GIF is converted to a PNG. I´m able to have the label but when I print it out the quality is very low and difficult to read what is inside the label.
I need to understand how to add a better quality as otherwise, the label is not possible to use it.
The class I'm working on:
class UPSLabelConsolidation
attr_reader :base64_encoded_gif_labels
def initialize(base64_encoded_gif_labels)
#base64_encoded_gif_labels = base64_encoded_gif_labels
end
def perform!
identifier = SecureRandom.hex(3)
tmp_label_files = base64_encoded_gif_labels.each_with_index.map do |base64_encoded_gif_label, index|
tmp_label_file = Tempfile.new(["ups-label-#{identifier}-#{index}", ".png"], Rails.root.join("tmp"))
tmp_label_file.binmode
image = MiniMagick::Image.read(Base64.decode64(base64_encoded_gif_label), "gif")
image.combine_options do |b|
b.rotate "90" # Rotate 90-degrees clockwise
b.crop "800x1000+0+0" # Crop ~200px whitespace from the bottom of the label
end
image.format("png") # Prawn does not support GIF, so we convert it here.
image.write(tmp_label_file.path)
tmp_label_file.rewind
tmp_label_file
end
new_page = false
#pdf = Prawn::Document.new(page_size: [297, 390], margin: 20, page_layout: :portrait) # Margin is ~0.7cm
tmp_label_files.each do |label_file|
#pdf.start_new_page if new_page
#pdf.image label_file, width: 250, position: :left # Width is ~8.8cm
new_page = true
end
end
def write!(path)
if #pdf
#pdf.render_file(path)
else
raise "`#perform!` before `#write!`"
end
end
end

How to encrypt url in rails

For example, I have the url in project: http://localhost:3000/images/20/thumb/300x300. Where 300x300 - dynamic params in url for crop width and height of image. How I can encrypt this url? May be by add the token for http header? I need this to protect the server from generates different urls with different width and height of image (100x100, 150x200, 300x200...) Show code example, please.
From your question I understand that you want server to render only one of accepted dimensions. So instead encrypt the URL, you can just filter it in your controller
...
ALLOW_THUMB_SIZES = %w( 100x100 150x200 300x200 )
...
def generate_image
thumb_size = params[:thumb_size]
if ALLOW_THUMB_SIZES.include? thumb_size
# do resize image to thumb_size here
else
# resize to some default size e.g. 300x300
# or throw exception...
end
end
...
You can use this at your route:
get 'images/:id/thumb/:size', size: /^[0-9]+x[0-9]+$/
and at your controller you can access id and size of the image like this:
def show
#image= Image.find( params[:id] )
width, height=params[:size].split("x").map{|s| s.to_i}
# ...
end
If you have few fixed size of image that you accept then you can use constraints like following:
Rails.application.routes.draw do
get 'images/:id/thumb/:size', size: /^[0-9]+x[0-9]+$/,
constraints: ImageSizeConstraint.new
end
class ImageSizeConstraint
def matches?(request)
params = request.path_parameters
if %w(100x100 150x200 300x200).include? params[:size]
return true
else
return false
end
end
end

How to crop image manually with paperclip?

I'm building a website with Rails 4.2 and Mongoid. I'm using mongoid-paperclip, and I'm trying to crop an image down to a square while preserving the dimensions of the short side (so the image will fill the full square). Here's my custom processor:
module Paperclip
class Cropper < Thumbnail
def initialize(file, options = {}, attachment = nil)
super
#preserved_size = [#current_geometry.width, #current_geometry.height].min
#current_geometry.width = #preserved_size
#current_geometry.height = #preserved_size
end
def target
#attachment.instance
end
def transformation_command
if crop_command
crop_command + super.join(' ').sub(/ -crop \S+/, '').split(' ')
else
super
end
end
def crop_command
["-crop", "#{#preserved_size}x#{#preserved_size}+#{#preserved_size}+#{#preserved_size}"]
end
end
end
And the model that it's attached to looks has this line:
has_mongoid_attached_file :image, styles: {square: {processors: [:cropper]}}
But it doesn't seem to work. A version of the image named 'square' is saved, but it's identical to the original. How can I get this to work?
I was able to fix this without using a paperclip processor. In my model, I specified the styles for the image using a lambda:
has_mongoid_attached_file :image, styles: lambda {|a|
tmp = a.queued_for_write[:original]
return {} if tmp.nil?
geometry = Paperclip::Geometry.from_file(tmp)
preserved_size = [geometry.width.to_i, geometry.height.to_i].min
{square: "#{preserved_size}x#{preserved_size}#"}
}
Note that the # at the end of the dimensions ensured that the cropped image was always the specified dimensions, rather than just scaling down the image.

Carrierwave Cropping

I have an CarrierWave ImageUploader which creates a couple of versions of an original image that need to be cropped based on values in my model (crop_x, crop_y, crop_w, and crop_h).
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
...
version :t do
process :cropper
process :resize_to_fill => [75, 75]
end
...
def cropper
manipulate! do |img|
img = img.crop "#{model.crop_x}x#{model.crop_y}+#{model.crop_w}+#{model.crop_h}"
img
end
end
end
The problem that I'm having is that I need to calculate some default cropping values if we don't have any set but I don't know where to put this logic. I tried putting this in my Photo model (which the uploader is mounted to) in a before_validation but this seems to be called after the cropper function has executed. I'm thinking that It either needs to be in the ImageUploader file, or in some callback that occurs before the thumbs are created.
You can do something like this:
process :cropper
def cropper
manipulate! do |img|
if model.crop_x.blank?
image = MiniMagick::Image.open(current_path)
model.crop_w = ( image[:width] * 0.8 ).to_i
model.crop_h = ( image[:height] * 0.8 ).to_i
model.crop_x = ( image[:width] * 0.1 ).to_i
model.crop_y = ( image[:height] * 0.1 ).to_i
end
img.crop "#{model.crop_w}x#{model.crop_h}+#{model.crop_x}+#{model.crop_y}"
end
end
I'm running code equivalent to that in one of my apps.

Resources