Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to thumbnail a multi-page pdf with paperclip

I'd like to have Paperclip create 2 thumbnails for each page of a multipage PDF file that is uploaded.

I'm running Paperclip 2.3.1.1 and using this in my Asset model:

    has_attached_file :asset,
                  :styles => { :medium => "800x600>", :thumb => "100x100>" }

So, when I upload a 3 page pdf file, I was hoping this would create 2 thumbs per page (one at 800x600 and a smaller image at 100x100). Instead, I get a 3 folders created (thumb, medium, original) - the original folder contains the origianl pdf file, while thumb and medium each contain a pdf with just the first page of the pdf all pixelated.

What do I need to do to get paperclip to create 2 thumbs for each page of the pdf? Ideally, I'd like one image per page like this (6 images created):


assets/1/medium/file-0.png

assets/1/medium/file-1.png

assets/1/medium/file-2.png

assets/1/thumb/file-0.png

assets/1/thumb/file-1.png

assets/1/thumb/file-2.png

Does anyone know how to do this? Do I need a custom processor? If so, what would the processor look like?

Thanks.

like image 905
Jim Jones Avatar asked Aug 16 '10 18:08

Jim Jones


2 Answers

Here how I implemented similar task.

Document model:

class Document < ActiveRecord::Base

  has_many :pages, :dependent => :destroy

  has_attached_file :asset

  after_asset_post_process :make_pages

  private

  def make_pages
    if valid?
      Paperclip.run('convert', "-quality #{Page::QUALITY} -density #{Page::DENSITY} #{asset.queued_for_write[:original].path} #{asset.queued_for_write[:original].path}%d.png")
      images = Dir.glob("#{asset.queued_for_write[:original].path}*.png").sort_by do |line|
        line.match(/(\d+)\.png$/)[1].to_i
      end

      images.each do |page_image|
        pages.build(:asset => File.open(page_image))
      end
      FileUtils.rm images
    end
  end
end

Page model:

class Page < ActiveRecord::Base

  belongs_to :document

  has_attached_file :asset

  QUALITY = 100
  DENSITY = '80x80'

end
like image 52
taro Avatar answered Nov 15 '22 08:11

taro


I have a half-working solution to this... but it isn't very elegant. I'd really like to come up with something better, but I thought I'd share anyway.

I started by defining a bunch of new styles, one for each page... up to however many pages I want to be able to handle. (stupid, I know, but I don't know how to access the path interpolations in Paperclip so that each page gets saved/deleted in the store properly unless there is a unique style for each image)

{ ...
:page_0 =>    {:geometry=>'800[0]',   :format=>:png, :processors=>[:multipage_thumbnail]},
:page_1 =>    {:geometry=>'800[1]',   :format=>:png, :processors=>[:multipage_thumbnail]},
:page_2 =>    {:geometry=>'800[2]',   :format=>:png, :processors=>[:multipage_thumbnail]},
:page_3 =>    {:geometry=>'800[3]',   :format=>:png, :processors=>[:multipage_thumbnail]},
:page_4 =>    {:geometry=>'800[4]',   :format=>:png, :processors=>[:multipage_thumbnail]},
:page_5 =>    {:geometry=>'800[5]',   :format=>:png, :processors=>[:multipage_thumbnail]},
}

Then... I have a custom processor which subclasses from the Thumbnail processor, with some extra logic for running the convert command with the proper page #.

module Paperclip
  # Handles thumbnailing images that are uploaded.
  class MultipageThumbnail < Thumbnail

    # 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
      @page = options[:geometry].match(/\[(\d+)\]/)[1] rescue 0
      @page ||= 0
      options[:geometry] = options[:geometry].sub(/\[\d+\]/, '')
      super
    end

    # Performs the conversion of the +file+ into a thumbnail. Returns the Tempfile
    # that contains the new image.
    def make
      return nil if @page >= page_count

      src = @file
      dst = Tempfile.new([@basename, @format].compact.join("."))
      dst.binmode

      begin
        options = [
          source_file_options,
          "#{ File.expand_path(src.path) }[#{@page}]",
          transformation_command,
          convert_options,
          "#{ File.expand_path(dst.path) }"
        ].flatten.compact

        success = Paperclip.run("convert", *options)
      rescue PaperclipCommandLineError => e
        raise PaperclipError, "There was an error processing the thumbnail for #{@basename}" if @whiny
      end

      dst
    end

    def page_count
      @page_count ||= begin
        files = Paperclip.run("identify", "#{@file.path}")
        files.split(/\n/).size
      rescue PaperclipCommandLineError
        1
      end
    end

  end
end
like image 28
jkrall Avatar answered Nov 15 '22 10:11

jkrall