2012-05-07 29 views
8

Mam formularz PDF, który należy wypełnić kilka razy (dokładniej jest to arkusz czasu pracy). Teraz, ponieważ nie chcę tego robić ręcznie, szukałem sposobu na wypełnienie ich za pomocą skryptu Pythona lub narzędzi, które mogłyby być użyte w skrypcie basha.Wypełnianie wsadowe formularzy PDF z pythonu lub basha

Czy ktoś ma z tym doświadczenie?

+0

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

Odpowiedz

12

dla Pythona musisz lib fdfgen i pdftk

komentarz @Hugh Bothwell jest 100% poprawne więc będę przedłużać tę odpowiedź z realizacji pracy.

Jeśli używasz Windowsa, musisz się upewnić, że zarówno Python, jak i Pdftk są zawarte w ścieżce systemowej (chyba że chcesz używać długich nazw folderów).

Oto kod do automatycznego partii napełnić zbiór formularzy PDF z pliku danych w formacie 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) 

Uwaga: To nie powinno być rakieta-chirurgia, aby dowiedzieć się, jak dostosować ten. Początkowe deklaracje zmiennych zawierają niestandardową konfigurację.

W pliku CSV w pierwszym wierszu każda kolumna będzie zawierała nazwę odpowiadającej nazwy pola w pliku PDF. Wszystkie kolumny, które nie mają odpowiednich pól w szablonie, zostaną zignorowane.

W szablonie PDF po prostu utwórz edytowalne pola, w których chcesz wypełnić dane, i upewnij się, że nazwy pasują do danych CSV.

Dla tej konkretnej konfiguracji, po prostu umieść ten plik w tym samym folderze co plik NVC.csv, NVC.pdf i folder o nazwie "output". Uruchom go i automagicznie zajmie się resztą.

+0

To działa pięknie. Jedyne co musiałem dodać to ścieżka do PDFtk: 'code'os.environ ['PATH'] + = os.pathsep + 'C: \\ Program Files (x86) \\ PDFtk \\ bin;' – Suzanne

0

Zastąp oryginalny plik

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

To prawdopodobnie miało być komentarzem do powyższej odpowiedzi. –

3

Znacznie szybciej wersję, nie pdftk ani fdfgen potrzebne, czysty Python 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

To jest świetne !!! Dziękuję za tę łatwą odpowiedź – SmittySmee

+0

pokazuje błąd składni tutaj {k: f '# {i} {k} = {v}'. za pomocą python 3.5. czy to jest powód? –