Bad image quality with CarrierWave - ruby-on-rails

I want to create the ability for the user to upload an image into a pdf which will be generated inside of the app. To do so I'm using gem prawn to pdf creation and CarrierWave::MiniMagic to create thumbnail versions of images for a Rails 6 app, and I've noticed loss of quality when the images are downsized.
original image:
the same image inside of pdf:
Such a bad quality is not acceptable for me. I don't know if it's a common problem but I've found a few posts without any resolution, is there any alternative solution/tool to handle it? I have been struggling with this problem for weeks.
I don't have nothing special in my uploader file:
class LogoUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
EXTENSION_WHITELIST = %w[jpg jpeg png].freeze
storage :file
process convert: 'png'
version :thumb do
process resize_to_fit: [300, 300]
end
def extension_whitelist
EXTENSION_WHITELIST
end
end

Related

Carrierwave with MiniMagick is adding black background color to transparent .png when using remote_url with resize_to_[fit/fill/ect]

Expect the following Uploader, when I upload .png with no background via remote_XXX_url the :thumb version using a resize_to_fit is converted with a black background.
The original file uploaded is as the same as the original with the transparent background.
If I upload the image via a file_input the issue is not occurring.
Therefore I deduce that the issue is only occurring when doing a resize_to_XX transformation on remote_XXX_url file. Do you know what is happening ?
carrierwave (2.2.2) -- mini_magick (4.11.0) - The original image has a transparent background.
Image used : https://whirlpool-cdn.thron.com/delivery/public/thumbnail/whirlpool/pi-81b54ac7-7614-4a32-bdd1-a3f79db530d5/sckne7/std/320x320/859791401010.jpg?fill=zoom&fillcolor=rgba:255,255,255&scalemode=product
Thumb Image resized_to_fit
Image without resizing
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
def store_dir
"#{ENV['AWS_S3_BUCKET_NAME']}/#{model.class.to_s.underscore}/#{model.id}"
end
def extension_allowlist
%w(jpg jpeg gif png svg webp)
end
version :thumb do
process resize_to_fit: [150, 150]
end
def filename
#name ||= "#{mounted_as}_#{timestamp}.#{file.extension}" if original_filename.present?
end
def timestamp
var = :"##{mounted_as}_timestamp"
model.instance_variable_get(var) or model.instance_variable_set(var, Time.now.to_i)
end
end
Your original file appears to have the extension .jpg but actually contains a PNG image.
ImageMagick itself is smart enough to spot that and it won't cause you any trouble. I don't know or use carrierwave but it appears to be carrying forward the original .jpg extension and forcing ImageMagick to write a JPEG which will make transparent regions black. I think it's this line:
#name ||= "#{mounted_as}_#{timestamp}.#{file.extension}" if original_filename.present?

Preview of PDF while using Minimagick and Carrierwave in Rails

I've been trying to develop a site that allows for the uploading of various types of documents including pdfs. Below is my uploader:
class PictureUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :jpg do
process :convert_to_images
process :set_content_type_jpg
def convert_to_images(*args)
image = MiniMagick::Image.open(current_path)
image.pages.each_with_index do |page, index|
MiniMagick::Tool::Convert.new do |convert|
convert.background 'white'
convert.flatten
convert.density 300
convert.quality 95
convert << page.path
convert << "#{CarrierWave.root}/#{store_dir}/image-#{index}.jpg"
end
end
end
end
def set_content_type_jpg(*args)
self.file.instance_variable_set(:#content_type, "image/jpg")
end
def extension_whitelist
%w(jpg jpeg gif png pdf)
end
end
So far the site will allow uploads to the server of all of the whitelisted file types, and everything but pdfs will actually display. Pdfs, however, will only show a broken image rather than an image of that pdf. What can I do to fix this?
You should first convert the pdf into a jpg or other image as described here Then try to run the converted file through your above code

How to use carrierwave's from_version to reference the processed base image, rather than the unprocessed one

I am working on a site which needs to have a newsletter page, with thumbnails. The PDFs are stored on a different site, but that site does not have thumbnails, so I must generate them myself and link the thumbnail to that site.
I cannot store the original PDF — the linked version is canonical, and having out-of-date cached versions is unacceptable. The additional storage usage might also cause an extra monthly expense for the non-profit organisation the site is for, so I do not want to leave these lying around when they're not needed.
I have the following code:
class DocumentUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def content_type_whitelist
'application/pdf'
end
def full_filename(for_file)
parent_name = super(for_file)
return unless parent_name
extension = '.pdf'
base_name = parent_name.chomp(extension)
if version_name
base_name = base_name[version_name.size.succ..-1]
end
[base_name, version_name].compact.join("_") + '.png'
end
# Process files as they are uploaded:
process convert_pdf_front: [500, 500]
version :medium do
process convert_pdf_front: [160, 160]
end
version :small do
process convert_pdf_front: [80, 80]
end
private
def convert_pdf_front(width, height)
manipulate! do |img|
img.format('png') do |c|
c.resize "#{width}x#{height}"
end
img
end
end
end
This works as expected, but the medium and small versions are converted from the original PDF, making for a rather slow process.
If I replace the processing code with this:
version :full do
process convert_pdf_front: [500, 500]
end
version :medium, from_version: :full do
process convert: 'png'
process resize_to_fit: [160, 160]
end
version :small, from_version: :full do
process convert: 'png'
process resize_to_fit: [80, 80]
end
then the medium and small versions are converted from the full version, but the original PDF is left lying around.
Is there a way to use from_version to do the conversion from the converted original document — that is, from the 500x500 PNG rather than the PDF — that doesn't leave the original PDF cluttering the place up? Is there a special version name that points to the original document after it's gone through any process commands, rather than before?
(I would rather not simply have a reaper process nuke the PDFs later, as Carrierwave regards those as the originals. I'd like Carrierwave to treat the 500x500 PNG as the original in all cases and never store the PDF once it's done with the conversion process.)

Upload image default image to s3 with carrierwave

We are using carrierwave gem to upload our images on a rails project.
We need to support the creation of the object without an image, but the application will run on several platforms and we don't want to support objects without images in everyone o that platforms, so we want to have a default image.
So this was the first solution
class IconUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
def store_dir
"uploads/#{model.id}"
end
def default_url
"/images/icons/" + [version_name, "default.png"].compact.join('_')
end
version :thumbnail do
process resize_to_fill: [200, 200]
end
version :high_resolution do
process resize_to_fill: [400, 400]
end
end
The problem with this is that all the devices will come to the server (not to S3) looking for that default image.
At the same time its a problem with the multiple versions of the image.
So this second solution came:
class CategoryController < ApplicationController
...
def create
#category = Category.new(category_params)
#uploader = IconUploader.new(#category, :icon)
uploader.cache!(File.open("app/assets/images/photos/picture.jpg"))
#category.image=#uploader
#object.save
end
...
end
This solves the problem of the multiple resolutions and it always goes to S3 to get the image. But the problem here is that it will duplicate the default image on S3 many times.
Our third solution could be a to use an already created image on Amazon as default URL, but it has the same problem with the version so we don't even implement it.
Can any one think of a better solution for this problem?

With Carrierwave and Rails 3 is it possible to nicely manage image and non-image files with the same uploader?

In my Rails app I would like to allow users to upload either image or non-image files via Carrierwave. Currently Carrierwave is working fine handeling and processing image files, but unfortunately its dropping non-images files completely. Is there a clean way for a single Carrierwave uploader to process both image and non-image files?
I'll include my current uploader below:
class AssetUploader < 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 :resize_to_fill => [300, 300]
end
version :icon do
process :resize_to_fill => [48, 48]
end
def extension_white_list
%w(jpg jpeg gif png pdf doc xls docx xlsx ppt)
end
end
I had this exact problem. I solved it with the hoary comp sci solution of one-more-level-of-indirection: a trampoline/thunk method that dynamically decides whether to process based on file extension.
You can find the implementation here: https://gist.github.com/995663
(the naive approach of introducing logic in the version block actually doesn't work because of how the CarrierWave DSL works - the logic needs to be deferred until invocation)
I hope that helps.
I know this has been answered, but aaron's answer doesn't completely solve the problem. You can use what he suggested at https://gist.github.com/995663 if you only need to call process, but not if you want to selectively process with version.
For version, see the carrierwave wiki page here: https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Do-conditional-processing
You can now do this in your code, and it will only process the version block on images
version :thumb, :if => :image? do
process ...
end
protected
def image?(new_file)
new_file.content_type.include? 'image'
end

Resources