Preeeeet commited on
Commit
f576f2c
·
verified ·
1 Parent(s): 184d675

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -74
app.py CHANGED
@@ -17,12 +17,7 @@ pillow_heif.register_heif_opener()
17
 
18
  # --- CONSTANTS & LAYOUT ---
19
  # Page: Letter 8.5" x 11", margins below
20
- CONTENT_WIDTH_IN = 8.5 - (0.5 + 0.5) # 7.5"
21
- DESC_COL_IN = CONTENT_WIDTH_IN * (1 / 5.0) # strict 1:4 ratio
22
- PHOTO_COL_IN = CONTENT_WIDTH_IN * (4 / 5.0)
23
- ROW_HEIGHT_IN = 4.5 # two rows per page (fits within 9.5" body height)
24
- PHOTO_MAX_W_IN = PHOTO_COL_IN - 0.20 # small padding
25
- PHOTO_MAX_H_IN = ROW_HEIGHT_IN - 0.20
26
 
27
  JOB_MAP = {
28
  "WM 3155 SSM Parking": "T250014 Walmart 3155 Sault Ste. Marie FY26 Parking Lot Refresh\n446 Great Northern Rd.\nSault Ste. Marie, Ontario, P6B 4Z9",
@@ -141,7 +136,6 @@ def generate_report(image_items, job_details, compress_images, quality, progress
141
 
142
  # Header
143
  header = section.header
144
- # --- FIX 1: The header's add_table method REQUIRES the width argument directly ---
145
  header_table = header.add_table(rows=1, cols=2, width=Inches(CONTENT_WIDTH_IN))
146
  header_table.autofit = False
147
  set_table_fixed_layout(header_table)
@@ -167,93 +161,101 @@ def generate_report(image_items, job_details, compress_images, quality, progress
167
  footer = section.footer
168
  add_page_number(footer.paragraphs[0])
169
 
170
- # Build pages: 2 items per page; last page may have 1
171
  num_images = len(image_items)
172
  processed = 0
173
 
174
- for start in range(0, num_images, 2):
175
  if start > 0:
176
  doc.add_page_break()
177
 
178
- page_items = image_items[start:start + 2]
179
 
180
- # --- FIX 2: The main document's add_table method does NOT accept the 'width' keyword ---
181
- # So we create the table first, then set its width property.
182
- page_table = doc.add_table(rows=2, cols=2)
183
  page_table.width = Inches(CONTENT_WIDTH_IN)
184
  page_table.autofit = False
185
  set_table_fixed_layout(page_table)
186
  page_table.style = 'Table Grid'
187
  page_table.alignment = WD_TABLE_ALIGNMENT.CENTER
188
 
189
- # Lock column widths and row heights
190
- page_table.columns[0].width = Inches(DESC_COL_IN)
191
- page_table.columns[1].width = Inches(PHOTO_COL_IN)
 
 
 
 
 
 
 
 
 
 
 
 
192
  for r in page_table.rows:
193
- r.height = Inches(ROW_HEIGHT_IN)
194
  r.height_rule = WD_ROW_HEIGHT_RULE.EXACTLY
195
- r.cells[0].width = Inches(DESC_COL_IN)
196
- r.cells[1].width = Inches(PHOTO_COL_IN)
197
 
198
- # Fill two rows
199
- for row_idx in range(2):
200
- desc_cell = page_table.cell(row_idx, 0)
201
- photo_cell = page_table.cell(row_idx, 1)
 
 
 
 
 
202
 
203
- ensure_cell_has_one_empty_paragraph(desc_cell)
204
- ensure_cell_has_one_empty_paragraph(photo_cell)
 
 
 
205
 
206
  desc_cell.vertical_alignment = WD_ALIGN_VERTICAL.TOP
207
  photo_cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
208
 
209
- if row_idx < len(page_items):
210
- item = page_items[row_idx]
211
- rotation = item.get('rotation', 0) or 0
212
-
213
- # Description
214
- p_desc = desc_cell.paragraphs[0]
215
- p_desc.paragraph_format.space_before = Pt(0)
216
- p_desc.paragraph_format.space_after = Pt(0)
217
- run_label = p_desc.add_run("Description:\n")
218
- run_label.bold = True
219
- p_desc.add_run(item.get('description', '') or '')
220
-
221
- # Determine fit by aspect ratio
222
- try:
223
- item['file'].seek(0)
224
- im = Image.open(item['file'])
225
- if rotation:
226
- im = im.rotate(rotation, expand=True)
227
- w_px, h_px = im.size
228
- except Exception:
229
- w_px, h_px = (1000, 1000)
230
-
231
- img_ratio = (w_px / h_px) if h_px else 1.0
232
- box_ratio = PHOTO_MAX_W_IN / PHOTO_MAX_H_IN
233
-
234
- # Process (rotate + compress)
235
- img_stream = process_image(
236
- item['file'],
237
- compress=compress_images,
238
- quality=quality,
239
- rotation=rotation
240
- )
 
 
 
241
 
242
- if img_stream:
243
- p_photo = photo_cell.paragraphs[0]
244
- p_photo.paragraph_format.space_before = Pt(0)
245
- p_photo.paragraph_format.space_after = Pt(0)
246
- p_photo.alignment = WD_ALIGN_PARAGRAPH.CENTER
247
- if img_ratio >= box_ratio:
248
- p_photo.add_run().add_picture(img_stream, width=Inches(PHOTO_MAX_W_IN))
249
- else:
250
- p_photo.add_run().add_picture(img_stream, height=Inches(PHOTO_MAX_H_IN))
251
-
252
- processed += 1
253
- if num_images:
254
- progress_bar.progress(processed / num_images)
255
- else:
256
- pass
257
 
258
  # Save DOCX
259
  doc_io = io.BytesIO()
@@ -318,6 +320,7 @@ with st.sidebar:
318
  st.rerun()
319
 
320
  st.header("Upload, Arrange, Rotate, and Describe Images")
 
321
  uploaded_files = st.file_uploader(
322
  "Upload all your photos here",
323
  accept_multiple_files=True,
@@ -338,7 +341,6 @@ if uploaded_files:
338
  }
339
 
340
  if st.session_state.image_dict:
341
- st.info("Set the order, rotate if needed, then add descriptions. Exactly 2 photos per page (last page may have 1).")
342
  items = list(st.session_state.image_dict.items())
343
  items.sort(key=lambda kv: kv[1].get('order', 0))
344
 
@@ -391,4 +393,5 @@ if generate_button:
391
  c2.download_button("⬇️ Download PDF (.pdf)", pdf_io, f"{file_base}.pdf", use_container_width=True)
392
  except Exception as e:
393
  st.error(f"A critical error occurred: {e}")
394
- progress_bar.empty()
 
 
17
 
18
  # --- CONSTANTS & LAYOUT ---
19
  # Page: Letter 8.5" x 11", margins below
20
+ CONTENT_WIDTH_IN = 8.5 - (0.5 + 0.5) # 7.5" total content width
 
 
 
 
 
21
 
22
  JOB_MAP = {
23
  "WM 3155 SSM Parking": "T250014 Walmart 3155 Sault Ste. Marie FY26 Parking Lot Refresh\n446 Great Northern Rd.\nSault Ste. Marie, Ontario, P6B 4Z9",
 
136
 
137
  # Header
138
  header = section.header
 
139
  header_table = header.add_table(rows=1, cols=2, width=Inches(CONTENT_WIDTH_IN))
140
  header_table.autofit = False
141
  set_table_fixed_layout(header_table)
 
161
  footer = section.footer
162
  add_page_number(footer.paragraphs[0])
163
 
164
+ # --- LAYOUT CHANGE: 2x2 GRID (4 PHOTOS PER PAGE) ---
165
  num_images = len(image_items)
166
  processed = 0
167
 
168
+ for start in range(0, num_images, 4): # Process 4 images at a time
169
  if start > 0:
170
  doc.add_page_break()
171
 
172
+ page_items = image_items[start:start + 4]
173
 
174
+ # A 2x4 table holds two (Desc | Photo) pairs per row
175
+ page_table = doc.add_table(rows=2, cols=4)
 
176
  page_table.width = Inches(CONTENT_WIDTH_IN)
177
  page_table.autofit = False
178
  set_table_fixed_layout(page_table)
179
  page_table.style = 'Table Grid'
180
  page_table.alignment = WD_TABLE_ALIGNMENT.CENTER
181
 
182
+ # Define column widths for the new grid layout
183
+ PAIR_WIDTH_IN = CONTENT_WIDTH_IN / 2.0
184
+ DESC_COL_IN_GRID = PAIR_WIDTH_IN * (1 / 5.0)
185
+ PHOTO_COL_IN_GRID = PAIR_WIDTH_IN * (4 / 5.0)
186
+
187
+ # Lock column widths
188
+ page_table.columns[0].width = Inches(DESC_COL_IN_GRID)
189
+ page_table.columns[1].width = Inches(PHOTO_COL_IN_GRID)
190
+ page_table.columns[2].width = Inches(DESC_COL_IN_GRID)
191
+ page_table.columns[3].width = Inches(PHOTO_COL_IN_GRID)
192
+
193
+ # Define row heights to split the page
194
+ ROW_HEIGHT_GRID_IN = 4.4 # Height for each of the two main rows
195
+
196
+ # Lock row heights
197
  for r in page_table.rows:
198
+ r.height = Inches(ROW_HEIGHT_GRID_IN)
199
  r.height_rule = WD_ROW_HEIGHT_RULE.EXACTLY
 
 
200
 
201
+ # Clean all cells before populating
202
+ for row in page_table.rows:
203
+ for cell in row.cells:
204
+ ensure_cell_has_one_empty_paragraph(cell)
205
+
206
+ # Loop through the up-to-4 items for this page and place them in the grid
207
+ for item_idx, item in enumerate(page_items):
208
+ row_idx = item_idx // 2 # Item 0,1 -> row 0. Item 2,3 -> row 1.
209
+ pair_idx = item_idx % 2 # Item 0,2 -> pair 0. Item 1,3 -> pair 1.
210
 
211
+ desc_col_idx = pair_idx * 2
212
+ photo_col_idx = desc_col_idx + 1
213
+
214
+ desc_cell = page_table.cell(row_idx, desc_col_idx)
215
+ photo_cell = page_table.cell(row_idx, photo_col_idx)
216
 
217
  desc_cell.vertical_alignment = WD_ALIGN_VERTICAL.TOP
218
  photo_cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
219
 
220
+ rotation = item.get('rotation', 0) or 0
221
+
222
+ # Description
223
+ p_desc = desc_cell.paragraphs[0]
224
+ run_label = p_desc.add_run("Description:\n")
225
+ run_label.bold = True
226
+ p_desc.add_run(item.get('description', '') or '')
227
+
228
+ # Photo Sizing and Placement
229
+ PHOTO_MAX_W_IN_GRID = PHOTO_COL_IN_GRID - 0.15
230
+ PHOTO_MAX_H_IN_GRID = ROW_HEIGHT_GRID_IN - 0.15
231
+
232
+ try:
233
+ item['file'].seek(0)
234
+ im = Image.open(item['file'])
235
+ if rotation:
236
+ im = im.rotate(rotation, expand=True)
237
+ w_px, h_px = im.size
238
+ except Exception:
239
+ w_px, h_px = (1000, 1000)
240
+
241
+ img_ratio = (w_px / h_px) if h_px else 1.0
242
+ box_ratio = PHOTO_MAX_W_IN_GRID / PHOTO_MAX_H_IN_GRID
243
+
244
+ img_stream = process_image(
245
+ item['file'], compress=compress_images, quality=quality, rotation=rotation
246
+ )
247
+
248
+ if img_stream:
249
+ p_photo = photo_cell.paragraphs[0]
250
+ p_photo.alignment = WD_ALIGN_PARAGRAPH.CENTER
251
+ if img_ratio >= box_ratio:
252
+ p_photo.add_run().add_picture(img_stream, width=Inches(PHOTO_MAX_W_IN_GRID))
253
+ else:
254
+ p_photo.add_run().add_picture(img_stream, height=Inches(PHOTO_MAX_H_IN_GRID))
255
 
256
+ processed += 1
257
+ if num_images:
258
+ progress_bar.progress(processed / num_images)
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
  # Save DOCX
261
  doc_io = io.BytesIO()
 
320
  st.rerun()
321
 
322
  st.header("Upload, Arrange, Rotate, and Describe Images")
323
+ st.info("Arrange photos in the desired order. The report will be generated with a 2x2 grid (4 photos per page).")
324
  uploaded_files = st.file_uploader(
325
  "Upload all your photos here",
326
  accept_multiple_files=True,
 
341
  }
342
 
343
  if st.session_state.image_dict:
 
344
  items = list(st.session_state.image_dict.items())
345
  items.sort(key=lambda kv: kv[1].get('order', 0))
346
 
 
393
  c2.download_button("⬇️ Download PDF (.pdf)", pdf_io, f"{file_base}.pdf", use_container_width=True)
394
  except Exception as e:
395
  st.error(f"A critical error occurred: {e}")
396
+ progress_bar.empty()
397
+