Files
Coop-NL/scripts/badges/convert-illustrator-svg.py
T
Jakub_HostacnyandClaude Opus 5.5 e2e9a82094 feat: rozostup pismen vo vrstvach cenovky, tesnejsie pismeno za l/t/d s makcenom
Sablony Super cena: zlava a % podla navrhu (-0.03 / -0.02 em). Pridane
sablony Super vyhodne | Oranzova (| SK).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 16:20:44 +02:00

140 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Prevod testovacích SVG cenoviek z Illustratora na pozadia šablón:
# - ceny, zľava % a „zľava“ sa odstránia (budú z nich vrstvy editora),
# - statické nápisy (SUPER / CENA / VÝHODNÉ, písmo Gilroy-Heavy) sa prevedú na krivky,
# - triedy a id dostanú jedinečnú predponu (inak by sa viaceré cenovky na stránke prebíjali),
# - obsah sa posunie tak, aby telo cenovky bolo vo všetkých šablónach na rovnakom mieste.
#
# Použitie: pip install fonttools; python convert-illustrator-svg.py <priečinok so SVG> <výstup.json>
# (Gilroy-Heavy musí byť nainštalovaný; priradenie súborov k typu/farbe/pôvodu je v ASSETS nižšie.)
# Výstup obsahuje vyčistené SVG a namerané polohy textov – z nich sa vyrobili šablóny v tabuľke badges.
import json
import re
import sys
from fontTools.pens.svgPathPen import SVGPathPen
from fontTools.pens.transformPen import TransformPen
from fontTools.ttLib import TTFont
SRC = sys.argv[1]
OUT = sys.argv[2]
GILROY = r'C:\Users\jakub\AppData\Local\Microsoft\Windows\Fonts\Gilroy-Heavy.ttf'
font = TTFont(GILROY)
glyphs = font.getGlyphSet()
cmap = font.getBestCmap()
upem = font['head'].unitsPerEm
# referenčná poloha tela cenovky (zhora ľavý roh rámika s cenou)
REF_X = 27.98
ASSETS = {
'Asset 2.svg': ('SUPER CENA', 'cervena', 'SK'),
'Asset 3.svg': ('SUPER VÝHODNÉ', 'cervena', 'SK'),
'Asset 4.svg': ('SUPER CENA', 'oranzova', 'SK'),
'Asset 5.svg': ('SUPER CENA', 'zelena', 'SK'),
'Asset 6.svg': ('SUPER CENA', 'cervena', ''),
'Asset 7.svg': ('SUPER VÝHODNÉ', 'cervena', ''),
'Asset 8.svg': ('SUPER CENA', 'oranzova', ''),
'Asset 9.svg': ('SUPER VÝHODNÉ', 'zelena', ''),
'Asset 10.svg': ('SUPER VÝHODNÉ', 'oranzova', 'SK'),
'Asset 11.svg': ('SUPER VÝHODNÉ', 'oranzova', ''),
}
def parse_css(svg):
style = re.search(r'<style>(.*?)</style>', svg, re.S).group(1)
props = {}
for sel, body in re.findall(r'([^{}]+)\{([^}]*)\}', style):
decl = dict(
(k.strip(), v.strip()) for k, v in (d.split(':', 1) for d in body.split(';') if ':' in d)
)
for cls in sel.split(','):
props.setdefault(cls.strip().lstrip('.'), {}).update(decl)
return props
def text_to_path(transform, runs, size, fill):
"""runs: [(x, text, letter_spacing_em)] → <path> s tou istou transformáciou ako pôvodný <text>."""
s = size / upem
pen = SVGPathPen(glyphs)
for x, txt, ls in runs:
cursor = x
for ch in txt:
name = cmap.get(ord(ch))
if not name:
continue
glyphs[name].draw(TransformPen(pen, (s, 0, 0, -s, cursor, 0)))
cursor += glyphs[name].width * s + ls * size
d = pen.getCommands()
return f'<path transform="{transform}" fill="{fill}" d="{d}"/>' if d else ''
result = []
for fname, (btype, color, origin) in ASSETS.items():
svg = open(f'{SRC}/{fname}', encoding='utf-8').read()
key = 'b' + re.sub(r'\D', '', fname)
css = parse_css(svg)
texts = {} # rola → údaje
def replace_text(m):
classes = m.group(1).split()
transform = m.group(2)
body = m.group(3)
p = {}
for c in classes:
p.update(css.get(c, {}))
size = float(p.get('font-size', '0').replace('px', ''))
family = p.get('font-family', '')
fill = p.get('fill', '#000')
runs = []
for tcls, x, txt in re.findall(r'<tspan(?: class="([^"]*)")? x="([\d.\-]+)" y="[\d.\-]+">([^<]*)</tspan>', body):
ls = css.get(tcls, {}).get('letter-spacing', '0em').replace('em', '') if tcls else '0'
runs.append((float(x), txt, float(ls or 0)))
content = ''.join(r[1] for r in runs)
tr = re.match(r'translate\(([\d.\-]+) ([\d.\-]+)\)', transform)
tx, ty = float(tr.group(1)), float(tr.group(2))
if not content.strip():
return ''
if 'Gilroy' in family:
return text_to_path(transform, runs, size, fill)
# Aptly: ceny, zľava % a „zľava“ → vrstvy editora
if '%' in content:
role = 'discount'
elif content.isdigit() and size >= 25:
role = 'main'
elif content.isdigit():
role = 'cents'
else:
role = 'label'
texts[role] = {'text': content, 'x': tx, 'y': ty, 'size': size, 'fill': fill, 'runs': runs}
return ''
svg = re.sub(r'<text class="([^"]+)" transform="([^"]+)">(.*?)</text>', replace_text, svg, flags=re.S)
# posun obsahu: rámik s cenou vždy na REF_X a jeho horná hrana na rovnakej výške v rámci druhu cenovky
rects = re.findall(r'<rect class="(cls-\d+)" x="([\d.]+)" y="([\d.]+)" width="48.53" height="41.94"/>', svg)
box_x, box_y = float(rects[0][1]), float(rects[0][2])
dx = REF_X - box_x
dy = (92.67 if btype == 'SUPER CENA' else 44.98) - box_y
# jedinečné triedy a id
svg = re.sub(r'\bcls-(\d+)\b', rf'{key}-c\1', svg)
for ident in set(re.findall(r'id="([^"]+)"', svg)):
svg = re.sub(rf'(id="|url\(#|xlink:href="#){re.escape(ident)}(["\)])', rf'\1{key}-{ident}\2', svg)
inner = re.sub(r'^.*?<svg[^>]*>', '', svg, flags=re.S)
inner = re.sub(r'</svg>\s*$', '', inner)
defs = re.search(r'<defs>.*?</defs>', inner, re.S).group(0)
body = inner.replace(defs, '')
vb = [float(v) for v in re.search(r'viewBox="([^"]+)"', svg).group(1).split()]
result.append({
'file': fname, 'key': key, 'type': btype, 'color': color, 'origin': origin,
'dx': dx, 'dy': dy, 'boxY': box_y, 'vb': vb, 'texts': texts,
'svg': f'{defs}<g transform="translate({dx:.2f} {dy:.2f})">{body.strip()}</g>',
})
json.dump(result, open(OUT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
for r in result:
print(r['file'], r['type'], r['color'], r['origin'], 'dx', round(r['dx'], 2), 'dy', round(r['dy'], 2), 'boxY', r['boxY'], 'texts', {k: v['text'] for k, v in r['texts'].items()}, len(r['svg']))