any2human / document.py
idnameraj's picture
Upload 8 files
492ec2c verified
Raw
History Blame Contribute Delete
2.86 kB
"""Extract plain text from uploaded documents."""
from __future__ import annotations
import io
from pathlib import Path
SUPPORTED_EXTENSIONS = {".txt", ".md", ".docx", ".pdf"}
def extract_text_from_path(path: str | Path) -> str:
path = Path(path)
suffix = path.suffix.lower()
if suffix not in SUPPORTED_EXTENSIONS:
raise ValueError(
f"Unsupported file type '{suffix}'. Use .txt, .md, .docx, or .pdf."
)
data = path.read_bytes()
return extract_text_from_bytes(data, suffix, path.name)
def extract_text_from_bytes(data: bytes, suffix: str, filename: str = "") -> str:
suffix = suffix.lower()
if not suffix.startswith("."):
suffix = f".{suffix}"
if suffix in {".txt", ".md"}:
return _decode_text(data)
if suffix == ".docx":
return _extract_docx(data)
if suffix == ".pdf":
return _extract_pdf(data)
raise ValueError(f"Unsupported file type for '{filename or suffix}'.")
def _decode_text(data: bytes) -> str:
for encoding in ("utf-8", "utf-8-sig", "cp1252", "latin-1"):
try:
text = data.decode(encoding)
break
except UnicodeDecodeError:
continue
else:
text = data.decode("utf-8", errors="replace")
text = text.strip()
if not text:
raise ValueError("The file is empty.")
return text
def _extract_docx(data: bytes) -> str:
try:
from docx import Document
except ImportError as exc:
raise RuntimeError("python-docx is required for .docx files.") from exc
doc = Document(io.BytesIO(data))
parts: list[str] = []
for para in doc.paragraphs:
line = para.text.strip()
if line:
parts.append(line)
for table in doc.tables:
for row in table.rows:
cells = [c.text.strip() for c in row.cells if c.text.strip()]
if cells:
parts.append(" | ".join(cells))
text = "\n\n".join(parts).strip()
if not text:
raise ValueError("No readable text found in the Word document.")
return text
def _extract_pdf(data: bytes) -> str:
try:
from pypdf import PdfReader
except ImportError as exc:
raise RuntimeError("pypdf is required for .pdf files.") from exc
reader = PdfReader(io.BytesIO(data))
parts: list[str] = []
for page in reader.pages:
page_text = (page.extract_text() or "").strip()
if page_text:
parts.append(page_text)
text = "\n\n".join(parts).strip()
if not text:
raise ValueError(
"No readable text found in the PDF. Scanned/image PDFs are not supported."
)
return text
def word_count(text: str) -> int:
return len(text.split())