ASCII-портрет в GitHub-профиле (README)
GitHub рендерит README профиля из репозитория, имя которого совпадает с вашим ником (например, e1teck/e1teck). Раскрасить текст в markdown нельзя — GitHub вырезает inline-стили при санитайзинге. Единственный способ получить цветной моноширинный арт — вставить его как SVG-картинку. Чтобы он выглядел правильно и в тёмной, и в светлой теме, делают два SVG и переключают их тегом <picture>.
Три составляющих:
- Python-скрипт (Pillow), превращающий фото в ASCII-символы.
- Скрипт, собирающий символы плюс инфопанель в два SVG-файла.
README.mdс тегом<picture>в репозитории<username>/<username>.
Шаг 1. Фото → ASCII
Уменьшаем фото до символьной сетки и заменяем каждый пиксель глифом подходящей «плотности». Для тёмной темы яркие пиксели становятся плотными глифами (@M$%); для светлой — наоборот, плотные глифы получают тёмные пиксели.
Ключевые приёмы, без которых получается каша:
- Убрать фон. Заливка (flood fill) от краёв кадра помечает фон, который становится маской. Важно: запускать заливку только от светлых пикселей (яркость > 120), иначе она «съест» тёмную одежду. Оставшиеся островки фона удаляются через связные компоненты — оставляем только самый большой.
- Поднять контраст и резкость перед конвертацией:
ImageEnhance.Contrast(1.4)+UnsharpMask— иначе черты лица пропадают. - Вертикальное сжатие ~0.5: глиф в терминале примерно вдвое выше своей ширины, поэтому высота сетки = ширина × пропорции фото × 0.48.
- Монохром лучше цвета. Пофрагментный цвет из фото выглядит эффектно, но убивает контраст плотности — лицо читается хуже.
Минимальный пример:
from PIL import Image, ImageOps, ImageEnhance, ImageFilter
W = 116 # ширина в символах
ramp = " .'`^,:;i!|(fjrxnkmpqwZO%$M@" # от разреженных к плотным
img = Image.open("photo.jpg")
g = ImageOps.grayscale(img)
g = ImageEnhance.Contrast(g).enhance(1.45)
g = g.filter(ImageFilter.UnsharpMask(radius=3, percent=200))
H = int(W * img.height / img.width * 0.48)
g = g.resize((W, H))
px = g.load()
for y in range(H):
line = ""
for x in range(W):
v = px[x, y] / 255 # светлая тема: v = 1 - v
line += ramp[int(v * (len(ramp) - 1))]
print(line)
Шаг 2. ASCII → SVG
SVG даёт три «суперсилы», которых нет у обычного markdown-текста:
- Цвета: подписи, значения и прогресс-бары стилизуются CSS-классами внутри
<style>. - Меньший шрифт для арта: панель 14px, арт 7.5px. На той же площади помещается сетка 116×75 вместо 60×38 — вдвое больше деталей.
textLength: у зрителя может не быть вашего шрифта, и ширины строк «поедут».textLength="…" lengthAdjust="spacingAndGlyphs"принудительно задаёт каждой строке точную ширину в пикселях — выравнивание переживает любой шрифт.
Каждая строка — элемент <text> с xml:space="preserve" (иначе браузер схлопнет пробелы); цветные фрагменты — <tspan class="…">:
<svg xmlns="http://www.w3.org/2000/svg" width="1081" height="709" viewBox="0 0 1081 709">
<style>
text { font-family: 'SFMono-Regular', Consolas, Menlo, monospace;
font-size: 14px; white-space: pre }
.art { font-size: 7.5px; fill: #c9d1d9 }
.label { fill: #ffa657 }
.value { fill: #79c0ff }
.barF { fill: #58a6ff }
</style>
<rect width="100%" height="100%" fill="#0d1117" rx="8"/>
<text x="14" y="27" xml:space="preserve" textLength="522.5"
lengthAdjust="spacingAndGlyphs"><tspan class="label">. OS:</tspan><tspan class="value"> macOS, Linux</tspan></text>
<!-- … строки арта и панели … -->
</svg>
Прогресс-бары — это просто глифы █ и ░ в tspan с разными классами:
. Python:................... ██████████████████░░ 90%
Цвета берём из палитры GitHub: тёмная тема — фон #0d1117, текст #c9d1d9, синий #79c0ff, оранжевый #ffa657; светлая — фон #ffffff, текст #24292f, синий #0550ae, красный #cf222e.
Шаг 3. Публикация
- Создайте публичный репозиторий с именем своего ника:
github.com/new→ имяe1teck. GitHub подтвердит фразой «You found a secret!». - Загрузите
dark_mode.svgиlight_mode.svg— в этот же репозиторий или любой другой (например, репозиторий сайта — тогда картинки отдаются и с вашего домена). - В
README.mdиспользуйте<picture>— браузер сам выберет версию под тему посетителя:
<a href="https://a1eks.com">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://raw.githubusercontent.com/e1teck/a1eks.com/master/img/dark_mode.svg">
<img alt="Aleksei Ryzhkov"
src="https://raw.githubusercontent.com/e1teck/a1eks.com/master/img/light_mode.svg">
</picture>
</a>
Частые грабли: инвертированный вид в светлой теме, шум фона из-за неполной заливки, «съеденная» футболка при заливке, рассинхрон из-за шрифта, задержки кэша картинок у GitHub и неверная ветка в URL.