multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
62ec035 verified
Raw History Blame Contribute Delete
3.2 kB
from __future__ import annotations
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable
from urllib.parse import urlparse
from urllib.request import urlopen
from PIL import Image
IMAGE_SUFFIXES = {".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp", ".tif", ".tiff"}
@dataclass
class ParserInput:
kind: str
source: str
path: Path | None = None
image: Image.Image | None = None
@dataclass
class RenderedPage:
page_index: int
image: Image.Image
def is_url(value: str) -> bool:
return urlparse(value).scheme in {"http", "https"}
def _suffix_from_url(url: str, content_type: str = "") -> str:
suffix = Path(urlparse(url).path).suffix.lower()
if suffix:
return suffix
if "pdf" in content_type:
return ".pdf"
if "png" in content_type:
return ".png"
if "jpeg" in content_type or "jpg" in content_type:
return ".jpg"
return ".bin"
def download_url(url: str) -> Path:
with urlopen(url) as response:
content_type = response.headers.get("content-type", "")
suffix = _suffix_from_url(url, content_type)
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
tmp.write(response.read())
return Path(tmp.name)
def _kind_from_path(path: Path) -> str:
suffix = path.suffix.lower()
if suffix == ".pdf":
return "pdf"
if suffix in IMAGE_SUFFIXES:
return "image"
raise ValueError(f"Unsupported input suffix: {path.suffix}")
def normalize_inputs(inputs) -> list[ParserInput]:
if isinstance(inputs, (list, tuple)):
items: Iterable = inputs
else:
items = [inputs]
normalized: list[ParserInput] = []
for item in items:
if isinstance(item, Image.Image):
normalized.append(ParserInput(kind="image", source="PIL.Image", image=item.convert("RGB")))
continue
if isinstance(item, (str, Path)):
raw = str(item)
path = download_url(raw) if is_url(raw) else Path(raw).expanduser()
normalized.append(ParserInput(kind=_kind_from_path(path), source=raw, path=path))
continue
raise TypeError(f"Unsupported input type: {type(item)!r}")
return normalized
def load_image(item: ParserInput) -> Image.Image:
if item.image is not None:
return item.image.convert("RGB")
if item.path is None:
raise ValueError("Image input has no image or path.")
return Image.open(item.path).convert("RGB")
def render_pdf(path: str | Path, *, dpi: int = 300, pages: Iterable[int] | None = None) -> list[RenderedPage]:
try:
import pypdfium2 as pdfium
except ImportError as exc:
raise ImportError("PDF parsing requires pypdfium2. Install molparser[pdf] or molparser[inference].") from exc
document = pdfium.PdfDocument(str(path))
page_indexes = list(pages) if pages is not None else list(range(len(document)))
scale = float(dpi) / 72.0
return [
RenderedPage(page_index=page_index, image=document[page_index].render(scale=scale).to_pil().convert("RGB"))
for page_index in page_indexes
]