Update images

This commit is contained in:
jpirnay
2026-05-04 15:00:44 +02:00
parent 830cde615a
commit 2a30882c91
26 changed files with 548 additions and 8 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

+71 -8
View File
@@ -1,22 +1,31 @@
"""Generate a markdown font preview page and images for SD font families."""
import urllib.request
import urllib.parse
from pathlib import Path
from typing import Optional
from PIL import Image, ImageDraw, ImageFont
import yaml
ROOT = Path(__file__).resolve().parent.parent / 'assets' / 'sd-fonts'
OUTPUT_DIR = Path(__file__).resolve().parent.parent / 'docs' / 'images' / 'sd-font-previews'
MD_PATH = Path(__file__).resolve().parent.parent / 'docs' / 'sd-fonts-preview.md'
REPO_ROOT = Path(__file__).resolve().parent.parent
ROOT = REPO_ROOT / 'assets' / 'sd-fonts'
OUTPUT_DIR = REPO_ROOT / 'docs' / 'images' / 'sd-font-previews'
MD_PATH = REPO_ROOT / 'docs' / 'sd-fonts-preview.md'
YAML_PATH = REPO_ROOT / 'lib' / 'EpdFont' / 'scripts' / 'sd-fonts.yaml'
CACHE_DIR = Path(__file__).resolve().parent / 'font-cache'
SAMPLE_TEXT = 'Everyone has the right to freedom of thought...'
DEFAULT_FONT_SIZE = 28
IMAGE_SIZE = (1080, 220)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
CACHE_DIR.mkdir(parents=True, exist_ok=True)
try:
font = ImageFont.truetype('arial.ttf', DEFAULT_FONT_SIZE)
except Exception:
font = ImageFont.load_default()
with YAML_PATH.open('r', encoding='utf-8') as yaml_file:
manifest = yaml.safe_load(yaml_file)
family_map = {family['name']: family for family in manifest['families']}
families = [p.name for p in sorted(ROOT.iterdir()) if p.is_dir()]
lines = [
'# SD Font Preview',
'',
@@ -26,11 +35,65 @@ lines = [
'|---|---|',
]
def download_font(url: str, cache_dir: Path) -> Path:
parsed = urllib.parse.urlparse(url)
filename = Path(parsed.path).name
output_path = cache_dir / filename
if not output_path.exists():
print(f'Downloading {url}...')
urllib.request.urlretrieve(url, output_path)
return output_path
def resolve_instanced_font_path(family_name: str, style_name: str) -> Optional[Path]:
instanced_dir = REPO_ROOT / 'lib' / 'EpdFont' / 'scripts' / 'instanced_fonts' / family_name
if not instanced_dir.exists():
return None
matches = sorted(instanced_dir.glob(f'{style_name}*.ttf'))
return matches[0] if matches else None
def resolve_font_path(family_name: str) -> Optional[Path]:
family = family_map.get(family_name)
if family is None:
return None
styles = family.get('styles', {})
regular = styles.get('regular') or next(iter(styles.values()), None)
if regular is None:
return None
if 'path' in regular:
local_path = REPO_ROOT / 'lib' / 'EpdFont' / regular['path']
if local_path.exists():
return local_path
instanced = resolve_instanced_font_path(family_name, 'regular')
if instanced is not None:
return instanced
if 'url' in regular:
return download_font(regular['url'], CACHE_DIR)
return None
for family in families:
font_path = resolve_font_path(family)
try:
if font_path is None:
raise FileNotFoundError('No source font found')
font = ImageFont.truetype(str(font_path), DEFAULT_FONT_SIZE)
except Exception as exc:
print(f'Warning: unable to load font for {family}: {exc}. Falling back to default font.')
font = ImageFont.load_default()
text = f'{family} - {SAMPLE_TEXT}'
image = Image.new('RGB', IMAGE_SIZE, color=(255, 255, 255))
draw = ImageDraw.Draw(image)
text_width, text_height = draw.textsize(text, font=font)
bbox = draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
x = 20
y = (IMAGE_SIZE[1] - text_height) // 2
draw.text((x, y), text, fill='black', font=font)
+130
View File
@@ -0,0 +1,130 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "6297ef3a",
"metadata": {},
"source": [
"# SD Font Preview Generation\n",
"\n",
"This notebook scans `./assets/sd-fonts`, generates preview images for each available SD font family, and creates a markdown listing page that embeds those images."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "adfac037",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from pathlib import Path\n",
"from PIL import Image, ImageDraw, ImageFont\n",
"from IPython.display import display, Markdown, Image as DisplayImage\n",
"\n",
"root_dir = Path('assets/sd-fonts')\n",
"output_image_dir = Path('docs/images/sd-font-previews')\n",
"output_image_dir.mkdir(parents=True, exist_ok=True)\n",
"output_markdown_path = Path('docs/sd-fonts-preview.md')\n",
"\n",
"print(f'Root font directory: {root_dir.resolve()}')\n",
"print(f'Preview image output directory: {output_image_dir.resolve()}')\n",
"print(f'Markdown output path: {output_markdown_path.resolve()}')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "305e0f28",
"metadata": {},
"outputs": [],
"source": [
"# Discover available font families\n",
"font_families = [p.name for p in sorted(root_dir.iterdir()) if p.is_dir()]\n",
"print(f'Found {len(font_families)} font families:')\n",
"for family in font_families:\n",
" print(f' - {family}')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5771f18f",
"metadata": {},
"outputs": [],
"source": [
"sample_text = 'Everyone has the right to freedom of thought...'\n",
"preview_texts = {family: f'{family} - {sample_text}' for family in font_families}\n",
"\n",
"for family, text in preview_texts.items():\n",
" print(f'{family}: {text}')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "86bf3966",
"metadata": {},
"outputs": [],
"source": [
"# Render and save preview images\n",
"font_size = 28\n",
"try:\n",
" default_font = ImageFont.truetype('arial.ttf', font_size)\n",
"except Exception:\n",
" default_font = ImageFont.load_default()\n",
"\n",
"image_width = 1080\n",
"image_height = 220\n",
"\n",
"for family, text in preview_texts.items():\n",
" image = Image.new('RGB', (image_width, image_height), 'white')\n",
" draw = ImageDraw.Draw(image)\n",
" text_width, text_height = draw.textsize(text, font=default_font)\n",
" x = 20\n",
" y = (image_height - text_height) // 2\n",
" draw.text((x, y), text, fill='black', font=default_font)\n",
" output_path = output_image_dir / f'{family}.png'\n",
" image.save(output_path)\n",
" print(f'Saved {output_path}')\n",
"\n",
"print('All preview images generated.')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d5636615",
"metadata": {},
"outputs": [],
"source": [
"# Generate markdown listing with embedded preview images\n",
"lines = [\n",
" '# SD Font Preview',\n",
" '',\n",
" 'This page lists the available SD font families under `assets/sd-fonts` and embeds rendered preview images for each family.',\n",
" '',\n",
" '| Font Family | Preview |',\n",
" '|---|---|',\n",
"]\n",
"\n",
"for family in font_families:\n",
" image_rel_path = f'images/sd-font-previews/{family}.png'\n",
" lines.append(f'| {family} | ![Preview of {family}]({image_rel_path}) |')\n",
"\n",
"output_markdown_path.write_text('\\n'.join(lines) + '\\n', encoding='utf-8')\n",
"print(f'Generated markdown page at {output_markdown_path}')\n",
"\n",
"# Display a preview of the generated markdown in the notebook\n",
"display(Markdown('\\n'.join(lines[:8]) + '\\n...'))"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
@@ -0,0 +1,347 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "b9bce3b3",
"metadata": {},
"source": [
"# SD Font Preview Generation\n",
"\n",
"This notebook scans `./assets/sd-fonts`, generates preview images for each available SD font family, and creates a markdown listing page that embeds those images."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "30e78310",
"metadata": {},
"outputs": [
{
"ename": "NameError",
"evalue": "name '__file__' is not defined",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mNameError\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 15\u001b[39m\n\u001b[32m 13\u001b[39m output_markdown_path = repo_root / \u001b[33m'\u001b[39m\u001b[33mdocs\u001b[39m\u001b[33m'\u001b[39m / \u001b[33m'\u001b[39m\u001b[33msd-fonts-preview.md\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m 14\u001b[39m manifest_path = repo_root / \u001b[33m'\u001b[39m\u001b[33mlib\u001b[39m\u001b[33m'\u001b[39m / \u001b[33m'\u001b[39m\u001b[33mEpdFont\u001b[39m\u001b[33m'\u001b[39m / \u001b[33m'\u001b[39m\u001b[33mscripts\u001b[39m\u001b[33m'\u001b[39m / \u001b[33m'\u001b[39m\u001b[33msd-fonts.yaml\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m---> \u001b[39m\u001b[32m15\u001b[39m cache_dir = Path(\u001b[34;43m__file__\u001b[39;49m).resolve().parent / \u001b[33m'\u001b[39m\u001b[33mfont-cache\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m 16\u001b[39m cache_dir.mkdir(parents=\u001b[38;5;28;01mTrue\u001b[39;00m, exist_ok=\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[32m 18\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m manifest_path.open(\u001b[33m'\u001b[39m\u001b[33mr\u001b[39m\u001b[33m'\u001b[39m, encoding=\u001b[33m'\u001b[39m\u001b[33mutf-8\u001b[39m\u001b[33m'\u001b[39m) \u001b[38;5;28;01mas\u001b[39;00m manifest_file:\n",
"\u001b[31mNameError\u001b[39m: name '__file__' is not defined"
]
}
],
"source": [
"import urllib.request\n",
"import urllib.parse\n",
"from pathlib import Path\n",
"from typing import Optional\n",
"from PIL import Image, ImageDraw, ImageFont\n",
"from IPython.display import display, Markdown\n",
"import yaml\n",
"\n",
"repo_root = Path.cwd().resolve().parent\n",
"root_dir = repo_root / 'assets' / 'sd-fonts'\n",
"output_image_dir = repo_root / 'docs' / 'images' / 'sd-font-previews'\n",
"output_image_dir.mkdir(parents=True, exist_ok=True)\n",
"output_markdown_path = repo_root / 'docs' / 'sd-fonts-preview.md'\n",
"manifest_path = repo_root / 'lib' / 'EpdFont' / 'scripts' / 'sd-fonts.yaml'\n",
"cache_dir = repo_root / 'scripts' / 'font-cache'\n",
"cache_dir.mkdir(parents=True, exist_ok=True)\n",
"\n",
"with manifest_path.open('r', encoding='utf-8') as manifest_file:\n",
" manifest = yaml.safe_load(manifest_file)\n",
"\n",
"family_map = {family['name']: family for family in manifest['families']}\n",
"\n",
"print(f'Repo root: {repo_root}')\n",
"print(f'Root font directory: {root_dir.resolve()}')\n",
"print(f'Preview image output directory: {output_image_dir.resolve()}')\n",
"print(f'Markdown output path: {output_markdown_path.resolve()}')"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "217146fe",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Found 23 font families:\n",
" - Alegreya\n",
" - Arimo\n",
" - AtkinsonHyperlegibleNext\n",
" - Bitter\n",
" - Gelasio\n",
" - GentiumBookPlus\n",
" - IBMPlexMono\n",
" - IBMPlexSans\n",
" - IBMPlexSerif\n",
" - Inter\n",
" - LexicaUltralegible\n",
" - Literata\n",
" - Lora\n",
" - Merriweather\n",
" - NotoSansExtended\n",
" - NotoSerifExtended\n",
" - OpenDyslexic\n",
" - Readerly\n",
" - SourceCodePro\n",
" - SourceSans3\n",
" - SourceSerif4\n",
" - Spectral\n",
" - Vollkorn\n"
]
},
{
"ename": "NameError",
"evalue": "name 'Optional' is not defined",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mNameError\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[8]\u001b[39m\u001b[32m, line 18\u001b[39m\n\u001b[32m 14\u001b[39m urllib.request.urlretrieve(url, local_path)\n\u001b[32m 15\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m local_path\n\u001b[32m---> \u001b[39m\u001b[32m18\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mresolve_instanced_font_path\u001b[39m(family_name: \u001b[38;5;28mstr\u001b[39m, style_name: \u001b[38;5;28mstr\u001b[39m) -> \u001b[43mOptional\u001b[49m[Path]:\n\u001b[32m 19\u001b[39m instanced_dir = repo_root / \u001b[33m'\u001b[39m\u001b[33mlib\u001b[39m\u001b[33m'\u001b[39m / \u001b[33m'\u001b[39m\u001b[33mEpdFont\u001b[39m\u001b[33m'\u001b[39m / \u001b[33m'\u001b[39m\u001b[33mscripts\u001b[39m\u001b[33m'\u001b[39m / \u001b[33m'\u001b[39m\u001b[33minstanced_fonts\u001b[39m\u001b[33m'\u001b[39m / family_name\n\u001b[32m 20\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m instanced_dir.exists():\n",
"\u001b[31mNameError\u001b[39m: name 'Optional' is not defined"
]
}
],
"source": [
"# Discover available font families\n",
"font_families = [p.name for p in sorted(root_dir.iterdir()) if p.is_dir()]\n",
"print(f'Found {len(font_families)} font families:')\n",
"for family in font_families:\n",
" print(f' - {family}')\n",
"\n",
"\n",
"def download_font(url: str, cache_dir: Path) -> Path:\n",
" parsed = urllib.parse.urlparse(url)\n",
" filename = Path(parsed.path).name\n",
" local_path = cache_dir / filename\n",
" if not local_path.exists():\n",
" print(f'Downloading {url}...')\n",
" urllib.request.urlretrieve(url, local_path)\n",
" return local_path\n",
"\n",
"\n",
"def resolve_instanced_font_path(family_name: str, style_name: str) -> Optional[Path]:\n",
" instanced_dir = repo_root / 'lib' / 'EpdFont' / 'scripts' / 'instanced_fonts' / family_name\n",
" if not instanced_dir.exists():\n",
" return None\n",
" matches = sorted(instanced_dir.glob(f'{style_name}*.ttf'))\n",
" return matches[0] if matches else None\n",
"\n",
"\n",
"def resolve_font_path(family_name: str) -> Optional[Path]:\n",
" family = family_map.get(family_name)\n",
" if family is None:\n",
" return None\n",
" styles = family.get('styles', {})\n",
" style = styles.get('regular') or next(iter(styles.values()), None)\n",
" if style is None:\n",
" return None\n",
"\n",
" if 'path' in style:\n",
" local_path = repo_root / 'lib' / 'EpdFont' / style['path']\n",
" if local_path.exists():\n",
" return local_path\n",
"\n",
" instanced = resolve_instanced_font_path(family_name, 'regular')\n",
" if instanced is not None:\n",
" return instanced\n",
"\n",
" if 'url' in style:\n",
" return download_font(style['url'], cache_dir)\n",
" return None"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "a0843a3e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Alegreya: Alegreya - Everyone has the right to freedom of thought...\n",
"Arimo: Arimo - Everyone has the right to freedom of thought...\n",
"AtkinsonHyperlegibleNext: AtkinsonHyperlegibleNext - Everyone has the right to freedom of thought...\n",
"Bitter: Bitter - Everyone has the right to freedom of thought...\n",
"Gelasio: Gelasio - Everyone has the right to freedom of thought...\n",
"GentiumBookPlus: GentiumBookPlus - Everyone has the right to freedom of thought...\n",
"IBMPlexMono: IBMPlexMono - Everyone has the right to freedom of thought...\n",
"IBMPlexSans: IBMPlexSans - Everyone has the right to freedom of thought...\n",
"IBMPlexSerif: IBMPlexSerif - Everyone has the right to freedom of thought...\n",
"Inter: Inter - Everyone has the right to freedom of thought...\n",
"LexicaUltralegible: LexicaUltralegible - Everyone has the right to freedom of thought...\n",
"Literata: Literata - Everyone has the right to freedom of thought...\n",
"Lora: Lora - Everyone has the right to freedom of thought...\n",
"Merriweather: Merriweather - Everyone has the right to freedom of thought...\n",
"NotoSansExtended: NotoSansExtended - Everyone has the right to freedom of thought...\n",
"NotoSerifExtended: NotoSerifExtended - Everyone has the right to freedom of thought...\n",
"OpenDyslexic: OpenDyslexic - Everyone has the right to freedom of thought...\n",
"Readerly: Readerly - Everyone has the right to freedom of thought...\n",
"SourceCodePro: SourceCodePro - Everyone has the right to freedom of thought...\n",
"SourceSans3: SourceSans3 - Everyone has the right to freedom of thought...\n",
"SourceSerif4: SourceSerif4 - Everyone has the right to freedom of thought...\n",
"Spectral: Spectral - Everyone has the right to freedom of thought...\n",
"Vollkorn: Vollkorn - Everyone has the right to freedom of thought...\n"
]
}
],
"source": [
"sample_text = 'Everyone has the right to freedom of thought...'\n",
"preview_texts = {family: f'{family} - {sample_text}' for family in font_families}\n",
"\n",
"for family, text in preview_texts.items():\n",
" print(f'{family}: {text}')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "e23a6728",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Alegreya.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Arimo.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\AtkinsonHyperlegibleNext.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Bitter.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Gelasio.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\GentiumBookPlus.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\IBMPlexMono.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\IBMPlexSans.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\IBMPlexSerif.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Inter.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\LexicaUltralegible.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Literata.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Lora.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Merriweather.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\NotoSansExtended.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\NotoSerifExtended.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\OpenDyslexic.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Readerly.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\SourceCodePro.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\SourceSans3.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\SourceSerif4.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Spectral.png\n",
"Saved C:\\_development\\crosspoint-reader\\docs\\images\\sd-font-previews\\Vollkorn.png\n",
"All preview images generated.\n"
]
}
],
"source": [
"# Render and save preview images\n",
"font_size = 28\n",
"image_width = 1080\n",
"image_height = 220\n",
"\n",
"for family, text in preview_texts.items():\n",
" font_path = resolve_font_path(family)\n",
" try:\n",
" if font_path is None:\n",
" raise FileNotFoundError('No source font path available')\n",
" font = ImageFont.truetype(str(font_path), font_size)\n",
" except Exception as exc:\n",
" print(f'Warning: could not load {family} from {font_path}: {exc}')\n",
" font = ImageFont.load_default()\n",
"\n",
" image = Image.new('RGB', (image_width, image_height), 'white')\n",
" draw = ImageDraw.Draw(image)\n",
" bbox = draw.textbbox((0, 0), text, font=font)\n",
" text_width = bbox[2] - bbox[0]\n",
" text_height = bbox[3] - bbox[1]\n",
" x = 20\n",
" y = (image_height - text_height) // 2\n",
" draw.text((x, y), text, fill='black', font=font)\n",
" output_path = output_image_dir / f'{family}.png'\n",
" image.save(output_path)\n",
" print(f'Saved {output_path}')\n",
"\n",
"print('All preview images generated.')"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "7d53f4f2",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Generated markdown page at C:\\_development\\crosspoint-reader\\docs\\sd-fonts-preview.md\n"
]
},
{
"data": {
"text/markdown": [
"# SD Font Preview\n",
"\n",
"This page lists the available SD font families under `assets/sd-fonts` and embeds rendered preview images for each family.\n",
"\n",
"| Font Family | Preview |\n",
"|---|---|\n",
"| Alegreya | ![Preview of Alegreya](images/sd-font-previews/Alegreya.png) |\n",
"| Arimo | ![Preview of Arimo](images/sd-font-previews/Arimo.png) |\n",
"..."
],
"text/plain": [
"<IPython.core.display.Markdown object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Generate markdown listing with embedded preview images\n",
"lines = [\n",
" '# SD Font Preview',\n",
" '',\n",
" 'This page lists the available SD font families under `assets/sd-fonts` and embeds rendered preview images for each family.',\n",
" '',\n",
" '| Font Family | Preview |',\n",
" '|---|---|',\n",
"]\n",
"\n",
"for family in font_families:\n",
" image_rel_path = f'images/sd-font-previews/{family}.png'\n",
" lines.append(f'| {family} | ![Preview of {family}]({image_rel_path}) |')\n",
"\n",
"output_markdown_path.write_text('\\n'.join(lines) + '\\n', encoding='utf-8')\n",
"print(f'Generated markdown page at {output_markdown_path}')\n",
"\n",
"# Display a preview of the generated markdown in the notebook\n",
"display(Markdown('\\n'.join(lines[:8]) + '\\n...'))"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}