The names of fonts are rendered using the corresponding font.

Each font's name is rendered to an OffscreenCanvas, and that image is cached in OPFS.

There's a list of links to download font files in the preview.
This commit is contained in:
Christian Lawson-Perfect 2025-03-20 13:11:40 +00:00
parent 51d57259b0
commit a4b046a784
3 changed files with 165 additions and 15 deletions

View file

@ -79,7 +79,8 @@ const css_template = (info,range,weight,style,variable_info) => {
if(axes.includes('slnt')) {
extras.push(` font-style: oblique ${variable_info.axes.slnt.min}deg ${variable_info.axes.slnt.max}deg;`);
}
}
}
if(info.variable) {
return `
@ -113,6 +114,52 @@ async function fetch_json(url) {
}
async function draw_font_preview(f) {
const d = await navigator.storage.getDirectory();
const filename = `${f.family}.png`;
let fh;
try {
fh = await d.getFileHandle(filename);
} catch(e) {
const style = document.createElement('style');
try {
document.head.append(style);
const info = await fetch_json(font_info_url(f.id));
const variable_info = info.variable ? await fetch_json(variable_info_url(info.id)) : {};
const css = css_template(info,info.defSubset,info.weights[0],'normal',variable_info);
style.textContent += css;
await new Promise((resolve,reject) => {
setTimeout(async () => {
const ffs = Array.from(document.fonts).filter(ff=>ff.family == `"${f.family}"`);
await Promise.all(ffs.map(ff => ff.load())).catch(reject)
resolve();
},1);
});
} catch(e) {
return;
}
const cv = new OffscreenCanvas(1,1);
const ctx = cv.getContext('2d');
const font_style = `100px "${f.family}"`;
ctx.font = font_style;
const {width, fontBoundingBoxAscent, fontBoundingBoxDescent} = ctx.measureText(f.family);
cv.width = width;
cv.height = fontBoundingBoxAscent + fontBoundingBoxDescent;
ctx.font = font_style;
ctx.fillText(f.family,0,fontBoundingBoxAscent);
const blob = await cv.toBlob();
fh = await d.getFileHandle(filename,{create:true});
const w = await fh.createWritable();
await w.write(blob);
await w.close();
window.blob = blob;
style.parentElement.removeChild(style);
}
const file = await fh.getFile();
return URL.createObjectURL(file);
}
async function go() {
let [fonts, axes_info] = await Promise.all([
@ -120,14 +167,20 @@ async function go() {
fetch_json('https://api.fontsource.org/v1/axis-registry')
]);
window.available_fonts = fonts;
axes_info = Object.fromEntries(Object.entries(axes_info).map(([k,v]) => [k.toLowerCase(), v]));
async function use_font() {
const name = select.value;
console.log(name);
const info = await fetch_json(font_info_url(name));
const variable_info = info.variable ? await fetch_json(variable_info_url(info.id)) : {};
console.log(info);
const preview = document.querySelector(`[data-font="${name}"]`);
if(preview) {
preview.scrollIntoView({block: 'center'});
document.body.scrollTop = 0;
}
document.body.style.setProperty('--family',info.family);
@ -153,7 +206,6 @@ async function go() {
if(info.variable) {
const variable_axes = document.getElementById('variable-axes');
variable_axes.innerHTML = '';
console.log(variable_info);
const ranges = {};
@ -208,7 +260,6 @@ async function go() {
document.body.style['font-variation-settings'] = settings;
}
update_variables();
}
const css_declarations = [];
@ -240,7 +291,7 @@ async function go() {
}
fonts = fonts.filter(f=>!f.id.includes('noto'));
fonts = fonts.filter(f=>!f.id.match(/noto|playwrite/i));
select.innerHTML = '';
for(let font of fonts) {
const option = document.createElement('option');
@ -260,6 +311,27 @@ async function go() {
document.getElementById('random-font').addEventListener('click', random_font);
random_font();
const fl = document.getElementById('font-list');
const step = 10;
for(let i=0;i<fonts.length;i+=step) {
await Promise.all(fonts.slice(i+0,i+step).map(async f => {
const li = document.createElement('li');
fl.append(li);
const a = document.createElement('a');
li.append(a);
a.addEventListener('click', () => {
select.value = f.id;
use_font();
})
const img = document.createElement('img');
img.dataset.font = f.id;
a.append(img);
img.src = await draw_font_preview(f);
img.style.height = '1em';
img.alt = f.family;
}));
}
}
go();