Prototype for review, not deployed. Newsreader + Schibsted Grotesk (served locally, italic included), palette from the logo (petrol, sage, sand, rose) instead of gold, the logo blob as a recurring shape, petrol bands with a wave edge, margin-note section labels and uneven columns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
41 lines
2.8 KiB
Python
41 lines
2.8 KiB
Python
# Aufruf: python scripts/fonts-holen.py -> alle Schriften neu holen
|
|
# python scripts/fonts-holen.py "Name" ... -> nur diese, Manifest wird ergänzt
|
|
import re, json, sys, urllib.request, urllib.error, pathlib
|
|
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0 Safari/537.36"
|
|
FAMS = ['Playfair Display','Lora','Merriweather','Libre Baskerville','DM Serif Display','Inter','Roboto','Open Sans','Poppins','Montserrat','Nunito','Work Sans','Source Sans 3','Newsreader','Schibsted Grotesk']
|
|
# Diese Schriften auch kursiv holen (echte Kursive statt schräg gerechneter)
|
|
ITAL = {'Newsreader'}
|
|
manifest_path = pathlib.Path('src/lib/font-faces.json')
|
|
if sys.argv[1:]:
|
|
FAMS = sys.argv[1:]
|
|
out_dir = pathlib.Path('public/fonts'); out_dir.mkdir(parents=True, exist_ok=True)
|
|
manifest = json.load(open(manifest_path, encoding='utf8')) if sys.argv[1:] else {}
|
|
def get(url):
|
|
return urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent': UA})).read()
|
|
for fam in FAMS:
|
|
css = None
|
|
for w in ['400;500;600;700', '400;700', '400']:
|
|
axis = 'wght'
|
|
if fam in ITAL:
|
|
axis = 'ital,wght'; w = ';'.join([f'0,{x}' for x in w.split(';')] + [f'1,{x}' for x in w.split(';')])
|
|
try:
|
|
css = get(f"https://fonts.googleapis.com/css2?family={fam.replace(' ','+')}:{axis}@{w}&display=swap").decode(); break
|
|
except urllib.error.HTTPError: pass
|
|
slug = fam.lower().replace(' ', '-'); d = out_dir / slug; d.mkdir(exist_ok=True)
|
|
faces = []; seen = {}
|
|
for subset, body in re.findall(r'/\* ([\w-]+) \*/\s*@font-face \{(.*?)\}', css, re.S):
|
|
if subset not in ('latin', 'latin-ext'): continue
|
|
weight = re.search(r'font-weight: (\d+)', body).group(1)
|
|
style = re.search(r'font-style: (\w+)', body).group(1)
|
|
url = re.search(r'url\((.*?)\)', body).group(1)
|
|
rng = re.search(r'unicode-range: (.*?);', body).group(1)
|
|
if url not in seen:
|
|
fn = f"{slug}-{subset}-{len(seen)}.woff2"; (d / fn).write_bytes(get(url)); seen[url] = fn
|
|
faces.append({'weight': int(weight), 'style': style, 'subset': subset, 'file': f"/fonts/{slug}/{seen[url]}", 'unicodeRange': rng})
|
|
# gleiche Datei fuer mehrere Gewichte (variable Schrift) -> eine Regel mit Gewichtsbereich
|
|
merged = {}
|
|
for f in faces:
|
|
k = (f['file'], f['subset'], f['style']); merged.setdefault(k, dict(f, weights=[]))['weights'].append(f['weight'])
|
|
manifest[fam] = [dict({'file': v['file'], 'weight': f"{min(v['weights'])} {max(v['weights'])}" if len(v['weights']) > 1 else str(v['weights'][0]), 'unicodeRange': v['unicodeRange']}, **({'style': 'italic'} if v['style'] == 'italic' else {})) for v in merged.values()]
|
|
print(fam, len(manifest[fam]), 'Regeln', len(seen), 'Dateien')
|
|
json.dump(manifest, open(manifest_path, 'w', encoding='utf8'), indent=2)
|