# -*- coding: utf-8 -*-
"""Genera el kit de marca de 100cIA. El texto se convierte a curvas con Inkscape,
asi que los SVG no dependen de que la fuente este instalada."""
import os, re, subprocess, tempfile
import xml.etree.ElementTree as ET
HERE = os.path.dirname(os.path.abspath(__file__))
BG = "#0B0F1A"
PUNTOS = "#243050"
BLANCO = "#FFFFFF"
AMBAR = "#FFD166"
TRACK = "#1B2233"
GRIS = "#8D99AE"
FUENTE = "Roboto Black"
TRACKING = -0.035 # em
def curvas(partes, size=400, tracking=TRACKING):
"""partes = [(texto, color), ...] -> (svg_interno, x, y, w, h) ya en curvas."""
tspans = "".join(
f'{t}'
for i, (t, c) in enumerate(partes))
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)
nodo = next(e for e in root.iter() if e.get("id") == "w")
for e in nodo.iter():
e.attrib.pop("id", None)
e.attrib.pop("aria-label", None)
cuerpo = ET.tostring(nodo, encoding="unicode")
cuerpo = cuerpo.replace(f' xmlns="{NS}"', "")
if not cuerpo.lstrip().startswith("{cuerpo}"
return cuerpo, q["x"], q["y"], q["width"], q["height"]
def colocar(g, bx, by, bw, bh, cx, cy, ancho):
s = ancho / bw
tx = cx - s * (bx + bw / 2)
ty = cy - s * (by + bh / 2)
return f'{g}'
def puntos(id_, sep=118, r=4.2, color=PUNTOS, op=0.55):
return (f''
f'')
def arco(cx, cy, r, desde, hasta, color, w):
"""Arco en grados, 0 = arriba, sentido horario."""
import math
def p(a):
t = math.radians(a - 90)
return cx + r * math.cos(t), cy + r * math.sin(t)
x0, y0 = p(desde)
x1, y1 = p(hasta)
largo = 1 if (hasta - desde) % 360 > 180 else 0
return (f'')
# ---------------------------------------------------------------- avatar
def avatar(archivo, chip=False, pct=0.62):
D = 1024
R = D / 2
anillo_r, anillo_w = R - 26, 20
if chip:
g1, x1, y1, w1, h1 = curvas([("100c", BLANCO)])
g2, x2, y2, w2, h2 = curvas([("IA", "#0B0F1A")])
else:
g1, x1, y1, w1, h1 = curvas([("100c", BLANCO), ("IA", AMBAR)])
cuerpo = [
f'',
f'',
f'',
arco(R, R, anillo_r, 0, 360 * pct, AMBAR, anillo_w),
]
if chip:
ancho_total = 760
# el chip de "IA" ocupa ~38% del ancho, como los chips de los videos
w_txt = ancho_total * 0.60
cuerpo.append(colocar(g1, x1, y1, w1, h1, R - ancho_total / 2 + w_txt / 2, R, w_txt))
esc = w_txt / w1
cw, ch = w2 * esc * 1.0 + 70, h1 * esc + 62
cxc = R - ancho_total / 2 + w_txt + 26 + cw / 2
cuerpo.append(f'')
cuerpo.append(colocar(g2, x2, y2, w2, h2, cxc, R, w2 * esc))
else:
cuerpo.append(colocar(g1, x1, y1, w1, h1, R, R, 740))
svg = (f'')
open(os.path.join(HERE, archivo), "w", encoding="utf-8").write(svg)
return archivo
# ---------------------------------------------------------------- logotipo
# El chip alrededor del "IA" es la marca: va en todas las variantes menos en la
# "plana", que queda como alternativa.
MODOS = {
# tinta fondo del chip letra del chip
"oscuro": (BLANCO, AMBAR, "#0B0F1A"),
"claro": ("#0B0F1A", "#D98C00", "#FFFFFF"),
"mono": (BLANCO, BLANCO, None), # letras caladas
}
def _lockup(modo, alto=520):
"""Devuelve (piezas_svg, ancho, alto) del lockup '100c' + chip 'IA'."""
tinta, fondo_chip, letra = MODOS[modo]
g1, x1, y1, w1, h1 = curvas([("100c", tinta)])
g2, x2, y2, w2, h2 = curvas([("IA", letra or "#000000")])
esc = alto / h1
wt = w1 * esc
cw = w2 * esc + 0.68 * alto
ch = alto + 0.60 * alto
hueco = 0.13 * alto
W = wt + hueco + cw
cy = ch / 2
rx = ch * 0.28
cx_chip = wt + hueco + cw / 2
chip_x = wt + hueco
piezas = [colocar(g1, x1, y1, w1, h1, wt / 2, cy, wt)]
if letra is None: # una sola tinta: el IA se cala
mascara = (f''
f''
+ colocar(g2, x2, y2, w2, h2, cx_chip, cy, w2 * esc)
+ '')
piezas.append(f'{mascara}')
piezas.append(f'')
else:
piezas.append(f'')
piezas.append(colocar(g2, x2, y2, w2, h2, cx_chip, cy, w2 * esc))
return piezas, W, ch
def wordmark(archivo, modo="oscuro", margen=0.10):
piezas, W0, H0 = _lockup(modo)
m = margen * H0
W, H = int(round(W0 + 2 * m)), int(round(H0 + 2 * m))
svg = (f'')
open(os.path.join(HERE, archivo), "w", encoding="utf-8").write(svg)
return archivo
def wordmark_plano(archivo):
"""Alternativa sin chip: '100cIA' de corrido, el IA en ambar."""
g, x, y, w, h = curvas([("100c", BLANCO), ("IA", AMBAR)])
W = 2400
ancho = W * 0.88
H = int(round(h / w * ancho + 2 * 0.06 * W))
svg = (f'')
open(os.path.join(HERE, archivo), "w", encoding="utf-8").write(svg)
return archivo
# ---------------------------------------------------------------- banner
def banner(archivo, tagline="ciencia en 40 segundos"):
W, H = 1920, 1080
g, x, y, w, h = curvas([("100c", BLANCO)])
t, tx, ty, tw, th = curvas([(tagline, "#C9D3E5")], size=200, tracking=0.012)
cuerpo = [
f'',
f'',
f'',
f'',
None, # el lockup se arma abajo (con chip, igual que el avatar)
colocar(t, tx, ty, tw, th, W / 2, H / 2 + 205, 830),
]
# lockup con chip, centrado, a 1150 px de ancho total
g2, x2, y2, w2, h2 = curvas([("IA", "#0B0F1A")])
ANCHO, cy = 1150, H / 2 - 95
alto = 210
esc = alto / h
wt = w * esc
cw, ch = w2 * esc + 0.68 * alto, alto + 0.60 * alto
hueco = 0.13 * alto
total = wt + hueco + cw
k = ANCHO / total
wt, cw, ch, hueco = wt * k, cw * k, ch * k, hueco * k
izq = W / 2 - ANCHO / 2
cuerpo[cuerpo.index(None)] = (
colocar(g, x, y, w, h, izq + wt / 2, cy, wt)
+ f''
+ colocar(g2, x2, y2, w2, h2, izq + wt + hueco + cw / 2, cy, w2 * esc * k))
svg = (f'')
open(os.path.join(HERE, archivo), "w", encoding="utf-8").write(svg)
return archivo
if __name__ == "__main__":
# la variante con chip es la marca: se queda con los nombres principales
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)