Rendering API
Path Types
GlyphPath
interface GlyphPath {
commands: PathCommand[];
bounds: { xMin: number; yMin: number; xMax: number; yMax: number } | null;
flags?: OutlineFlags;
}PathCommand
type PathCommand =
| { type: "M"; x: number; y: number } // Move to
| { type: "L"; x: number; y: number } // Line to
| { type: "Q"; x1: number; y1: number; x: number; y: number } // Quadratic curve
| { type: "C"; x1: number; y1: number; x2: number; y2: number; x: number; y: number } // Cubic curve
| { type: "Z" } // Close pathShapedGlyph
interface ShapedGlyph {
glyphId: GlyphId;
xOffset: number;
yOffset: number;
xAdvance: number;
yAdvance: number;
}Path Functions
getGlyphPath()
Get path commands for a single glyph.
function getGlyphPath(font: Font, glyphId: GlyphId): GlyphPath | nullExample:
import { Font, getGlyphPath } from "typeshaper";
const font = await Font.fromFile("font.ttf");
const glyphId = font.glyphIdForChar("A");
const path = getGlyphPath(font, glyphId);
if (path) {
console.log(`Glyph has ${path.commands.length} commands`);
console.log(`Bounds:`, path.bounds);
}getGlyphPathWithVariation()
Get path with variable font variation applied.
function getGlyphPathWithVariation(
font: Font,
glyphId: GlyphId,
axisCoords: number[]
): GlyphPath | nullExample:
const coords = [0.5, 0]; // Normalized coordinates
const path = getGlyphPathWithVariation(font, glyphId, coords);contourToPath()
Convert TrueType contours to path commands.
function contourToPath(contour: Contour): PathCommand[]Example:
const contours = font.getGlyphContours(glyphId);
if (contours) {
for (const contour of contours) {
const commands = contourToPath(contour);
// Process commands
}
}SVG Rendering
pathToSVG()
Convert path commands to SVG path data string.
function pathToSVG(
path: GlyphPath,
options?: { flipY?: boolean; scale?: number }
): stringExample:
const path = getGlyphPath(font, glyphId);
if (path) {
const svgPath = pathToSVG(path, { flipY: true, scale: 1 });
console.log(`<path d="${svgPath}" />`);
}glyphToSVG()
Generate a complete SVG element for a glyph.
function glyphToSVG(
font: Font,
glyphId: GlyphId,
options?: { fontSize?: number; fill?: string }
): string | nullExample:
const svg = glyphToSVG(font, glyphId, {
fontSize: 100,
fill: "black"
});
console.log(svg);
// <svg xmlns="http://www.w3.org/2000/svg" width="..." height="...">
// <path d="..." fill="black"/>
// </svg>pathToSVGWithMatrix()
Convert path to SVG with 2D affine matrix transformation applied to coordinates.
function pathToSVGWithMatrix(
path: GlyphPath,
matrix: Matrix2D,
options?: { flipY?: boolean }
): stringExample:
import { getGlyphPath, pathToSVGWithMatrix, rotate2D, scale2D, multiply2D } from "typeshaper";
const path = getGlyphPath(font, glyphId);
if (path) {
const matrix = multiply2D(rotate2D(Math.PI / 4), scale2D(0.1, 0.1));
const svgPath = pathToSVGWithMatrix(path, matrix);
console.log(`<path d="${svgPath}" />`);
}pathToSVGWithMatrix3D()
Convert path to SVG with 3D perspective matrix transformation.
function pathToSVGWithMatrix3D(
path: GlyphPath,
matrix: Matrix3x3,
options?: { flipY?: boolean }
): stringExample:
const perspective: Matrix3x3 = [
[0.1, 0, 100],
[0, 0.1, 100],
[0.0001, 0, 1] // Slight perspective effect
];
const svgPath = pathToSVGWithMatrix3D(path, perspective);matrixToSVGTransform()
Convert 2D affine matrix to SVG transform attribute string.
function matrixToSVGTransform(matrix: Matrix2D): stringExample:
const matrix = multiply2D(rotate2D(0.5), scale2D(2, 2));
const attr = matrixToSVGTransform(matrix);
// "matrix(1.755 0.958 -0.958 1.755 0 0)"
console.log(`<path d="..." transform="${attr}" />`);shapedTextToSVG()
Generate SVG for shaped text.
function shapedTextToSVG(
font: Font,
glyphs: ShapedGlyph[],
options?: {
fontSize?: number;
fill?: string;
matrix?: Matrix2D; // 2D transform (path data)
matrix3D?: Matrix3x3; // 3D perspective (path data)
useNativeTransform?: boolean; // Use SVG transform attr instead
}
): stringExample:
import { Font, UnicodeBuffer, shape, glyphBufferToShapedGlyphs, shapedTextToSVG } from "typeshaper";
const font = await Font.fromFile("font.ttf");
const buffer = new UnicodeBuffer();
buffer.addStr("Hello");
const shaped = shape(font, buffer);
const glyphs = glyphBufferToShapedGlyphs(shaped);
const svg = shapedTextToSVG(font, glyphs, { fontSize: 48 });
document.body.innerHTML = svg;shapedTextToSVGWithVariation()
Generate SVG for shaped text with variable font support.
function shapedTextToSVGWithVariation(
font: Font,
glyphs: ShapedGlyph[],
axisCoords: number[],
options?: { fontSize?: number; fill?: string }
): stringCanvas Rendering
pathToCanvas()
Render path commands to a Canvas 2D context or Path2D.
function pathToCanvas(
ctx: CanvasRenderingContext2D | Path2D,
path: GlyphPath,
options?: {
flipY?: boolean;
scale?: number;
offsetX?: number;
offsetY?: number;
}
): voidExample:
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const path = getGlyphPath(font, glyphId);
if (path && ctx) {
ctx.beginPath();
pathToCanvas(ctx, path, {
scale: 0.1,
flipY: true,
offsetX: 100,
offsetY: 100
});
ctx.fill();
}createPath2D()
Create a Path2D object from glyph path.
function createPath2D(
path: GlyphPath,
options?: {
flipY?: boolean;
scale?: number;
offsetX?: number;
offsetY?: number;
}
): Path2DExample:
const path = getGlyphPath(font, glyphId);
if (path) {
const path2d = createPath2D(path, { scale: 0.1, flipY: true });
ctx.fill(path2d);
}pathToCanvasWithMatrix()
Render path to canvas with 2D affine matrix transformation applied to coordinates.
function pathToCanvasWithMatrix(
ctx: CanvasRenderingContext2D | Path2D,
path: GlyphPath,
matrix: Matrix2D,
options?: { flipY?: boolean }
): voidExample:
import { getGlyphPath, pathToCanvasWithMatrix, rotate2D, scale2D, multiply2D } from "typeshaper";
const path = getGlyphPath(font, glyphId);
if (path && ctx) {
const matrix = multiply2D(
multiply2D(rotate2D(Math.PI / 4), scale2D(0.1, 0.1)),
translate2D(100, 100)
);
ctx.beginPath();
pathToCanvasWithMatrix(ctx, path, matrix);
ctx.fill();
}pathToCanvasWithMatrix3D()
Render path to canvas with 3D perspective matrix transformation.
function pathToCanvasWithMatrix3D(
ctx: CanvasRenderingContext2D | Path2D,
path: GlyphPath,
matrix: Matrix3x3,
options?: { flipY?: boolean }
): voidExample:
const perspective: Matrix3x3 = [
[0.1, 0, 100],
[0, 0.1, 100],
[0.001, 0, 1] // Perspective effect
];
ctx.beginPath();
pathToCanvasWithMatrix3D(ctx, path, perspective);
ctx.fill();applyMatrixToContext()
Apply 2D affine matrix to canvas context using native transform.
function applyMatrixToContext(
ctx: CanvasRenderingContext2D,
matrix: Matrix2D
): voidExample:
ctx.save();
applyMatrixToContext(ctx, rotate2D(Math.PI / 6));
pathToCanvas(ctx, path); // Rendered with rotation
ctx.fill();
ctx.restore();renderShapedText()
Render shaped text to Canvas.
function renderShapedText(
ctx: CanvasRenderingContext2D,
font: Font,
glyphs: ShapedGlyph[],
options?: {
fontSize?: number;
x?: number;
y?: number;
fill?: string;
stroke?: string;
strokeWidth?: number;
matrix?: Matrix2D; // 2D transform for glyph coordinates
matrix3D?: Matrix3x3; // 3D perspective (takes precedence over matrix)
}
): voidExample:
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const buffer = new UnicodeBuffer();
buffer.addStr("Hello World");
const shaped = shape(font, buffer);
const glyphs = glyphBufferToShapedGlyphs(shaped);
if (ctx) {
renderShapedText(ctx, font, glyphs, {
fontSize: 48,
x: 50,
y: 100,
fill: "black"
});
}renderShapedTextWithVariation()
Render shaped text with variable font support.
function renderShapedTextWithVariation(
ctx: CanvasRenderingContext2D,
font: Font,
glyphs: ShapedGlyph[],
axisCoords: number[],
options?: { fontSize?: number; x?: number; y?: number; fill?: string }
): voidUtility Functions
glyphBufferToShapedGlyphs()
Convert GlyphBuffer output to ShapedGlyph array.
function glyphBufferToShapedGlyphs(buffer: GlyphBuffer): ShapedGlyph[]Example:
const shaped = shape(font, buffer);
const glyphs = glyphBufferToShapedGlyphs(shaped);getTextWidth()
Calculate the total advance width of shaped text.
function getTextWidth(
glyphs: ShapedGlyph[],
font: Font,
fontSize: number
): numberExample:
const glyphs = glyphBufferToShapedGlyphs(shaped);
const width = getTextWidth(glyphs, font, 48);
console.log(`Text width: ${width}px`);Complete Example
import {
Font,
UnicodeBuffer,
shape,
glyphBufferToShapedGlyphs,
renderShapedText,
shapedTextToSVG
} from "typeshaper";
async function renderText() {
// Load font
const font = await Font.fromFile("font.ttf");
// Create buffer and add text
const buffer = new UnicodeBuffer();
buffer.addStr("Hello, TypeShaper!");
// Shape the text
const shaped = shape(font, buffer);
const glyphs = glyphBufferToShapedGlyphs(shaped);
// Render to Canvas
const canvas = document.createElement("canvas");
canvas.width = 800;
canvas.height = 200;
const ctx = canvas.getContext("2d");
if (ctx) {
renderShapedText(ctx, font, glyphs, {
fontSize: 48,
x: 50,
y: 100,
fill: "navy"
});
}
// Or get as SVG
const svg = shapedTextToSVG(font, glyphs, {
fontSize: 48,
fill: "navy"
});
document.body.appendChild(canvas);
document.body.innerHTML += svg;
}
renderText();Variable Font Example
import {
Font,
Face,
UnicodeBuffer,
shape,
glyphBufferToShapedGlyphs,
renderShapedText
} from "typeshaper";
async function renderVariableText() {
const font = await Font.fromFile("variable.ttf");
// Create face with weight=700
const face = new Face(font, { wght: 700 });
const buffer = new UnicodeBuffer();
buffer.addStr("Variable");
const shaped = shape(face, buffer);
const glyphs = glyphBufferToShapedGlyphs(shaped);
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
renderShapedText(ctx, font, glyphs, {
fontSize: 72,
x: 50,
y: 100
});
}
}Outline Transforms
Geometric transformations for glyph paths.
Matrix Types
// 2D affine transform: [a, b, c, d, tx, ty]
// Transforms point (x, y) to:
// x' = a*x + c*y + tx
// y' = b*x + d*y + ty
type Matrix2D = [number, number, number, number, number, number];
// 3D homogeneous transform (3x3 for perspective effects)
// | m00 m01 m02 | | x | | x' |
// | m10 m11 m12 | × | y | = | y' |
// | m20 m21 m22 | | 1 | | w |
// Final coordinates: (x'/w, y'/w)
type Matrix3x3 = [
[number, number, number],
[number, number, number],
[number, number, number]
];
interface BoundingBox {
xMin: number;
yMin: number;
xMax: number;
yMax: number;
}
interface ControlBox {
xMin: number;
yMin: number;
xMax: number;
yMax: number;
}Matrix Creation
// Identity matrices
function identity2D(): Matrix2D
function identity3x3(): Matrix3x3
// 2D transforms
function translate2D(tx: number, ty: number): Matrix2D
function scale2D(sx: number, sy: number): Matrix2D
function rotate2D(angle: number): Matrix2D
function shear2D(shx: number, shy: number): Matrix2D
// 3D perspective
function perspectiveMatrix(
fov: number,
aspect: number,
near: number,
far: number
): Matrix3x3
// Matrix multiplication
function multiply2D(a: Matrix2D, b: Matrix2D): Matrix2D
function multiply3x3(a: Matrix3x3, b: Matrix3x3): Matrix3x3Point Transforms
function transformPoint2D(
x: number,
y: number,
m: Matrix2D
): { x: number; y: number }
function transformPoint3x3(
x: number,
y: number,
m: Matrix3x3
): { x: number; y: number }Outline Transforms
// Scale outline by factors
function scaleOutline(
path: GlyphPath,
scaleX: number,
scaleY: number
): GlyphPath
// Scale by power of 2 (optimized)
function scaleOutlinePow2(path: GlyphPath, level: number): GlyphPath
// Rotate 90° counter-clockwise
function rotateOutline90(path: GlyphPath): GlyphPath
// General rotation
function rotateOutline(path: GlyphPath, angle: number): GlyphPath
// Translate
function translateOutline(
path: GlyphPath,
dx: number,
dy: number
): GlyphPath
// 2D affine transform
function transformOutline2D(path: GlyphPath, m: Matrix2D): GlyphPath
// 3D perspective transform
function transformOutline3D(path: GlyphPath, m: Matrix3x3): GlyphPath
// Italicize (shear transform)
function italicizeOutline(path: GlyphPath, slant: number): GlyphPathBounds Computation
// Control box (bounds of control points only)
function computeControlBox(path: GlyphPath): ControlBox
// Tight bounds (actual curve extrema)
function computeTightBounds(path: GlyphPath): BoundingBoxPath Utilities
// Deep clone a path
function clonePath(path: GlyphPath): GlyphPath
// Combine multiple paths into one
function combinePaths(...paths: GlyphPath[]): GlyphPathExample: Text Effects
import {
getGlyphPath,
transformOutline2D,
scale2D,
rotate2D,
multiply2D,
rasterizePath
} from "typeshaper";
// Rotate and scale a glyph
const path = getGlyphPath(font, glyphId);
if (path) {
const transform = multiply2D(
rotate2D(Math.PI / 6), // 30 degree rotation
scale2D(1.5, 1.5) // 150% scale
);
const transformed = transformOutline2D(path, transform);
const bitmap = rasterizePath(transformed, {
width: 200,
height: 200,
scale: 0.1
});
}Example: Canvas Transform Methods
import {
getGlyphPath,
pathToCanvasWithMatrix,
pathToCanvasWithMatrix3D,
applyMatrixToContext,
rotate2D,
scale2D,
translate2D,
multiply2D,
type Matrix3x3
} from "typeshaper";
const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 200;
const ctx = canvas.getContext("2d");
const path = getGlyphPath(font, glyphId);
if (path && ctx) {
// Method 1: Transform path data directly (2D)
const matrix = multiply2D(
translate2D(100, 100),
multiply2D(rotate2D(Math.PI / 4), scale2D(0.1, 0.1))
);
ctx.beginPath();
pathToCanvasWithMatrix(ctx, path, matrix);
ctx.fillStyle = "blue";
ctx.fill();
// Method 2: Transform path data with 3D perspective
const perspective: Matrix3x3 = [
[0.1, 0, 250], // Scale X + translate X
[0, 0.1, 100], // Scale Y + translate Y
[0.0005, 0, 1] // Perspective effect
];
ctx.beginPath();
pathToCanvasWithMatrix3D(ctx, path, perspective);
ctx.fillStyle = "red";
ctx.fill();
// Method 3: Use native canvas transform (2D only)
ctx.save();
applyMatrixToContext(ctx, multiply2D(translate2D(350, 100), scale2D(0.1, 0.1)));
ctx.beginPath();
pathToCanvas(ctx, path);
ctx.fillStyle = "green";
ctx.fill();
ctx.restore();
}Example: SVG Transform Methods
import {
getGlyphPath,
pathToSVG,
pathToSVGWithMatrix,
pathToSVGWithMatrix3D,
matrixToSVGTransform,
rotate2D,
scale2D,
multiply2D,
type Matrix3x3
} from "typeshaper";
const path = getGlyphPath(font, glyphId);
if (path) {
const scale = scale2D(0.1, 0.1);
// Method 1: Transform path coordinates directly (2D)
const rotated = pathToSVGWithMatrix(path, multiply2D(rotate2D(0.3), scale));
// Method 2: Transform with 3D perspective
const perspective: Matrix3x3 = [
[0.1, 0, 0],
[0, 0.1, 0],
[0.001, 0, 1] // Slight perspective
];
const perspectivePath = pathToSVGWithMatrix3D(path, perspective);
// Method 3: Use native SVG transform attribute
const pathData = pathToSVG(path);
const transformAttr = matrixToSVGTransform(scale);
// Generate SVG
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-200 -200 400 400">
<path d="${rotated}" fill="blue" />
<path d="${perspectivePath}" fill="red" transform="translate(100, 0)" />
<path d="${pathData}" fill="green" transform="${transformAttr}" />
</svg>
`;
}Example: Shaped Text with Transforms
import {
Font,
UnicodeBuffer,
shape,
glyphBufferToShapedGlyphs,
renderShapedText,
shapedTextToSVG,
rotate2D,
scale2D,
multiply2D,
type Matrix3x3
} from "typeshaper";
const font = await Font.fromFile("font.ttf");
const buffer = new UnicodeBuffer();
buffer.addStr("Hello");
const shaped = shape(font, buffer);
const glyphs = glyphBufferToShapedGlyphs(shaped);
// Canvas with 2D rotation
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (ctx) {
renderShapedText(ctx, font, glyphs, {
fontSize: 48,
x: 100,
y: 100,
fill: "navy",
matrix: rotate2D(Math.PI / 12) // 15° rotation
});
// Canvas with 3D perspective
const perspective: Matrix3x3 = [
[1, 0, 0],
[0, 1, 0],
[0.002, 0, 1]
];
renderShapedText(ctx, font, glyphs, {
fontSize: 48,
x: 100,
y: 200,
fill: "crimson",
matrix3D: perspective
});
}
// SVG with matrix transform in path data
const svg1 = shapedTextToSVG(font, glyphs, {
fontSize: 48,
fill: "navy",
matrix: multiply2D(rotate2D(Math.PI / 12), scale2D(1, 1))
});
// SVG with native transform attribute
const svg2 = shapedTextToSVG(font, glyphs, {
fontSize: 48,
fill: "navy",
matrix: rotate2D(Math.PI / 12),
useNativeTransform: true // Uses SVG transform attr instead
});