PL
#!/usr/bin/env python3"""Pictures of the web site from the app's own screenshots (demo pool only).    python3 images.py <raw shots dir> <site/images dir> <assets dir>Raw shots (2560x1600, made by shots.sh): general, performance, snapshots + the game tests' shots (console, search, logs, ...).Writes site/images/*.jpg (1280x800), og.png (1200x630), favicon.png,and assets/thumbnail.png (1280x720), assets/icon.png when it is missing."""import osimport sysfrom PIL import Image, ImageDraw, ImageFontraw, out, assets = sys.argv[1:4]os.makedirs(out, exist_ok=True)fonts = os.path.join(assets, 'fonts')DARK, LIGHT, ACCENT = (27, 51, 90), (47, 93, 148), (47, 111, 214)SHOTS = ['general', 'console', 'performance', 'search', 'logs', 'snapshots', 'new-vm', 'menu',         'dashboard', 'updates', 'new-pool', 'cross-pool', 'new-sr-nfs', 'new-network', 'folders', 'alerts-dialog']def load(name):    return Image.open(os.path.join(raw, name + '.png')).convert('RGB')def gradient(w, h):    img = Image.new('RGB', (w, h))    d = ImageDraw.Draw(img)    for x in range(w):        t = x / max(1, w - 1)        d.line([(x, 0), (x, h)], fill=tuple(int(DARK[i] + (LIGHT[i] - DARK[i]) * t) for i in range(3)))    return imgdef icon(size):    """A blue rounded square with three white server racks (drawn 4x, then smoothed)."""    s = size * 4    img = Image.new('RGBA', (s, s), (0, 0, 0, 0))    mask = Image.new('L', (s, s), 0)    ImageDraw.Draw(mask).rounded_rectangle([0, 0, s - 1, s - 1], radius=s // 5, fill=255)    img.paste(gradient(s, s).convert('RGBA'), (0, 0), mask)    d = ImageDraw.Draw(img)    m, gap = s // 5, s // 22    rack_h = (s - 2 * m - 2 * gap) // 3    for i in range(3):        y = m + i * (rack_h + gap)        d.rounded_rectangle([m, y, s - m, y + rack_h], radius=s // 40, fill=(255, 255, 255, 255))        r = rack_h // 5        cx, cy = s - m - rack_h // 2, y + rack_h // 2        d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=(46, 170, 80, 255))        d.rectangle([m + s // 16, cy - s // 80, m + s // 2.6, cy + s // 80], fill=(*ACCENT, 255))    return img.resize((size, size), Image.LANCZOS)# screenshots of the page: 1280x800, JPEGfor name in SHOTS:    if os.path.exists(os.path.join(raw, name + '.png')):        load(name).resize((1280, 800), Image.LANCZOS).save(os.path.join(out, name + '.jpg'), quality=86, optimize=True)# favicon + the app's icon (only when it has none)icon(64).save(os.path.join(out, 'favicon.png'))icon(192).save(os.path.join(out, 'icon-192.png'))if not os.path.exists(os.path.join(assets, 'icon.png')):    icon(256).save(os.path.join(assets, 'icon.png'))# thumbnail of the directory: 16:9, the performance graphs of the demo poolperf = load('performance')perf.crop((0, 0, 2560, 1440)).resize((1280, 720), Image.LANCZOS).save(os.path.join(assets, 'thumbnail.png'), optimize=True)# og.png: 1200x630, the banner + a framed screenshotog = gradient(1200, 630)d = ImageDraw.Draw(og)bold = ImageFont.truetype(os.path.join(fonts, 'ui-bold.ttf'), 58)reg = ImageFont.truetype(os.path.join(fonts, 'ui.ttf'), 28)og.paste(icon(84).convert('RGB'), (60, 60), icon(84))d.text((164, 62), 'XenCenter', font=bold, fill='white')d.text((166, 128), 'for XCP-ng / XenServer, in PHP', font=reg, fill=(214, 226, 243))d.text((60, 200), 'Pools, servers, VMs, console, live graphs.', font=reg, fill='white')d.text((60, 240), 'Desktop and browser. Demo pool built in.', font=reg, fill=(214, 226, 243))shot = load('general').resize((900, 563), Image.LANCZOS)frame = Image.new('RGB', (912, 575), (255, 255, 255))frame.paste(shot, (6, 6))og.paste(frame, (250, 300))og.save(os.path.join(out, 'og.png'), optimize=True)print('images written to', out)