create bookmarks for separate pdf files using the filename - python

Hi I am trying to merge multiple pdfs with the filename of each pdf as the bookmark I intent to do this by merging the pdfs once the bookmark has been created on each pdf individually, I have managed to merge all pdfs and I have managed to make a bookmark for an individual pdf using the pdfs filename however I have not been able to add a bookmark to multiple pdfs at once. please could anyone advise on how this could be done? below is the code for making the bookmark the pdf name:
import os
from PyPDF2 import PdfFileMerger, PdfFileReader, PdfFileWriter
directory = r'C:filepath\\21-07398.pdf'
source_dir = os.getcwd()
pathname, extension = os.path.splitext(directory)
filename = pathname.split('\\')
filename1 = filename[-1]
print(filename1)
reader = PdfFileReader(directory)
writer = PdfFileWriter()
n = reader.getNumPages()
for i in range(n):
writer.addPage(reader.getPage(i))
writer.addBookmark(filename1, 0, parent=None)
with open("./result/result.pdf", "wb") as gg:
writer.write(gg)

https://pypdf2.readthedocs.io/en/latest/user/merging-pdfs.html
https://pypdf2.readthedocs.io/en/latest/modules/PdfMerger.html?highlight=bookmark#PyPDF2.PdfMerger.add_bookmark
You can merge the pdfs and create the bookmarks as you go.
from PyPDF2 import PdfMerger
merger = PdfMerger()
//files is supposed to be a list of your filenames.
for file in files:
merger.append(file)
for i in range(len(files)):
merger.add_outline_item(title=str(files[i],pagenum=i))
merger.write(<outputFileName>)
merger.close()

Related

Getting this error: RuntimeError: Proxy error(FileNotFoundException): Could not find file 'C:\Users\user\stuff\tests\pythonlearn.pdf'

import os
import aspose.words as aw
rootdir = 'C:/Users/user/stuff/tests'
for subdir, dirs, files in os.walk(rootdir):
for file in files:
a = os.path.join(subdir, file)
doc = aw.Document(a)
doc.save("utput.docx")
doc = aw.Document("Output.docx")
doc.save("output.pdf")
This is my program.
I am trying to run python through a folder containing pdf files, and decrypt them one by one by converting it to word, and then to pdf. What am I doing wrong?
Don't use os.walk. use os.listdir(rootdir) instead. Please note that the saved file and the used file have the same name.
Example:
import os
import aspose.words as aw
root = "C:/Users/user/stuff/tests"
for item in os.listdir(root):
if os.path.isfile(os.path.join(root, item)):
doc = aw.Document(item)
doc.save("Output.docx")
doc = aw.Document("Output.docx")
doc.save("output.pdf")
[EDIT]
above code cant find other folders so i decide to use glob to find all folders
Here:
import os
import aspose.words as aw
import glob
# Set base directory
os.chdir(os.path.join("C:/Users/user/stuff/tests"))
# Geting all pdf files in list
pdf_files = glob.glob("*.pdf")
for files in pdf_files:
doc = aw.Document(files)
doc.save("Output.docx")
doc = aw.Document("Output.docx")
doc.save("output.pdf")
[EDIT-2]
First take all .pdf files in one list :
pdf_files = glob.glob("*.pdf")
other_pdf_files = glob.glob('*/*.pdf')
all_pdf_files=(*pdf_files,*other_pdf_files)
Secondly, you need to use PyPDF2 to get rid of password.
Get unencrypted pdfs by sending all pdf files into decrypt_pdf (don't forget to specify the password). For example: (More detail here and here)
from PyPDF2 import PdfFileReader, PdfFileWriter
def decrypt_pdf(input_path, output_path, password):
with open(input_path, 'rb') as input_file, \
open(output_path, 'wb') as output_file:
reader = PdfFileReader(input_file)
reader.decrypt(password)
writer = PdfFileWriter()
for i in range(reader.getNumPages()):
writer.addPage(reader.getPage(i))
writer.write(output_file)
You can run other parts in the same way.
for files in all_pdf_files:
doc = aw.Document(files)
...

Python Pypdf2 pdf merger, choose directory manually when saving

I am trying to merge multiple pdfs and save to a folder. How can I make it allow user to choose manually which directory to save?
import os.path
from os import path
from PyPDF2 import PdfFileMerger
pdfs = ['text1.pdf', 'text2.pdf', 'text3.pdf']
merger = PdfFileMerger()
for pdf in pdfs:
if path.exists(pdf) == True:
merger.append(pdf)
merger.write("result.pdf")
merger.close()

Created Python Script to merge PDF files works fine in Pycharm but not as a solo EXE I made via pyinstaller

So this is my code below.AS stated in the title it works as intended in PyCharm but not outside of it. Would it be because I used PyPDF2 library? Thank you any help would be much appreciated.
import os
from PyPDF2 import PdfFileMerger
def main():
print("PDF Merger Initialized")
pdfs = [pdf_file for pdf_file in os.listdir() if pdf_file.endswith(".pdf")] #sets pdfs as a list containing all files with the .pdf extenstion
merger = PdfFileMerger()
for pdf in pdfs:
merger.append(pdf)
merger.write("merged_bills.pdf")
merger.close()
print("PDF Merger Completed")
main()
try instead
import glob
pdfs = [pdf_file for pdf_file in glob.glob(os.path.join(os.getcwd(),"*.pdf")]
glob will return the full path to the file in this way
alternatively try
pdfs = [os.path.join(os.getcwd(),pdf_file) for pdf_file in os.listdir(os.getcwd()) if pdf_file.endswith(".pdf")]

Separate all multipage PDF files inside a directory into single page PDFs via Python

I need to take hundreds of randomly named multipage PDF files and split them into single-page PDF files. I have been experimenting with Python with some success. The following code does exactly what I need, but it is written to split a 'specific' PDF file (w9.pdf). I need to split all PDF files in a directory (*.pdf).
Can somebody point me in the right direction, please?
# pdf_splitter.py
import os
from PyPDF2 import PdfFileReader, PdfFileWriter
def pdf_splitter(path):
fname = os.path.splitext(os.path.basename(path))[0]
pdf = PdfFileReader(path)
for page in range(pdf.getNumPages()):
pdf_writer = PdfFileWriter()
pdf_writer.addPage(pdf.getPage(page))
output_filename = '{}_page_{}.pdf'.format(
fname, page+1)
with open(output_filename, 'wb') as out:
pdf_writer.write(out)
print('Created: {}'.format(output_filename))
if __name__ == '__main__':
path = 'w9.pdf'
pdf_splitter(path)
I suggest you the following solution:
import glob
if __name__ == '__main__':
paths = glob.glob('*.pdf')
for path in paths:
pdf_splitter(path)

Merge PDF files

Is it possible, using Python, to merge separate PDF files?
Assuming so, I need to extend this a little further. I am hoping to loop through folders in a directory and repeat this procedure.
And I may be pushing my luck, but is it possible to exclude a page that is contained in each of the PDFs (my report generation always creates an extra blank page).
You can use pypdfs PdfMerger class.
File Concatenation
You can simply concatenate files by using the append method.
from pypdf import PdfMerger
pdfs = ['file1.pdf', 'file2.pdf', 'file3.pdf', 'file4.pdf']
merger = PdfMerger()
for pdf in pdfs:
merger.append(pdf)
merger.write("result.pdf")
merger.close()
You can pass file handles instead file paths if you want.
File Merging
If you want more fine grained control of merging there is a merge method of the PdfMerger, which allows you to specify an insertion point in the output file, meaning you can insert the pages anywhere in the file. The append method can be thought of as a merge where the insertion point is the end of the file.
e.g.
merger.merge(2, pdf)
Here we insert the whole PDF into the output but at page 2.
Page Ranges
If you wish to control which pages are appended from a particular file, you can use the pages keyword argument of append and merge, passing a tuple in the form (start, stop[, step]) (like the regular range function).
e.g.
merger.append(pdf, pages=(0, 3)) # first 3 pages
merger.append(pdf, pages=(0, 6, 2)) # pages 1,3, 5
If you specify an invalid range you will get an IndexError.
Note: also that to avoid files being left open, the PdfMergers close method should be called when the merged file has been written. This ensures all files are closed (input and output) in a timely manner. It's a shame that PdfMerger isn't implemented as a context manager, so we can use the with keyword, avoid the explicit close call and get some easy exception safety.
You might also want to look at the pdfly cat command provided by the pypdf developers. You can potentially avoid the need to write code altogether.
The pypdf documentation also includes some example code demonstrating merging.
PyMuPdf
Another library perhaps worth a look is PyMuPdf. Merging is equally simple.
From command line:
python -m fitz join -o result.pdf file1.pdf file2.pdf file3.pdf
and from code
import fitz
result = fitz.open()
for pdf in ['file1.pdf', 'file2.pdf', 'file3.pdf']:
with fitz.open(pdf) as mfile:
result.insert_pdf(mfile)
result.save("result.pdf")
With plenty of options, detailed in the projects wiki.
note: in older versions of PyMuPDF insert_pdf was insertPDF
Use Pypdf or its successor PyPDF2:
A Pure-Python library built as a PDF toolkit. It is capable of:
splitting documents page by page,
merging documents page by page,
(and much more)
Here's a sample program that works with both versions.
#!/usr/bin/env python
import sys
try:
from PyPDF2 import PdfFileReader, PdfFileWriter
except ImportError:
from pyPdf import PdfFileReader, PdfFileWriter
def pdf_cat(input_files, output_stream):
input_streams = []
try:
# First open all the files, then produce the output file, and
# finally close the input files. This is necessary because
# the data isn't read from the input files until the write
# operation. Thanks to
# https://stackoverflow.com/questions/6773631/problem-with-closing-python-pypdf-writing-getting-a-valueerror-i-o-operation/6773733#6773733
for input_file in input_files:
input_streams.append(open(input_file, 'rb'))
writer = PdfFileWriter()
for reader in map(PdfFileReader, input_streams):
for n in range(reader.getNumPages()):
writer.addPage(reader.getPage(n))
writer.write(output_stream)
finally:
for f in input_streams:
f.close()
output_stream.close()
if __name__ == '__main__':
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
pdf_cat(sys.argv[1:], sys.stdout)
Merge all pdf files that are present in a dir
Put the pdf files in a dir. Launch the program. You get one pdf with all the pdfs merged.
import os
from PyPDF2 import PdfMerger
x = [a for a in os.listdir() if a.endswith(".pdf")]
merger = PdfMerger()
for pdf in x:
merger.append(open(pdf, 'rb'))
with open("result.pdf", "wb") as fout:
merger.write(fout)
How would I make the same code above today
from glob import glob
from PyPDF2 import PdfMerger
def pdf_merge():
''' Merges all the pdf files in current directory '''
merger = PdfMerger()
allpdfs = [a for a in glob("*.pdf")]
[merger.append(pdf) for pdf in allpdfs]
with open("Merged_pdfs.pdf", "wb") as new_file:
merger.write(new_file)
if __name__ == "__main__":
pdf_merge()
The pdfrw library can do this quite easily, assuming you don't need to preserve bookmarks and annotations, and your PDFs aren't encrypted. cat.py is an example concatenation script, and subset.py is an example page subsetting script.
The relevant part of the concatenation script -- assumes inputs is a list of input filenames, and outfn is an output file name:
from pdfrw import PdfReader, PdfWriter
writer = PdfWriter()
for inpfn in inputs:
writer.addpages(PdfReader(inpfn).pages)
writer.write(outfn)
As you can see from this, it would be pretty easy to leave out the last page, e.g. something like:
writer.addpages(PdfReader(inpfn).pages[:-1])
Disclaimer: I am the primary pdfrw author.
Is it possible, using Python, to merge seperate PDF files?
Yes.
The following example merges all files in one folder to a single new PDF file:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from argparse import ArgumentParser
from glob import glob
from pyPdf import PdfFileReader, PdfFileWriter
import os
def merge(path, output_filename):
output = PdfFileWriter()
for pdffile in glob(path + os.sep + '*.pdf'):
if pdffile == output_filename:
continue
print("Parse '%s'" % pdffile)
document = PdfFileReader(open(pdffile, 'rb'))
for i in range(document.getNumPages()):
output.addPage(document.getPage(i))
print("Start writing '%s'" % output_filename)
with open(output_filename, "wb") as f:
output.write(f)
if __name__ == "__main__":
parser = ArgumentParser()
# Add more options if you like
parser.add_argument("-o", "--output",
dest="output_filename",
default="merged.pdf",
help="write merged PDF to FILE",
metavar="FILE")
parser.add_argument("-p", "--path",
dest="path",
default=".",
help="path of source PDF files")
args = parser.parse_args()
merge(args.path, args.output_filename)
from PyPDF2 import PdfFileMerger
import webbrowser
import os
dir_path = os.path.dirname(os.path.realpath(__file__))
def list_files(directory, extension):
return (f for f in os.listdir(directory) if f.endswith('.' + extension))
pdfs = list_files(dir_path, "pdf")
merger = PdfFileMerger()
for pdf in pdfs:
merger.append(open(pdf, 'rb'))
with open('result.pdf', 'wb') as fout:
merger.write(fout)
webbrowser.open_new('file://'+ dir_path + '/result.pdf')
Git Repo: https://github.com/mahaguru24/Python_Merge_PDF.git
You can use pikepdf too (source code documentation).
Example code could be (taken from the documentation):
from glob import glob
from pikepdf import Pdf
pdf = Pdf.new()
for file in glob('*.pdf'): # you can change this to browse directories recursively
with Pdf.open(file) as src:
pdf.pages.extend(src.pages)
pdf.save('merged.pdf')
pdf.close()
If you want to exclude pages, you might proceed another way, for instance copying pages to a new pdf (you can select which ones you do not copy, then, the pdf.pages object behaving like a list).
It is still actively maintained, which, as of february 2022, does not seem to be the case of PyPDF2 nor pdfrw.
I haven't benchmarked it, so I don't know if it is quicker or slower than other solutions.
One advantage over PyMuPDF, in my case, is that an official Ubuntu package is available (python3-pikepdf), what is practical to package my own software depending on it.
here, http://pieceofpy.com/2009/03/05/concatenating-pdf-with-python/, gives an solution.
similarly:
from pyPdf import PdfFileWriter, PdfFileReader
def append_pdf(input,output):
[output.addPage(input.getPage(page_num)) for page_num in range(input.numPages)]
output = PdfFileWriter()
append_pdf(PdfFileReader(file("C:\\sample.pdf","rb")),output)
append_pdf(PdfFileReader(file("c:\\sample1.pdf","rb")),output)
append_pdf(PdfFileReader(file("c:\\sample2.pdf","rb")),output)
append_pdf(PdfFileReader(file("c:\\sample3.pdf","rb")),output)
output.write(file("c:\\combined.pdf","wb"))
------ Updated on 25th Nov. ------
------ Seems above code doesn't work anymore------
------ Please use the following:------
from PyPDF2 import PdfFileMerger, PdfFileReader
import os
merger = PdfFileMerger()
file_folder = "C:\\My Ducoments\\"
root, dirs, files = next(os.walk(file_folder))
for path, subdirs, files in os.walk(root):
for f in files:
if f.endswith(".pdf"):
merger.append(file_folder + f)
merger.write(file_folder + "Economists-1.pdf")
Here's a time comparison for the most common answers for my specific use case: combining a list of 5 large single-page pdf files. I ran each test twice.
(Disclaimer: I ran this function within Flask, your mileage may vary)
TL;DR
pdfrw is the fastest library for combining pdfs out of the 3 I tested.
PyPDF2
start = time.time()
merger = PdfFileMerger()
for pdf in all_pdf_obj:
merger.append(
os.path.join(
os.getcwd(), pdf.filename # full path
)
)
formatted_name = f'Summary_Invoice_{date.today()}.pdf'
merge_file = os.path.join(os.getcwd(), formatted_name)
merger.write(merge_file)
merger.close()
end = time.time()
print(end - start) #1 66.50084733963013 #2 68.2995400428772
PyMuPDF
start = time.time()
result = fitz.open()
for pdf in all_pdf_obj:
with fitz.open(os.path.join(os.getcwd(), pdf.filename)) as mfile:
result.insertPDF(mfile)
formatted_name = f'Summary_Invoice_{date.today()}.pdf'
result.save(formatted_name)
end = time.time()
print(end - start) #1 2.7166640758514404 #2 1.694727897644043
pdfrw
start = time.time()
result = fitz.open()
writer = PdfWriter()
for pdf in all_pdf_obj:
writer.addpages(PdfReader(os.path.join(os.getcwd(), pdf.filename)).pages)
formatted_name = f'Summary_Invoice_{date.today()}.pdf'
writer.write(formatted_name)
end = time.time()
print(end - start) #1 0.6040127277374268 #2 0.9576816558837891
A slight variation using a dictionary for greater flexibility (e.g. sort, dedup):
import os
from PyPDF2 import PdfFileMerger
# use dict to sort by filepath or filename
file_dict = {}
for subdir, dirs, files in os.walk("<dir>"):
for file in files:
filepath = subdir + os.sep + file
# you can have multiple endswith
if filepath.endswith((".pdf", ".PDF")):
file_dict[file] = filepath
# use strict = False to ignore PdfReadError: Illegal character error
merger = PdfFileMerger(strict=False)
for k, v in file_dict.items():
print(k, v)
merger.append(v)
merger.write("combined_result.pdf")
I used pdf unite on the linux terminal by leveraging subprocess (assumes one.pdf and two.pdf exist on the directory) and the aim is to merge them to three.pdf
import subprocess
subprocess.call(['pdfunite one.pdf two.pdf three.pdf'],shell=True)
You can use PdfFileMerger from the PyPDF2 module.
For example, to merge multiple PDF files from a list of paths you can use the following function:
from PyPDF2 import PdfFileMerger
# pass the path of the output final file.pdf and the list of paths
def merge_pdf(out_path: str, extracted_files: list [str]):
merger = PdfFileMerger()
for pdf in extracted_files:
merger.append(pdf)
merger.write(out_path)
merger.close()
merge_pdf('./final.pdf', extracted_files)
And this function to get all the files recursively from a parent folder:
import os
# pass the path of the parent_folder
def fetch_all_files(parent_folder: str):
target_files = []
for path, subdirs, files in os.walk(parent_folder):
for name in files:
target_files.append(os.path.join(path, name))
return target_files
# get a list of all the paths of the pdf
extracted_files = fetch_all_files('./parent_folder')
Finally, you use the two functions declaring.a parent_folder_path that can contain multiple documents, and an output_pdf_path for the destination of the merged PDF:
# get a list of all the paths of the pdf
parent_folder_path = './parent_folder'
outup_pdf_path = './final.pdf'
extracted_files = fetch_all_files(parent_folder_path)
merge_pdf(outup_pdf_path, extracted_files)
You can get the full code from here (Source): How to merge PDF documents using Python
The answer from Giovanni G. PY in an easily usable way (at least for me):
import os
from PyPDF2 import PdfFileMerger
def merge_pdfs(export_dir, input_dir, folder):
current_dir = os.path.join(input_dir, folder)
pdfs = os.listdir(current_dir)
merger = PdfFileMerger()
for pdf in pdfs:
merger.append(open(os.path.join(current_dir, pdf), 'rb'))
with open(os.path.join(export_dir, folder + ".pdf"), "wb") as fout:
merger.write(fout)
export_dir = r"E:\Output"
input_dir = r"E:\Input"
folders = os.listdir(input_dir)
[merge_pdfs(export_dir, input_dir, folder) for folder in folders];
Use right python interpreter:
conda activate py_envs
pip install PyPDF2
Python code:
from PyPDF2 import PdfMerger
#set path files
import os
os.chdir('/ur/path/to/folder/')
cwd = os.path.abspath('')
files = os.listdir(cwd)
def merge_pdf_files():
merger = PdfMerger()
pdf_files = [x for x in files if x.endswith(".pdf")]
[merger.append(pdf) for pdf in pdf_files]
with open("merged_pdf_all.pdf", "wb") as new_file:
merger.write(new_file)
if __name__ == "__main__":
merge_pdf_files()
def pdf_merger(path):
"""Merge the pdfs into one pdf"""
import logging
logging.basicConfig(filename = 'output.log', level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s' )
try:
import glob, os
import PyPDF2
os.chdir(path)
pdfs = []
for file in glob.glob("*.pdf"):
pdfs.append(file)
if len(pdfs) == 0:
logging.info("No pdf in the given directory")
else:
merger = PyPDF2.PdfFileMerger()
for pdf in pdfs:
merger.append(pdf)
merger.write('result.pdf')
merger.close()
except Exception as e:
logging.error('Error has happened')
logging.exception('Exception occured' + str(e))

Categories

Resources