Na era da transição para o fluxo de trabalho digital, casos engraçados aparecem quando a digitalização parece existir, mas parece que não. Um desses casos foi uma situação em que os funcionários imprimiram um contrato enviado por e-mail, colocaram um fac-símile ou carimbaram na impressão, depois o escanearam e o enviaram de volta.
Para corrigir esse mal-entendido, parece-me possível de duas maneiras: mudando para assinaturas digitais, o que exigirá mudanças no fluxo de trabalho para ambas as partes, ou inserindo programaticamente uma imagem impressa. Diante da impossibilidade de influenciar o fluxo de trabalho dos clientes, tive que usar a segunda forma, inserindo programaticamente uma imagem em um documento.
Existem muitos programas para trabalhar com PDF, mas inserir imagens neles é pago ou limitado. A tarefa atual requer uma capacidade ilimitada de edição de documentos e a interface mais simples para que qualquer pessoa possa usar o programa imediatamente, sem nenhum treinamento.
Assim, decidi escrever meu aplicativo de inserção de imagens em pdf que atendesse a todos os requisitos acima. E uma vez que o tamanho do aplicativo e a velocidade de trabalho (dentro do razoável!) Não são fundamentais, me pareceu ideal escrever um aplicativo em python e, em seguida, envolvê-lo em um arquivo executável.
Então, o aplicativo. Para criar a interface gráfica, foi usado o módulo tkinter, uma vez que é dominado "on the fly", e a aparência do aplicativo foi sacrificada por uma questão de velocidade de desenvolvimento. Então, algo assim aconteceu:
: . - 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