First commit

This commit is contained in:
Léo 2018-05-18 08:49:19 +02:00
commit 171db182aa
17 changed files with 2646 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.pyc
*.pdf
.*.swp
*__pycache__*

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# PDF Embanner
## Presentation
This is a GUI software designed to manipulate existing pdf. It allows to :
* Concatenate several PDF files, changing page order and selecting only relevant pages and adding blank pages
* Rotate and/or crop PDF pages
* Editting PDF metadata
* Watermarking of PDF with other existing pdf or with custom text
* Extracting text from PDF
* Splitting a single PDF file into a file per page
* Encrypting PDF and decrypting if the encryption method is supported by python PyPDF2 package
## Installation
Simply clone or download this repository, and copy it on your computer.
On linux systems simply type `./pdfembanner` should work, else you will have to launch pdfembanner with python3 manually.
You have to have a python 3.5+ installed, and for watermarking with custom text a Latex installation is also necessary. To tell PDF embanner which latex compiler to use, please edit the `pdfembannersrc/compiler.conf`.
## Licence and credits
This software is distributed under GNU/GPL v3 licence, see www.gnu.org/licenses/

62
pdfembanner Executable file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env python3
# -*-coding:utf-8 -*
import tkinter as tk
from pdfembannersrc import choice
import logging
import argparse
#TODO :
# * Remove Forms
parser = argparse.ArgumentParser(description="""
PDF Embanner
Program to manipulate PDFs based on PyPDF2 python library
use pdfembanner to launch GUI
""", formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--verbose', '-v', action='count')
args = parser.parse_args()
# Logger
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
if(args.verbose is not None):
if(args.verbose==1):
console_handler.setLevel(logging.WARN)
elif(args.verbose==2):
console_handler.setLevel(logging.INFO)
elif(args.verbose>=3):
console_handler.setLevel(logging.DEBUG)
else:
console_handler.setLevel(logging.ERROR)
formatter = logging.Formatter('%(levelname)s :: %(message)s')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
if(args.verbose>=2):
logger.info("Logger level set to {}".format("INFO" if args.verbose==2 else "DEBUG"))
logger.debug("Launching app")
# Main window
fenetre = tk.Tk()
fenetre.geometry("+400+200")
fenetre.title("PDF Embanner")
interface = choice.Choice(fenetre)
fenetre.resizable(0,0)
fenetre.bind("<Escape>", interface.close)
fenetre.bind("1", interface.do1)
fenetre.bind("2", interface.do2)
fenetre.bind("3", interface.do3)
fenetre.bind("4", interface.do4)
fenetre.bind("5", interface.do5)
fenetre.bind("6", interface.do6)
fenetre.bind("7", interface.do7)
logger.debug("App launched")
fenetre.mainloop()
logger.debug("App closed")

View File

257
pdfembannersrc/cat.py Normal file
View File

@ -0,0 +1,257 @@
# -*-coding:utf-8 -*
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from pdfembannersrc.fich import Fich
from pdfembannersrc import subwindows
import logging
import PyPDF2
logger = logging.getLogger()
class Interface(tk.Toplevel):
"""
Main window
"""
def __init__(self, parent, **kwargs):
tk.Toplevel.__init__(self, parent)
self.transient(parent)
self.grab_set()
self.title("PDF Embanner : concatenation")
self.geometry("800x400")
self.protocol("WM_DELETE_WINDOW", self.close)
self.bind("<Escape>", self.close)
self.bind("<Control-o>", self.open)
self.bind("<Control-s>", self.saveas)
self.bind("<Control-m>", self.editmetadata)
self.bind("<Control-Return>", self.do)
self.lfiles = []
self.save_file = None
self.output_produced=False
self.metadata = {'/Title': '',
'/Author': '',
'/Subject': '',
'/Creator': 'PDF Embanner'}
self.f = tk.Frame(self, width=768, height=576, **kwargs)
self.f.pack(fill=tk.BOTH)
# Création de nos widgets
self.f.columnconfigure(2, weight=1)
self.f.rowconfigure(1, weight=1)
tk.Button(self.f, text="Open", command=self.open).grid(row=0, column=0)
tk.Button(self.f, text="Delete all", command=self.delall).grid(row=0, column=1)
vsb = tk.Scrollbar(self.f, orient=tk.VERTICAL)
vsb.grid(row=1, column=5, sticky=tk.N+tk.S)
self.c = tk.Canvas(self.f,yscrollcommand=vsb.set)
self.c.grid(row=1, column=0, columnspan=5, sticky="news")
vsb.config(command=self.c.yview)
self.frame_files = tk.Frame(self.c)
self.c.create_window(0, 0, window=self.frame_files, anchor=tk.NW)
self.frame_files.update_idletasks()
self.c.config(scrollregion=self.c.bbox("all"))
self.frame_files.columnconfigure(4, weight=1)
tk.Button(self.f, text="Save as", command=self.saveas).grid(row=2, column=0)
self.save_label = tk.Label(self.f, text="-" if self.save_file is None else self.save_file)
self.save_label.grid(row=2, column=1, columnspan=2, sticky=tk.W)
tk.Button(self.f, text="Metadata", command=self.editmetadata).grid(row=2, column=4)
tk.Button(self.f, text="Close", command=self.close).grid(row=3, column=3)
tk.Button(self.f, text="Generate PDF", fg="blue", command=self.do).grid(row=3, column=4)
self.message = tk.Label(self.f, text="Welcome !")
self.message.grid(row=4, column=0, columnspan=5, sticky=tk.W)
def open(self, *args):
"""
Ouvrir un fichier
"""
self.message["text"] = "Open..."
ftypes = [('PDF files (Portable Document Format)', '*.pdf'), ('All files', '*')]
lfl = filedialog.askopenfilenames(filetypes = ftypes)
if len(lfl) >0:
for fl in lfl :
self.message["text"] = "Opening {}".format(fl)
try:
flo = Fich(fl)
i = len(self.lfiles)
self.lfiles.append(flo)
self.message["text"] = "Opened {}".format(flo.name)
logger.info("Concatenate : Opened {}".format(fl))
flo.add_widgets(self.frame_files, self, i)
self.frame_files.update_idletasks()
self.c.config(scrollregion=self.c.bbox("all"))
except IOError:
self.message["text"] = "Failed to open {}".format(fl)
logger.warning("Concatenate : Error while opening {}".format(fl))
else:
self.message["text"] = "Nothing to open !"
logger.info("Concatenate : Asked for opening but nothing to open")
def delall(self):
"""
Delete all opened files
"""
if messagebox.askyesno("Delete all", "Are you sure you want to delete all ?"):
for e in self.lfiles:
e.destroy_widgets()
self.lfiles = []
self.message["text"] = "Everything deleted..."
def do(self, *args):
"""
do concatenation of PDFs with correct options !
"""
if(self.save_file is None):
messagebox.showwarning(title="PDF Output", message="Please define the 'Save As' path before !")
else:
self.message["text"] = "Generating PDF..."
nmax=0
for elem in self.lfiles:
nmax+=elem.npages
progress = subwindows.Progress(self, nmax, "Producing PDF {}".format(self.save_file))
output = PyPDF2.PdfFileWriter()
output.addMetadata(self.metadata)
in_f_list = []
currentf=''
try:
for elem in self.lfiles:
currentf=elem.path
in_f = open(elem.path, "rb")
in_f_list.append(in_f)
logger.debug("Concatenate : Processing {}".format(elem.path))
inpdf = PyPDF2.PdfFileReader(in_f)
progress.message["text"] = elem.path
if(elem.use is None):
# All is used
for i in range(elem.npages):
output.addPage(inpdf.getPage(i))
progress.next()
logger.debug("Concatenate : All pages concatenated")
else:
# variables used, order are read. Possible crop or rotation
for i in elem.order:
if(elem.use[i]):
if(i>=elem.npages): # It is a blank page
refpage = inpdf.getPage(0)
refbox = refpage.mediaBox
larg = abs(refbox.upperRight[0]-refbox.upperLeft[0])
haut = abs(refbox.lowerLeft[1]-refbox.upperLeft[1])
if(elem.crop is not None):
larg = larg * (elem.crop[1][0]-elem.crop[0][0])
haut = haut * (elem.crop[1][1]-elem.crop[0][1])
if(elem.rotate==1 or elem.rotate==3):
output.addBlankPage(width=haut, height=larg)
else:
output.addBlankPage(width=larg, height=haut)
else: # Get correspondig page of pdf
page = inpdf.getPage(i)
if(elem.crop is not None):
xmax = page.mediaBox.getUpperRight_x()
ymax = page.mediaBox.getUpperRight_y()
uR = (elem.crop[1][0]*float(xmax), elem.crop[1][1]*float(ymax))
lL = (elem.crop[0][0]*float(xmax), elem.crop[0][1]*float(ymax))
page.cropBox.lowerLeft = lL
page.cropBox.upperRight = uR
if(elem.rotate is not None):
if(elem.rotate==1):
page.rotateClockwise(270)
elif(elem.rotate==2):
page.rotateClockwise(180)
elif(elem.rotate==3):
page.rotateClockwise(90)
output.addPage(page)
progress.next()
logger.debug("Concatenate : Relevant pages concatenated")
currentf=self.save_file
progress.message["text"] = "Saving in {}".format(currentf)
with open(self.save_file, "wb") as out_f:
logger.debug("Concatenate : Writing into file : file open")
output.write(out_f)
logger.debug("Concatenate : Writing into file : done")
self.message["text"] = "Done"
self.output_produced=True
except IOError as e:
logger.warn("Concatenate : Could not open one of the files :: {} {} {}".format(currentf, e.errno, e.strerror))
messagebox.showerror(title="Error",
message="IO Error occured :\nImpossible to open one of the files ({})\nNo output produced!".format(currentf))
self.message["text"] = "An error occured!"
except Exception as e:
logger.warn("Concatenate : Unknown error occured during PDF production. {}".format(str(e)))
messagebox.showerror(title="Error",
message="An Error occured :\n{}\nNo output produced!".format(e))
self.message["text"] = "An error occured!"
finally:
for f in in_f_list:
f.close()
progress.close()
def close(self, *args):
if(self.output_produced or messagebox.askyesno("Quit", "Are you sure you want to quit ?")):
logger.debug("Concatenate : Quit")
self.destroy()
def saveas(self, *args):
fsas = filedialog.asksaveasfilename()
if fsas != '':
self.save_file = fsas
self.save_label["text"] = fsas
def delete(self, ident):
i=0
for i in range(len(self.lfiles)):
if(ident==self.lfiles[i].id):
break
elem = self.lfiles[i]
elem.destroy_widgets()
del self.lfiles[i]
for j in range(i,len(self.lfiles)):
self.lfiles[j].unset_i_widgets()
self.lfiles[j].set_i_widgets(j)
logger.debug("Concatenate : Delete {}".format(i))
def up(self, ident):
if(not self.lfiles[0].id==ident):
for i in range(1, len(self.lfiles)):
if(ident==self.lfiles[i].id):
break
self.lfiles[i-1].unset_i_widgets()
self.lfiles[i].unset_i_widgets()
obj1 = self.lfiles[i]
self.lfiles[i] = self.lfiles[i-1]
self.lfiles[i-1] = obj1
self.lfiles[i].set_i_widgets(i)
self.lfiles[i-1].set_i_widgets(i-1)
logger.debug("Concatenate : Up {}".format(i))
def down(self, ident):
if(not self.lfiles[-1].id==ident):
for i in range(0, len(self.lfiles)-1):
if(ident==self.lfiles[i].id):
break
self.lfiles[i].unset_i_widgets()
self.lfiles[i+1].unset_i_widgets()
obj1 = self.lfiles[i+1]
self.lfiles[i+1] = self.lfiles[i]
self.lfiles[i] = obj1
self.lfiles[i].set_i_widgets(i)
self.lfiles[i+1].set_i_widgets(i+1)
logger.debug("Concatenate : Down {}".format(i))
def editmetadata(self, *args):
interface3 = subwindows.Metadata(self)
logger.debug("Concatenate : Output metadata set")
interface3.mainloop()
interface3.destroy()
logger.debug("Concatenate : End output metadata set with value {}".format(self.metadata))

91
pdfembannersrc/choice.py Normal file
View File

@ -0,0 +1,91 @@
# -*-coding:utf-8 -*
import tkinter as tk
from tkinter import messagebox
import logging
from pdfembannersrc import cat, help, encrypt, full_split, get_text, watermark
logger = logging.getLogger()
class Choice(tk.Frame):
"""
Window to chose what to do...
"""
def __init__(self, fenetre, **kwargs):
self.parent = fenetre
tk.Frame.__init__(self, fenetre, **kwargs)
self.pack(fill=tk.BOTH)
# Création de nos widgets
crocus = tk.PhotoImage(file='pdfembannersrc/crocus.png')
img1 = tk.Label(self, image=crocus)
img1.image=crocus
img1.grid(row=0, column=0, rowspan=10, sticky=tk.W)
pdfem = tk.PhotoImage(file='pdfembannersrc/pdfembanner.png')
img2 = tk.Label(self, image=pdfem)
img2.image=pdfem
img2.grid(row=0, column=1, sticky=tk.W+tk.N)
self.bouton_open = tk.Button(self, text="1. Concatenate", command=self.do1)
self.bouton_open.grid(row=1, column=1, sticky=tk.W+tk.E)
self.bouton_open = tk.Button(self, text="2. Watermark", command=self.do2)
self.bouton_open.grid(row=2, column=1, sticky=tk.W+tk.E)
self.bouton_open = tk.Button(self, text="3. Encryption", command=self.do3)
self.bouton_open.grid(row=3, column=1, sticky=tk.W+tk.E)
self.bouton_open = tk.Button(self, text="4. Extract text", command=self.do4)
self.bouton_open.grid(row=4, column=1, sticky=tk.W+tk.E)
self.bouton_open = tk.Button(self, text="5. Full split", command=self.do5)
self.bouton_open.grid(row=5, column=1, sticky=tk.W+tk.E)
self.bouton_open = tk.Button(self, text="6. Forms", command=self.do6)
self.bouton_open.grid(row=6, column=1, sticky=tk.W+tk.E)
self.bouton_open = tk.Button(self, text="7. Help", command=self.do7)
self.bouton_open.grid(row=7, column=1, sticky=tk.W+tk.E)
self.bouton_open = tk.Button(self, text="Close", command=self.quit)
self.bouton_open.grid(row=8, column=1, sticky=tk.W+tk.E)
def do1(self, *args):
"""
Open concatenation interface
"""
cat.Interface(self.parent)
def do2(self, *args):
"""
Open Watermerk interface
"""
watermark.Interface(self.parent)
def do3(self, *args):
"""
Open encryption interface
"""
encrypt.Interface(self.parent)
def do4(self, *args):
"""
Open Text extraction interface
"""
get_text.Interface(self.parent)
def do5(self, *args):
"""
Open Full split interface
"""
full_split.Interface(self.parent)
def do6(self, *args):
"""
Open Forms interface
"""
messagebox.showwarning(title="Warning", message="Not yet implemented!")
def do7(self, *args):
"""
Open help interface
"""
help.Interface(self.parent)
def close(self, *args):
self.quit()

304
pdfembannersrc/editfich.py Normal file
View File

@ -0,0 +1,304 @@
# -*-coding:utf-8 -*
import tkinter as tk
from tkinter import messagebox
from tkinter import simpledialog
from pdfembannersrc import subwindows
import logging
logger = logging.getLogger()
class InterfaceEdit(tk.Toplevel):
"""
Edition window
"""
def __init__(self, parent, fichobj, **kwargs):
tk.Toplevel.__init__(self, parent)
self.transient(parent)
self.grab_set()
self.geometry("800x400")
self.bind("<Return>", self.ok)
self.fich = fichobj
self.title("Edit {}".format(self.fich.name))
if(self.fich.use is None):
self.fich.use = []
if(self.fich.order is None):
self.fich.order = []
self.pages = []
self.f = tk.Frame(self, width=768, height=576, **kwargs)
self.f.pack(fill=tk.BOTH)
# Création de nos widgets
self.f.columnconfigure(7, weight=1)
self.f.rowconfigure(3, weight=1)
info1 = tk.Label(self.f, text="Choose used pages :", fg="white", padx=20, bg="blue")
info1.grid(row=0, column=0, columnspan=10, sticky=tk.W)
tk.Button(self.f, text="Select all", command=self.selectall).grid(row=1, column=0)
tk.Button(self.f, text="Unselect all", command=self.deselectall).grid(row=2, column=0)
tk.Button(self.f, text="Select range", command=self.selectrange).grid(row=1, column=1)
tk.Button(self.f, text="Unselect range", command=self.deselectrange).grid(row=2, column=1)
tk.Button(self.f, text="Select pair", command=self.selectpair).grid(row=1, column=2)
tk.Button(self.f, text="Unselect pair", command=self.unselectpair).grid(row=2, column=2)
tk.Button(self.f, text="Select impair", command=self.selectimpair).grid(row=1, column=3)
tk.Button(self.f, text="Unselect impair", command=self.unselectimpair).grid(row=2, column=3)
tk.Button(self.f, text="Toggle", command=self.toggle).grid(row=1, column=4)
tk.Button(self.f, text="Add blank page", command=self.addblankpage).grid(row=1, column=10)
vsb = tk.Scrollbar(self.f, orient=tk.VERTICAL)
vsb.grid(row=3, column=11, sticky=tk.N+tk.S)
self.c = tk.Canvas(self.f, yscrollcommand=vsb.set)
self.c.grid(row=3, column=0, columnspan=10, sticky="news")
vsb.config(command=self.c.yview)
self.frame_pages = tk.Frame(self.c)
self.frame_pages.columnconfigure(4, weight=1)
for e in range(self.fich.npages+self.fich.nblankpages):
if(len(self.fich.use)<=e):
self.fich.use.append(True)
if(len(self.fich.order)<=e):
self.fich.order.append(e)
page = Page(self, e)
self.pages.append(page)
page.add_widgets(self.frame_pages)
self.c.create_window(0, 0, window=self.frame_pages, anchor=tk.NW)
self.frame_pages.update_idletasks()
self.c.config(scrollregion=self.c.bbox("all"))
info1 = tk.Label(self.f, text="Options on the whole file :", fg="white", padx=20, bg="blue")
info1.grid(row=4, column=0, columnspan=10, sticky=tk.W)
tk.Button(self.f, text="Rotate", command=self.rotate).grid(row=5, column=0)
tk.Button(self.f, text="Crop", command=self.crop).grid(row=5, column=1)
tk.Button(self.f, text="Ok", command=self.ok).grid(row=6, column=10)
self.message = tk.Label(self.f, text="Editing {}".format(self.fich.name))
self.message.grid(row=7, column=0, columnspan=11, sticky=tk.W)
def ok(self, *args):
"""
Save and quit
"""
self.message["text"] = "Done"
self.quit()
self.destroy()
def up(self, i):
pos = self.fich.order[i]
if(pos>0 and pos<self.fich.npages):
for toech in range(self.fich.npages):
if(self.fich.order[toech]==pos-1):
break
self.fich.order[i] -= 1
self.fich.order[toech] += 1
self.pages[i].unset_i_widgets()
self.pages[toech].unset_i_widgets()
self.pages[i].set_i_widgets(pos-1)
self.pages[toech].set_i_widgets(pos)
def down(self, i):
pos = self.fich.order[i]
if(pos<self.fich.npages-1):
for toech in range(self.fich.npages):
if(self.fich.order[toech]==pos+1):
break
self.fich.order[i] += 1
self.fich.order[toech] -= 1
self.pages[i].unset_i_widgets()
self.pages[toech].unset_i_widgets()
self.pages[i].set_i_widgets(pos+1)
self.pages[toech].set_i_widgets(pos)
def selectall(self):
for i in range(self.fich.npages):
self.fich.use[i] = True
self.pages[i].select()
def deselectall(self):
for i in range(self.fich.npages):
self.fich.use[i] = False
self.pages[i].deselect()
def selectrange(self):
selstr = simpledialog.askstring("Select range", "Range to select (ex 0-2;4):")
for e1 in selstr.split(';'):
e1 = e1.split('-')
if(len(e1)==1 and e1[0].isdigit() and int(e1[0])>0 and int(e1[0])<=len(self.fich.use)):
i = int(e1[0])
self.fich.use[i-1] = True
self.pages[i-1].select()
elif(len(e1)==2 and e1[0].isdigit() and e1[1].isdigit()):
i = int(e1[0])
j = int(e1[1])
if(i>0 and i<j and j<=len(self.fich.use)):
for k in range(i,j+1):
self.fich.use[k-1] = True
self.pages[k-1].select()
else:
messagebox.showwarning(title="Warning", message="Some ranges could not be interpeted, skipped.")
logger.info("Concatenate : Select range : Could not interpret : {}".format(e1))
else:
messagebox.showwarning(title="Warning", message="Some ranges could not be interpeted, skipped.")
logger.info("Concatenate : Select range : Could not interpret_ : {}".format(e1))
def deselectrange(self):
selstr = simpledialog.askstring("Select range", "Range to select (ex 0-2;4):")
for e1 in selstr.split(';'):
e1 = e1.split('-')
if(len(e1)==1 and e1[0].isdigit() and int(e1[0])>0 and int(e1[0])<=len(self.fich.use)):
i = int(e1[0])
self.fich.use[i-1] = False
self.pages[i-1].deselect()
elif(len(e1)==2 and e1[0].isdigit() and e1[1].isdigit()):
i = int(e1[0])
j = int(e1[1])
if(i>0 and i<j and j<=len(self.fich.use)):
for k in range(i,j+1):
self.fich.use[k-1] = False
self.pages[k-1].deselect()
else:
messagebox.showwarning(title="Warning", message="Some ranges could not be interpeted, skipped.")
logger.info("Concatenate : Select range : Could not interpret : {}".format(e1))
else:
messagebox.showwarning(title="Warning", message="Some ranges could not be interpeted, skipped.")
logger.info("Concatenate : Select range : Could not interpret_ : {}".format(e1))
def selectpair(self):
for i in range(self.fich.npages):
if((i+1)%2==0):
self.fich.use[i] = True
self.pages[i].select()
def selectimpair(self):
for i in range(self.fich.npages):
if((i+1)%2==1):
self.fich.use[i] = True
self.pages[i].select()
def unselectpair(self):
for i in range(self.fich.npages):
if((i+1)%2==0):
self.fich.use[i] = False
self.pages[i].deselect()
def unselectimpair(self):
for i in range(self.fich.npages):
if((i+1)%2==1):
self.fich.use[i] = False
self.pages[i].deselect()
def toggle(self):
for i in range(self.fich.npages):
self.pages[i].Wcheck.toggle()
self.pages[i].toggle()
def crop(self):
interface = subwindows.SetCrop(self, self.fich)
logger.debug("Concatenate : Crop set")
interface.mainloop()
interface.destroy()
logger.debug("Concatenate : End crop set with value {}".format(self.fich.crop))
def rotate(self):
interfaceRotate = subwindows.Rotate(self, self.fich)
logger.debug("Rotate {}".format(self.fich.path))
interfaceRotate.mainloop()
interfaceRotate.destroy()
logger.debug("End Rotate {} with value {}".format(self.fich.path, self.fich.rotate))
def addblankpage(self):
values={"nr":0, "after":[]}
fen = subwindows.AskBlankPage(self, self.fich, values)
fen.mainloop()
fen.destroy()
if(values["nr"]>0):
added=0
for e in values["after"]:
e = e+added
# Decalage vers le bas des pages suivantes
for i in range(e,len(self.fich.order)):
self.pages[self.fich.order[i]].unset_i_widgets()
self.pages[self.fich.order[i]].set_i_widgets(i+1+values["nr"])
# Ajout des pages blanches
part1 = self.fich.order[:e]
part2 = self.fich.order[e:]
for i in range(values["nr"]):
self.fich.use.append(True)
self.fich.nblankpages+=1
added+=1
self.fich.order.append(self.fich.npages+self.fich.nblankpages-1)
page = Page(self, self.fich.npages+self.fich.nblankpages-1)
self.pages.append(page)
page.add_widgets(self.frame_pages)
page.unset_i_widgets()
page.set_i_widgets(e+i)
part1.append(self.fich.npages+self.fich.nblankpages-1)
self.fich.order = part1 + part2
self.frame_pages.update_idletasks()
self.c.config(scrollregion=self.c.bbox("all"))
class Page:
def __init__(self, parent, i):
self.num = i
self.parent = parent
self.Wlabel = None
self.Wcheck = None
self.Wup = None
self.Wdown = None
def add_widgets(self, frame):
if self.num>=self.parent.fich.npages:
self.Wlabel = tk.Label(frame, text="Blank page")
else:
self.Wlabel = tk.Label(frame, text="Page {}".format(self.num+1))
self.Wnum = tk.Label(frame, text="{}.".format(self.num+1))
self.Wup = tk.Button(frame, text="Up", command=lambda: self.parent.up(self.num))
self.Wdown = tk.Button(frame, text="Down", command=lambda: self.parent.down(self.num))
self.Wcheck = tk.Checkbutton(frame, command=self.toggle)
if(self.parent.fich.use[self.num]):
self.select()
else:
self.deselect()
self.set_i_widgets(self.parent.fich.order[self.num])
def set_i_widgets(self, i):
self.Wnum["text"] = "{}.".format(i+1)
self.Wnum.grid(row=i, column=0, sticky=tk.W)
self.Wlabel.grid(row=i, column=4)
self.Wcheck.grid(row=i, column=1)
self.Wup.grid(row=i, column=2)
self.Wdown.grid(row=i, column=3)
def unset_i_widgets(self):
self.Wnum.grid_forget()
self.Wlabel.grid_forget()
self.Wcheck.grid_forget()
self.Wup.grid_forget()
self.Wdown.grid_forget()
def toggle(self):
if(self.parent.fich.use[self.num]):
self.deselect()
self.parent.fich.use[self.num] = False
else:
self.select()
self.parent.fich.use[self.num] = True
def select(self):
self.Wlabel.config(fg="black")
self.Wcheck.select()
def deselect(self):
self.Wlabel.config(fg="grey")
self.Wcheck.deselect()

260
pdfembannersrc/editwmark.py Normal file
View File

@ -0,0 +1,260 @@
# -*-coding:utf-8 -*
import tkinter as tk
from tkinter import messagebox
from tkinter import ttk
from pdfembannersrc import strings
import logging
logger = logging.getLogger()
class InterfaceEdit(tk.Toplevel):
"""
Edition for wmark
"""
def __init__(self, parent, wmark, **kwargs):
tk.Toplevel.__init__(self, parent)
self.transient(parent)
self.grab_set()
self.geometry("500x300")
self.bind("<Return>", self.ok)
self.bind("<Escape>", self.close)
self.bind('<<ComboboxSelected>>', self.comboselectmaj)
self.wmark = wmark
self.name = tk.StringVar()
if(self.wmark.name is not None):self.name.set(self.wmark.name)
self.position = tk.StringVar()
if(self.wmark.position is not None):self.position.set(self.wmark.position)
self.userposition = tk.StringVar()
if(self.wmark.userposition is not None and self.position.get()==strings.custom):
self.userposition.set("{},{}".format(self.wmark.userposition[0],self.wmark.userposition[1]))
self.text = tk.StringVar()
if(self.wmark.text is not None):self.text.set(self.wmark.text)
self.size = tk.StringVar()
if(self.wmark.size is not None):self.size.set(self.wmark.size)
self.notfirstpage = self.wmark.notfirstpage
self.notlastpage = self.wmark.notlastpage
self.onlyfirstpage = self.wmark.onlyfirstpage
self.onlylastpage = self.wmark.onlylastpage
self.notpages = tk.StringVar()
if(self.wmark.notpages is not None):self.notpages.set(self.wmark.notpages)
self.onlypages = tk.StringVar()
if(self.wmark.onlypages is not None):self.onlypages.set(self.wmark.onlypages)
self.bold = self.wmark.bold
self.italic = self.wmark.italic
self.boxed = self.wmark.boxed
self.lined = self.wmark.lined
self.title("Edit {}".format(self.wmark.name))
self.f = tk.Frame(self, width=768, height=576, **kwargs)
self.f.pack(fill=tk.BOTH)
# Création de nos widgets
self.f.columnconfigure(1, weight=1)
self.f.rowconfigure(10, weight=1)
tk.Label(self.f, text="Name:").grid(row=0, column=0)
tk.Entry(self.f, textvariable=self.name).grid(row=0, column=1, columnspan=2, sticky=tk.W+tk.E)
tk.Label(self.f, text="Text:", fg="red").grid(row=1, column=0)
tk.Entry(self.f, textvariable=self.text).grid(row=1, column=1, columnspan=2, sticky=tk.W+tk.E)
tk.Label(self.f, text="Position:").grid(row=2, column=0)
ttk.Combobox(self.f, state='readonly', textvariable=self.position, values=strings.positionlist).grid(row=2, column=1, columnspan=2, sticky=tk.W+tk.E)
self.Wuserposition = tk.Entry(self.f, textvariable=self.userposition, state='readonly')
self.Wuserposition.grid(row=3, column=1, columnspan=2, sticky=tk.W+tk.E)
if(self.position.get()==strings.custom):self.Wuserposition['state']='normal'
self.sf = ttk.Labelframe(self.f, text='Text formatting')
self.sf.grid(row=4, column=0, columnspan=3, sticky=tk.W+tk.E)
self.pf = ttk.Labelframe(self.f, text='Pages to apply')
self.pf.grid(row=5, column=0, columnspan=3, sticky=tk.W+tk.E)
self.pf.columnconfigure(0, weight=1)
self.pf.columnconfigure(1, weight=1)
self.Wnotfirstpage = tk.Checkbutton(self.pf, text="Not on first page", command=self.toggle_notfirstpage)
self.Wnotfirstpage.grid(row=0, column=0)
if(self.notfirstpage):self.Wnotfirstpage.select()
self.Wonlyfirstpage = tk.Checkbutton(self.pf, text="Only on first page", command=self.toggle_onlyfirstpage)
self.Wonlyfirstpage.grid(row=0, column=1)
if(self.onlyfirstpage):self.Wonlyfirstpage.select()
self.Wnotlastpage = tk.Checkbutton(self.pf, text="Not on last page", command=self.toggle_notlastpage)
self.Wnotlastpage.grid(row=1, column=0)
if(self.notlastpage):self.Wnotlastpage.select()
self.Wonlylastpage = tk.Checkbutton(self.pf, text="Only on last page", command=self.toggle_onlylastpage)
self.Wonlylastpage.grid(row=1, column=1)
if(self.onlylastpage):self.Wonlylastpage.select()
tk.Label(self.pf, text="Not on pages:").grid(row=2, column=0)
tk.Entry(self.pf, textvariable=self.notpages).grid(row=2, column=1)
tk.Label(self.pf, text="Only on pages:").grid(row=3, column=0)
tk.Entry(self.pf, textvariable=self.onlypages).grid(row=3, column=1)
self.sf.columnconfigure(0, weight=1)
self.sf.columnconfigure(1, weight=1)
self.sf.columnconfigure(2, weight=1)
tk.Label(self.sf, text="Size:").grid(row=0, column=0)
ttk.Combobox(self.sf, state='readonly', textvariable=self.size, values=strings.sizelist).grid(row=0, column=1, columnspan=2)
self.Wbold = tk.Checkbutton(self.sf, text="Bold", command=self.toggle_bold)
self.Wbold.grid(row=1, column=0)
if(self.bold):self.Wbold.select()
self.Witalic = tk.Checkbutton(self.sf, text="Italic", command=self.toggle_italic)
self.Witalic.grid(row=1, column=1)
if(self.italic):self.Witalic.select()
self.Wboxed = tk.Checkbutton(self.sf, text="Boxed", command=self.toggle_boxed)
self.Wboxed.grid(row=2, column=0)
if(self.boxed):self.Wboxed.select()
self.Wlined = tk.Checkbutton(self.sf, text="Lined", command=self.toggle_lined)
self.Wlined.grid(row=2, column=1)
if(self.lined):self.Wlined.select()
tk.Button(self.f, text="Ok", command=self.ok).grid(row=10, column=1, columnspan=2, sticky=tk.W+tk.E)
tk.Button(self.f, text="Close", command=self.close).grid(row=10, column=0)
ttk.Separator(self.f, orient="vertical").grid(row=0, column=3, rowspan=7, sticky=tk.N+tk.S, padx=5, pady=5)
self.tf = ttk.Labelframe(self.f, text='Text directives')
self.tf.grid(row=0, column=4, columnspan=2, rowspan=4)
tk.Label(self.tf, text="%p").grid(row=1, column=0)
tk.Label(self.tf, text="%P").grid(row=2, column=0)
tk.Label(self.tf, text="Page number").grid(row=1, column=1, sticky=tk.W)
tk.Label(self.tf, text="Total page number").grid(row=2, column=1, sticky=tk.W)
self.tf = ttk.Labelframe(self.f, text='Text directives')
self.tf.grid(row=4, column=4, columnspan=2, rowspan=2)
tk.Label(self.tf, text="Custom position:", bg="grey").grid(row=1, column=0, sticky=tk.W+tk.E)
tk.Label(self.tf, text="coma-separated coord-").grid(row=2, column=0, sticky=tk.W)
tk.Label(self.tf, text="inates between 0 and 1").grid(row=3, column=0, sticky=tk.W)
tk.Label(self.tf, text="Not/only on pages:", bg="grey").grid(row=4, column=0, sticky=tk.W+tk.E)
tk.Label(self.tf, text="use e for even pages").grid(row=5, column=0, sticky=tk.W)
tk.Label(self.tf, text="and o for even pages").grid(row=6, column=0, sticky=tk.W)
tk.Label(self.tf, text="end is the last page").grid(row=7, column=0, sticky=tk.W)
tk.Label(self.tf, text="Separator: ; ranges: -").grid(row=8, column=0, sticky=tk.W)
def ok(self, *args):
"""
Save and quit
"""
self.wmark.name = self.name.get()
if(self.position.get()==''):
self.wmark.position = None
else:
self.wmark.position = self.position.get()
if(self.position.get()==strings.custom):
up = self.userposition.get().split(',')
if(len(up)==2):
x = up[0]
y = up[1]
try:
x = float(x)
y = float(y)
except:
messagebox.showwarning(title="Position", message="User defined position have to be coma-separated coordinates between 0 and 1")
return
if(x>=0. and y>=0. and y<=1. and x<=1.):
self.wmark.userposition = (x, y)
else:
messagebox.showwarning(title="Position", message="User defined position have to be coma-separated coordinates between 0 and 1")
return
else:
messagebox.showwarning(title="Position", message="User defined position have to be coma-separated coordinates between 0 and 1")
return
else:
self.wmark.userposition = None
self.wmark.text = self.text.get()
if(self.size.get()==''):
self.wmark.size=None
else:
self.wmark.size = self.size.get()
self.wmark.notfirstpage = self.notfirstpage
self.wmark.notlastpage = self.notlastpage
self.wmark.notpages = self.notpages.get()
self.wmark.onlyfirstpage = self.onlyfirstpage
self.wmark.onlylastpage = self.onlylastpage
self.wmark.onlypages = self.onlypages.get()
self.wmark.Wlabel["text"] = self.wmark.name
self.wmark.bold = self.bold
self.wmark.italic = self.italic
self.wmark.boxed = self.boxed
self.wmark.lined = self.lined
self.close()
def close(self, *args):
self.destroy()
def toggle_notfirstpage(self):
if(self.notfirstpage):
self.notfirstpage = False
self.Wnotfirstpage.deselect()
else:
self.notfirstpage = True
self.onlyfirstpage=False
self.Wnotfirstpage.select()
self.Wonlyfirstpage.deselect()
def toggle_notlastpage(self):
if(self.notlastpage):
self.notlastpage = False
self.Wnotlastpage.deselect()
else:
self.notlastpage = True
self.onlylastpage = False
self.Wnotlastpage.select()
self.Wonlylastpage.deselect()
def toggle_onlyfirstpage(self):
if(self.onlyfirstpage):
self.onlyfirstpage = False
self.Wonlyfirstpage.deselect()
else:
self.onlyfirstpage = True
self.notfirstpage = False
self.Wonlyfirstpage.select()
self.Wnotfirstpage.deselect()
def toggle_onlylastpage(self):
if(self.onlylastpage):
self.onlylastpage = False
self.Wonlylastpage.deselect()
else:
self.onlylastpage = True
self.notlastpage = False
self.Wonlylastpage.select()
self.Wnotlastpage.deselect()
def toggle_bold(self):
if(self.bold):
self.bold = False
self.Wbold.deselect()
else:
self.bold = True
self.Wbold.select()
def toggle_italic(self):
if(self.italic):
self.italic = False
self.Witalic.deselect()
else:
self.italic = True
self.Witalic.select()
def toggle_boxed(self):
if(self.boxed):
self.boxed = False
self.Wboxed.deselect()
else:
self.boxed = True
self.Wboxed.select()
def toggle_lined(self):
if(self.lined):
self.lined = False
self.Wlined.deselect()
else:
self.lined = True
self.Wlined.select()
def comboselectmaj(self, *args):
if(self.position.get()==strings.custom):
self.Wuserposition['state']='normal'
else:
self.Wuserposition['state']='readonly'

199
pdfembannersrc/encrypt.py Normal file
View File

@ -0,0 +1,199 @@
# -*-coding:utf-8 -*
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
from tkinter import simpledialog
from tkinter import ttk
from pdfembannersrc import subwindows
import logging
import PyPDF2
logger = logging.getLogger()
class Interface(tk.Toplevel):
"""
Main encryption window
Allows to :
* Encrypt a pdf
* Decrypt a pdf
"""
def __init__(self, parent, **kwargs):
tk.Toplevel.__init__(self, parent)
self.transient(parent)
self.grab_set()
self.title("PDF Embanner : encryption")
self.geometry("800x300")
self.protocol("WM_DELETE_WINDOW", self.close)
self.bind("<Escape>", self.close)
self.enc_file = None
self.dec_file = None
self.enc_saveas = None
self.dec_saveas = None
self.f = tk.Frame(self, width=768, height=576, **kwargs)
self.f.pack(fill=tk.BOTH)
# Création de nos widgets
self.f.columnconfigure(1, weight=1)
self.f.rowconfigure(12, weight=1)
tk.Label(self.f, text="Encryption...", bg="blue", fg="white", padx=20).grid(row=0, column=0, columnspan=3, sticky=tk.W)
tk.Button(self.f, text="Open", command=self.enc_open).grid(row=1, column=0)
self.enc_open_label = tk.Label(self.f, text="-" if self.enc_file is None else self.enc_file)
self.enc_open_label.grid(row=1, column=1, columnspan=3, sticky=tk.W)
tk.Button(self.f, text="Save as", command=self.set_enc_saveas).grid(row=2, column=0)
self.enc_save_label = tk.Label(self.f, text="-" if self.enc_saveas is None else self.enc_saveas)
self.enc_save_label.grid(row=2, column=1, columnspan=3, sticky=tk.W)
tk.Button(self.f, text="Encrypt", command=self.enc_do, fg="blue").grid(row=4, column=3)
ttk.Separator(self.f, orient="horizontal").grid(row=5, column=0, columnspan=4, sticky=tk.W+tk.E, padx=5, pady=10)
tk.Label(self.f, text="Decrypt...", bg="blue", fg="white", padx=20).grid(row=6, column=0, columnspan=3, sticky=tk.W)
tk.Button(self.f, text="Open", command=self.dec_open).grid(row=7, column=0)
self.dec_open_label = tk.Label(self.f, text="-" if self.dec_file is None else self.dec_file)
self.dec_open_label.grid(row=7, column=1, columnspan=3, sticky=tk.W)
tk.Button(self.f, text="Save as", command=self.set_dec_saveas).grid(row=8, column=0)
self.dec_save_label = tk.Label(self.f, text="-" if self.dec_saveas is None else self.dec_saveas)
self.dec_save_label.grid(row=8, column=1, columnspan=3, sticky=tk.W)
tk.Button(self.f, text="Decrypt", command=self.dec_do, fg="blue").grid(row=9, column=3)
ttk.Separator(self.f, orient="horizontal").grid(row=10, column=0, columnspan=4, sticky=tk.W+tk.E, padx=5, pady=10)
tk.Button(self.f, text="Close", command=self.close).grid(row=11, column=3)
self.message = tk.Label(self.f, text="Welcome!")
self.message.grid(row=13, column=0, columnspan=4, sticky=tk.W)
def enc_open(self, *args):
ftypes = [('PDF files (Portable Document Format)', '*.pdf'), ('All files', '*')]
fl = filedialog.askopenfilename(filetypes = ftypes)
if fl!='':
self.enc_file = fl
self.enc_open_label["text"] = fl
def dec_open(self, *args):
ftypes = [('PDF files (Portable Document Format)', '*.pdf'), ('All files', '*')]
fl = filedialog.askopenfilename(filetypes = ftypes)
if fl!='':
self.dec_file = fl
self.dec_open_label["text"] = fl
def enc_do(self, *args):
if(self.enc_file is None):
messagebox.showwarning(title="PDF Output", message="Please open the PDF to encrypt before !")
elif(self.enc_saveas is None):
messagebox.showwarning(title="PDF Output", message="Please define the 'Save As' path before !")
else:
pwd1 = simpledialog.askstring("Encryption", "Password:", show='*')
if(pwd1 is not None):
pwd2 = simpledialog.askstring("Encryption", "Re-type password:", show='*')
if(pwd2 is None):
return
elif(pwd1==pwd2):
pwd=pwd1
else:
messagebox.showwarning(title="Encryption", message="Passwords do not match!")
return
else:
return
currentf = self.enc_file
progress = subwindows.Progress(self, 4, "Producing PDF...")
progress.message["text"] = 'Reading files'
self.message["text"] = "Encrypting"
try:
with open(self.enc_file, 'rb') as in_f:
inpdf = PyPDF2.PdfFileReader(in_f)
progress.next()
output = PyPDF2.PdfFileWriter()
output.addMetadata(inpdf.getDocumentInfo())
output.appendPagesFromReader(inpdf)
progress.next()
output.encrypt(pwd)
progress.next()
currentf = self.enc_saveas
with open(self.enc_saveas, 'wb') as out_f:
logger.debug("Encrypt : Writing into file : start")
progress.message["text"] = 'Encryption and writing file'
output.write(out_f)
logger.debug("Encrypt : Writing into file : done")
self.message["text"] = "Done"
progress.next()
except IOError:
logger.warn("Encrypt : Could not open one of the files :: {} {} {}".format(currentf, e.errno, e.strerror))
messagebox.showerror(title="Error",
message="IO Error occured :\nImpossible to open one of the files ({})\nNo output produced!".format(currentf))
except Exception as e:
logger.warn("Encrypt : Unknown error occured during PDF production. {}".format(str(e)))
messagebox.showerror(title="Error",
message="An Error occured :\n{}\nNo output produced!".format(e))
finally:
progress.close()
def dec_do(self, *args):
if(self.dec_file is None):
messagebox.showwarning(title="PDF Output", message="Please open the PDF to encrypt before !")
elif(self.dec_saveas is None):
messagebox.showwarning(title="PDF Output", message="Please define the 'Save As' path before !")
else:
pwd = simpledialog.askstring("Encrypted", "Password:", show='*')
if(pwd is None):
return
currentf = self.dec_file
progress = subwindows.Progress(self, 4, "Producing PDF...")
progress.message["text"] = 'Reading file'
self.message["text"] = "Decrypting"
try:
with open(self.dec_file, 'rb') as in_f:
inpdf = PyPDF2.PdfFileReader(in_f)
progress.next()
okpwd = inpdf.decrypt(pwd)
if(okpwd==0):
messagebox.showwarning(title="Encrypted", message="Incorrect password!")
return
progress.next()
output = PyPDF2.PdfFileWriter()
output.addMetadata(inpdf.getDocumentInfo())
output.appendPagesFromReader(inpdf)
progress.next()
currentf = self.dec_saveas
with open(self.dec_saveas, 'wb') as out_f:
logger.debug("Decrypt : Writing into file : start")
output.write(out_f)
logger.debug("Decrypt : Writing into file : done")
self.message["text"] = "Done"
progress.next()
except IOError as e:
logger.warn("Encrypt : Could not open one of the files :: {} {} {}".format(currentf, e.errno, e.strerror))
messagebox.showerror(title="Error",
message="IO Error occured :\nImpossible to open one of the files ({})\nNo output produced!".format(currentf))
except NotImplementedError as e:
logger.warn("Decrypt : Not supported encryption method :: {}".format(e))
messagebox.showerror(title="Error",
message="The encryption method is not supported! Sorry :(")
except Exception as e:
logger.warn("Encrypt : Unknown error occured during PDF production. {}".format(str(e)))
messagebox.showerror(title="Error",
message="An Error occured :\n{}\nNo output produced!".format(e))
finally:
progress.close()
def set_enc_saveas(self, *args):
fsas = filedialog.asksaveasfilename()
if fsas != '':
self.enc_saveas = fsas
self.enc_save_label["text"] = fsas
def set_dec_saveas(self, *args):
fsas = filedialog.asksaveasfilename()
if fsas != '':
self.dec_saveas = fsas
self.dec_save_label["text"] = fsas
def close(self, *args):
self.destroy()

93
pdfembannersrc/fich.py Normal file
View File

@ -0,0 +1,93 @@
# -*-coding:utf-8 -*
from PyPDF2 import PdfFileReader, utils as PyPDF2utils
import tkinter as tk
from pdfembannersrc import editfich
import logging
logger = logging.getLogger()
class Fich:
"""
Class to store data on a pdf file
"""
count = 0
def __init__(self, filename):
self.id = Fich.count
Fich.count += 1
self.name = None
self.path = None
self.npages = 0
self.nblankpages = 0
self.use = None
self.order = None
self.rotate = None
self.crop = None
self.Wnum =None
self.Wlabel = None
self.Wedit = None
self.Wdel = None
self.Wup = None
self.Wdown = None
if(filename is not None):
self._initFromFile(filename)
def _initFromFile(self, filename):
self.path = filename
self.name = filename.split('/')[-1]
try:
with open(filename, "rb") as in_f:
input1 = PdfFileReader(in_f)
self.npages = input1.getNumPages()
except (PyPDF2utils.PdfReadError, EOFError, IOError, NotImplementedError):
raise IOError("Impossible to read file {}".format(filename))
def print1l(self):
if(self.name is not None):
return self.name
else:
return '-'
def edit(self, parent):
interface2 = editfich.InterfaceEdit(parent, self)
logger.debug("Concatenate : Editting {}".format(self.path))
interface2.mainloop()
interface2.destroy()
logger.debug("Concatenate : End Editting {}".format(self.path))
def add_widgets(self, frame, parent, i):
self.Wnum = tk.Label(frame, text="{}.".format(i+1))
self.Wlabel = tk.Label(frame, text=self.print1l())
self.Wedit = tk.Button(frame, text="Edit", command=lambda: self.edit(parent))
self.Wdel = tk.Button(frame, text="Del", command=lambda: parent.delete(self.id))
self.Wup = tk.Button(frame, text="Up", command=lambda: parent.up(self.id))
self.Wdown = tk.Button(frame, text="Down", command=lambda: parent.down(self.id))
self.set_i_widgets(i)
def unset_i_widgets(self):
self.Wnum.grid_forget()
self.Wlabel.grid_forget()
self.Wedit.grid_forget()