File size: 4,995 Bytes
b411c37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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()