Spaces:
Running on Zero
Running on Zero
| 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() | |