UNPKG

autokerning

Version:

autokerning computes suggested kerning values for glyph pairs from TrueType/OpenType fonts by rendering glyph bitmaps, applying a small Gaussian blur, and measuring pixel overlap across horizontal offsets. It can be used programmatically (as an imported E

52 lines (51 loc) 1.9 kB
import { renderGlyph } from "./glyph.js"; import { gaussianBlur } from "./blur.js"; import { kernPair } from "./kernPair.js"; const TUNING_CHARS = "lno"; let KERNEL_WIDTH = 20; // 0.2 * FONT_SIZE (100) /** Calibrate the kernel width and find overlap thresholds */ export function findS(font) { let kernelWidth = KERNEL_WIDTH; while (true) { const ss = []; for (const char of TUNING_CHARS) { const glyph = renderGlyph(font, char); const blurred = { ...glyph, bitmap: gaussianBlur(glyph.bitmap) }; // Self-kerning: how much does a character overlap with itself? const kern = kernPair(blurred, blurred, 0, 1e10); const s = overlap(blurred, blurred, kern); ss.push(s); } const minS = Math.min(...ss); const maxS = Math.max(...ss); // If the self-overlap is balanced (min > max/2), we're good if (minS > maxS / 2) { return [minS, maxS]; } // Otherwise, increase kernel width and try again kernelWidth += 2; if (kernelWidth > 200) { throw new Error("Failed to find reasonable kernel size."); } } } /** Compute pixel overlap (sum of squared products) */ function overlap(left, right, kern) { const height = Math.max(left.height, right.height); const rightX = left.advance - kern; let sum = 0; for (let y = 0; y < height; y++) { for (let x = 0; x < left.width + right.width; x++) { const lx = x; const rx = x - rightX; const lVal = lx >= 0 && lx < left.width && y < left.height ? left.bitmap.get(y, lx) : 0; const rVal = rx >= 0 && rx < right.width && y < right.height ? right.bitmap.get(y, rx) : 0; sum += lVal * rVal; } } return sum; }