Создание плоского PDF-файла с помощью Python

Когда я печатаю PDF-файл из любого из исходных PDF-файлов, размер файла уменьшается, и текстовые поля, представленные в форме, удаляются. Короче говоря, он сглаживает файл. Я хочу добиться такого поведения.

Следующий код для создания PDF-файла с использованием другого PDF-файла в качестве источника (того, который я хочу сгладить), он также записывает форму текстовых полей.

Могу ли я получить PDF-файл без текстовых полей, сгладить его? Точно так же, как Adobe, когда я печатаю PDF как PDF.

Другой мой код выглядит примерно так, за исключением некоторых вещей:

import os
import StringIO
from pyPdf import PdfFileWriter, PdfFileReader
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter

directory = os.path.join(os.getcwd(), "source")  # dir we are interested in
fif = [f for f in os.listdir(directory) if f[-3:] == 'pdf'] # get the PDFs
for i in fif:
    packet = StringIO.StringIO()
    can = canvas.Canvas(packet, pagesize=letter)
    can.rotate(-90)
    can.save()

    packet.seek(0)
    new_pdf = PdfFileReader(packet)
    fname = os.path.join('source', i)
    existing_pdf = PdfFileReader(file(fname, "rb"))
    output = PdfFileWriter()
    nump = existing_pdf.getNumPages()
    page = existing_pdf.getPage(0)
    for l in range(nump):
        output.addPage(existing_pdf.getPage(l))
    page.mergePage(new_pdf.getPage(0))
    outputStream = file("out-"+i, "wb")
    output.write(outputStream)
    outputStream.close()
    print fName + " written as", i

Подводя итог: у меня есть PDF-файл, я добавляю к нему текстовое поле, скрывая информацию и добавляя новую информацию, а затем распечатываю PDF-файл из этого PDF-файла. Текстовое поле становится недоступным для редактирования или перемещения. Я хотел автоматизировать этот процесс, но все, что я пробовал, по-прежнему позволяло редактировать это текстовое поле.


person MakeCents    schedule 19.11.2014    source источник
comment
Также ищу решение этой проблемы. У меня есть скрипт Python для водяных знаков, но водяной знак мешает при попытке выделить или выделить текст в документе. Если бы я мог сгенерировать PDF-файл с плоскими водяными знаками, а затем объединить его с исходными PDF-файлами, это решило бы проблему.   -  person Joseph Mansfield    schedule 18.11.2015
comment
Соответствуют ли имена файлов определенному соглашению? если да, то какой семантический? Зачем нужно разбивать имя файла по пробелу, а затем по запятой? (в противном случае сценарий не работает, но я не уверен, имеет ли он отношение к проблеме, с которой вы столкнулись)   -  person gpoo    schedule 22.11.2015
comment
+ MakeCents Я не могу воспроизвести проблему. Я не получаю коробок. Можете ли вы вставить изображение с полученным результатом и ожидаемым результатом?   -  person gpoo    schedule 22.11.2015
comment
@gpoo Я думаю, что коробки существуют в оригиналах, однако я не знаю, что это за коробка, у меня есть pdf-файл с коробкой на первой странице, но я не могу удалить его путем печати (возможно, Acrobat Pro это делает)   -  person rll    schedule 23.11.2015
comment
@gpoo В то время я собирался сделать следующее: у меня есть PDF-файл, я добавляю к нему текстовое поле, скрывая информацию и добавляя новую информацию, а затем распечатываю PDF-файл из этого PDF-файла. Текстовое поле становится недоступным для редактирования или перемещения. Я хотел автоматизировать этот процесс, но все, что я пробовал, по-прежнему позволяло редактировать это текстовое поле. Надеюсь, это проясняет ситуацию. Я использую Acrobat 9.5   -  person MakeCents    schedule 23.11.2015


Ответы (5)


Если возможна установка пакета ОС, вы можете использовать pdftk с его оболочкой python pypdftk следующим образом:

import pypdftk
pypdftk.fill_form('filled.pdf', out_file='flattened.pdf', flatten=True)

Вам также потребуется установить пакет pdftk, что в Ubuntu можно сделать так:

sudo apt-get install pdftk

Библиотеку pypdftk можно загрузить из PyPI:

pip install pypdftk

Обновление: pdftk был ненадолго удален из Ubuntu в версии 18.04, но кажется, что он вернулся с 20.04.

person naktinis    schedule 23.11.2015
comment
есть ли способ сделать это без pdftk? Я спрашиваю, потому что пытаюсь написать клон pdftk, поскольку pdftk не работает на centos7. Любая помощь будет принята с благодарностью. - person Oscar Smith; 08.08.2016
comment
это не работает на ubuntu 18.04, поскольку pdftk больше не находится в репо - person Fabrizio Miano; 01.05.2020
comment
@FabrizioMiano Я видел, как здесь люди обсуждали обходные пути: askubuntu.com/a/1029451/70751 также qpdf может быть альтернативой . - person naktinis; 01.05.2020
comment
pdftk работает как принтер pdf, поэтому cups может работать. - person caot; 20.04.2021
comment
Похоже, pdftk вернулся в Ubuntu 20.04 и новее. Он был перенесен на Java и может быть установлен как pdftk или pdftk-java. Последний визит 21.04.2020 - person naktinis; 09.07.2021

Согласно Adobe Docs, вы можете изменить битовую позицию редактируемых полей формы на 1, чтобы сделать поле доступным только для чтения. Я предоставил здесь полное решение, но оно использует Django:

https://stackoverflow.com/a/55301804/8382028

Adobe Docs (страница 552):

https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf

Используйте PyPDF2 для заполнения полей, затем прокрутите аннотации, чтобы изменить битовую позицию:

from io import BytesIO
import PyPDF2
from PyPDF2.generic import BooleanObject, NameObject, IndirectObject, NumberObject

# open the pdf
input_stream = open("YourPDF.pdf", "rb")
pdf_reader = PyPDF2.PdfFileReader(input_stream, strict=False)
if "/AcroForm" in pdf_reader.trailer["/Root"]:
    pdf_reader.trailer["/Root"]["/AcroForm"].update(
        {NameObject("/NeedAppearances"): BooleanObject(True)})

pdf_writer = PyPDF2.PdfFileWriter()
set_need_appearances_writer(pdf_writer)
if "/AcroForm" in pdf_writer._root_object:
    # Acro form is form field, set needs appearances to fix printing issues
    pdf_writer._root_object["/AcroForm"].update(
        {NameObject("/NeedAppearances"): BooleanObject(True)})

data_dict = dict() # this is a dict of your form values

pdf_writer.addPage(pdf_reader.getPage(0))
page = pdf_writer.getPage(0)
# update form fields
pdf_writer.updatePageFormFieldValues(page, data_dict)
for j in range(0, len(page['/Annots'])):
    writer_annot = page['/Annots'][j].getObject()
    for field in data_dict:
        if writer_annot.get('/T') == field:
            writer_annot.update({
                NameObject("/Ff"): NumberObject(1)    # make ReadOnly
            })
output_stream = BytesIO()
pdf_writer.write(output_stream)

# output_stream is your flattened PDF


def set_need_appearances_writer(writer):
    # basically used to ensured there are not 
    # overlapping form fields, which makes printing hard
    try:
        catalog = writer._root_object
        # get the AcroForm tree and add "/NeedAppearances attribute
        if "/AcroForm" not in catalog:
            writer._root_object.update({
                NameObject("/AcroForm"): IndirectObject(len(writer._objects), 0, writer)})

        need_appearances = NameObject("/NeedAppearances")
        writer._root_object["/AcroForm"][need_appearances] = BooleanObject(True)


    except Exception as e:
        print('set_need_appearances_writer() catch : ', repr(e))

    return writer  
person ViaTech    schedule 22.03.2019

Простой, но более обходной способ превратить PDF в изображения, чем поместить эти изображения в PDF.

Вам понадобятся pdf2image и PIL

Вот так

from pdf2image import convert_from_path 
from PIL import Image

images = convert_from_path('temp.pdf') 
im1 = images[0]
images.pop(0)

pdf1_filename = "flattened.pdf"

im1.save(pdf1_filename, "PDF" ,resolution=100.0, save_all=True, append_images=images)

Редактировать:

Я создал для этого библиотеку под названием fillpdf.

pip install fillpdf

from fillpdf import fillpdfs
fillpdfs.flatten_pdf('input.pdf', 'newflat.pdf')
person Tyler Houssian    schedule 20.11.2020
comment
Я думаю, что OP все еще пытается сохранить векторные свойства содержимого страницы, тогда как это предложение преобразовало бы документ в изображение (с низким разрешением)? - person AllanLRH; 21.11.2020
comment
@AllanLRH Исправьте векторные свойства страницы будут потеряны, и это приведет к изображению с более низким разрешением, но изображение по-прежнему отлично читается и может использоваться во многих различных случаях, когда читаемость является единственным требованием. - person Tyler Houssian; 23.11.2020

Решение, которое также подходит для Windows, конвертирует многие страницы PDF и выравнивает значения чекбокса. По какой-то причине код @ViaTech не работал на моем компьютере (Windows7 python 3.8)

Следовал указаниям @ViaTech и широко использовал код @hchillon из этого сообщения

from PyPDF2 import PdfFileReader, PdfFileWriter
from PyPDF2.generic import BooleanObject, NameObject, IndirectObject, TextStringObject, NumberObject


def set_need_appearances_writer(writer):

    try:
        catalog = writer._root_object
        # get the AcroForm tree and add "/NeedAppearances attribute
        if "/AcroForm" not in catalog:
            writer._root_object.update({
                NameObject("/AcroForm"): IndirectObject(len(writer._objects), 0, writer)})

        need_appearances = NameObject("/NeedAppearances")
        writer._root_object["/AcroForm"][need_appearances] = BooleanObject(True)
        return writer

    except Exception as e:
        print('set_need_appearances_writer() catch : ', repr(e))
        return writer



class PdfFileFiller(object):

    def __init__(self, infile):

        self.pdf = PdfFileReader(open(infile, "rb"), strict=False)
        if "/AcroForm" in self.pdf.trailer["/Root"]:
            self.pdf.trailer["/Root"]["/AcroForm"].update(
            {NameObject("/NeedAppearances"): BooleanObject(True)})

    # newvals and newchecks have keys have to be filled. '' is not accepted
    def update_form_values(self, outfile, newvals=None, newchecks=None):

        self.pdf2 = MyPdfFileWriter()


        trailer = self.pdf.trailer['/Root'].get('/AcroForm', None)
        if trailer:
            self.pdf2._root_object.update({
                NameObject('/AcroForm'): trailer})

        set_need_appearances_writer(self.pdf2)
        if "/AcroForm" in self.pdf2._root_object:
            self.pdf2._root_object["/AcroForm"].update(
            {NameObject("/NeedAppearances"): BooleanObject(True)})

        for i in range(self.pdf.getNumPages()):
            self.pdf2.addPage(self.pdf.getPage(i))

            self.pdf2.updatePageFormFieldValues(self.pdf2.getPage(i), newvals)
            for j in range(0, len(self.pdf.getPage(i)['/Annots'])):
                writer_annot = self.pdf.getPage(i)['/Annots'][j].getObject()
                for field in newvals:
                    writer_annot.update({NameObject("/Ff"): NumberObject(1)})

            self.pdf2.updatePageFormCheckboxValues(self.pdf2.getPage(i), newchecks)

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


class MyPdfFileWriter(PdfFileWriter):

    def __init__(self):
        super().__init__()

    def updatePageFormCheckboxValues(self, page, fields):

        for j in range(0, len(page['/Annots'])):
            writer_annot = page['/Annots'][j].getObject()
            for field in fields:
                writer_annot.update({NameObject("/Ff"): NumberObject(1)})




origin = ## Put input pdf path here
destination = ## Put output pdf path here, even if the file does not exist yet

newchecks = {} # A dict with all checkbox values that need to be changed
newvals = {'':''} # A dict with all entry values that need to be changed
# newvals dict has to be equal to {'':''} in case that no changes are needed

c = PdfFileFiller(origin)
c.update_form_values(outfile=destination, newvals=newvals, newchecks=newchecks)
print('PDF has been created\n')
person Charalamm    schedule 17.11.2020

У меня возникли проблемы с выравниванием формы, в которую я ввел содержимое, с помощью pdfrw (Как заполнить заполняемые PDF-файлы с помощью Python) и обнаружил, что мне пришлось добавить дополнительный шаг с помощью generate_fdf (pdftk flatten теряет заполняемое поле данные).

os.system('pdftk '+outtemp+' generate_fdf output '+outfdf)
os.system('pdftk '+outtemp+' fill_form '+outfdf+' output '+outpdf)

Я пришел к этому решению, потому что мне удалось просто сгладить файл, используя pdf2ps от ghostscript, а затем ps2pdf на моем Mac, но качество имело низкое разрешение, когда я запускал его на экземпляре Amazon Linux. Я не мог понять, почему это так, и поэтому перешел на решение pdftk.

person user3473556    schedule 08.01.2019