Spaces:
Build error
Build error
File size: 6,297 Bytes
aca64a8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | 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()
|