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
Related
Rails 5.2.0 (as API)
/config/application.rb
config.active_storage.variant_processor = :vips
Problem:
/serializers/api/v1/user/current_user_serializer.rb
class Api::V1::User::CurrentUserSerializer < Api::V1::User::BaseSerializer
include Rails.application.routes.url_helpers
attributes(
[...]
:avatar
:created_at
)
def avatar
if object.avatar.attachment
avatar = {
image: url_for( object.avatar ), # This one works
thumb: url_for( object.avatar.variant(resize_to_fit: [800, 800]) ), # EXCEPTION
thumb_test: url_for( object.avatar.variant(resize: '800x800') ) # Returns image of size: 640x800 (expected 800x800)
}
end
end
end
I get the following exception:
exception: "<MiniMagick::Error: `mogrify -resize-to-fit [800, 800] /tmp/mini_magick20180625-19749-rghjbg.jpg` failed with error: mogrify.im6: unrecognized option `-resize-to-fit' # error/mogrify.c/MogrifyImageCommand/5519. >"
EDIT
Thanks #George Claghorn
I now created my own variant based on this post:
https://prograils.com/posts/rails-5-2-active-storage-new-approach-to-file-uploads
lib/active_storage_variants.rb
class ActiveStorageVariants
class << self
def resize_to_fill(width:, height:, blob:, gravity: 'Center')
blob.analyze unless blob.analyzed?
cols = blob.metadata[:width].to_f
rows = blob.metadata[:height].to_f
if width != cols || height != rows
scale_x = width / cols
scale_y = height / rows
if scale_x >= scale_y
cols = (scale_x * (cols + 0.5)).round
resize = cols.to_s
else
rows = (scale_y * (rows + 0.5)).round
resize = "x#{rows}"
end
end
{
resize: resize,
gravity: gravity,
background: 'rgba(255,255,255,0.0)',
extent: cols != width || rows != height ? "#{width}x#{height}" : ''
}.merge(optimize_hash(blob))
end
end
end
/models/concerns/users/active_storage_variants.rb
require 'active_storage_variants' # /lib/active_storage_variants.rb
module Users::ActiveStorageVariants
def avatar_thumbnail
variation = ActiveStorage::Variation.new(
ActiveStorageVariants.resize_to_fill(
width: 300, height: 300, blob: avatar.blob
)
)
ActiveStorage::Variant.new(avatar.blob, variation)
end
end
/models/user.rb
class User < ApplicationRecord
...
## Concerns
include Users::ActiveStorageVariants
...
end
To call it:
user.avatar_thumbnail
resize_to_fit: [800, 800] is an ImageProcessing transformation. Rails 5.2 doesn’t use ImageProcessing and thus doesn’t support libvips; it uses MiniMagick directly instead.
Rails 6 will switch to ImageProcessing and add libvips support. To use libvips now, prior to the release of Rails 6, bundle the master branch of the rails/rails repository on GitHub:
# Gemfile
gem "rails", github: "rails/rails"
Found a solution here
Replacing this,
<%= image_tag user.avatar.variant(resize_to_fit: [100, 100]) %>
with,
<%= image_tag user.avatar.variant(resize: "100 x100") %>
Worked for me. Even though the office document does the former way.
So I have this code in my index action, would love to move it to a model, just a little confused on how to do it.
Original Code
def index
urls = %w[http://cltampa.com/blogs/potlikker http://cltampa.com/blogs/artbreaker http://cltampa.com/blogs/politicalanimals http://cltampa.com/blogs/earbuds http://cltampa.com/blogs/dailyloaf http://cltampa.com/blogs/bedpost]
#final_images = []
#final_urls = []
urls.each do |url|
blog = Nokogiri::HTML(open(url))
images = blog.xpath('//*[#class="postBody"]/div[1]//img/#src')
images.each do |image|
#final_images << image
end
story_path = blog.xpath('//*[#class="postTitle"]/a/#href')
story_path.each do |path|
#final_urls << path
end
end
end
I tested this code in my model and it works perfectly for one url, just not sure how to integrate all of the urls like the original code.
New Code
Model
class Photocloud < ActiveRecord::Base
attr_reader :url, :data
def initialize(url)
#url = url
end
def data
#data ||= Nokogiri::HTML(open(url))
end
def get_elements(path)
data.xpath(path)
end
end
Controller
def index
#scraper = Photocloud.new('http://cltampa.com/blogs/artbreaker')
#photos = #scraper.get_elements('//*[#class="postBody"]/div[1]//img/#src')
#story_urls = #scraper.get_elements('//*[#class="postBody"]/div[1]//img/#src')
end
My main questions are how would I initialize multiple urls and loop through them like my original code. I have tried different things but feel like I have hit a wall. I need to save them to the database, but would like to get this working first. Any help is greatly appreciated.
Updated Controller - WIP
def index
start_urls = %w[http://cltampa.com/blogs/potlikker
http://cltampa.com/blogs/artbreaker
http://cltampa.com/blogs/politicalanimals
http://cltampa.com/blogs/earbuds
http://cltampa.com/blogs/dailyloaf
http://cltampa.com/blogs/bedpost]
#scraper = Photocloud.new(start_urls)
#images =
#paths =
end
Need some help with this part...
It seems that you don't persist scraped images and paths to the database so Photocloud doesn't need to inherit from ActiveRecord::Base - it can be just a plain old ruby object (PORO):
class Photocloud
attr_reader :start_urls
attr_accessor :images, :paths
def initialize(start_urls)
#start_urls = start_urls
#images = []
#paths = []
end
def scrape
start_urls.each do |start_url|
blog = Nokogiri::HTML(open(url))
scrape_images(blog)
scrape_paths(blog)
end
end
private
def scrape_images(blog)
images = blog.xpath('//*[#class="postBody"]/div[1]//img/#src')
images.each do |image|
images << image
end
end
def scrape_paths(blog)
story_path = blog.xpath('//*[#class="postTitle"]/a/#href')
story_path.each do |path|
paths << path
end
end
end
In controller:
scraper = Photocloud.new(start_urls)
scraper.scrape
#images = scraper.images
#paths = scraper.paths
This is only one of the possibilities how you could structure code, of course.
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
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
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