Генерация PDF с помощью Prawn

    Prawn Rails PDF
    Введение

    В топике я рассмотрю создание PDF-документов средствами Ruby/Rails, их генерацию, скачивание напрямую с сайта или создание через rake task, а также возможность отправки во вложении через Mailer.
    Работать будем с Rails 3.0.9 и gem'ом Prawn.


    Установка Prawn

    Bundler

    gem 'prawn', :git => "git://github.com/sandal/prawn.git", :submodules => true


    Интеграция с Рельсами

    Создадим директорию для классов отчетов:
    $ mkdir app/reports

    Для автоподгрузки классов из этой директории в config/application.rb добавим:
    config.autoload_paths << "#{Rails.root}/app/reports"

    И зарегистрируем mime-тип (config/initializers/mime_types.rb)
    Mime::Type.register_alias "application/pdf", :pdf

    Включим gem в автозагрузку (предварительно создав файл) config/initializers/prawn.rb
    require "prawn"

    Проверим, работает ли Prawn (я использую RVM)
    $ rails c
    Loading development environment (Rails 3.0.9)
    ruby-1.9.2-p180 :001 > Prawn::BASEDIR
     => "/home/kir/.rvm/gems/ruby-1.9.2-p180@pdfer/bundler/gems/prawn-1288242ddece"

    Генерируем PDF

    Чтобы Prawn поддерживал кириллицу, необходимо предоставить ему русские шрифты. Положим их в домашнюю директорию, у меня это /home/kir/prawn_fonts. «Вердану», проверенную в работе с Prawn, я выложил тут.

    Чтобы иметь какую-нибудь таблицу с данными, создадим скаффолдер с моделью Customer.

    rails g scaffold customer name:string amount:float
    rake db:migrate


    Наполним таблицу любыми данными.

    Теперь можно создать файл-генератор отчета. Пусть это будет app/reports/customers_report.rb со следующим кодом:

    # encoding: utf-8
    class CustomersReport < Prawn::Document
      # ширина колонок
      Widths = [200, 200, 120]
      # заглавия колонок
      Headers = ['Дата добавления', 'Клиент', 'Баланс']
      
      def row(date, customer_name, amount)
        row = [date, customer_name, amount]
        make_table([row]) do |t|
          t.column_widths = Widths
          t.cells.style :borders => [:left, :right], :padding => 2
        end
      end
        
      def to_pdf
        # привязываем шрифты
        font_families.update(
          "Verdana" => {
            :bold => "/home/kir/prawn_fonts/verdanab.ttf",
            :italic => "/home/kir/prawn_fonts/verdanai.ttf",
            :normal  => "/home/kir/prawn_fonts/verdana.ttf" })
        font "Verdana", :size => 10
        text "Отчет за #{Time.zone.now.strftime('%b %Y')}", :size => 15, :style => :bold, :align => :center
        move_down(18)
        # выборка записей
        @customers = Customer.order('created_at')
        data = []
        items = @customers.each do |item|
          data << row(item.created_at.strftime('%d/%m/%y %H:%M'), item.friendly_name, item.)
        end
        
        head = make_table([Headers], :column_widths => Widths)
        table([[head], *(data.map{|d| [d]})], :header => true, :row_colors => %w[cccccc ffffff]) do
          row(0).style :background_color => '000000', :text_color => 'ffffff'
          cells.style :borders => []
        end
        # добавим время создания внизу страницы
        creation_date = Time.zone.now.strftime("Отчет сгенерирован %e %b %Y в %H:%M")
        go_to_page(page_count)
        move_down(710)
        text creation_date, :align => :right, :style => :italic, :size => 9
        render
      end
      
    end

    Генератор PDF готов. Добавим в контроллер Customers action, который будет отвечать за скачивание PDF'a.
    
    def download_pdf
      output = CustomersReport.new.to_pdf
      send_data output, :type => 'application/pdf', :filename => "customers.pdf"
    end

    Не забудьте прописать route для этого экшена! Например, так:
    resources :customers do
      collection do
        get 'download_pdf'
      end
    end

    Переходим по адресу /customers/download_pdf. В результате получится вот такой документ.

    rake task


    Но, допустим, что мы хотим генерировать PDF через rake task. Рассмотрим реализацию (код lib/tasks/customers_report.rake):
    
    namespace :customers_report do
      desc 'Generates report with customers'
      task :generate_pdf => :environment do
        output = CustomersReport.new.to_pdf
        filename = "report.pdf"
        File.open(Rails.root.join('public', filename), 'wb') do |f|
          f.write(output)
        end
        puts "Report was written to #{filename}"
      end
    end

    Выполняем: rake customers_report:generate_pdf --trace
    Отчет будет генерироваться в public/report.pdf.

    rake task + mailer


    Еще одна ситуация: PDF надо отправлять ежемесячно на e-mail. Для этого нам понадобится создать mailer и еще один task.
    Сгенерируем mailer: rails g mailer customers_mailer report_email и изменим код app/mailers/customers_mailer.rb:
    
    class CustomersMailer < ActionMailer::Base
      default :from => "me@yandexteam.ru" # укажите свой адрес!
      def report_email(pdf_output, to)
        report_filename = Time.zone.now.strftime('Report %d-%m-%Y')
        attachments[report_filename] = {
          :mime_type => 'application/pdf',
          :content => pdf_output
        }
        mail(:to => to, :subject => report_filename.titleize)
      end
    end

    И также прописываем task для его отправки (тот же lib/tasks/customers_report.rake):
    
    namespace :customers_report do
      desc 'Generates report with customers'
      task :generate_pdf => :environment do
        #...
      end
      
      desc 'Send report by email'
      task :send_by_email => :environment do
        # можно указывать массивом
        # recipient = ["vasya@gmail.com", "petya@gmail.com"]
        recipient = 'me@yandexteam.ru' # укажите свой адрес!
        output = CustomersReport.new.to_pdf
        CustomersMailer.report_email(output, recipient).deliver
        puts "Report was sent to #{recipient}"
      end
    end

    Готово. Не забудьте указать настоящие адреса и настройки action_mailer в параметрах окружения!
    Проверяем работоспособность: rake customers_report:send_by_email --trace

    Ссылки:

    Репозиторий Prawn на Github: https://github.com/sandal/prawn/
    Мой репозиторий с демо-приложением, описанным в статье: https://github.com/kirs/pdfer
    Об установке Prawn на Гитхабе: https://github.com/sandal/prawn/wiki/Using-Prawn-in-Rails
    P.S. Большое спасибо Дмитрию Галинскому из evrone за апрельскую презентацию на Rails Club!
    Share post

    Similar posts

    Comments 0

    Only users with full accounts can post comments. Log in, please.