В настоящее время я использую расширение PyPDF2 с Python, и у меня есть данные (которые изначально представляли собой форму Google), а затем они загружаются в виде файла CSV, и я надеюсь скопировать эти данные в существующий PDF-файл с полями, аналогичными исходной форме Google, но не буду быть единообразным. На веб-сайте PyPDF2 они предлагают несколько примеров (https://pypdf2.readthedocs.io/en/3.0.0/user/forms.html), но кажется, что они создают совершенно новый PDF-файл для перемещения исходных данных. в, а не в существующий PDF-файл. Я неправильно читаю их код?
Это код, который у меня есть на данный момент. Я знаю, что первые несколько строк до тех пор, пока «прочитает существующий PDF-файл...» работают, и они отображают CSV-файл в том виде, в каком он предназначен для списка, но после этого я только что ввел код с веб-сайта PyPDF2 и добавил еще несколько описательных комментарии, чтобы попытаться понять это. Имеет ли смысл использовать PyPDF2 для поиска полей формы в существующем PDF-файле и использовать цикл for для перебора файла CSV для вставки соответствующей информации?
import csv
from PyPDF2 import PdfReader, PdfWriter
# opens csv file and returns a file object - type of file is “_io.TextIOWrapper”
file = open("AfF.csv")
csvreader = csv.reader(file)
# creates an empty list called header and obtains the header from each row
header = next(csvreader)
print(header)
# iterates through csvobject and append each row to the rows list
rows = []
for row in csvreader:
rows.append(row)
print(rows)
# reads an existing PDF file "form.pdf" that contains fillable form fields
reader = PdfReader("form.pdf")
fields = reader.get_form_text_fields() # extracts text fields from the PDF form, stores extracted form fields in the "fields" variable
fields == {"key": "value", "key2": "value2"} # fields will contain a dictionary mapping field names (keys) to their corresponding curret values (value) in the PDF form
# fills out form fields in PDF "form.pdf" and saves the filled PDF as "filled-out.pdf"
reader = PdfReader("form.pdf") # instantiates "PdfReader" object "reader" for reading the existing PDF file
writer = PdfWriter() #instatiates "PdfWriter" object "writer" for creating a new PDF
page = reader.pages[0] # retrieves the first page of pdf
fields = reader.get_fields() # gets all form fields from the PDF
writer.add_page(page) # add the retrieved page (page) to the PdfWriter object (writer) using writer.add_page(page)
writer.update_page_form_field_values( # uses this to update the form field values on the first page (writer.page[0]) with a dictionary specifying field names and their new values {"fieldname": "some filled in text"}
writer.pages[0], {"fieldname": "some filled in text"}
)
# write "output" to PyPDF2-output.pdf
with open("filled-out.pdf", "wb") as output_stream: # write the modified pdf to "filled-out.pdf" by opening a binary file "wb" and using writer.write(output_stream)
writer.write(output_stream)
В примере открывается один PDF-файл с помощью программы чтения, а затем копируется в программу записи. Этот шаг является обязательным, поскольку вы не можете открыть PDF-файл для «редактирования» с помощью PyPDF2. Код примера также сохраняет его в другой файл, создавая копию на диске. Я бы сказал, что этот пример соответствует шаблону пустого PDF-файла с полями, который служит шаблоном, и они ожидают, что вы захотите создать заполненные копии на основе динамических данных. Я предполагаю, что из ваших данных Google Forms вам понадобится один PDF-файл для каждой строки отправленных значений формы. Если да, читайте дальше.
Кроме того, если вам не нужен конкретно PyPDF2, рассмотрите pypdf : усилия, которые были направлены на PyPDF2, были изменены на pypdf. Чтение из pypdf: Назад к истокам (2023-сегодня):
Чтобы упростить задачу новичкам, PyPDF2 был снова объединен с pypdf. Теперь все строчные, без цифр. Мы надеемся, что к нам присоединятся и разработчики PyPDF3 и PyPDF4.
Если не считать этого... Я бы начал как можно проще и постепенно продвигался вверх.
Я создал этот простой PDF-файл (который вы можете скачать и следить за ним) всего с двумя полями: «Имя» и «Любимый цвет»:
Я буду использовать pypdf, чтобы получить имена полей:
from pypdf import PdfReader
reader = PdfReader("form.pdf")
page = reader.pages[0]
fields = reader.get_fields()
print(fields)
и я получаю:
{
'Name': {'/T': 'Name', '/FT': '/Tx'},
'Fav_color': {'/T': 'Fav_color', '/FT': '/Tx'}
}
Часть {'/T' ...}
не имеет значения, только имена клавиш, Name и Fav_color.
Затем используйте эту программу чтения и попробуйте обновить Name и Fav_color:
from pypdf import PdfWriter
writer = PdfWriter()
writer.append(reader)
fields = {"Name": "Alice", "Fav_color": "blue"}
writer.update_page_form_field_values(
writer.pages[0],
fields,
auto_regenerate=False,
)
with open("filled-out.pdf", "wb") as output_stream:
writer.write(output_stream)
Открываю заполненный .pdf, а он выглядит так:
так что это сработало! Затем я попытался объединить это в функцию, которая позволяла бы мне указать новое имя и значения полей, которые нужно использовать:
def fill_out_pdf(new_name: str, fields: dict[str, str]):
reader = PdfReader("form.pdf")
page = reader.pages[0]
writer = PdfWriter()
writer.append(reader)
writer.update_page_form_field_values(
writer.pages[0],
fields,
auto_regenerate=False,
)
with open(new_name, "wb") as output_stream:
writer.write(output_stream)
fill_out_pdf("filled-out.pdf", {"Name": "Alice", "Fav_color": "blue"})
и это выглядит так же, как указано выше.
После этого я мог бы попытаться интегрировать динамические данные из CSV:
Name,Favorite color
Alice,blue
Bobbie,blue
Charlie,vermilion
Для простоты в этом примере я буду использовать csv.reader и сопоставлять позиции полей CSV в строке (отсчет от 0) с именами полей PDF, 0 → имя, 1 → любимый цвет:
import csv
with open("input.csv", newline = "") as f:
reader = csv.reader(f)
next(reader) # skip header
rows = list(reader)
for row in rows:
name = row[0]
fav_color = row[1]
new_name = f"{name}.pdf"
fields = {"Name": name, "Fav_color": fav_color}
fill_out_pdf(new_name, fields)
Когда я запускаю это, я получаю три PDF-файла, которые выглядят так:
Тем не менее, это очень простой пример: всего одна страница PDF и никаких проблем с самим PDF-файлом.
Такая работа может быстро усложниться, поскольку проблемы с самим PDF-файлом могут привести к тому, что любое поле может выглядеть неправильно. Я работал над проектом, в котором одно поле из более чем 300 полей отображалось неправильно в сохраненной заполненной версии: явно не проблема с программой Python... просто что-то глубокое, в PDF-файле. Так что будьте внимательны и удачи!
Порядок не должен иметь значения. Возможно, первым шагом будет понять, какие поля у вас общие между CSV и PDF, даже если они не имеют одинаковых названий, и выяснить соответствие между ними?
И я считаю, что вы можете получить все значения из CSV в один словарь полей, а затем применить этот единственный словарь к любой странице в PDF-файле, и если на этой странице есть имена полей, соответствующие ключам в вашем словаре, их следует применить. . Длинный способ сказать, что порядок и страницы не имеют значения, имеют значение только имена между ключами в вашем словаре и полями на странице PDF… Я верю. Я проверю это позже.
Первоначальная концепция Acrobat Forms принадлежала не только Adobe, но они приобрели права на Acello JetForms, что послужило основанием для обесценивания XFA (чего следует избегать).
Цель заключалась в том, чтобы обеспечить быструю текстовую передачу «только данных» из PDF-файла на принимающий сервер электронной почты. Теперь это обычно делается более безопасно через PHP-сервер https://.
Основная технология очень проста: вы заставляете пользователя нажать кнопку отправки и автоматически сохранить входящий текст в базе данных. И наоборот, вам не нужно много раздутых форм, просто «Пустой шаблон» и данные через одноразовую текстовую обертку для отправки наружу.
Система достаточно сложна, чтобы передавать изображения, но редко используется таким образом, поскольку ею сложно управлять. Здесь мы видим справа устаревшую возможность Adobe Reader, которая позволяет программно импортировать и экспортировать данные.
Требование состоит в том, чтобы значение /Value (переменная в текстовой, шестнадцатеричной или другой кодировке передачи) было связано с полем /Tagged или /Titled (предпочтительно нижний регистр). Порядок не имеет значения, и /T может присутствовать без /V, но должно быть в паре, если /Value должно быть назначено. В обычном тексте пара ключей или отдельный тег должны быть обернуты << /(ключ) (пара) /(ключ) (пара) >>
%FDF-1.4
1 0 obj<</FDF <</F (SO-form.pdf)/Fields [<<
/V (Goldenrod Yellow) /T (Fav_color)
>> <<
/T (Name) /V (Alice Pleasance Liddell)
>>] /ID [<D549C1EB4E09240A886799276170ED1A> <D549C1EB4E09240A886799276170ED1A>]/UF (SO-form.pdf)>>/Type /Catalog>>endobj
trailer
<</Root 1 0 R>>
%%EOF
Многие высокоскоростные заполнители форм PDF используют этот метод в той или иной ФОРМЕ. Adobe предоставила разработчикам набор инструментов FDF.
Для Python есть несколько, которые, вероятно, все еще используют устаревшие возможности PDFtk. Но существуют различия между использованием формата версии 1.2 и 1.4.
Основная причина, я добавляю эту историю.
Поясним: вам следует использовать ТОЛЬКО этот подход типа FDF при редактировании других форм XFA.
В противном случае достаточно хрупкие внутренние структуры почти наверняка будут нарушены, в первую очередь потому, что он использует идентификатор для имени файла и действий, связанных с соответствующими данными.
Ваш вклад был очень полезен! Файл CSV и PDF, с которыми я работаю, имеют десятки, если не более 100 полей, и мне интересно, порекомендуете ли вы иметь одну функцию, которая циклично проходит по всем полям и ищет совпадающие заголовки в CSV/PDF, а затем использует Функция fill_out_pdf сверху для обновления PDF-файла. Моя главная проблема заключается в том, что заголовки не обязательно расположены в одном и том же порядке.