File size: 5,209 Bytes
c519923
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Render a courtroom-sketch witness placard as the portrait placeholder.

    python3 scripts/make_portrait_placeholder.py   ->   assets/marcus_reid.png

app.py shows assets/marcus_reid.png if it exists, else an empty box. A real
AI portrait (HF ZeroGPU) can overwrite this file later; until then this gives the
demo an intentional, on-theme visual instead of a blank frame. Pure PIL — no GPU,
no network — and it matches the app's parchment palette.
"""
from __future__ import annotations

import os

from PIL import Image, ImageDraw, ImageFont

W, H = 768, 960
PARCH = (239, 231, 211)      # #efe7d3 page
CARD = (247, 241, 225)       # #f7f1e1
BORDER = (201, 183, 141)     # #c9b78d
INK = (58, 44, 24)           # #3a2c18
SUB = (107, 88, 54)          # #6b5836
MAROON = (122, 47, 47)       # #7a2f2f
SKETCH = (90, 74, 53)        # sepia for the silhouette
SKETCH_HI = (120, 102, 78)

FONT_DIRS = [
    "/System/Library/Fonts/Supplemental/",
    "/System/Library/Fonts/",
    "/Library/Fonts/",
]
SERIF = ["Georgia.ttf", "Palatino.ttc", "Times New Roman.ttf", "Baskerville.ttc"]
SERIF_B = ["Georgia Bold.ttf", "Times New Roman Bold.ttf", "Georgia.ttf"]


def _font(names, size):
    for d in FONT_DIRS:
        for n in names:
            p = os.path.join(d, n)
            if os.path.exists(p):
                try:
                    return ImageFont.truetype(p, size)
                except Exception:
                    pass
    return ImageFont.load_default()


def _spaced(draw, xy, text, font, fill, spacing=6, anchor_center=None):
    """Draw letter-spaced text; if anchor_center given, center on that x."""
    widths = [draw.textlength(c, font=font) for c in text]
    total = sum(widths) + spacing * (len(text) - 1)
    x = (anchor_center - total / 2) if anchor_center is not None else xy[0]
    y = xy[1]
    for c, w in zip(text, widths):
        draw.text((x, y), c, font=font, fill=fill)
        x += w + spacing
    return total


def _scales(draw, cx, top):
    """A small balance-scale glyph, drawn from primitives."""
    col = INK
    draw.line([(cx, top), (cx, top + 54)], fill=col, width=4)            # post
    draw.ellipse([cx - 5, top - 5, cx + 5, top + 5], fill=col)          # finial
    beam_y, span = top + 14, 70
    draw.line([(cx - span, beam_y), (cx + span, beam_y)], fill=col, width=4)
    for sx in (cx - span, cx + span):
        draw.line([(sx, beam_y), (sx - 18, beam_y + 34)], fill=col, width=2)
        draw.line([(sx, beam_y), (sx + 18, beam_y + 34)], fill=col, width=2)
        draw.arc([sx - 20, beam_y + 24, sx + 20, beam_y + 50], 0, 180, fill=col, width=3)
    draw.line([(cx - 26, top + 54), (cx + 26, top + 54)], fill=col, width=4)  # base


def _silhouette(draw, cx, cy):
    """A courtroom-sketch bust: shoulders, neck, head, with a suit + tie hint."""
    # shoulders / suit
    draw.ellipse([cx - 165, cy + 70, cx + 165, cy + 360], fill=SKETCH)
    draw.rectangle([cx - 165, cy + 215, cx + 165, cy + 360], fill=SKETCH)
    # collar V + tie
    draw.polygon([(cx - 40, cy + 95), (cx, cy + 185), (cx + 40, cy + 95)], fill=CARD)
    draw.polygon([(cx - 12, cy + 120), (cx + 12, cy + 120), (cx + 18, cy + 210),
                  (cx, cy + 235), (cx - 18, cy + 210)], fill=(64, 40, 40))  # tie
    draw.polygon([(cx - 40, cy + 95), (cx - 14, cy + 112), (cx, cy + 150),
                  (cx - 16, cy + 150)], fill=SKETCH_HI)                      # lapel L
    draw.polygon([(cx + 40, cy + 95), (cx + 14, cy + 112), (cx, cy + 150),
                  (cx + 16, cy + 150)], fill=SKETCH_HI)                      # lapel R
    # neck + head
    draw.rectangle([cx - 26, cy + 40, cx + 26, cy + 110], fill=SKETCH)
    draw.ellipse([cx - 70, cy - 110, cx + 70, cy + 60], fill=SKETCH)
    # hair sweep
    draw.chord([cx - 72, cy - 120, cx + 72, cy + 10], 180, 360, fill=SKETCH_HI)


def main():
    root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    out_dir = os.path.join(root, "assets")
    os.makedirs(out_dir, exist_ok=True)
    out = os.path.join(out_dir, "marcus_reid.png")

    img = Image.new("RGB", (W, H), PARCH)
    d = ImageDraw.Draw(img)

    # card with double frame
    m = 28
    d.rectangle([m, m, W - m, H - m], fill=CARD, outline=BORDER, width=3)
    d.rectangle([m + 12, m + 12, W - m - 12, H - m - 12], outline=BORDER, width=1)

    f_top = _font(SERIF_B, 30)
    f_name = _font(SERIF_B, 58)
    f_sub = _font(SERIF, 27)
    f_foot = _font(SERIF, 20)

    _scales(d, W // 2, 62)
    _spaced(d, (0, 150), "SWORN WITNESS", f_top, MAROON, spacing=10, anchor_center=W // 2)

    _silhouette(d, W // 2, 330)

    # nameplate bar
    bar_y = 720
    d.rectangle([m + 40, bar_y, W - m - 40, bar_y + 86], fill=INK)
    _spaced(d, (0, bar_y + 16), "MARCUS REID", f_name, CARD, spacing=4, anchor_center=W // 2)

    sub = "Chief Financial Officer  ·  Halcyon Dynamics"
    tw = d.textlength(sub, font=f_sub)
    d.text(((W - tw) / 2, bar_y + 104), sub, font=f_sub, fill=SUB)

    foot = "WitnessBox  —  State's Exhibit"
    fw = d.textlength(foot, font=f_foot)
    d.text(((W - fw) / 2, H - m - 52), foot, font=f_foot, fill=BORDER)

    img.save(out)
    print(f"wrote {out}  ({W}x{H})")


if __name__ == "__main__":
    main()