AlegoPdf

Gem responsável por juntar 1 ou mais PDF's no mesmo arquivo

Instalação

Adicione esta linha no seu Gemfile:

gem 'alego_pdf'

Desenvolvimento

Para desenvolvimento, use no seu gemfile o seguinte código:

gem 'alego_shared', path: '../alego_shared'

E execute:

$ bundle install

Ou instale isto você mesmo usando o comando:

$ gem install alego_pdf

Uso

A gem 'alego_pdf' depende da gem 'hexapdf' para fazer a juntada de arquivos. Veja mais sobre a gem 'hexapdf' no Github: https://github.com/gettalong/hexapdf. A gem 'alego_pdf' busca os objetos, faz a leitura dos arquivos via URI e Tempfile e a gem 'hexapdf' junta-os. Veja o exemplo na gem alego_pdf:

require 'hexapdf'
require 'open-uri'

module AlegoPdf
  class Merging
    attr_reader :ids, :model, :attribute, :filename, :urls, :orderize, :target

    def initialize(ids, model, attribute, filename = 'tmp/merge_files.pdf', urls = [], orderize = 'url')
      @ids = ids
      @model = model
      @attribute = attribute
      @filename = filename
      @urls = urls
      @orderize = orderize
      @target = HexaPDF::Document.new
    end

    def call
      if orderize == 'url'
        join_urls
        join_array
      else
        join_array
        join_urls
      end

      target.write(filename, optimize: true)
    end

    def join_urls
      urls.each do |filepath|
        next if filepath.blank?

        uri = URI.parse(filepath).open(&:read)
        tempfile = Tempfile.new
        tempfile.write(uri.force_encoding('UTF-8'))
        tempfile.close

        pdf = HexaPDF::Document.open(tempfile)
        pdf.pages.each { |page| target.pages << target.import(page) }
      end 
    end

    def join_array
      array = model.is_a?(String) ? model.camelize.constantize.where(id: ids) : model.where(id: ids)

      array.each do |object|
        file = object[attribute]
        filepath = file.try(:url) || object.try(:arquivo_url)

        next if filepath.blank?

        uri = URI.parse(filepath).open(&:read)
        tempfile = Tempfile.new
        tempfile.write(uri.force_encoding('UTF-8'))
        tempfile.close

        pdf = HexaPDF::Document.open(tempfile)
        pdf.pages.each { |page| target.pages << target.import(page) }
      end
    end
  end
end

Obs.: Os parâmetros 'ids', 'model' e 'attribute' são obrigatórios. O parâmetro 'filename' é por default 'tmp/merge_files.pdf', porém pode-se atribuir um caminho pré-definido. o atributo 'filename' será o local e nome onde será salvo o arquivo temporário. O parâmetro 'urls' é um array de url's e serve para ler PDF's a partir de url e juntá-los num mesmo arquivo. Default = []. Utilizar os 'ids' e as 'urls', serve para juntar arquivos além de um model apenas, como acontece no projeto 'emendas-rails' da Alego. Após instalar a gem 'alego_pdf', deve criar uma rota '/merge_arquivos' em seu projeto e deve buscar os ids dos arquivos e passá-los como parâmetro junto com o model, o atributo e as urls que onde é guardado os metadados dos arquivos. A gem procura também por um atributo chamado 'arquivo_url', caso não encontre a 'url' (ex.: attribute.try(:url)). Ainda no método call, com o atributo 'orderize' podemos ordenar por urls ou ids no arquivo PDF. Default = 'url'. Veja o exemplo a seguir:

# organogramas_controller.rb
def merge_arquivos
    organograma_ids = ::Transparencia::Organograma.order('data desc').map(&:id)

    respond_to do |format|
        format.html
        format.pdf do
            url = 'https://teste.com/teste.pdf' # url precisa ser um PDF
            filename = 'tmp/organogramas.pdf' # nome e local onde será salvo o arquivo temporário
            AlegoPdf::Merging.new(organograma_ids, ::Transparencia::Organograma, :arquivo, filename, [url].compact).call

            pdf_filename = File.join(Rails.root, filename)
            send_file(pdf_filename, filename: 'organogramas.pdf', type: 'application/pdf')
        end
    end
end

PS: caso não exista nenhum arquivo, é feito download de uma página em branco, portanto certifique-se que contenha ao menos 1 objeto com upload PDF antes de fazer a chamada AlegoPdf::Merging ou apresentar o botão na view que chama a rota e faz o download dos arquivos. Veja o exemplo:

// index.html.erb
<% @organograma_pdf_any = ::Transparencia::Organograma.any?(&:arquivo_url) # verifica se existe ao menos 1 url do arquivo %>

<% if @organograma_pdf_any %>
    <a href="/organogramas/merge_arquivos.pdf" target="_blank" title="Juntar arquivos" class="bt bt--secundario">
        Todos organogramas
        <svg class="ico">
        <use xlink:href="<%= asset_path('ico.svg#download') %>" />
        </svg>
    </a>
<% end %>

Contribuição

Relatórios de bugs e pull requests são bem-vindos no GitLab em https://gitlab.al.go.leg.br/sistemas/gem/alego_pdf.