rehan953 commited on
Commit
06541c6
·
verified ·
1 Parent(s): fffb995

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -11
app.py CHANGED
@@ -18,11 +18,10 @@ Explicitly omitted vs the heavy Space build:
18
  - No text-layer row injection, institution-specific splits (UCB / Navy /
19
  TD / First Horizon / …), or doc-wide dedupe passes.
20
 
21
- Included (data-driven, header-agnostic):
22
- - HTML tables: infer modal logical column width from each table's own rows
23
- (colspan-aware), pad short rows, trim trailing empty cells on over-wide
24
- rows. No fixed N, no header keywords, no date/money heuristics. Does not
25
- fix same-width rows with content in the wrong cell (needs bbox / PDF text).
26
 
27
  Configure GLMOCR_API_KEY (environment variable). Optional: glmocr + gradio +
28
  pymupdf + pillow installed.
@@ -315,7 +314,7 @@ def _logical_row_width(entries: List[Tuple[str, int]]) -> int:
315
 
316
 
317
  def _cell_text_empty(full_cell: str) -> bool:
318
- m = _CELL.fullmatch(full_cell.strip(), flags=re.IGNORECASE | re.DOTALL)
319
  if not m:
320
  inner = re.sub(r"<[^>]+>", " ", full_cell)
321
  else:
@@ -325,6 +324,47 @@ def _cell_text_empty(full_cell: str) -> bool:
325
  return inner == ""
326
 
327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  def _infer_modal_logical_width(tr_inners: List[str]) -> int:
329
  """
330
  Modal logical column count across rows (colspan sums). On frequency ties,
@@ -355,13 +395,16 @@ def _normalize_one_tr_inner(tr_inner: str, target: int) -> str:
355
  w = sum(spans)
356
  if w < target:
357
  cells.extend(["<td></td>"] * (target - w))
358
- return "".join(cells)
 
359
  while w > target and cells:
360
  if spans[-1] != 1 or not _cell_text_empty(cells[-1]):
361
  break
362
  w -= spans[-1]
363
  cells.pop()
364
  spans.pop()
 
 
365
  return "".join(cells)
366
 
367
 
@@ -371,8 +414,11 @@ def normalize_html_table_row_widths(md: str) -> str:
371
  rows (colspan-aware), then pad rows that are too narrow or strip trailing
372
  empty single-colspan cells from rows that are too wide.
373
 
374
- No column names, dates, currency patterns, or fixed N only per-table
375
- statistics. Tables containing rowspan are left unchanged (unsafe to infer).
 
 
 
376
  """
377
  if not md or "<table" not in md.lower():
378
  return md
@@ -664,8 +710,8 @@ def _create_gradio_demo():
664
  with gr.Blocks(title="GLM-OCR (simple)") as demo:
665
  gr.Markdown(
666
  "# GLM-OCR (simple)\n"
667
- "Upload a PDF or image. Header and footer bands are included; "
668
- "body OCR is passed through with only light markdown cleanup."
669
  )
670
  file_in = gr.File(
671
  label="Upload PDF or image",
 
18
  - No text-layer row injection, institution-specific splits (UCB / Navy /
19
  TD / First Horizon / …), or doc-wide dedupe passes.
20
 
21
+ Included (data-driven, no institution names):
22
+ - HTML tables: modal logical width from rowspan-free rows (colspan-aware);
23
+ pad short rows; trim trailing empty cells; if last cell is empty and the
24
+ previous cell is only a currency token, move that token to the last column.
 
25
 
26
  Configure GLMOCR_API_KEY (environment variable). Optional: glmocr + gradio +
27
  pymupdf + pillow installed.
 
314
 
315
 
316
  def _cell_text_empty(full_cell: str) -> bool:
317
+ m = _CELL.fullmatch(full_cell.strip())
318
  if not m:
319
  inner = re.sub(r"<[^>]+>", " ", full_cell)
320
  else:
 
324
  return inner == ""
325
 
326
 
327
+ def _cell_plain_text(full_cell: str) -> str:
328
+ """Visible text of one td/th, no tags."""
329
+ m = _CELL.fullmatch(full_cell.strip())
330
+ if not m:
331
+ t = re.sub(r"<[^>]+>", " ", full_cell)
332
+ else:
333
+ t = m.group(3)
334
+ t = html.unescape(re.sub(r"\s+", " ", t).strip())
335
+ return t
336
+
337
+
338
+ def _is_whole_cell_currency(text: str) -> bool:
339
+ """
340
+ True iff the cell is nothing but a currency-looking amount (optional $, commas, 2 decimals).
341
+ Excludes dates (slashes) and arbitrary prose — not keyed to column headers.
342
+ """
343
+ if not text or "/" in text:
344
+ return False
345
+ return bool(
346
+ re.fullmatch(
347
+ r"-?(?:\$|€|£)?\s*\d{1,3}(?:,\d{3})*\.\d{2}\s*",
348
+ text,
349
+ )
350
+ or re.fullmatch(r"-?(?:\$|€|£)?\s*\d+\.\d{2}\s*", text)
351
+ )
352
+
353
+
354
+ def _realign_money_if_last_cell_empty(cells: List[str]) -> List[str]:
355
+ """
356
+ … | X | empty -> … | empty | X when X is currency-only (fixes amount parked
357
+ one column left of an empty trailing cell, including after width padding).
358
+ """
359
+ if len(cells) < 2:
360
+ return cells
361
+ if not _cell_text_empty(cells[-1]):
362
+ return cells
363
+ if not _is_whole_cell_currency(_cell_plain_text(cells[-2])):
364
+ return cells
365
+ return cells[:-2] + ["<td></td>", cells[-2]]
366
+
367
+
368
  def _infer_modal_logical_width(tr_inners: List[str]) -> int:
369
  """
370
  Modal logical column count across rows (colspan sums). On frequency ties,
 
395
  w = sum(spans)
396
  if w < target:
397
  cells.extend(["<td></td>"] * (target - w))
398
+ spans.extend([1] * (target - w))
399
+ w = target
400
  while w > target and cells:
401
  if spans[-1] != 1 or not _cell_text_empty(cells[-1]):
402
  break
403
  w -= spans[-1]
404
  cells.pop()
405
  spans.pop()
406
+ if cells and all(s == 1 for s in spans):
407
+ cells = _realign_money_if_last_cell_empty(cells)
408
  return "".join(cells)
409
 
410
 
 
414
  rows (colspan-aware), then pad rows that are too narrow or strip trailing
415
  empty single-colspan cells from rows that are too wide.
416
 
417
+ No column names or fixed N: width comes from per-table row statistics.
418
+ If the last cell is empty and the previous cell is only a currency token,
419
+ that amount is moved into the last column (whole-cell shape, not headers).
420
+ Tables with rowspan are skipped. Same-width wrong text that is not
421
+ currency-shaped is not altered.
422
  """
423
  if not md or "<table" not in md.lower():
424
  return md
 
710
  with gr.Blocks(title="GLM-OCR (simple)") as demo:
711
  gr.Markdown(
712
  "# GLM-OCR (simple)\n"
713
+ "Upload a PDF or image. Header and footer bands use PDF text when available. "
714
+ "Body markdown is produced by the vision model and may disagree with the PDF text layer."
715
  )
716
  file_in = gr.File(
717
  label="Upload PDF or image",