diff --git a/docs/images/sd-font-previews/Alegreya.png b/docs/images/sd-font-previews/Alegreya.png index ac9d7746..b4636f86 100644 Binary files a/docs/images/sd-font-previews/Alegreya.png and b/docs/images/sd-font-previews/Alegreya.png differ diff --git a/docs/images/sd-font-previews/Arimo.png b/docs/images/sd-font-previews/Arimo.png index a7c948e3..c6b1c75e 100644 Binary files a/docs/images/sd-font-previews/Arimo.png and b/docs/images/sd-font-previews/Arimo.png differ diff --git a/docs/images/sd-font-previews/AtkinsonHyperlegibleNext.png b/docs/images/sd-font-previews/AtkinsonHyperlegibleNext.png index 16b61aa3..449fcb41 100644 Binary files a/docs/images/sd-font-previews/AtkinsonHyperlegibleNext.png and b/docs/images/sd-font-previews/AtkinsonHyperlegibleNext.png differ diff --git a/docs/images/sd-font-previews/Bitter.png b/docs/images/sd-font-previews/Bitter.png index fba7fb60..dd4f9020 100644 Binary files a/docs/images/sd-font-previews/Bitter.png and b/docs/images/sd-font-previews/Bitter.png differ diff --git a/docs/images/sd-font-previews/Gelasio.png b/docs/images/sd-font-previews/Gelasio.png index 5b412c0d..b25e431f 100644 Binary files a/docs/images/sd-font-previews/Gelasio.png and b/docs/images/sd-font-previews/Gelasio.png differ diff --git a/docs/images/sd-font-previews/GentiumBookPlus.png b/docs/images/sd-font-previews/GentiumBookPlus.png index fa7b962c..dfcb6f1e 100644 Binary files a/docs/images/sd-font-previews/GentiumBookPlus.png and b/docs/images/sd-font-previews/GentiumBookPlus.png differ diff --git a/docs/images/sd-font-previews/IBMPlexMono.png b/docs/images/sd-font-previews/IBMPlexMono.png index 49b3141f..ad6cbec1 100644 Binary files a/docs/images/sd-font-previews/IBMPlexMono.png and b/docs/images/sd-font-previews/IBMPlexMono.png differ diff --git a/docs/images/sd-font-previews/IBMPlexSans.png b/docs/images/sd-font-previews/IBMPlexSans.png index 032c806d..064552e3 100644 Binary files a/docs/images/sd-font-previews/IBMPlexSans.png and b/docs/images/sd-font-previews/IBMPlexSans.png differ diff --git a/docs/images/sd-font-previews/IBMPlexSerif.png b/docs/images/sd-font-previews/IBMPlexSerif.png index c35c3b5f..5ee535ed 100644 Binary files a/docs/images/sd-font-previews/IBMPlexSerif.png and b/docs/images/sd-font-previews/IBMPlexSerif.png differ diff --git a/docs/images/sd-font-previews/Inter.png b/docs/images/sd-font-previews/Inter.png index fa8e0b62..46b8b7d4 100644 Binary files a/docs/images/sd-font-previews/Inter.png and b/docs/images/sd-font-previews/Inter.png differ diff --git a/docs/images/sd-font-previews/LexicaUltralegible.png b/docs/images/sd-font-previews/LexicaUltralegible.png index 27e5161a..3705e2a4 100644 Binary files a/docs/images/sd-font-previews/LexicaUltralegible.png and b/docs/images/sd-font-previews/LexicaUltralegible.png differ diff --git a/docs/images/sd-font-previews/Literata.png b/docs/images/sd-font-previews/Literata.png index 3a85fcf6..f1f51755 100644 Binary files a/docs/images/sd-font-previews/Literata.png and b/docs/images/sd-font-previews/Literata.png differ diff --git a/docs/images/sd-font-previews/Lora.png b/docs/images/sd-font-previews/Lora.png index d0e9e68b..1cf87ac8 100644 Binary files a/docs/images/sd-font-previews/Lora.png and b/docs/images/sd-font-previews/Lora.png differ diff --git a/docs/images/sd-font-previews/Merriweather.png b/docs/images/sd-font-previews/Merriweather.png index ae2362ac..a314f73d 100644 Binary files a/docs/images/sd-font-previews/Merriweather.png and b/docs/images/sd-font-previews/Merriweather.png differ diff --git a/docs/images/sd-font-previews/NotoSansExtended.png b/docs/images/sd-font-previews/NotoSansExtended.png index 4029610d..7e979309 100644 Binary files a/docs/images/sd-font-previews/NotoSansExtended.png and b/docs/images/sd-font-previews/NotoSansExtended.png differ diff --git a/docs/images/sd-font-previews/NotoSerifExtended.png b/docs/images/sd-font-previews/NotoSerifExtended.png index e51b4567..9f08c8d9 100644 Binary files a/docs/images/sd-font-previews/NotoSerifExtended.png and b/docs/images/sd-font-previews/NotoSerifExtended.png differ diff --git a/docs/images/sd-font-previews/OpenDyslexic.png b/docs/images/sd-font-previews/OpenDyslexic.png index 9f21de6b..d476e78b 100644 Binary files a/docs/images/sd-font-previews/OpenDyslexic.png and b/docs/images/sd-font-previews/OpenDyslexic.png differ diff --git a/docs/images/sd-font-previews/Readerly.png b/docs/images/sd-font-previews/Readerly.png index 0958ba42..d9af57cd 100644 Binary files a/docs/images/sd-font-previews/Readerly.png and b/docs/images/sd-font-previews/Readerly.png differ diff --git a/docs/images/sd-font-previews/SourceCodePro.png b/docs/images/sd-font-previews/SourceCodePro.png index fac175c0..e7e5fb9d 100644 Binary files a/docs/images/sd-font-previews/SourceCodePro.png and b/docs/images/sd-font-previews/SourceCodePro.png differ diff --git a/docs/images/sd-font-previews/SourceSans3.png b/docs/images/sd-font-previews/SourceSans3.png index 42a3e689..919695a7 100644 Binary files a/docs/images/sd-font-previews/SourceSans3.png and b/docs/images/sd-font-previews/SourceSans3.png differ diff --git a/docs/images/sd-font-previews/SourceSerif4.png b/docs/images/sd-font-previews/SourceSerif4.png index f215b1a4..42362f87 100644 Binary files a/docs/images/sd-font-previews/SourceSerif4.png and b/docs/images/sd-font-previews/SourceSerif4.png differ diff --git a/docs/images/sd-font-previews/Spectral.png b/docs/images/sd-font-previews/Spectral.png index 53815b7b..3d512234 100644 Binary files a/docs/images/sd-font-previews/Spectral.png and b/docs/images/sd-font-previews/Spectral.png differ diff --git a/docs/images/sd-font-previews/Vollkorn.png b/docs/images/sd-font-previews/Vollkorn.png index 46083823..262f97ce 100644 Binary files a/docs/images/sd-font-previews/Vollkorn.png and b/docs/images/sd-font-previews/Vollkorn.png differ diff --git a/scripts/generate_sd_font_previews.py b/scripts/generate_sd_font_previews.py index 901e134c..c4afa3c3 100644 --- a/scripts/generate_sd_font_previews.py +++ b/scripts/generate_sd_font_previews.py @@ -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) diff --git a/scripts/sd_font_preview_generation.ipynb b/scripts/sd_font_preview_generation.ipynb new file mode 100644 index 00000000..0e62a50e --- /dev/null +++ b/scripts/sd_font_preview_generation.ipynb @@ -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 +} diff --git a/scripts/sd_font_preview_generation_new.ipynb b/scripts/sd_font_preview_generation_new.ipynb new file mode 100644 index 00000000..0247ebde --- /dev/null +++ b/scripts/sd_font_preview_generation_new.ipynb @@ -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": [ + "" + ] + }, + "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 +}