2012-05-07 168 views
8

Tengo un formulario en PDF que debe completarse varias veces (es una hoja de tiempo para ser exactos). Ahora que no quiero hacer esto a mano, estaba buscando una manera de completarlos usando un script de python o herramientas que podrían usarse en un script bash.Lote llenar formularios PDF de python o bash

¿Alguien tiene experiencia con esto?

+0

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

Respuesta

12

Para Python que necesita la lib fdfgen y PDFTK

comentario de @Hugh Bothwell es 100% correcto así que voy a extender esa respuesta con una aplicación de trabajo.

Si se encuentra en Windows, también deberá asegurarse de que tanto python como pdftk estén incluidos en la ruta del sistema (a menos que desee usar nombres largos de carpeta).

Aquí está el código de auto-lotes llenar una colección de formularios PDF a partir de un archivo de datos 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: No debe ser por cohetes cirugía para averiguar cómo personalizar esto. Las declaraciones de variables iniciales contienen la configuración personalizada.

En el CSV, en la primera fila cada columna contendrá el nombre del campo correspondiente en el archivo PDF. Se ignorarán todas las columnas que no tengan campos correspondientes en la plantilla.

En la plantilla PDF, simplemente cree campos editables donde desee que se llenen sus datos y asegúrese de que los nombres coincidan con los datos CSV.

Para esta configuración específica, simplemente coloque este archivo en la misma carpeta que su NVC.csv, NVC.pdf, y una carpeta llamada 'salida'. Ejecútelo y automaticamente hace el resto.

+0

Esto funciona muy bien. Lo único que tuve que agregar fue la ruta a PDFtk: 'code'os.environ ['PATH'] + = os.pathsep + 'C: \\ Archivos de programa (x86) \\ PDFtk \\ bin;' – Suzanne

0

Sustituir el archivo original

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

Posiblemente fue un comentario de una respuesta anterior. –

3

Mucho más rápido versión, sin pdftk ni fdfgen necesario, Python puro 3.6+:

# -*- 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

Esto es genial! Gracias por esta sencilla respuesta – SmittySmee

+0

muestra un error de sintaxis aquí {k: f '# {i} {k} = {v}'. usando Python 3.5. es ese el motivo? –

Cuestiones relacionadas