Add text to Existing PDF using Python
Asked Answered
S

9

168

I need to add some extra text to an existing PDF using Python, what is the best way to go about this and what extra modules will I need to install.

Note: Ideally I would like to be able to run this on both Windows and Linux, but at a push Linux only will do.

Edit: pypdf and ReportLab look good but neither one will allow me to edit an existing PDF, are there any other options?

Schiff answered 24/7, 2009 at 20:58 Comment(1)
PyPDF2 allows you to copy every page + add a text annotation on top:Twoway
A
107

I know this is an older post, but I spent a long time trying to find a solution. I came across a decent one using only ReportLab and PyPDF so I thought I'd share:

  1. read your PDF using PdfFileReader(), we'll call this input
  2. create a new pdf containing your text to add using ReportLab, save this as a string object
  3. read the string object using PdfFileReader(), we'll call this text
  4. create a new PDF object using PdfFileWriter(), we'll call this output
  5. iterate through input and apply .mergePage(*text*.getPage(0)) for each page you want the text added to, then use output.addPage() to add the modified pages to a new document

This works well for simple text additions. See PyPDF's sample for watermarking a document.

Here is some code to answer the question below:

packet = StringIO.StringIO()
can = canvas.Canvas(packet, pagesize=letter)
<do something with canvas>
can.save()
packet.seek(0)
input = PdfFileReader(packet)

From here you can merge the pages of the input file with another document.

Auvil answered 1/2, 2010 at 23:28 Comment(2)
I recommend using PyPDF2 since it is more updated, also check their sample code: github.com/mstamy2/PyPDF2/blob/…Scheld
This code will create a new pdf file and will skip all metadata. So it's not appending to existing pdf.Archipenko
I
185

Example for [Python 2.7]:

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

packet = StringIO.StringIO()
can = canvas.Canvas(packet, pagesize=letter)
can.drawString(10, 100, "Hello world")
can.save()

#move to the beginning of the StringIO buffer
packet.seek(0)

# create a new PDF with Reportlab
new_pdf = PdfFileReader(packet)
# read your existing PDF
existing_pdf = PdfFileReader(file("original.pdf", "rb"))
output = PdfFileWriter()
# add the "watermark" (which is the new pdf) on the existing page
page = existing_pdf.getPage(0)
page.mergePage(new_pdf.getPage(0))
output.addPage(page)
# finally, write "output" to a real file
outputStream = file("destination.pdf", "wb")
output.write(outputStream)
outputStream.close()

Example for Python 3.x:


from PyPDF2 import PdfFileWriter, PdfFileReader
import io
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter

packet = io.BytesIO()
can = canvas.Canvas(packet, pagesize=letter)
can.drawString(10, 100, "Hello world")
can.save()

#move to the beginning of the StringIO buffer
packet.seek(0)

# create a new PDF with Reportlab
new_pdf = PdfFileReader(packet)
# read your existing PDF
existing_pdf = PdfFileReader(open("original.pdf", "rb"))
output = PdfFileWriter()
# add the "watermark" (which is the new pdf) on the existing page
page = existing_pdf.pages[0]
page.merge_page(new_pdf.pages[0])
output.add_page(page)
# finally, write "output" to a real file
output_stream = open("destination.pdf", "wb")
output.write(output_stream)
output_stream.close()
Inserted answered 9/7, 2013 at 0:16 Comment(10)
For python3, packet should be io.BytesIO and use PyPDF2 rather than pyPDF (which is unmaintained). Great answer!Wartime
Thanks for sharing. It works great. One note: I believe it's better to use open instead of file.Phonation
Careful: The new document only includes the first page of the original! It's easy enough to copy the rest of the pages from existing_pdf to output, the sample code just doesn't.Devoe
@alexis: How would you modify the code to put something on the second page of the pdf? I have a form that uses two pages and I am stuck on the first page. Thanks in advance.Laevogyrate
@alexis: I did and it does work (yesterday also worked but I had another problem to solve), but it's really really really really slow. Like 1 document per 10 seconds. And I need 250 of them. Any thoughts on how to fix that? Thanks.Laevogyrate
Mhh, get a faster computer? Buy a different solution from Adobe? I'm sorry but I'm not a regular user of this software, I don't even know why you are asking me. Profile your code to make sure the problem is in the pdf library, and take it from there. Or just go get lunch while your script runs, it'll be done before the hour is over (so, faster than you can code an improved solution.)Devoe
Is there a way to also ensure that fillable fields get merged? I'm trying this in combination with reportlab to add fillable fields to an existing document, but it seems because the merge only adds the pages themselves and not the fields, nothing in the merged document is fillable (including fields that were fillable in the original). How do I merge the fields as well? (I noticed that when copying a document, if I use appendPagesFromReader the copy isn't fillable, but if I use cloneReaderDocumentRoot, the copy is fillable. Is there a way to merge two roots?)Wintry
@Laevogyrate substitute 0 with 1Insider
page.mergePage(new_pdf.getPage(0)), here PageObject object has no attribute mergepageCarbonization
@David Dehghan Thanks a lot. The code draws the string in the first page only. How can I draw the string to all the pages of the existing pdf?Drynurse
A
107

I know this is an older post, but I spent a long time trying to find a solution. I came across a decent one using only ReportLab and PyPDF so I thought I'd share:

  1. read your PDF using PdfFileReader(), we'll call this input
  2. create a new pdf containing your text to add using ReportLab, save this as a string object
  3. read the string object using PdfFileReader(), we'll call this text
  4. create a new PDF object using PdfFileWriter(), we'll call this output
  5. iterate through input and apply .mergePage(*text*.getPage(0)) for each page you want the text added to, then use output.addPage() to add the modified pages to a new document

This works well for simple text additions. See PyPDF's sample for watermarking a document.

Here is some code to answer the question below:

packet = StringIO.StringIO()
can = canvas.Canvas(packet, pagesize=letter)
<do something with canvas>
can.save()
packet.seek(0)
input = PdfFileReader(packet)

From here you can merge the pages of the input file with another document.

Auvil answered 1/2, 2010 at 23:28 Comment(2)
I recommend using PyPDF2 since it is more updated, also check their sample code: github.com/mstamy2/PyPDF2/blob/…Scheld
This code will create a new pdf file and will skip all metadata. So it's not appending to existing pdf.Archipenko
C
19

pdfrw will let you read in pages from an existing PDF and draw them to a reportlab canvas (similar to drawing an image). There are examples for this in the pdfrw examples/rl1 subdirectory on github. Disclaimer: I am the pdfrw author.

Consuetudinary answered 11/7, 2015 at 4:47 Comment(1)
FWIW, there are some more reportlab/pdfrw examples if you start following this link. I answered there, based on an answer in the dupe target.Consuetudinary
P
8

cpdf will do the job from the command-line. It isn't python, though (afaik):

cpdf -add-text "Line of text" input.pdf -o output .pdf
Poundal answered 5/3, 2014 at 11:51 Comment(1)
Carefully check the license for cpdf before using - it's not Open Source.Annunciation
G
7

Leveraging David Dehghan's answer above, the following works in Python 2.7.13:

from PyPDF2 import PdfFileWriter, PdfFileReader, PdfFileMerger

import StringIO

from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter

packet = StringIO.StringIO()
# create a new PDF with Reportlab
can = canvas.Canvas(packet, pagesize=letter)
can.drawString(290, 720, "Hello world")
can.save()

#move to the beginning of the StringIO buffer
packet.seek(0)
new_pdf = PdfFileReader(packet)
# read your existing PDF
existing_pdf = PdfFileReader("original.pdf")
output = PdfFileWriter()
# add the "watermark" (which is the new pdf) on the existing page
page = existing_pdf.getPage(0)
page.mergePage(new_pdf.getPage(0))
output.addPage(page)
# finally, write "output" to a real file
outputStream = open("destination.pdf", "wb")
output.write(outputStream)
outputStream.close()
Giuditta answered 22/4, 2017 at 21:52 Comment(2)
If the existing pdf has multiple pages, how do you ensure the output has the same number of pages with only difference being the edited page? Im hoping there is a simpler way without making weird loopsDealer
PyPDF2 is deprecated. Please use pypdf: pypdf.readthedocs.io/en/stableTwoway
E
4

The PyPDF2 as of date of writing has depreciated the PdfFileReader, PdfFileWriter and few other methods and changed it to different names and methods and has also changed methods like getPage() directly to attribute of PdfReader.

Here is a very Simple Class to add text to existing pdf file: (Use is demonstrated at end)

from PyPDF2 import PdfWriter, PdfReader, Transformation
import io
from reportlab.pdfgen.canvas import Canvas

class GenerateFromTemplate:
    def __init__(self,template):
        self.template_pdf = PdfReader(open(template, "rb"))
        self.template_page= self.template_pdf.pages[0]

        self.packet = io.BytesIO()
        self.c = Canvas(self.packet,pagesize=(self.template_page.mediabox.width,self.template_page.mediabox.height))

    
    def addText(self,text,point):
        self.c.drawString(point[0],point[1],text)

    def merge(self):
        self.c.save()
        self.packet.seek(0)
        result_pdf = PdfReader(self.packet)
        result = result_pdf.pages[0]

        self.output = PdfWriter()

        op = Transformation().rotate(0).translate(tx=0, ty=0)
        result.add_transformation(op)
        self.template_page.merge_page(result)
        self.output.add_page(self.template_page)
    
    def generate(self,dest):
        outputStream = open(dest,"wb")
        self.output.write(outputStream)
        outputStream.close()

"""
Use as:
gen = GenerateFromTemplate("template.pdf")
gen.addText("Hello!",(100,200))
gen.addText("PDF!",(100,300))
gen.merge()
gen.generate("Output.pdf")
"""

Hope this helps.

Edgy answered 8/3, 2023 at 16:2 Comment(0)
V
1

Don't use mergePage, It may not work for some pdfs You should use mergeRotatedTranslatedPage

from PyPDF2 import PdfFileWriter, PdfFileReader
import io
from reportlab.pdfgen.canvas import Canvas

page_to_merge = 0 #Refers to the First page of PDF 
xcoor = 250 #To be changed according to your pdf
ycoor = 650 #To be changed according to your pdf

input_pdf = PdfFileReader(open("Source.pdf", "rb"))
page_count = input_pdf.getNumPages()
inputpdf_page_to_be_merged = input_pdf.getPage(page_to_merge)

packet = io.BytesIO()
c = Canvas(packet,pagesize=(inputpdf_page_to_be_merged.mediaBox.getWidth(),inputpdf_page_to_be_merged.mediaBox.getHeight()))
c.drawString(xcoor,ycoor,"Hello World")
c.save()
packet.seek(0)

overlay_pdf = PdfFileReader(packet)
overlay = overlay_pdf.getPage(0)

output = PdfFileWriter()

for PAGE in range(page_count):
    if PAGE == page_to_merge:
        inputpdf_page_to_be_merged.mergeRotatedTranslatedPage(overlay, 
                inputpdf_page_to_be_merged.get('/Rotate') or 0, 
                overlay.mediaBox.getWidth()/2, overlay.mediaBox.getWidth()/2)
        output.addPage(inputpdf_page_to_be_merged)
    
    else:
        Page_in_pdf = input_pdf.getPage(PAGE)
        output.addPage(Page_in_pdf)

outputStream = open("destination.pdf", "wb")
output.write(outputStream)
outputStream.close()
Vatican answered 23/2, 2022 at 10:37 Comment(2)
What version the PyPDF2 is in this answer?Sand
@Sand pypdf2 version is 1.26.0Vatican
P
-2

If you're on Windows, this might work:

PDF Creator Pilot

There's also a whitepaper of a PDF creation and editing framework in Python. It's a little dated, but maybe can give you some useful info:

Using Python as PDF Editing and Processing Framework

Phosphatize answered 24/7, 2009 at 21:14 Comment(1)
The white paper looks good but is a little light on code, and I don't really have the resource to implement a whole PDF framework myself! ;)Schiff
A
-4

You may have better luck breaking the problem down into converting PDF into an editable format, writing your changes, then converting it back into PDF. I don't know of a library that lets you directly edit PDF but there are plenty of converters between DOC and PDF for example.

Alfi answered 24/7, 2009 at 21:3 Comment(4)
Problem is that I only have the source in PDF (from a 3rd party) and PDF -> DOC -> PDF will lose a lot in the conversion. Also I need this to run on Linux so DOC may not be the best choice.Schiff
I believe Adobe keeps PDF editing capability pretty closed and proprietary so that they can sell licenses for their better versions of Acrobat. Maybe you can find a way to automate the usage of Acrobat Pro to edit it, using some kind of macro interface.Alfi
If the parts you want to write to are form fields, there are XML interfaces to editing them - otherwise I can't find anything.Alfi
No I just wanted to add a few lines of text to each page.Schiff

© 2022 - 2024 — McMap. All rights reserved.