Converting PDF page previews using Minimagic and Active Storage and Rails 6

Hello Folks!

In this post I am going to demonstrate that how to upload PDF file into Rails using Active Storage and create image previews of pages of uploaded PDF. I am going to explain everything required to achieve the task into this post but I would like you to prefer my following blog posts because both will give you idea of adding bootstrap and active storage into rails app respectively.

How to add bootstrap 4 to your Rails 6 application?

Uploading Attachments in Rails Using Active Storage

However this is not nacessory though, because I will mainly cover the creating previews of PDF images part and at the end of this blog post I will provide the repository url for the work we have done in this post.

So enough talking and let's start with the task now. Just go through following steps:

1. Create a new rails app

Here I am creating a new rails application but please read the post carefully and you can apply the main feature in your existing application as well.

                
                  rails new pdf-preview-demo
                
              
                  
                  cd pdf-preview-demo
                
              
                  
                  bundle install
                
              

2. Create a model for uploading PDF

Here at this stage I am creating a model with name document, but I am just taking this for an example, you can choose any name for your resource. You just need to do few things in your model class.

Run following command into your terminal:

                
                  rails g scaffold document title:string description:text
                
              

3. Adding Active storage to application

Since Active Storage also require active_storage_blobs and active_storage_attachments tables to store attachments so we also need to create these tables as well. You need to run following command for getting migration for both tables:

                
                  rails active_storage:install
                
              

4. Run Migration

Now Run following commands to get all tables into your schema:

                
                  rails db:migrate
                
              

5. Add attachments attributes into model

Now we need attachments attributes for our model class. Since we are going to create preview of uploaded PDF pages, we require two attachment fields. Add both fields into your model. Open app/models/document.rb and following lines into it:

                
                  # This is for uploading PDF
has_one_attached :doc_file # This is uploading inages of pages of PDF
has_many_attached :doc_file_pages

So far at this stage your model should look like as below:

                
                  class Document < ApplicationRecord
                    validates :title, :description, presence: true
                    # Active storage attachments
                    has_one_attached :doc_file
                    has_many_attached :doc_file_pages
                  end
                
              

6. Permitting doc_file as params

Open your app/controllers/documents_controller.rb and replace following method

                
                  def document_params
                    params.require(:document).permit(:title, :description)
                  end
                
              

with following:

                
                  def document_params
                    params.require(:document).permit(:title, :description, :doc_file)
                  end
                
              

7. Add doc_file field to form

Now since active storage attachment attributes will not be added to the table of model class, we need to add this into form manually. Add following snippet into app/views/documents/_form.html.erb, before submit button

                
                  
                    <div class="field">
                      <%= form.label :upload_your_pdf_file_here %><br>
                      <%= form.file_field :doc_file, required: true %>
                    </div>
                  
                
              

8. Add Minimagic gem to Gemfile

Add following line to bottom of your Gemfile and Run bundle install:

                
                  gem 'mini_magick', '>= 4.9.5'
                
              

now run

                
                  bundle install
                
              

9. Add method to convert pdf pages into images into model

Open app/models/document.rb and add following method into it:

                
                  def convert_pdf_to_image
                    document = self
                    # path to current doc_file
                    doc_path = ActiveStorage::Blob.service.send(:path_for, document.doc_file.key)
                    # set minimagick image wrapper for pdf stored in @doc.uplaod
                    magick = MiniMagick::Image.open(doc_path)
                    magick.pages.each_with_index do |page, index|
                      file_name = "page_#{(index+1).to_s}"
                      # set path for tempfile that you are about to create
                      converted_file_path = File.join(Dir.tmpdir, "#{file_name}-#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}-.png")
                      # create png and save to tempfile path
                      MiniMagick::Tool::Convert.new do |convert|
                        # prep format
                        convert.background "white"
                        convert.flatten
                        convert.density 300
                        convert.quality 100
                        # add page to be converted,./
                        convert << page.path
                        # add path of page to be converted
                        convert << converted_file_path
                      end
                      document.doc_file_pages.attach(io: File.open(converted_file_path), filename: file_name, content_type: "image/png")
                    end
                  end
                
              

10. Call convert_pdf_to_image method after saving document

Since now we have method for converting pdf pages into images into our model class we need to call this method into controller right after saving the document. Open your app/controllers/documents_controller.rb and replace create action into it by following method:

                
                  def create
                    @document = Document.new(document_params)
                    respond_to do |format|
                      if @document.save
                        @document.convert_pdf_to_image
                        format.html { redirect_to @document, notice: "Document was successfully created." }
                        format.json { render :show, status: :created, location: @document }
                      else
                        format.html { render :new, status: :unprocessable_entity }
                        format.json { render json: @document.errors, status: :unprocessable_entity }
                      end
                    end
                  end
                
              

And you done with all steps here.

Now give it a try. Open http://localhost:3000/documents/new and Create a document. Upload a PDF file of minimum of 2 pages (you can add single page PDF as well but to test previews in better way, I would recommend to upload a multipage PDF).

11. Display PDF pages as images

Now since you have upload your document and created its preview, you will definitely like to see the images. No worries, just open your app/views/documents/show.html.erb and paste following snippet into it:

                
                  <% if @document.doc_file_pages.attached? %>
                    <% @document.doc_file_pages.each do |doc_page| %>
                      
                        <div style="margin-top: 20px;">
                          <%= image_tag doc_page, style: "width:500px;height:500px;" %>
                        </div>
                      
                    <% end %>
                  <% end %>
                
              

That's it! We are done. Now you can customise this according to your need. But the main challenge for creating previews of PDF has been solved.

WARNING: Minimagic Auth Error While Creating Preview

You might get following error while creating preview images of pdf pages.

                
                  MiniMagick::Invalid (`identify /tmp/mini_magick20210515-8656-tjz3ib` failed with error:
                  identify-im6.q16: not authorized `/tmp/mini_magick20210515-8656-tjz3ib' @ error/constitute.c/ReadImage/412.
                  ):
                
              

This error usually occurs if you use Minimagic first time on your system or try to create tmp files using Minimagic

Don't Worry.

Do following things:

Run following command on terminal:

                
                  sudo subl /etc/ImageMagick-6/policy.xml
                
              

and find following line into the file opened:

                
                  
                    <policy domain="coder" rights="none" pattern="PDF" />
                  
                
              

and replace with following line:

                
                  
                    <policy domain="coder" rights="read | write" pattern="PDF" />
                  
                
              

Now you done completely.

You can find more organised codebase for this blog post here: https://github.com/RaviSys/pdf-preview-demo.