2013-02-07 23 views
14

In Rails3, sto usando il WickedPDF gem per eseguire il rendering di un formato PDF di uno dei miei modelli. Funziona perfettamente: /invoices/123 restituisce HTML, /invoices/123.pdf scarica un PDF.Ottenere PDF da WickedPDF per allegato via Carrierwave

Nel mio modello di fattura, sto utilizzando la gemma state_machine per tenere traccia dello stato della fattura. Quando una fattura passa dallo stato "non completato" a "fatturato", desidero prendere una copia del PDF della fattura e allegarlo al modello di fattura utilizzando CarrierWave.

Ho le tre parti che funzionano separatamente: il controller crea una vista PDF, il modello traccia lo stato e attiva una richiamata quando si effettua la transizione corretta e CarrierWave è impostato correttamente. Comunque, mi sto divertendo un mondo a farli giocare bene insieme.

Se volevo semplicemente prendere la versione HTML della fattura, potrei chiamare render_to_string dal modello. Ma render_to_string sembra soffocare sulla ricezione di un file binario PDF. Se riesco a recuperare un flusso di dati, è piuttosto semplice scrivere quei dati in un file temporaneo e collegarli all'uploader, ma non riesco a capire come ottenere il flusso di dati.

Qualche idea? Codice qui sotto:

regolatore Fattura

def show 
    @invoice = @agg_class.find(params[:id]) 

    respond_to do |format| 
    format.pdf do 
     render_pdf 
    end 
    format.html # show.html.erb 
    format.json { render json: @aggregation } 
    end 
end 

...

def render_pdf(options = {}) 
    options[:pdf] = pdf_filename 
    options[:layout] = 'pdf.html' 
    options[:page_size] = 'Letter' 
    options[:wkhtmltopdf] = '/usr/local/bin/wkhtmltopdf' 
    options[:margin] = { 
    :top  => '0.5in', 
    :bottom => '1in', 
    :left  => '0in', 
    :right => '0in' 
    } 
    options[:footer] = { 
    :html => { 
     :template => 'aggregations/footer.pdf.haml', 
     :layout  => false, 
    } 
    } 
    options[:header] = { 
    :html => { 
     :template => 'aggregations/header.pdf.haml', 
     :layout  => false, 
    } 
    } 
    render options 
end 

Invoice.rb

def create_pdf_copy 

    # This does not work.  
    pdf_file = ApplicationController.new.render_to_string(
    :action => 'aggregations/show', 
    :format => :pdf, 
    :locals => { 
     :invoice => self 
    } 
) 

    # This part works fine if the above works. 
    unless pdf_file.blank? 
    self.uploads.clear 
    self.uploads.create(:fileinfo => File.new(pdf_file), :job_id => self.job.id) 
    end 

end 

UPDATE trovato una soluzione.

def create_pdf_copy 

    wicked = WickedPdf.new 

    # Make a PDF in memory 
    pdf_file = wicked.pdf_from_string( 
     ActionController::Base.new().render_to_string(
      :template => 'aggregations/show.pdf.haml', 
      :layout  => 'layouts/pdf.html.haml', 
      :locals  => { 
       :aggregation => self 
      } 
     ), 
     :pdf => "#{self.type}-#{self}", 
     :layout => 'pdf.html', 
     :page_size => 'Letter', 
     :wkhtmltopdf => '/usr/local/bin/wkhtmltopdf', 
     :margin => { 
      :top  => '0.5in', 
      :bottom => '1in', 
      :left  => '0in', 
      :right => '0in' 
     }, 
     :footer => { 
      :content => ActionController::Base.new().render_to_string({ 
       :template => 'aggregations/footer.pdf.haml', 
       :layout => false 
      }) 
     }, 
     :header => { 
      :content => ActionController::Base.new().render_to_string({ 
       :template => 'aggregations/header.pdf.haml', 
       :layout => false 
      }) 
     } 
    ) 

    # Write it to tempfile 
    tempfile = Tempfile.new(['invoice', '.pdf'], Rails.root.join('tmp')) 
    tempfile.binmode 
    tempfile.write pdf_file 
    tempfile.close 

    # Attach that tempfile to the invoice 
    unless pdf_file.blank? 
     self.uploads.clear 
     self.uploads.create(:fileinfo => File.open(tempfile.path), :job_id => self.job.id) 
     tempfile.unlink 
    end 

end 
+0

OK, ho funzionato. Il passaggio 1 stava lavorando sul fatto che WickedPDF ignora le impostazioni globali di configurazione quando non è in esecuzione nel contesto di un controller. Passaggio 2: utilizzare Tempfile per salvare l'output PDF come tempfile in modalità binaria, quindi collegarlo a Carrierwave. Post originale modificato per riflettere la soluzione. – lascarides

+0

Potete per favore fornirmi maggiori dettagli sul punto 1? Avevo una vecchia versione di wkhtmltopdf localmente e stavo esportando con render_to_string bene. Sul server ho wkhtmltopdf 0.11.0, e render_to_string funziona ma PDF è illeggibile .. Aggiornato a 0.11.0 localmente e render_to_string indeboli .. Sembra essere lo stesso problema che stai avendo .. FYI: Io uso 'file = StringIO (render_to_string (options)) 'così posso saltare il Tempfile. Questo è con graffetta, ma potresti provare se ti piace l'idea. –

+2

Felice di averlo risolto. Dovresti scrivere la tua soluzione come risposta, e quindi accettare la tua risposta. Nessun danno nel rispondere alla tua stessa domanda! – sockmonk

risposta

6

Soluzione:

def create_pdf_copy 

    wicked = WickedPdf.new 

    # Make a PDF in memory 
    pdf_file = wicked.pdf_from_string( 
     ActionController::Base.new().render_to_string(
      :template => 'aggregations/show.pdf.haml', 
      :layout  => 'layouts/pdf.html.haml', 
      :locals  => { 
       :aggregation => self 
      } 
     ), 
     :pdf => "#{self.type}-#{self}", 
     :layout => 'pdf.html', 
     :page_size => 'Letter', 
     :wkhtmltopdf => '/usr/local/bin/wkhtmltopdf', 
     :margin => { 
      :top  => '0.5in', 
      :bottom => '1in', 
      :left  => '0in', 
      :right => '0in' 
     }, 
     :footer => { 
      :content => ActionController::Base.new().render_to_string({ 
       :template => 'aggregations/footer.pdf.haml', 
       :layout => false 
      }) 
     }, 
     :header => { 
      :content => ActionController::Base.new().render_to_string({ 
       :template => 'aggregations/header.pdf.haml', 
       :layout => false 
      }) 
     } 
    ) 

    # Write it to tempfile 
    tempfile = Tempfile.new(['invoice', '.pdf'], Rails.root.join('tmp')) 
    tempfile.binmode 
    tempfile.write pdf_file 
    tempfile.close 

    # Attach that tempfile to the invoice 
    unless pdf_file.blank? 
     self.uploads.clear 
     self.uploads.create(:fileinfo => File.open(tempfile.path), :job_id => self.job.id) 
     tempfile.unlink 
    end 

end