2012-05-07 12 views
8

Ho un modulo PDF che deve essere compilato un paio di volte (è una scheda attività per l'esattezza). Ora, dal momento che non voglio farlo a mano, stavo cercando un modo per riempirli usando uno script python o strumenti che potrebbero essere usati in uno script bash.Compila moduli PDF da Python o bash

Qualcuno ha esperienza con questo?

+0

Vedi http://stackoverflow.com/questions/1890570/how- can-i-auto-populate-a-pdf-form-in-django-python –

risposta

12

Per Python è necessario il lib fdfgen e PDFTK

commento di @Hugh Bothwell è corretta al 100% quindi mi estendo quella risposta con un'implementazione di lavoro.

Se ci si trova in Windows, è necessario assicurarsi che sia python sia pdftk siano contenuti nel percorso di sistema (a meno che non si desideri utilizzare nomi di cartelle lunghi).

Ecco il codice di auto-batch riempire una collezione di moduli PDF da un file di dati CSV:

import csv 
from fdfgen import forge_fdf 
import os 
import sys 

sys.path.insert(0, os.getcwd()) 
filename_prefix = "NVC" 
csv_file = "NVC.csv" 
pdf_file = "NVC.pdf" 
tmp_file = "tmp.fdf" 
output_folder = './output/' 

def process_csv(file): 
    headers = [] 
    data = [] 
    csv_data = csv.reader(open(file)) 
    for i, row in enumerate(csv_data): 
     if i == 0: 
     headers = row 
     continue; 
     field = [] 
     for i in range(len(headers)): 
     field.append((headers[i], row[i])) 
     data.append(field) 
    return data 

def form_fill(fields): 
    fdf = forge_fdf("",fields,[],[],[]) 
    fdf_file = open(tmp_file,"w") 
    fdf_file.write(fdf) 
    fdf_file.close() 
    output_file = '{0}{1} {2}.pdf'.format(output_folder, filename_prefix, fields[1][1]) 
    cmd = 'pdftk "{0}" fill_form "{1}" output "{2}" dont_ask'.format(pdf_file, tmp_file, output_file) 
    os.system(cmd) 
    os.remove(tmp_file) 

data = process_csv(csv_file) 
print('Generating Forms:') 
print('-----------------------') 
for i in data: 
    if i[0][1] == 'Yes': 
    continue 
    print('{0} {1} created...'.format(filename_prefix, i[1][1])) 
    form_fill(i) 

Nota: Non dovrebbe essere razzo-chirurgia di capire come personalizzare questo. Le dichiarazioni delle variabili iniziali contengono la configurazione personalizzata.

Nel CSV, nella prima riga ogni colonna conterrà il nome del campo corrispondente nel file PDF. Qualsiasi colonna che non ha campi corrispondenti nel modello verrà ignorata.

Nel modello PDF, è sufficiente creare campi modificabili in cui si desidera riempire i dati e assicurarsi che i nomi corrispondano ai dati CSV.

Per questa configurazione specifica, basta mettere questo file nella stessa cartella di NVC.csv, NVC.pdf e una cartella denominata 'output'. Eseguilo e automaticamente fa il resto.

+0

Questo funziona magnificamente. L'unica cosa che dovevo aggiungere era il percorso di PDFtk: 'code'os.environ ['PATH'] + = os.pathsep + 'C: \\ Programmi (x86) \\ PDFtk \\ bin;' – Suzanne

0

Sostituisci file originale

os.system('pdftk "original.pdf" fill_form "data.fdf" output "output.pdf"') 
os.remove("data.fdf") 
os.remove("original.pdf") 
os.rename("output.pdf","original.pdf") 
+0

Probabilmente è stato pensato per essere un commento ad una risposta sopra. –

3

Molto più veloce versione, non pdftk nè fdfgen necessario, Python puro 3.6 e versioni successive:

# -*- coding: utf-8 -*- 

from collections import OrderedDict 
from PyPDF2 import PdfFileWriter, PdfFileReader 


def _getFields(obj, tree=None, retval=None, fileobj=None): 
    """ 
    Extracts field data if this PDF contains interactive form fields. 
    The *tree* and *retval* parameters are for recursive use. 

    :param fileobj: A file object (usually a text file) to write 
     a report to on all interactive form fields found. 
    :return: A dictionary where each key is a field name, and each 
     value is a :class:`Field<PyPDF2.generic.Field>` object. By 
     default, the mapping name is used for keys. 
    :rtype: dict, or ``None`` if form data could not be located. 
    """ 
    fieldAttributes = {'/FT': 'Field Type', '/Parent': 'Parent', '/T': 'Field Name', '/TU': 'Alternate Field Name', 
         '/TM': 'Mapping Name', '/Ff': 'Field Flags', '/V': 'Value', '/DV': 'Default Value'} 
    if retval is None: 
     retval = OrderedDict() 
     catalog = obj.trailer["/Root"] 
     # get the AcroForm tree 
     if "/AcroForm" in catalog: 
      tree = catalog["/AcroForm"] 
     else: 
      return None 
    if tree is None: 
     return retval 

    obj._checkKids(tree, retval, fileobj) 
    for attr in fieldAttributes: 
     if attr in tree: 
      # Tree is a field 
      obj._buildField(tree, retval, fileobj, fieldAttributes) 
      break 

    if "/Fields" in tree: 
     fields = tree["/Fields"] 
     for f in fields: 
      field = f.getObject() 
      obj._buildField(field, retval, fileobj, fieldAttributes) 

    return retval 


def get_form_fields(infile): 
    infile = PdfFileReader(open(infile, 'rb')) 
    fields = _getFields(infile) 
    return OrderedDict((k, v.get('/V', '')) for k, v in fields.items()) 


def update_form_values(infile, outfile, newvals=None): 
    pdf = PdfFileReader(open(infile, 'rb')) 
    writer = PdfFileWriter() 

    for i in range(pdf.getNumPages()): 
     page = pdf.getPage(i) 
     try: 
      if newvals: 
       writer.updatePageFormFieldValues(page, newvals) 
      else: 
       writer.updatePageFormFieldValues(page, 
               {k: f'#{i} {k}={v}' 
                for i, (k, v) in enumerate(get_form_fields(infile).items()) 
                }) 
      writer.addPage(page) 
     except Exception as e: 
      print(repr(e)) 
      writer.addPage(page) 

    with open(outfile, 'wb') as out: 
     writer.write(out) 


if __name__ == '__main__': 
    from pprint import pprint 

    pdf_file_name = '2PagesFormExample.pdf' 

    pprint(get_form_fields(pdf_file_name)) 

    update_form_values(pdf_file_name, 'out-' + pdf_file_name) # enumerate & fill the fields with their own names 
    update_form_values(pdf_file_name, 'out2-' + pdf_file_name, 
         {'my_fieldname_1': 'My Value', 
         'my_fieldname_2': 'My Another alue'}) # update the form fields 
+0

Questo è fantastico !!! Grazie per questa semplice risposta – SmittySmee

+0

mostra l'errore di sintassi qui {k: f '# {i} {k} = {v}'. usando python 3.5. è questa la ragione? –