Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -25,6 +25,12 @@ Included (data-driven, no institution names):
|
|
| 25 |
|
| 26 |
Configure GLMOCR_API_KEY (environment variable). Optional: glmocr + gradio +
|
| 27 |
pymupdf + pillow installed.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
"""
|
| 29 |
|
| 30 |
# Patch asyncio first (before Gradio imports it) to reduce Python 3.13 loop noise
|
|
@@ -109,6 +115,8 @@ PAGE_PNG_COMPRESS_LEVEL = 3
|
|
| 109 |
# JPEG quality for small header/footer crops sent to the API.
|
| 110 |
ZONE_JPEG_QUALITY = 95
|
| 111 |
|
|
|
|
|
|
|
| 112 |
_parser = None
|
| 113 |
|
| 114 |
|
|
@@ -186,6 +194,41 @@ def extract_zone_text_pdf(pdf_path, page_num, y_start_frac, y_end_frac):
|
|
| 186 |
return ""
|
| 187 |
|
| 188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
def extract_pdf_text_in_band(pdf_path, page_num, y_start_frac, y_end_frac):
|
| 190 |
try:
|
| 191 |
import pymupdf as fitz
|
|
@@ -613,6 +656,12 @@ def run_ocr(uploaded_file):
|
|
| 613 |
try:
|
| 614 |
path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)
|
| 615 |
is_pdf = path.lower().endswith(".pdf")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 616 |
parser = get_parser()
|
| 617 |
|
| 618 |
page_heights: List[int] = []
|
|
@@ -710,8 +759,8 @@ def _create_gradio_demo():
|
|
| 710 |
with gr.Blocks(title="GLM-OCR (simple)") as demo:
|
| 711 |
gr.Markdown(
|
| 712 |
"# GLM-OCR (simple)\n"
|
| 713 |
-
"
|
| 714 |
-
"
|
| 715 |
)
|
| 716 |
file_in = gr.File(
|
| 717 |
label="Upload PDF or image",
|
|
|
|
| 25 |
|
| 26 |
Configure GLMOCR_API_KEY (environment variable). Optional: glmocr + gradio +
|
| 27 |
pymupdf + pillow installed.
|
| 28 |
+
|
| 29 |
+
When GLMOCR_PREFER_PDF_TEXT_BODY is 1 (default), searchable PDFs skip vision OCR
|
| 30 |
+
for the body and return each page's embedded text so output matches the PDF text
|
| 31 |
+
layer. Set GLMOCR_PREFER_PDF_TEXT_BODY=0 to force the image OCR path. Optional
|
| 32 |
+
GLMOCR_MIN_PDF_BODY_CHARS (default 120) is the minimum characters per page required
|
| 33 |
+
to use the text-layer path for the whole document.
|
| 34 |
"""
|
| 35 |
|
| 36 |
# Patch asyncio first (before Gradio imports it) to reduce Python 3.13 loop noise
|
|
|
|
| 115 |
# JPEG quality for small header/footer crops sent to the API.
|
| 116 |
ZONE_JPEG_QUALITY = 95
|
| 117 |
|
| 118 |
+
MIN_PDF_TEXT_CHARS_NATIVE_LAYER = 1500
|
| 119 |
+
|
| 120 |
_parser = None
|
| 121 |
|
| 122 |
|
|
|
|
| 194 |
return ""
|
| 195 |
|
| 196 |
|
| 197 |
+
def _prefer_pdf_text_body() -> bool:
|
| 198 |
+
v = os.environ.get("GLMOCR_PREFER_PDF_TEXT_BODY", "1").strip().lower()
|
| 199 |
+
return v in ("1", "true", "yes", "")
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def extract_pdf_body_text_all_pages_if_suitable(pdf_path: str) -> Optional[List[str]]:
|
| 203 |
+
try:
|
| 204 |
+
import pymupdf as fitz
|
| 205 |
+
except ImportError:
|
| 206 |
+
return None
|
| 207 |
+
try:
|
| 208 |
+
min_c = int(os.environ.get("GLMOCR_MIN_PDF_BODY_CHARS", "120"))
|
| 209 |
+
except ValueError:
|
| 210 |
+
min_c = 120
|
| 211 |
+
try:
|
| 212 |
+
doc = fitz.open(pdf_path)
|
| 213 |
+
except Exception:
|
| 214 |
+
return None
|
| 215 |
+
try:
|
| 216 |
+
if len(doc) < 1:
|
| 217 |
+
return None
|
| 218 |
+
out: List[str] = []
|
| 219 |
+
for i in range(len(doc)):
|
| 220 |
+
t = (doc[i].get_text("text") or "").strip()
|
| 221 |
+
if len(t) < min_c:
|
| 222 |
+
return None
|
| 223 |
+
out.append(t)
|
| 224 |
+
return out
|
| 225 |
+
finally:
|
| 226 |
+
try:
|
| 227 |
+
doc.close()
|
| 228 |
+
except Exception:
|
| 229 |
+
pass
|
| 230 |
+
|
| 231 |
+
|
| 232 |
def extract_pdf_text_in_band(pdf_path, page_num, y_start_frac, y_end_frac):
|
| 233 |
try:
|
| 234 |
import pymupdf as fitz
|
|
|
|
| 656 |
try:
|
| 657 |
path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)
|
| 658 |
is_pdf = path.lower().endswith(".pdf")
|
| 659 |
+
|
| 660 |
+
if is_pdf and _prefer_pdf_text_body():
|
| 661 |
+
pdf_pages = extract_pdf_body_text_all_pages_if_suitable(path)
|
| 662 |
+
if pdf_pages is not None:
|
| 663 |
+
return "\n\n---page-separator---\n\n".join(pdf_pages)
|
| 664 |
+
|
| 665 |
parser = get_parser()
|
| 666 |
|
| 667 |
page_heights: List[int] = []
|
|
|
|
| 759 |
with gr.Blocks(title="GLM-OCR (simple)") as demo:
|
| 760 |
gr.Markdown(
|
| 761 |
"# GLM-OCR (simple)\n"
|
| 762 |
+
"Searchable PDFs use embedded page text by default (matches the PDF text layer). "
|
| 763 |
+
"Otherwise the vision model reads rasterized pages. Images always use vision OCR."
|
| 764 |
)
|
| 765 |
file_in = gr.File(
|
| 766 |
label="Upload PDF or image",
|