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()