DEV Community

Thomas Blevins
Thomas Blevins

Posted on • Edited on

Creating thumbnails for multi-page Pdfs in Carrierwave 2.2.3 and Rails 7

Prerequisites:

  1. Have set up Carrierwave to upload files
  2. Know what version :thumb does
  3. Have imagemagick and ghostscript installed on the host machine.

Please review this Carrierwave Uploader as the final solution. The documentation should be worthy enough to explain what's going on.

class VersionUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick
  include CarrierWave::RMagick
  storage :fog

  # @doc Specifies the position on remote hosting to save the file
  def store_dir = "shared/system/#{klass}/#{attachment}/#{id_partition}/#{style}/"

  # @doc Returns the model class name, camel_case and pluralized.
  # @example 
  #   irb(main):038:0> MyModel.first.file.file.klass
  #   => "my_models"
  def klass = model.class.name.underscore.pluralize

  # @doc Specifies the uploader's association name that is mounted on the model.  In the model `MyModel.rb`, we mount an uploader called VersionUploader, and we call that mount `file`.  This is considered the `attachment`
  # @example
  #   irb(main):039:0> MyModel.first.file.file.attachment
  #   => "files"
  def attachment = mounted_as.to_s.downcase.pluralize

  # @doc Creates a section of the final path partitioned by the id's of the model
  # @note
  #   These appear to be the id of the model, with a weird operation done to them.
  #   I sourced this code from the paperclip compatibility source code of Carrierwave's github as in my situation, I was converting from Paperclip to Carrierwave
  def id_partition = ("%09d" % model.id).scan(/.{3}/).join("/")

  # @doc Specifies the style of file for storage directory purposes
  def style = :original

  # @doc Returns the first page of a file
  version :thumb do
    process :to_array
    process :front
    process convert: "jpg"
    def full_filename(filename = model.file.filename) = "#{filename.split(".").first}.jpg"
    def style = :thumb
  end

  private

  # @doc Utilizes minimagick to split the uploading file from it's temporary path into an array
  # @note This code was sourced from https://github.com/janko/image_processing/wiki/Splitting-a-PDF-into-multiple-images, though I've made a few edits
  def to_array
    MiniMagick::Image.new(path).pages.count.times.map{ |page_number|
      ImageProcessing::MiniMagick
        .source(path)
        .convert("jpg")
        .colorspace("RGB")
        .loader(page: page_number).call
    }.first
  end

  # @doc Used in conjunction with `to_array` to return the first page of the array of pages
  # @note This utilizes the module CarrierWave::RMagick in some way...
  def front
    manipulate! do |frame, index|
      frame if index.zero?
    end
  end

end
Enter fullscreen mode Exit fullscreen mode

Hopefully I've made this journey easier than mine was, thanks!

Top comments (3)

Collapse
 
renhiyama profile image
Ren Hiyama

Hi Thomas, could you make use of relevant hashtags so this blog can be shown more to users of the specified category & will also let users know what language it uses and other essential details. I dont know much about c++ or rust but it looks like this blog uses anyone of these. Do let me know what language it was!

Also, you could make use of language based code blocks so it highlights the code, by adding "js" or the language short name after the starting of the codeblock.
It changes from:

console.log("hello");
Enter fullscreen mode Exit fullscreen mode

To

console.log("hello");
Enter fullscreen mode Exit fullscreen mode

I hope I explained some necessary details that will aid you in making better blogs in the future! (Btw you can edit this blog and apply the fixes)

Collapse
 
tb-development profile image
Thomas Blevins

Thanks! I didn't notice the tags and forgot to include the syntax for the code brackets.

Collapse
 
renhiyama profile image
Ren Hiyama

Ah so it was ruby!