# -*- 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'{tspans}') 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'{puntos("dots")}' + "".join(cuerpo) + '') 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'' + "".join(piezas) + '') 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'' + colocar(g, x, y, w, h, W / 2, H / 2, ancho) + '') 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'{puntos("dots", sep=140, r=5)}' + "".join(cuerpo) + '') 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)