import io from PyPDF2 import PdfReader, PdfWriter, PageObject import streamlit as st def validate_pdf(file_stream): """ Validates if the file is a proper PDF by trying to read it. """ try: PdfReader(file_stream) return True except Exception: return False def merge_two_pages_on_one(page1, page2): """ Merges two PDF pages into a single PDF page (side by side). """ width = page1.mediabox.width + page2.mediabox.width height = max(page1.mediabox.height, page2.mediabox.height) new_page = PageObject.create_blank_page(width=width, height=height) new_page.merge_page(page1) page2_content = PageObject.create_blank_page(width=width, height=height) page2_content.merge_page(page2) page2_content.add_transformation([1, 0, 0, 1, page1.mediabox.width, 0]) # x translation by page1 width new_page.merge_page(page2_content) return new_page def reverse_pdf(input_pdf_stream): """ Reverses the order of pages in a PDF and returns the reversed file stream. """ reader = PdfReader(input_pdf_stream) writer = PdfWriter() for page_num in reversed(range(len(reader.pages))): writer.add_page(reader.pages[page_num]) output_stream = io.BytesIO() writer.write(output_stream) output_stream.seek(0) return output_stream def create_booklet_with_two_pages_per_sheet(pdf_file): """ Processes the uploaded PDF to create a booklet with two pages per sheet. """ input_pdf_stream = io.BytesIO(pdf_file.read()) # Validate the PDF file if not validate_pdf(input_pdf_stream): raise ValueError("Uploaded file is not a valid PDF.") input_pdf_stream.seek(0) reader = PdfReader(input_pdf_stream) total_pages = len(reader.pages) writer = PdfWriter() for page in reader.pages: writer.add_page(page) remainder = total_pages % 4 blank_pages_needed = (4 - remainder) if remainder > 0 else 0 for _ in range(blank_pages_needed): writer.add_blank_page() temp_pdf_stream = io.BytesIO() writer.write(temp_pdf_stream) temp_pdf_stream.seek(0) modified_reader = PdfReader(temp_pdf_stream) total_pages = len(modified_reader.pages) front_writer = PdfWriter() back_writer = PdfWriter() # Create front pages for i in range(total_pages // 4): page1 = modified_reader.pages[total_pages - 1 - (2 * i)] page2 = modified_reader.pages[2 * i] merged_page = merge_two_pages_on_one(page1, page2) front_writer.add_page(merged_page) # Create back pages for i in reversed(range(total_pages // 4)): page1 = modified_reader.pages[2 * i + 1] page2 = modified_reader.pages[total_pages - 2 - (2 * i)] merged_page = merge_two_pages_on_one(page1, page2) back_writer.add_page(merged_page) # Prepare output streams front_pdf_stream = io.BytesIO() back_pdf_stream = io.BytesIO() reversed_front_pdf_stream = io.BytesIO() reversed_back_pdf_stream = io.BytesIO() front_writer.write(front_pdf_stream) back_writer.write(back_pdf_stream) front_pdf_stream.seek(0) back_pdf_stream.seek(0) # Reverse PDFs reversed_front_pdf_stream = reverse_pdf(front_pdf_stream) reversed_back_pdf_stream = reverse_pdf(back_pdf_stream) return front_pdf_stream, back_pdf_stream, reversed_front_pdf_stream, reversed_back_pdf_stream # Streamlit UI # st.title("Booklet Generator: Create Print-Ready PDFs") st.title("Print-Ready PDF Booklet Generator") st.write(""" This tool simplifies the process of creating booklet-style PDFs. It generates booklet-ready files designed for back-to-back printing. Additionally, it provides multiple versions, including reversed formats, to accommodate all printer configurations seamlessly. Ideal for printing books, modules, and other documents, this tool specifically helps save paper while ensuring efficient, professional-quality results. """) # Initialize session state if "pdf_outputs" not in st.session_state: st.session_state["pdf_outputs"] = None uploaded_file = st.file_uploader("Upload PDF", type=["pdf"]) if uploaded_file is not None and st.button("Generate Booklet"): with st.spinner("Processing..."): try: front_pdf, back_pdf, reversed_front_pdf, reversed_back_pdf = create_booklet_with_two_pages_per_sheet(uploaded_file) st.session_state["pdf_outputs"] = { "front_pdf": front_pdf, "back_pdf": back_pdf, "reversed_front_pdf": reversed_front_pdf, "reversed_back_pdf": reversed_back_pdf, } st.success("Booklet generated successfully!") except Exception as e: st.error(f"Error: {str(e)}") # Add a separator before downloads st.divider() # Adds a horizontal line # Display download buttons if PDFs are available if st.session_state["pdf_outputs"] is not None: outputs = st.session_state["pdf_outputs"] st.download_button("Download Booklet Front PDF", outputs["front_pdf"], "booklet_front.pdf", "application/pdf") st.download_button("Download Booklet Back PDF", outputs["back_pdf"], "booklet_back.pdf", "application/pdf") st.download_button("Download Reversed Booklet Front PDF", outputs["reversed_front_pdf"], "reversed_booklet_front.pdf", "application/pdf") st.download_button("Download Reversed Booklet Back PDF", outputs["reversed_back_pdf"], "reversed_booklet_back.pdf", "application/pdf") # Add footer st.markdown(""" ---