How to esign a pdf document using streamlit

import streamlit as st
import os
import shutil
import base64
import PyPDF2
from datetime import datetime
from PyPDF2 import PdfFileMerger, PdfFileReader
from PIL import Image


st.set_page_config(
    page_title="QTG Review System",
    page_icon="πŸ“–",
    layout="wide",
    initial_sidebar_state="expanded",
)

source_folder = "source_folder"
pass_folder = "pass_folder"
fail_folder = "fail_folder"
signed_folder = "signed_folder"

def list_files(folder):
    try:
        return os.listdir(folder)
    except FileNotFoundError:
        return []

def move_file(src, dest):
    try:
        shutil.move(src, dest)
        return True
    except Exception as e:
        st.error(f"Error moving file {src} to {dest}: {e}")
        return False

def retrieve_files(selected_files, src_folder, dest_folder):
    moved_files = []
    for item in selected_files:
        src_path = os.path.join(src_folder, item)
        dest_path = os.path.join(dest_folder, item)
        if move_file(src_path, dest_path):
            moved_files.append(item)
    return moved_files

st.sidebar.header("QTG Review System")
st.sidebar.markdown("Manage and review your QTG files efficiently.")


def get_file_link(file_path):
    with open(file_path, "rb") as f:
        pdf_data = f.read()
        b64 = base64.b64encode(pdf_data).decode()  # Encode to base64
        return f'<a href="data:application/pdf;base64,{b64}" target="_blank">Open PDF</a>'

def add_signature_to_pdf(pdf_path, signature_path, output_path):
    try:
        pdf_reader = PdfFileReader(pdf_path)
        pdf_writer = PdfFileMerger()
        for page_num in range(len(pdf_reader.pages)):
            page = pdf_reader.pages[page_num]
            if page_num == 0:  # Add signature to the first page only
                signature_img = Image.open(signature_path)
            pdf_writer.add_page(page)

        with open(output_path, "wb") as f_out:
            pdf_writer.write(f_out)
        return True
    except Exception as e:
        st.error(f"Error signing PDF: {e}")
        return False


tab1, tab2, tab3 = st.tabs(["πŸ“‚ Manage QTG Files", "πŸ”„ Retrieve Files", "πŸ–ŠοΈ E-Sign Document"])

with tab1:
    st.header("πŸ“ QTG Review and Classification")

    with st.container(border=True):
        st.subheader("1. Review QTG Files")
        files = list_files(source_folder)
        
        if files:
            file_to_move = st.selectbox("Select the QTG to review", files)
            
            pdf_file_path = os.path.join(source_folder, file_to_move)
            if os.path.exists(pdf_file_path):
                # Create an Open PDF button
                open_pdf_link = get_file_link(pdf_file_path)
                st.markdown(open_pdf_link, unsafe_allow_html=True)

            status = st.radio("Status of QTG", ["Pass", "Fail"], horizontal=True)
            
            if st.button("Submit"):
                destination_folder = pass_folder if status == "Pass" else fail_folder
                src_path = os.path.join(source_folder, file_to_move)
                dest_path = os.path.join(destination_folder, file_to_move)
                
                if move_file(src_path, dest_path):
                    st.success(f"Moved '{file_to_move}' to **{'Pass' if status == 'Pass' else 'Fail'}** folder.")
        else:
            st.warning("🚫 No files to move in the source folder.")

    st.markdown("---")  # Separator

    with st.container(border=True):
        st.subheader("2. View Folder Contents")
        col1, col2, col3 = st.columns(3)

        with col1:
            st.markdown("### πŸ“‚ Source Folder")
            source_contents = list_files(source_folder)
            if source_contents:
                for item in source_contents:
                    st.markdown(f"- **{item}**")
            else:
                st.write("No files in source folder.")

        with col2:
            st.markdown("### Pass Folder")
            pass_contents = list_files(pass_folder)
            if pass_contents:
                for item in pass_contents:
                    st.markdown(f"- **{item}**")
            else:
                st.write("No files in pass folder.")

        with col3:
            st.markdown("### Fail Folder")
            fail_contents = list_files(fail_folder)
            if fail_contents:
                for item in fail_contents:
                    st.markdown(f"- **{item}**")
            else:
                st.write("No files in fail folder.")

with tab2:
    st.header("πŸ”„ Retrieve Files to Source Folder")

    with st.container(border=True):
        st.subheader("1. Select Files to Retrieve")

        st.markdown("#### πŸ“‚ Files in Pass Folder")
        pass_files = list_files(pass_folder)
        selected_pass = []
        if pass_files:
            selected_pass = st.multiselect("Select files to retrieve from Pass folder:", pass_files)
        else:
            st.write("No files to retrieve from Pass folder.")

        st.markdown("#### Files in Fail Folder")
        fail_files = list_files(fail_folder)
        selected_fail = []
        if fail_files:
            selected_fail = st.multiselect("Select files to retrieve from Fail folder:", fail_files)
        else:
            st.write("No files to retrieve from Fail folder.")

        if st.button("Retrieve Selected Files"):
            retrieved_files = []

            if selected_pass:
                moved_pass = retrieve_files(selected_pass, pass_folder, source_folder)
                retrieved_files.extend(moved_pass)

            if selected_fail:
                moved_fail = retrieve_files(selected_fail, fail_folder, source_folder)
                retrieved_files.extend(moved_fail)

            if retrieved_files:
                st.success(f"Retrieved files: {', '.join(retrieved_files)} back to the source folder!")
            else:
                st.info("ℹ️ No files selected for retrieval.")

    st.markdown("---")  # Separator

    with st.container(border=True):
        st.subheader("2. Updated Folder Contents")
        col1, col2, col3 = st.columns(3)

        with col1:
            st.markdown("### πŸ“‚ Source Folder")
            source_contents = list_files(source_folder)
            if source_contents:
                for item in source_contents:
                    st.markdown(f"- **{item}**")
            else:
                st.write("No files in source folder.")

        with col2:
            st.markdown("### Pass Folder")
            pass_contents = list_files(pass_folder)
            if pass_contents:
                for item in pass_contents:
                    st.markdown(f"- **{item}**")
            else:
                st.write("No files in pass folder.")

        with col3:
            st.markdown("### Fail Folder")
            fail_contents = list_files(fail_folder)
            if fail_contents:
                for item in fail_contents:
                    st.markdown(f"- **{item}**")
            else:
                st.write("No files in fail folder.")

with tab3:
    st.header("πŸ–ŠοΈ E-Sign Document")

    st.subheader("Upload Signature Image")
    signature_image = st.file_uploader("Choose an image file for your signature", type=["png", "jpg", "jpeg"])
    
    if signature_image:
        st.image(signature_image, caption="Uploaded Signature Image", use_column_width=True)

    st.subheader("Select Document to Sign")
    pass_files = list_files(pass_folder)
    if pass_files:
        file_to_sign = st.selectbox("Select a document to sign", pass_files)
        selected_file_path = os.path.join(pass_folder, file_to_sign)
        signed_file_path = os.path.join(signed_folder, f"signed_{file_to_sign}")

        if st.button("Apply Signature"):
            if signature_image:
                # Save signature image temporarily for adding to PDF
                signature_path = f"temp_signature_{file_to_sign}.png"
                with open(signature_path, "wb") as sig_file:
                    sig_file.write(signature_image.getbuffer())

                if add_signature_to_pdf(selected_file_path, signature_path, signed_file_path):
                    st.success(f"Signature applied to {file_to_sign} successfully!")
                    st.markdown(f"[Download Signed Document](signed_folder/{signed_file_path})", unsafe_allow_html=True)
                    
                os.remove(signature_path)
            else:
                st.warning("Please upload a signature image first.")
    else:
        st.info("No documents in the 'Pass' folder to sign.")

In the code shown above, I tried to esign the pdf after it is declared as pass. Please help me in this regard, whether streamlit is already having an option to esign the pdf document using the app.

What do you need help with? Did something go wrong when you tried?

No, Streamlit is not in the business of signing pdf documents.

First, you’ll need to find a Python library that lets you manage esignatures. I Googled β€œmanage pdf esign in python” and got quite a few results, but I have not worked with anything like this.

Streamlit is just the visual interface to take in user input; your app needs to have other libraries to handle the logical work.

I am not sure what your intent is. If you want to insert an image and some text, I guess pypdf can do it. But that is not really a signature.

For adding an actual signature, using a certificate, search the web as suggested above.

I have never triad that, but I found some ideas in the Internet.