File size: 5,358 Bytes
632bab6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""
Genera docs/linkedin_cover.png — immagine di copertina per l'articolo LinkedIn.

Dimensioni: 1920 x 1080 px (formato raccomandato LinkedIn 2024/2025)
"""

from pathlib import Path
from PIL import Image, ImageDraw, ImageFont

ROOT = Path(__file__).parent.parent
OUT  = ROOT / "docs" / "linkedin_cover.png"

W, H = 1920, 1080

# Palette
BG_TOP    = (10,  25,  47)   # navy scuro
BG_BOT    = (17,  34,  64)   # navy medio
ACCENT    = (100, 180, 255)  # azzurro chiaro
WHITE     = (255, 255, 255)
GRAY      = (160, 180, 210)
DOT_COLOR = (30,  60, 100)


def load_font(name: str, size: int) -> ImageFont.FreeTypeFont:
    fonts_dir = Path("C:/Windows/Fonts")
    candidates = [
        name,
        str(fonts_dir / name),
        str(fonts_dir / "segoeui.ttf"),
        str(fonts_dir / "arial.ttf"),
        str(fonts_dir / "calibri.ttf"),
    ]
    for path in candidates:
        try:
            return ImageFont.truetype(path, size)
        except (IOError, OSError):
            pass
    return ImageFont.load_default()


def draw_gradient(draw: ImageDraw.ImageDraw) -> None:
    for y in range(H):
        t = y / H
        r = int(BG_TOP[0] * (1 - t) + BG_BOT[0] * t)
        g = int(BG_TOP[1] * (1 - t) + BG_BOT[1] * t)
        b = int(BG_TOP[2] * (1 - t) + BG_BOT[2] * t)
        draw.line([(0, y), (W, y)], fill=(r, g, b))


def draw_dot_pattern(draw: ImageDraw.ImageDraw) -> None:
    """Pattern di punti che richiama le impronte della scrittura."""
    spacing = 48
    for row in range(0, H // spacing + 1):
        for col in range(0, W // spacing + 1):
            cx = col * spacing + (spacing // 2 if row % 2 else 0)
            cy = row * spacing
            r = 2
            draw.ellipse([cx - r, cy - r, cx + r, cy + r], fill=DOT_COLOR)


def draw_accent_lines(draw: ImageDraw.ImageDraw) -> None:
    """Linee orizzontali sottili a sinistra — richiamano righe di scrittura."""
    x0, x1 = 80, 520
    for y in [360, 380, 400, 420, 440, 460]:
        alpha = 180 if y == 360 else 80
        draw.line([(x0, y), (x1, y)], fill=(*ACCENT[:3], alpha), width=2)


def centered_text(draw: ImageDraw.ImageDraw, text: str, font, y: int,
                  fill=WHITE, x_offset: int = 0) -> None:
    bbox = draw.textbbox((0, 0), text, font=font)
    tw = bbox[2] - bbox[0]
    x = (W - tw) // 2 + x_offset
    draw.text((x, y), text, font=font, fill=fill)


def main() -> None:
    img  = Image.new("RGB", (W, H), BG_TOP)
    draw = ImageDraw.Draw(img)

    draw_gradient(draw)
    draw_dot_pattern(draw)

    # Linea accent verticale sinistra
    draw.rectangle([60, 200, 66, 880], fill=ACCENT)

    # Linee orizzontali "di scrittura"
    for i, y in enumerate(range(340, 560, 28)):
        length = 420 - i * 15
        opacity = max(40, 130 - i * 18)
        draw.line([(100, y), (100 + length, y)],
                  fill=(ACCENT[0], ACCENT[1], ACCENT[2]), width=1)

    # Simbolo grafico — lente stilizzata
    cx, cy, cr = 1650, 400, 120
    draw.ellipse([cx - cr, cy - cr, cx + cr, cy + cr],
                 outline=ACCENT, width=4)
    draw.ellipse([cx - cr + 16, cy - cr + 16, cx + cr - 16, cy + cr - 16],
                 outline=(*ACCENT, 80), width=2)
    # Manico lente
    lx = int(cx + cr * 0.7)
    ly = int(cy + cr * 0.7)
    draw.line([(lx, ly), (lx + 80, ly + 80)], fill=ACCENT, width=6)
    draw.line([(lx + 80, ly + 80), (lx + 95, ly + 95)], fill=ACCENT, width=10)

    # Righe stilizzate dentro la lente (testo simulato)
    for i, offset in enumerate([-40, -15, 10, 35]):
        length = [80, 60, 75, 45][i]
        draw.line([(cx - length // 2, cy + offset),
                   (cx + length // 2, cy + offset)],
                  fill=(*WHITE, 60), width=2)

    # Testo principale
    font_main   = load_font("segoeuib.ttf",  160)
    font_sub    = load_font("segoeui.ttf",    52)
    font_tag    = load_font("segoeui.ttf",    38)
    font_badge  = load_font("segoeui.ttf",    34)
    font_url    = load_font("segoeui.ttf",    28)

    # "GraphoLab"
    centered_text(draw, "GraphoLab", font_main, 300)

    # Linea accent sotto il titolo
    bbox = draw.textbbox((0, 0), "GraphoLab", font=font_main)
    tw = bbox[2] - bbox[0]
    lx0 = (W - tw) // 2
    draw.rectangle([lx0, 490, lx0 + tw, 496], fill=ACCENT)

    # Sottotitolo
    centered_text(draw, "Artificial Intelligence in Forensic Graphology",
                  font_sub, 530, fill=ACCENT)

    # Tag line
    centered_text(draw,
                  "Handwritten Text Recognition  ·  Signature Verification  ·  Writer Identification",
                  font_tag, 620, fill=GRAY)

    # Badge
    badge_text = "8 Labs  ·  Gradio App  ·  Open Source  ·  Apache 2.0"
    bbox = draw.textbbox((0, 0), badge_text, font=font_badge)
    bw = bbox[2] - bbox[0] + 60
    bh = bbox[3] - bbox[1] + 24
    bx = (W - bw) // 2
    by = 730
    draw.rounded_rectangle([bx, by, bx + bw, by + bh],
                            radius=8, fill=(20, 50, 90), outline=ACCENT, width=2)
    centered_text(draw, badge_text, font_badge, by + 12, fill=WHITE)

    # URL GitHub
    centered_text(draw, "github.com/fabioantonini/GraphoLab",
                  font_url, 840, fill=GRAY)

    # Linea di separazione in basso
    draw.rectangle([0, H - 6, W, H], fill=ACCENT)

    img.save(OUT, "PNG")
    print(f"Creato: {OUT}  ({W}x{H}px)")


if __name__ == "__main__":
    main()