# -*- coding: utf-8 -*-
"""Generates the 100cIA brand kit. Text is converted to curves with Inkscape,
so the SVGs do not depend on the font being installed."""
import os, re, subprocess, tempfile
import xml.etree.ElementTree as ET
HERE = os.path.dirname(os.path.abspath(__file__))
BG = "#0B0F1A"
POINTS = "#243050"
WHITE_C = "#FFFFFF"
AMBER = "#FFD166"
TRACK = "#1B2233"
C_GRAY = "#8D99AE"
FONT_NAME = "Roboto Black"
TRACKING = -0.035 # em
def curve_list(parts, size=400, tracking=TRACKING):
"""parts = [(text, color), ...] -> (inner_svg, x, y, w, h) already as curves."""
tspans = "".join(
f'{t}'
for i, (t, c) in enumerate(parts))
src = (f'')
with tempfile.TemporaryDirectory() as d:
a, b = os.path.join(d, "a.svg"), os.path.join(d, "b.svg")
open(a, "w", encoding="utf-8").write(src)
subprocess.run(["inkscape", "--export-text-to-path", "--export-plain-svg",
f"--export-filename={b}", a],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
raw = open(b, encoding="utf-8").read()
q = {k: float(subprocess.run(["inkscape", "--query-id=w", f"--query-{k}", b],
capture_output=True, text=True).stdout.strip())
for k in ("x", "y", "width", "height")}
NS = "http://www.w3.org/2000/svg"
ET.register_namespace("", NS)
root = ET.fromstring(raw)
node = next(e for e in root.iter() if e.get("id") == "w")
for e in node.iter():
e.attrib.pop("id", None)
e.attrib.pop("aria-label", None)
body_obj = ET.tostring(node, encoding="unicode")
body_obj = body_obj.replace(f' xmlns="{NS}"', "")
if not body_obj.lstrip().startswith("{body_obj}"
return body_obj, q["x"], q["y"], q["width"], q["height"]
def place(g, bx, by, bw, bh, cx, cy, width_px):
s = width_px / bw
tx = cx - s * (bx + bw / 2)
ty = cy - s * (by + bh / 2)
return f'{g}'
def point_list(id_, sep=118, r=4.2, color=POINTS, op=0.55):
return (f''
f'')
def arc_m(cx, cy, r, since, until, color, w):
"""Arc in degrees, 0 = up, clockwise."""
import math
def p(a):
t = math.radians(a - 90)
return cx + r * math.cos(t), cy + r * math.sin(t)
x0, y0 = p(since)
x1, y1 = p(until)
largo = 1 if (until - since) % 360 > 180 else 0
return (f'')
# ---------------------------------------------------------------- avatar
def avatar(file_path, chip=False, pct=0.62):
D = 1024
R = D / 2
ring_r, ring_w = R - 26, 20
if chip:
g1, x1, y1, w1, h1 = curve_list([("100c", WHITE_C)])
g2, x2, y2, w2, h2 = curve_list([("IA", "#0B0F1A")])
else:
g1, x1, y1, w1, h1 = curve_list([("100c", WHITE_C), ("IA", AMBER)])
body_obj = [
f'',
f'',
f'',
arc_m(R, R, ring_r, 0, 360 * pct, AMBER, ring_w),
]
if chip:
total_width = 760
# the "IA" chip takes ~38% of the width, like the chips in the videos
w_text = total_width * 0.60
body_obj.append(place(g1, x1, y1, w1, h1, R - total_width / 2 + w_text / 2, R, w_text))
scale_f = w_text / w1
cw, ch = w2 * scale_f * 1.0 + 70, h1 * scale_f + 62
cxc = R - total_width / 2 + w_text + 26 + cw / 2
body_obj.append(f'')
body_obj.append(place(g2, x2, y2, w2, h2, cxc, R, w2 * scale_f))
else:
body_obj.append(place(g1, x1, y1, w1, h1, R, R, 740))
svg = (f'')
open(os.path.join(HERE, file_path), "w", encoding="utf-8").write(svg)
return file_path
# ---------------------------------------------------------------- wordmark
# The chip around "IA" is the brand: it goes on every variant except the
# "flat" one, kept as an alternative.
MODES = {
# ink chip background chip letter
"oscuro": (WHITE_C, AMBER, "#0B0F1A"),
"claro": ("#0B0F1A", "#D98C00", "#FFFFFF"),
"mono": (WHITE_C, WHITE_C, None), # knocked-out letters
}
def _lockup(mode, alto=520):
"""Returns (svg_pieces, width, height) of the '100c' lockup + 'IA' chip."""
ink, chip_bg, letter = MODES[mode]
g1, x1, y1, w1, h1 = curve_list([("100c", ink)])
g2, x2, y2, w2, h2 = curve_list([("IA", letter or "#000000")])
scale_f = alto / h1
wt = w1 * scale_f
cw = w2 * scale_f + 0.68 * alto
ch = alto + 0.60 * alto
gap_m = 0.13 * alto
W = wt + gap_m + cw
cy = ch / 2
rx = ch * 0.28
cx_chip = wt + gap_m + cw / 2
chip_x = wt + gap_m
pieces = [place(g1, x1, y1, w1, h1, wt / 2, cy, wt)]
if letter is None: # a single ink: the IA is knocked out
mascara = (f''
f''
+ place(g2, x2, y2, w2, h2, cx_chip, cy, w2 * scale_f)
+ '')
pieces.append(f'{mascara}')
pieces.append(f'')
else:
pieces.append(f'')
pieces.append(place(g2, x2, y2, w2, h2, cx_chip, cy, w2 * scale_f))
return pieces, W, ch
def wordmark(file_path, mode="oscuro", margin=0.10):
pieces, W0, H0 = _lockup(mode)
m = margin * H0
W, H = int(round(W0 + 2 * m)), int(round(H0 + 2 * m))
svg = (f'')
open(os.path.join(HERE, file_path), "w", encoding="utf-8").write(svg)
return file_path
def wordmark_plano(file_path):
"""Chip-less alternative: '100cIA' in one run, the IA in amber."""
g, x, y, w, h = curve_list([("100c", WHITE_C), ("IA", AMBER)])
W = 2400
width_px = W * 0.88
H = int(round(h / w * width_px + 2 * 0.06 * W))
svg = (f'')
open(os.path.join(HERE, file_path), "w", encoding="utf-8").write(svg)
return file_path
# ---------------------------------------------------------------- banner
def banner(file_path, tagline="ciencia en 40 segundos"):
W, H = 1920, 1080
g, x, y, w, h = curve_list([("100c", WHITE_C)])
t, tx, ty, tw, th = curve_list([(tagline, "#C9D3E5")], size=200, tracking=0.012)
body_obj = [
f'',
f'',
f'',
f'',
None, # the lockup is built below (with the chip, like the avatar)
place(t, tx, ty, tw, th, W / 2, H / 2 + 205, 830),
]
# lockup with chip, centred, 1150 px total width
g2, x2, y2, w2, h2 = curve_list([("IA", "#0B0F1A")])
WIDTH, cy = 1150, H / 2 - 95
alto = 210
scale_f = alto / h
wt = w * scale_f
cw, ch = w2 * scale_f + 0.68 * alto, alto + 0.60 * alto
gap_m = 0.13 * alto
total = wt + gap_m + cw
k = WIDTH / total
wt, cw, ch, gap_m = wt * k, cw * k, ch * k, gap_m * k
left_part = W / 2 - WIDTH / 2
body_obj[body_obj.index(None)] = (
place(g, x, y, w, h, left_part + wt / 2, cy, wt)
+ f''
+ place(g2, x2, y2, w2, h2, left_part + wt + gap_m + cw / 2, cy, w2 * scale_f * k))
svg = (f'')
open(os.path.join(HERE, file_path), "w", encoding="utf-8").write(svg)
return file_path
if __name__ == "__main__":
# the chip variant is the brand: it keeps the main file names
for f in (avatar("avatar.svg", chip=True), avatar("avatar-plano.svg"),
wordmark("wordmark.svg", "oscuro"),
wordmark("wordmark-claro.svg", "claro"),
wordmark("wordmark-mono.svg", "mono"),
wordmark_plano("wordmark-plano.svg"),
banner("banner.svg")):
print(" ->", f)