Insertar firma en pdf o cómo guardar árboles

En la era de la transición al flujo de trabajo digital, aparecen casos divertidos cuando la digitalización parece estar ahí, pero parece que no. Uno de esos casos fue una situación en la que los empleados imprimieron un contrato enviado por correo electrónico, colocaron un fax o un sello en la impresión, luego lo escanearon y lo enviaron de vuelta.





Para corregir este malentendido, me parece posible de dos maneras: cambiando a firmas digitales, lo que requerirá cambios en el flujo de trabajo para ambas partes, o insertando programáticamente una imagen impresa. En vista de la imposibilidad de influir en el flujo de trabajo de los clientes, tuve que utilizar la segunda forma, insertando programáticamente una imagen en un documento.





Hay muchos programas para trabajar con pdf, pero insertar imágenes en ellos es de pago o limitado. La tarea actual requiere una capacidad ilimitada para editar documentos y la interfaz más simple para que cualquier persona pueda usar el programa inmediatamente sin ningún tipo de formación.





Por lo tanto, decidí escribir mi solicitud para insertar imágenes en pdf que cumpla con todos los requisitos anteriores. Y dado que el tamaño de la aplicación y la velocidad de trabajo (¡dentro de lo razonable!) No son clave, me pareció óptimo escribir una aplicación en python y luego envolverla en un archivo ejecutable.





Entonces, la aplicación. Para crear la interfaz gráfica se utilizó el módulo tkinter, ya que se domina "sobre la marcha", y se sacrificó la apariencia de la aplicación en aras de la velocidad de desarrollo. Entonces sucedió algo como esto:





: . - pdf, . poppler - pdf2image, convert_from_path pdf . , , ( 768*768 ) . = / max( , ). , . :





. pdf reportlab, , pdf , , , . pdf , pdf, PIL, , . : ( ), tkinter- 'coord' , . ( paste PIL Image). .





, python . pyinstaller, python . : - pdf poppler, exe , exe . -noconsole , . , :





from tkinter import *
from tkinter import filedialog
from PIL import ImageTk, Image
from pathlib import Path
from pdf2image import convert_from_path
import os

canvas_size = 768
document_type = (("document file", "*.jpg *.jpeg *.pdf"),
                 ("pdf files", "*.pdf"), ("image files", "*.jpg *.jpeg"))
sign_type = (("stamp file","*.png"),)

class DocCanv(Canvas):
	#Document
	DocumentList=None
	DocumentImage = None
	DocResize = 1
	DocImgLink = None
	CurentPage=0

	#Signature
	SignImage = None
	SignResize = DocResize
	SignImgLink = None
	SignObj = None


	def DocFile(self, use_in_func=False):
		if use_in_func is False:
			doc_path = filedialog.askopenfilename(filetypes=document_type)
			if (Path(doc_path).suffix).lower() == '.pdf':
				try:
					#try to use poppler from pyinstaller bundle temp directory
					self.DocumentList=convert_from_path(doc_path, poppler_path = os.path.join(sys._MEIPASS, "poppler") )
				except:
					#reserve for poppler
					self.DocumentList=convert_from_path(doc_path, poppler_path = "poppler" )
				self.DocumentImage=self.DocumentList[0]
			else:
				self.DocumentImage = Image.open(doc_path)
				self.DocumentList = [self.DocumentImage]
		(width, height) = self.DocumentImage.size
		self.DocResize = canvas_size / max(height, width)
		self.DocImgLink=ImageTk.PhotoImage(
           self.DocumentImage.resize((int(width * self.DocResize), int(height * self.DocResize)), Image.ANTIALIAS))
		self.create_image(0, 0, image=self.DocImgLink, anchor=NW)

	def SignFile(self, sign_path=None):
		if self.SignImage is not None:
			self.MergeFile()
			self.DocFile(True)
		if sign_path is None:
			sign_path = filedialog.askopenfilename(filetypes = sign_type)
		self.SignImage = Image.open(sign_path)
		(width, height) = self.SignImage.size
		self.SignResize=self.DocResize
		self.SignImgLink=ImageTk.PhotoImage(
           self.SignImage.resize((int(width * self.SignResize), int(height * self.SignResize)), Image.ANTIALIAS))
		self.SignObj = self.create_image(0, 0, image=self.SignImgLink, anchor=NW)

	def MoveSign(self, event):
		self.coords(self.SignObj, event.x, event.y)

	def ResizeSign(self, event):
		if event.delta > 0:
			self.SignResize = self.SignResize + 0.1
		else:
			self.SignResize = self.SignResize - 0.1

		(width, height) = self.SignImage.size
		self.SignImage.resize((int(width * self.SignResize), int(height * self.SignResize)), Image.ANTIALIAS)
		self.SignImgLink=ImageTk.PhotoImage(
           self.SignImage.resize((int(width * self.SignResize), int(height * self.SignResize)), Image.ANTIALIAS) )
		x, y = self.coords(self.SignObj)
		self.SignObj = self.create_image(x, y, image=self.SignImgLink, anchor=NW)

	def MergeFile(self):
		sign_coords =self.coords(self.SignObj)
		sign_coords = [(int)(x / self.DocResize) for x in sign_coords]
		(width, height) = self.SignImage.size
		width=int((width * self.SignResize)/self.DocResize)
		height=int((height * self.SignResize) / self.DocResize)
		ResizedSign=self.SignImage.resize((width,height), Image.ANTIALIAS)
		self.DocumentImage.paste(ResizedSign, box=sign_coords , mask=ResizedSign.convert('RGBA'))


	def SaveFile(self,f_type="jpg"):
		try:
			self.MergeFile()
		except:
			pass

		SavePath=filedialog.asksaveasfilename()
		if (SavePath.split('.'))[-1]!=f_type:
    			SavePath=(SavePath.split('.'))[0]+'.'+f_type
		if f_type == 'pdf':
			self.DocumentList[0].save(SavePath,save_all=True,append_images=self.DocumentList[1:])
		else:
			self.DocumentImage.save(SavePath)


	def NextPage(self):
		try:
			self.MergeFile()
			self.DocumentList[self.CurentPage]=self.DocumentImage
		except:
			pass

		if (len(self.DocumentList)-1) > self.CurentPage:
			self.CurentPage+=1
		self.DocumentImage=self.DocumentList[self.CurentPage]

		self.SignImage = None
		self.SignImgLink = None
		self.SignObj = None

		self.DocFile(True)



	def PrevPage(self):
		try:
			self.MergeFile()
			self.DocumentList[self.CurentPage]=self.DocumentImage
		except:
			pass

		if self.CurentPage>0:
			self.CurentPage-=1
		self.DocumentImage=self.DocumentList[self.CurentPage]

		self.SignImage = None
		self.SignImgLink = None
		self.SignObj = None

		self.DocFile(True)




root = Tk()
root.title("Documents signer")
DocCan = DocCanv(root, width=canvas_size, height=canvas_size)
DocCan.pack(side='right', fill=BOTH, expand=1)
MenuFrame = Frame(root, width=120, bg='gray22')
MenuFrame.pack(side='right', fill=Y)

OpenDocBtn = Button(MenuFrame, text='Open Document',command=DocCan.DocFile)
OpenDocBtn.pack(fill=X, padx=5,pady=3)
SignDocBtn = Button(MenuFrame, text='Open sign',command=DocCan.SignFile)
SignDocBtn.pack(fill=X, padx=5,pady=3)
SavePDFBtn = Button(MenuFrame, text='Save as pdf',command = lambda arg1=DocCan, arg2='pdf': DocCanv.SaveFile(arg1,arg2))
SavePDFBtn.pack(fill=X, padx=5,pady=3)
SaveJPGBtn = Button(MenuFrame, text='Save as jpg',command = lambda arg1=DocCan, arg2='jpg': DocCanv.SaveFile(arg1,arg2))
SaveJPGBtn.pack(fill=X, padx=5,pady=3)

NextPageBtn = Button(MenuFrame, text='Next page',command = DocCan.NextPage)
NextPageBtn.pack(fill=X, padx=5,pady=3)
PrevPageBtn = Button(MenuFrame, text='Prev page',command = DocCan.PrevPage)
PrevPageBtn.pack(fill=X, padx=5,pady=3)

DocCan.bind("<B1-Motion>", DocCan.MoveSign)
DocCan.bind("<MouseWheel>", DocCan.ResizeSign)


root.mainloop()

      
      



git : https://github.com/mostdefaultusername/SignPDF/releases/tag/1.0








All Articles