Update images
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 8.7 KiB |
@@ -1,22 +1,31 @@
|
|||||||
"""Generate a markdown font preview page and images for SD font families."""
|
"""Generate a markdown font preview page and images for SD font families."""
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
import yaml
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parent.parent / 'assets' / 'sd-fonts'
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||||
OUTPUT_DIR = Path(__file__).resolve().parent.parent / 'docs' / 'images' / 'sd-font-previews'
|
ROOT = REPO_ROOT / 'assets' / 'sd-fonts'
|
||||||
MD_PATH = Path(__file__).resolve().parent.parent / 'docs' / 'sd-fonts-preview.md'
|
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...'
|
SAMPLE_TEXT = 'Everyone has the right to freedom of thought...'
|
||||||
DEFAULT_FONT_SIZE = 28
|
DEFAULT_FONT_SIZE = 28
|
||||||
IMAGE_SIZE = (1080, 220)
|
IMAGE_SIZE = (1080, 220)
|
||||||
|
|
||||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
try:
|
with YAML_PATH.open('r', encoding='utf-8') as yaml_file:
|
||||||
font = ImageFont.truetype('arial.ttf', DEFAULT_FONT_SIZE)
|
manifest = yaml.safe_load(yaml_file)
|
||||||
except Exception:
|
|
||||||
font = ImageFont.load_default()
|
family_map = {family['name']: family for family in manifest['families']}
|
||||||
|
|
||||||
families = [p.name for p in sorted(ROOT.iterdir()) if p.is_dir()]
|
families = [p.name for p in sorted(ROOT.iterdir()) if p.is_dir()]
|
||||||
|
|
||||||
lines = [
|
lines = [
|
||||||
'# SD Font Preview',
|
'# 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:
|
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}'
|
text = f'{family} - {SAMPLE_TEXT}'
|
||||||
image = Image.new('RGB', IMAGE_SIZE, color=(255, 255, 255))
|
image = Image.new('RGB', IMAGE_SIZE, color=(255, 255, 255))
|
||||||
draw = ImageDraw.Draw(image)
|
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
|
x = 20
|
||||||
y = (IMAGE_SIZE[1] - text_height) // 2
|
y = (IMAGE_SIZE[1] - text_height) // 2
|
||||||
draw.text((x, y), text, fill='black', font=font)
|
draw.text((x, y), text, fill='black', font=font)
|
||||||
|
|||||||
@@ -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} |  |')\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 |  |\n",
|
||||||
|
"| Arimo |  |\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} |  |')\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
|
||||||
|
}
|
||||||