gschleusner commited on
Commit
b874e9b
·
1 Parent(s): 4b2c414
Files changed (2) hide show
  1. app.py +174 -0
  2. requirements.txt +0 -0
app.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fitz # PyMuPDF
2
+ import gradio as gr
3
+ import zipfile
4
+ import io
5
+ import os
6
+ import tempfile
7
+ from datetime import datetime # Import to get current date
8
+
9
+ # Helper function to find color areas
10
+ def find_color_areas(page, target_color, tolerance=30):
11
+ pix = page.get_pixmap()
12
+ width, height = pix.width, pix.height
13
+ visited = [[False for _ in range(width)] for _ in range(height)]
14
+ rectangles = []
15
+
16
+ def flood_fill(x, y):
17
+ stack = [(x, y)]
18
+ rects = []
19
+ while stack:
20
+ cx, cy = stack.pop()
21
+ if visited[cy][cx]:
22
+ continue
23
+
24
+ visited[cy][cx] = True
25
+ pixel_color = pix.pixel(cx, cy)
26
+ r, g, b = pixel_color[:3]
27
+
28
+ if (abs(r - target_color[0]) <= tolerance and
29
+ abs(g - target_color[1]) <= tolerance and
30
+ abs(b - target_color[2]) <= tolerance):
31
+ rects.append(fitz.Rect(cx, cy, cx + 1, cy + 1))
32
+ if cx > 0: stack.append((cx - 1, cy))
33
+ if cx < width - 1: stack.append((cx + 1, cy))
34
+ if cy > 0: stack.append((cx, cy - 1))
35
+ if cy < height - 1: stack.append((cx, cy + 1))
36
+
37
+ if rects:
38
+ bbox = fitz.Rect(min([r.x0 for r in rects]),
39
+ min([r.y0 for r in rects]),
40
+ max([r.x1 for r in rects]),
41
+ max([r.y1 for r in rects]))
42
+ return bbox
43
+ return None
44
+
45
+ for y in range(height):
46
+ for x in range(width):
47
+ if not visited[y][x]:
48
+ bbox = flood_fill(x, y)
49
+ if bbox:
50
+ rectangles.append(bbox)
51
+ return rectangles
52
+
53
+ def merge_overlapping_rectangles(rectangles):
54
+ merged_rects = []
55
+ while rectangles:
56
+ rect = rectangles.pop(0)
57
+ to_merge = [rect]
58
+ for other in rectangles[:]:
59
+ if rect.intersects(other):
60
+ to_merge.append(other)
61
+ rectangles.remove(other)
62
+ merged_rect = fitz.Rect(
63
+ min([r.x0 for r in to_merge]),
64
+ min([r.y0 for r in to_merge]),
65
+ max([r.x1 for r in to_merge]),
66
+ max([r.y1 for r in to_merge])
67
+ )
68
+ merged_rects.append(merged_rect)
69
+ return merged_rects
70
+
71
+ def markup_color_regions(doc, color_comment_pairs, tolerance=30):
72
+ for page_num in range(len(doc)):
73
+ page = doc[page_num]
74
+ for color_comment_pair in color_comment_pairs:
75
+ target_color = color_comment_pair['color']
76
+ comment = color_comment_pair['comment']
77
+ stroke_color = color_comment_pair['stroke_color']
78
+
79
+ rectangles = find_color_areas(page, target_color, tolerance)
80
+ if rectangles:
81
+ merged_rectangles = merge_overlapping_rectangles(rectangles)
82
+ for bbox in merged_rectangles:
83
+ annot = page.add_rect_annot(bbox)
84
+ annot.set_colors(stroke=stroke_color)
85
+ annot.set_border(width=2)
86
+ annot.set_info({"title": "Markup", "content": comment})
87
+ annot.update()
88
+
89
+ def process_pdf_files(input_pdfs, selected_color_comment_indices, tolerance, custom_color, custom_comment, custom_stroke_color):
90
+ color_comment_pairs = [
91
+ {
92
+ "color": (235, 128, 138),
93
+ "comment": "Structural Slab greater than architectural slab",
94
+ "stroke_color": (1, 0, 0)
95
+ },
96
+ {
97
+ "color": (128, 253, 128),
98
+ "comment": "Arch Slab greater than Structure",
99
+ "stroke_color": (0, 1, 0)
100
+ }
101
+ ]
102
+
103
+ # Add custom color-comment pair if provided
104
+ if custom_color and custom_comment and custom_stroke_color:
105
+ custom_color_tuple = tuple(map(int, custom_color.split(','))) # Convert color to tuple
106
+ custom_stroke_tuple = tuple(map(int, custom_stroke_color.split(','))) # Convert stroke to tuple
107
+ color_comment_pairs.append({
108
+ "color": custom_color_tuple,
109
+ "comment": custom_comment,
110
+ "stroke_color": custom_stroke_tuple
111
+ })
112
+
113
+ # Get the selected color-comment pairs
114
+ selected_color_comment_pairs = [color_comment_pairs[i] for i in selected_color_comment_indices]
115
+
116
+ # List to keep track of all modified PDFs (in-memory)
117
+ modified_pdfs = []
118
+
119
+ # Process each input PDF file
120
+ for pdf_file in input_pdfs:
121
+ with open(pdf_file.name, "rb") as file_stream:
122
+ doc = fitz.open(stream=file_stream.read(), filetype="pdf")
123
+ markup_color_regions(doc, selected_color_comment_pairs, tolerance)
124
+
125
+ # Save the modified PDF in memory
126
+ pdf_in_memory = io.BytesIO()
127
+ doc.save(pdf_in_memory)
128
+ doc.close()
129
+
130
+ # Move pointer to the beginning of the BytesIO object
131
+ pdf_in_memory.seek(0)
132
+ modified_pdfs.append((os.path.basename(pdf_file.name), pdf_in_memory))
133
+
134
+ # Create an in-memory ZIP file containing all modified PDFs
135
+ zip_in_memory = io.BytesIO()
136
+ with zipfile.ZipFile(zip_in_memory, 'w') as zipf:
137
+ for pdf_name, pdf_bytes in modified_pdfs:
138
+ zipf.writestr(pdf_name, pdf_bytes.read())
139
+
140
+ # Move pointer to the beginning of the ZIP file BytesIO object
141
+ zip_in_memory.seek(0)
142
+
143
+ # Generate the current date string
144
+ current_date = datetime.now().strftime("%Y-%m-%d")
145
+
146
+ # Create a temporary file with a custom name including the date
147
+ temp_filename = f"CoordinationPDFS_{current_date}.zip"
148
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".zip", prefix="CoordinationPDFS_", dir=".") as tmp_file:
149
+ tmp_file.write(zip_in_memory.read())
150
+ temp_file_name = tmp_file.name # Store the temp file name
151
+
152
+ return temp_file_name # Return the path to the temp file
153
+
154
+ # Define the Gradio interface
155
+ interface = gr.Interface(
156
+ fn=process_pdf_files,
157
+ inputs=[
158
+ gr.Files(label="Input PDF Files", file_types=[".pdf"]),
159
+ gr.CheckboxGroup( # Use CheckboxGroup for multi-select
160
+ label="Select Color-Comment Pairs",
161
+ choices=["Structural Slab vs Arch Slab", "Arch Slab vs Structural Slab", "Custom Option"],
162
+ type="index"
163
+ ),
164
+ gr.Slider(label="Tolerance", minimum=0, maximum=100, step=1, value=30),
165
+ gr.Textbox(label="Custom Color (R,G,B)", placeholder="Enter custom color in RGB format, e.g., 255,0,0"),
166
+ gr.Textbox(label="Custom Comment", placeholder="Enter custom comment for this color"),
167
+ gr.Textbox(label="Custom Stroke Color (R,G,B)", placeholder="Enter stroke color in RGB format, e.g., 0,0,255")
168
+ ],
169
+ outputs=gr.File(label="Download Modified PDFs as ZIP"),
170
+ title="PDF Color Region Markup"
171
+ )
172
+
173
+ # Launch the Gradio app
174
+ interface.launch()
requirements.txt ADDED
Binary file (1.91 kB). View file