rehan953 commited on
Commit
4685f40
·
verified ·
1 Parent(s): cc4a31f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +93 -18
app.py CHANGED
@@ -20,8 +20,10 @@ Explicitly omitted vs the heavy Space build:
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.
@@ -76,7 +78,7 @@ if not GLMOCR_API_KEY:
76
 
77
  # Rasterization: higher scale = more pixels per PDF point (helps small type,
78
  # boxed headers, and narrow columns). Same constant for all uploads.
79
- RENDER_SCALE = 3.05
80
 
81
  # White margin as a fraction of page width/height after render. Extra right
82
  # margin helps right-aligned currency columns that hug the page edge.
@@ -87,12 +89,12 @@ PAD_BOTTOM_FRAC = 0.018
87
 
88
  ENABLE_CONTRAST = True
89
  # Slight contrast lift only; same factor for every file.
90
- CONTRAST_FACTOR = 1.16
91
 
92
  # Subtle edge enhancement after contrast (helps hairlines and small digits).
93
  ENABLE_UNSHARP = True
94
  UNSHARP_RADIUS = 0.78
95
- UNSHARP_PERCENT = 72
96
  UNSHARP_THRESHOLD = 1
97
 
98
  DEFAULT_ZONE_FRAC = 0.12
@@ -353,18 +355,92 @@ def _is_whole_cell_currency(text: str) -> bool:
353
  )
354
 
355
 
356
- def _realign_money_if_last_cell_empty(cells: List[str]) -> List[str]:
 
 
 
 
 
 
 
 
 
357
  """
358
- | X | empty -> | empty | X when X is currency-only (fixes amount parked
359
- one column left of an empty trailing cell, including after width padding).
360
  """
361
- if len(cells) < 2:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  return cells
363
- if not _cell_text_empty(cells[-1]):
 
 
364
  return cells
365
- if not _is_whole_cell_currency(_cell_plain_text(cells[-2])):
 
366
  return cells
367
- return cells[:-2] + ["<td></td>", cells[-2]]
368
 
369
 
370
  def _infer_modal_logical_width(tr_inners: List[str]) -> int:
@@ -405,8 +481,8 @@ def _normalize_one_tr_inner(tr_inner: str, target: int) -> str:
405
  w -= spans[-1]
406
  cells.pop()
407
  spans.pop()
408
- if cells and all(s == 1 for s in spans):
409
- cells = _realign_money_if_last_cell_empty(cells)
410
  return "".join(cells)
411
 
412
 
@@ -417,10 +493,9 @@ def normalize_html_table_row_widths(md: str) -> str:
417
  empty single-colspan cells from rows that are too wide.
418
 
419
  No column names or fixed N: width comes from per-table row statistics.
420
- If the last cell is empty and the previous cell is only a currency token,
421
- that amount is moved into the last column (whole-cell shape, not headers).
422
- Tables with rowspan are skipped. Same-width wrong text that is not
423
- currency-shaped is not altered.
424
  """
425
  if not md or "<table" not in md.lower():
426
  return md
 
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; expand each row to a logical
24
+ grid, then if the rightmost non-empty slot is a solitary amount and only
25
+ blanks follow to the edge, slide that amount into the rightmost slot (works
26
+ with merged cells, not only uniform single-span rows).
27
 
28
  Configure GLMOCR_API_KEY (environment variable). Optional: glmocr + gradio +
29
  pymupdf + pillow installed.
 
78
 
79
  # Rasterization: higher scale = more pixels per PDF point (helps small type,
80
  # boxed headers, and narrow columns). Same constant for all uploads.
81
+ RENDER_SCALE = 3.12
82
 
83
  # White margin as a fraction of page width/height after render. Extra right
84
  # margin helps right-aligned currency columns that hug the page edge.
 
89
 
90
  ENABLE_CONTRAST = True
91
  # Slight contrast lift only; same factor for every file.
92
+ CONTRAST_FACTOR = 1.18
93
 
94
  # Subtle edge enhancement after contrast (helps hairlines and small digits).
95
  ENABLE_UNSHARP = True
96
  UNSHARP_RADIUS = 0.78
97
+ UNSHARP_PERCENT = 76
98
  UNSHARP_THRESHOLD = 1
99
 
100
  DEFAULT_ZONE_FRAC = 0.12
 
355
  )
356
 
357
 
358
+ def _replace_cell_plain_body(full_cell: str, new_body_plain: str) -> str:
359
+ """Rebuild one td/th preserving opening tag attributes; body is plain text (escaped)."""
360
+ m = _CELL.fullmatch(full_cell.strip())
361
+ if not m:
362
+ return full_cell
363
+ tag, attrs = m.group(1), m.group(2)
364
+ return f"<{tag}{attrs}>{html.escape(new_body_plain)}</{tag}>"
365
+
366
+
367
+ def _logical_plain_texts_from_entries(entries: List[Tuple[str, int]]) -> List[str]:
368
  """
369
+ Flatten one table row to one string per logical column: merged spans place
370
+ full visible text on the first slot only, remainder empty strings.
371
  """
372
+ w = sum(s for _, s in entries)
373
+ if w < 1:
374
+ return []
375
+ out = [""] * w
376
+ pos = 0
377
+ for full, span in entries:
378
+ span = max(1, span)
379
+ t = _cell_plain_text(full)
380
+ out[pos] = t
381
+ for k in range(1, span):
382
+ if pos + k < w:
383
+ out[pos + k] = ""
384
+ pos += span
385
+ return out
386
+
387
+
388
+ def _shift_lone_amount_past_trailing_blanks(log: List[str]) -> List[str]:
389
+ """
390
+ If the rightmost non-blank slot is only a currency token and every slot to
391
+ its right is blank, move that token into the rightmost slot (any column count).
392
+ """
393
+ w = len(log)
394
+ if w < 2:
395
+ return log
396
+ L = w - 1
397
+ while L >= 0 and not (log[L] or "").strip():
398
+ L -= 1
399
+ if L < 0:
400
+ return log
401
+ token = (log[L] or "").strip()
402
+ if not _is_whole_cell_currency(token):
403
+ return log
404
+ if L == w - 1:
405
+ return log
406
+ if any((log[i] or "").strip() for i in range(L + 1, w)):
407
+ return log
408
+ new_log = list(log)
409
+ new_log[L] = ""
410
+ new_log[w - 1] = token
411
+ return new_log
412
+
413
+
414
+ def _materialize_cells_from_logical(
415
+ entries: List[Tuple[str, int]], new_log: List[str]
416
+ ) -> List[str]:
417
+ """Rebuild physical td/th strings from a logical text row of length sum(span)."""
418
+ w = sum(s for _, s in entries)
419
+ if len(new_log) != w:
420
+ return [e[0] for e in entries]
421
+ pos = 0
422
+ rebuilt: List[str] = []
423
+ for full, span in entries:
424
+ span = max(1, span)
425
+ chunk = [(new_log[pos + k] or "").strip() for k in range(span)]
426
+ pos += span
427
+ body = " ".join(x for x in chunk if x).strip()
428
+ rebuilt.append(_replace_cell_plain_body(full, body))
429
+ return rebuilt
430
+
431
+
432
+ def _apply_row_amount_tail_shift(cells: List[str], spans: List[int]) -> List[str]:
433
+ """Colspan-aware tail shift; returns possibly unchanged cell HTML list."""
434
+ if not cells or len(cells) != len(spans):
435
  return cells
436
+ entries = list(zip(cells, spans))
437
+ old_log = _logical_plain_texts_from_entries(entries)
438
+ if len(old_log) < 2:
439
  return cells
440
+ new_log = _shift_lone_amount_past_trailing_blanks(old_log)
441
+ if new_log == old_log:
442
  return cells
443
+ return _materialize_cells_from_logical(entries, new_log)
444
 
445
 
446
  def _infer_modal_logical_width(tr_inners: List[str]) -> int:
 
481
  w -= spans[-1]
482
  cells.pop()
483
  spans.pop()
484
+ if cells:
485
+ cells = _apply_row_amount_tail_shift(cells, spans)
486
  return "".join(cells)
487
 
488
 
 
493
  empty single-colspan cells from rows that are too wide.
494
 
495
  No column names or fixed N: width comes from per-table row statistics.
496
+ Solitary amount tokens parked before a run of blank logical slots are slid
497
+ into the rightmost slot so OCR tables stay rectangular for downstream use.
498
+ Tables with rowspan are skipped. Non-currency text is not altered.
 
499
  """
500
  if not md or "<table" not in md.lower():
501
  return md