opentype-js
SKILL.md
opentype.js
Read and write OpenType fonts. Access every glyph, measure text, convert to SVG paths.
Setup
# Install opentype.js for font parsing and manipulation.
npm install opentype.js
Loading a Font
// src/fonts/load.ts — Load a font from file or URL and read metadata.
import opentype from "opentype.js";
import fs from "fs";
// From file (Node.js)
const buffer = fs.readFileSync("./fonts/Inter-Regular.otf");
const font = opentype.parse(buffer.buffer);
console.log(font.names.fontFamily); // "Inter"
console.log(font.names.designer); // designer name
console.log(font.unitsPerEm); // 2048
console.log(font.numGlyphs); // total glyph count
Measuring Text
// src/fonts/measure.ts — Calculate text width and bounding box at a given size.
// Useful for layout engines and canvas text positioning.
import opentype from "opentype.js";
export function measureText(font: opentype.Font, text: string, fontSize: number) {
const path = font.getPath(text, 0, 0, fontSize);
const bb = path.getBoundingBox();
const advance = font.getAdvanceWidth(text, fontSize);
return {
width: advance,
height: bb.y2 - bb.y1,
boundingBox: { x1: bb.x1, y1: bb.y1, x2: bb.x2, y2: bb.y2 },
};
}
Rendering Text to SVG
// src/fonts/to-svg.ts — Convert a text string to an SVG path element.
// This produces resolution-independent text that doesn't require the font file.
import opentype from "opentype.js";
export function textToSvg(
font: opentype.Font,
text: string,
fontSize: number,
x: number,
y: number
): string {
const path = font.getPath(text, x, y, fontSize);
const pathData = path.toPathData(2); // precision
const bb = path.getBoundingBox();
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${bb.x1} ${bb.y1} ${bb.x2 - bb.x1} ${bb.y2 - bb.y1}">
<path d="${pathData}" fill="currentColor"/>
</svg>`;
}
Accessing Individual Glyphs
// src/fonts/glyphs.ts — Extract glyph outlines and metadata for specific characters.
import opentype from "opentype.js";
export function getGlyphInfo(font: opentype.Font, char: string) {
const glyph = font.charToGlyph(char);
const path = glyph.getPath(0, 0, 72);
return {
name: glyph.name,
unicode: glyph.unicode,
advanceWidth: glyph.advanceWidth,
pathData: path.toPathData(2),
commands: path.commands,
};
}
// List all glyphs in a font
export function listGlyphs(font: opentype.Font) {
const glyphs: { index: number; name: string; unicode: number | undefined }[] = [];
for (let i = 0; i < font.numGlyphs; i++) {
const g = font.glyphs.get(i);
glyphs.push({ index: i, name: g.name, unicode: g.unicode });
}
return glyphs;
}
Font Subsetting
// src/fonts/subset.ts — Create a subset font containing only the glyphs needed
// for a specific string. Reduces font file size for web embedding.
import opentype from "opentype.js";
import fs from "fs";
export function subsetFont(font: opentype.Font, chars: string, outputPath: string) {
const glyphs = [font.glyphs.get(0)]; // always include .notdef
const seen = new Set<number>();
for (const char of chars) {
const glyph = font.charToGlyph(char);
if (glyph.index !== 0 && !seen.has(glyph.index)) {
glyphs.push(glyph);
seen.add(glyph.index);
}
}
const subset = new opentype.Font({
familyName: font.names.fontFamily?.en || "Subset",
styleName: font.names.fontSubfamily?.en || "Regular",
unitsPerEm: font.unitsPerEm,
ascender: font.ascender,
descender: font.descender,
glyphs,
});
const buffer = Buffer.from(subset.download() as any);
fs.writeFileSync(outputPath, buffer);
}
Weekly Installs
1
Repository
terminalskills/skillsGitHub Stars
15
First Seen
3 days ago
Security Audits
Installed on
amp1
cline1
augment1
opencode1
cursor1
kimi-cli1