document-ocr / scripts /make_assets.py
alirezaaminzadeh's picture
Publish Persian and English document OCR
b411c37 verified
Raw
History Blame Contribute Delete
5 kB
from __future__ import annotations
import sys
import urllib.request
from pathlib import Path
from arabic_reshaper import reshape
from bidi.algorithm import get_display
from PIL import Image, ImageDraw, ImageFont
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from ocr_studio.config import ARABIC_FONT_PATH, EXAMPLES_DIR, FONTS_DIR, LATIN_FONT_PATH
FONT_SOURCES = {
LATIN_FONT_PATH: (
"https://cdn.jsdelivr.net/gh/googlefonts/noto-fonts@main/hinted/ttf/NotoSans/NotoSans-Regular.ttf",
"https://github.com/notofonts/latin-greek-cyrillic/raw/main/fonts/NotoSans/hinted/ttf/NotoSans-Regular.ttf",
),
ARABIC_FONT_PATH: (
"https://cdn.jsdelivr.net/gh/googlefonts/noto-fonts@main/hinted/ttf/NotoNaskhArabic/NotoNaskhArabic-Regular.ttf",
"https://github.com/notofonts/notonaskharabic/raw/main/fonts/NotoNaskhArabic/hinted/ttf/NotoNaskhArabic-Regular.ttf",
),
}
def _download(path: Path, urls: tuple[str, ...]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
last_error: Exception | None = None
for url in urls:
try:
with urllib.request.urlopen(url, timeout=60) as response:
data = response.read()
if len(data) < 10_000:
continue
path.write_bytes(data)
return
except Exception as exc: # noqa: BLE001
last_error = exc
if last_error:
raise last_error
def _font(path: Path, size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
if path.exists():
return ImageFont.truetype(str(path), size=size)
return ImageFont.load_default()
def _shape(text: str) -> str:
if any("\u0600" <= char <= "\u06ff" for char in text):
return get_display(reshape(text))
return text
def _render(path: Path, lines: list[list[tuple[str, Path, int]]], align: str = "left") -> None:
width, height = 1280, 760
image = Image.new("RGB", (width, height), "#f7f3ea")
draw = ImageDraw.Draw(image)
draw.rectangle((36, 36, width - 36, height - 36), outline="#1f4f4a", width=3)
y = 88
for segments in lines:
x = 80 if align == "left" else width - 80
if align == "right":
for text, font_path, size in reversed(segments):
font = _font(font_path, size)
shaped = _shape(text)
bbox = draw.textbbox((0, 0), shaped, font=font)
text_w = bbox[2] - bbox[0]
x -= text_w
draw.text((x, y), shaped, fill="#1c1915", font=font)
x -= 12
else:
for text, font_path, size in segments:
font = _font(font_path, size)
shaped = _shape(text)
draw.text((x, y), shaped, fill="#1c1915", font=font)
bbox = draw.textbbox((0, 0), shaped, font=font)
x += bbox[2] - bbox[0] + 10
y += max(size for _, _, size in segments) + 28
path.parent.mkdir(parents=True, exist_ok=True)
image.save(path, format="PNG")
def main() -> None:
FONTS_DIR.mkdir(parents=True, exist_ok=True)
EXAMPLES_DIR.mkdir(parents=True, exist_ok=True)
for destination, urls in FONT_SOURCES.items():
if destination.exists():
continue
try:
_download(destination, urls)
except Exception as exc: # noqa: BLE001
print(f"Font download skipped for {destination.name}: {exc}")
_render(
EXAMPLES_DIR / "english-print.png",
[
[("INVOICE 1048", LATIN_FONT_PATH, 42)],
[("Customer: Jane Walton", LATIN_FONT_PATH, 32)],
[("Amount due: 128.40 USD", LATIN_FONT_PATH, 32)],
[("Due date: 24 August 2026", LATIN_FONT_PATH, 32)],
],
)
_render(
EXAMPLES_DIR / "persian-print.png",
[
[("فاکتور ۱۰۴۸", ARABIC_FONT_PATH, 44)],
[("خریدار: سارا محمدی", ARABIC_FONT_PATH, 34)],
[("مبلغ قابل پرداخت: ۱۲۸۴۰۰۰ تومان", ARABIC_FONT_PATH, 34)],
[("تاریخ سررسید: ۳ شهریور ۱۴۰۵", ARABIC_FONT_PATH, 34)],
],
align="right",
)
_render(
EXAMPLES_DIR / "mixed-print.png",
[
[("BOARDING PASS / ", LATIN_FONT_PATH, 36), ("کارت پرواز", ARABIC_FONT_PATH, 36)],
[("Passenger / ", LATIN_FONT_PATH, 30), ("مسافر: ", ARABIC_FONT_PATH, 30), ("Ali Rezaei", LATIN_FONT_PATH, 30)],
[("Flight / ", LATIN_FONT_PATH, 30), ("پرواز: ", ARABIC_FONT_PATH, 30), ("W5 1084", LATIN_FONT_PATH, 30)],
[("Seat / ", LATIN_FONT_PATH, 30), ("صندلی: ", ARABIC_FONT_PATH, 30), ("14A", LATIN_FONT_PATH, 30)],
],
)
if __name__ == "__main__":
main()