Spaces:
Build error
Build error
| import streamlit as st | |
| import fitz # PyMuPDF | |
| from PIL import Image, ImageDraw, ImageFont | |
| import difflib | |
| from reportlab.pdfgen import canvas | |
| from reportlab.lib.pagesizes import letter | |
| import tempfile | |
| import os | |
| from io import BytesIO | |
| # Load a standard font for annotations | |
| font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf" | |
| font_size = 10 # Reduced size for better CPU performance | |
| font = ImageFont.truetype(font_path, font_size) | |
| def load_and_compare_documents(file1, file2): | |
| file1_content = file1.read() | |
| file2_content = file2.read() | |
| # Convert PDFs to images | |
| images1 = pdf_to_images(file1_content) | |
| images2 = pdf_to_images(file2_content) | |
| # Perform text comparison | |
| text1 = extract_text_from_pdf(file1_content) | |
| text2 = extract_text_from_pdf(file2_content) | |
| differences = compare_texts(text1, text2) | |
| # Annotate differences on images | |
| marked_images_1, marked_images_2 = annotate_images(images1, images2, differences) | |
| # Generate the output PDF | |
| pdf_buffer = create_pdf_with_side_by_side(marked_images_1, marked_images_2, differences) | |
| overall_summary = generate_overall_summary(differences) | |
| return pdf_buffer, overall_summary | |
| def pdf_to_images(file_content): | |
| """Converts each page of the PDF into a medium-resolution image for CPU efficiency.""" | |
| images = [] | |
| pdf_document = fitz.open(stream=file_content, filetype="pdf") | |
| for page in pdf_document: | |
| pix = page.get_pixmap(dpi=200) # Lower DPI for faster processing | |
| img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples) | |
| images.append(img) | |
| pdf_document.close() | |
| return images | |
| def extract_text_from_pdf(file_content): | |
| """Extracts text content from the PDF.""" | |
| pdf_document = fitz.open(stream=file_content, filetype="pdf") | |
| text = [page.get_text("text") for page in pdf_document] | |
| pdf_document.close() | |
| return text | |
| def compare_texts(text1, text2): | |
| """Compares text content and generates differences.""" | |
| differences = [] | |
| position_counter = 1 | |
| for page_num, (page1, page2) in enumerate(zip(text1, text2), start=1): | |
| diff = list(difflib.ndiff(page1.splitlines(), page2.splitlines())) | |
| page_diffs = [] | |
| for change in diff: | |
| if change.startswith("+ ") or change.startswith("- "): | |
| change_type = "Added" if change.startswith("+ ") else "Deleted" | |
| value = change[2:] | |
| description = f"'{value}' {change_type.lower()} at position {position_counter}" | |
| page_diffs.append({"type": change_type, "value": value, "index": position_counter, "description": description}) | |
| position_counter += 1 | |
| differences.append({"page": page_num, "differences": page_diffs}) | |
| return differences | |
| def annotate_images(images1, images2, differences): | |
| """Annotates differences on the images.""" | |
| marked_images_1 = {} | |
| marked_images_2 = {} | |
| for page_num, (img1, img2) in enumerate(zip(images1, images2), start=1): | |
| draw1 = ImageDraw.Draw(img1) | |
| draw2 = ImageDraw.Draw(img2) | |
| for diff in differences[page_num - 1]["differences"]: | |
| value = diff["value"] | |
| position = diff["index"] | |
| # Annotate differences | |
| draw2.text((10, 10 + position * 15), f"{position}: {value}", fill="blue", font=font) | |
| draw2.rectangle([10, 10 + position * 15, 200, 30 + position * 15], outline="red", width=2) | |
| marked_images_1[page_num] = img1 | |
| marked_images_2[page_num] = img2 | |
| return marked_images_1, marked_images_2 | |
| def create_pdf_with_side_by_side(marked_images_1, marked_images_2, differences): | |
| """Generates a PDF with side-by-side comparison and observation summary.""" | |
| pdf_buffer = BytesIO() | |
| c = canvas.Canvas(pdf_buffer, pagesize=(letter[0] * 2, letter[1])) | |
| for page_num, img1 in marked_images_1.items(): | |
| img2 = marked_images_2[page_num] | |
| with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_img_file1: | |
| img1.save(temp_img_file1, format="PNG") | |
| temp_img_path1 = temp_img_file1.name | |
| with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as temp_img_file2: | |
| img2.save(temp_img_file2, format="PNG") | |
| temp_img_path2 = temp_img_file2.name | |
| c.drawImage(temp_img_path1, 0, 0, width=letter[0], height=letter[1]) | |
| c.drawImage(temp_img_path2, letter[0], 0, width=letter[0], height=letter[1]) | |
| c.showPage() | |
| os.remove(temp_img_path1) | |
| os.remove(temp_img_path2) | |
| y_position = 750 | |
| c.drawString(10, y_position, f"Observation Summary for Page {page_num}:") | |
| y_position -= 20 | |
| for diff in differences[page_num - 1]["differences"]: | |
| text = diff["description"] | |
| c.drawString(10, y_position, text) | |
| y_position -= 15 | |
| if y_position < 50: | |
| c.showPage() | |
| y_position = 750 | |
| c.showPage() | |
| c.save() | |
| pdf_buffer.seek(0) | |
| return pdf_buffer | |
| def generate_overall_summary(differences): | |
| """Generates a summary of additions and deletions.""" | |
| total_additions = sum(len([d for d in diff["differences"] if d["type"] == "Added"]) for diff in differences) | |
| total_deletions = sum(len([d for d in diff["differences"] if d["type"] == "Deleted"]) for diff in differences) | |
| return {"total_additions": total_additions, "total_deletions": total_deletions} | |
| def main(): | |
| st.title("PDF Comparison Tool with Annotations (CPU-Optimized)") | |
| st.write("Upload PDFs for side-by-side comparison and observation summary.") | |
| file1 = st.file_uploader("Document 1 (PDF only)", type=["pdf"]) | |
| file2 = st.file_uploader("Document 2 (PDF only)", type=["pdf"]) | |
| if st.button("Compare Documents") and file1 and file2: | |
| pdf_buffer, overall_summary = load_and_compare_documents(file1, file2) | |
| st.subheader("Overall Summary") | |
| for key, value in overall_summary.items(): | |
| st.write(f"{key.replace('_', ' ').capitalize()}: {value}") | |
| st.subheader("Download Comparison PDF") | |
| st.download_button("Download PDF", data=pdf_buffer, file_name="comparison_with_summary.pdf", mime="application/pdf") | |
| if __name__ == "__main__": | |
| main() | |