Skip to content

Fluent API ​

The fluent API provides an ergonomic, chainable interface for composing transforms and rendering operations. It offers two styles:

  1. Builder pattern - Method chaining with PathBuilder and BitmapBuilder classes
  2. Pipe pattern - Functional composition with curried operators

Quick Start ​

Builder Style ​

typescript
import { glyph } from "text-shaper";

const rgba = glyph(font, glyphId)
  ?.scale(2)
  .rotateDeg(15)
  .rasterizeAuto({ padding: 2 })
  .blur(5)
  .toRGBA();

Pipe Style ​

typescript
import { pipe, $scale, $rotate, $rasterize, $blur, $toRGBA, getGlyphPath } from "text-shaper";

const rgba = pipe(
  getGlyphPath(font, glyphId),
  $scale(2, 2),
  $rotate(Math.PI / 4),
  $rasterize({ width: 100, height: 100 }),
  $blur(5),
  $toRGBA
);

Entry Points ​

glyph() ​

Create a PathBuilder from a font glyph.

typescript
function glyph(font: Font, glyphId: GlyphId): PathBuilder | null

Example:

typescript
const builder = glyph(font, glyphId);
if (builder) {
  const rgba = builder.scale(2).rasterizeAuto().toRGBA();
}

char() ​

Create a PathBuilder from a character.

typescript
function char(font: Font, character: string): PathBuilder | null

Example:

typescript
const builder = char(font, "A");
const rgba = builder?.scale(2).rasterizeAuto().toRGBA();

glyphVar() ​

Create a PathBuilder from a variable font glyph with axis coordinates.

typescript
function glyphVar(font: Font, glyphId: GlyphId, axisCoords: number[]): PathBuilder | null

Example:

typescript
// Create with weight=700, width=100
const builder = glyphVar(font, glyphId, [700, 100]);

path() ​

Wrap an existing GlyphPath in a PathBuilder.

typescript
function path(p: GlyphPath): PathBuilder

Example:

typescript
const existingPath = getGlyphPath(font, glyphId);
if (existingPath) {
  const rgba = path(existingPath).scale(2).rasterizeAuto().toRGBA();
}

bitmap() ​

Wrap an existing Bitmap in a BitmapBuilder.

typescript
function bitmap(b: Bitmap): BitmapBuilder

Example:

typescript
const existingBitmap = rasterizePath(path, options);
const rgba = bitmap(existingBitmap).blur(5).toRGBA();

combine() ​

Combine multiple PathBuilder instances into one.

typescript
function combine(...paths: PathBuilder[]): PathBuilder

Example:

typescript
const h = glyph(font, hGlyphId)?.translate(0, 0);
const i = glyph(font, iGlyphId)?.translate(100, 0);

if (h && i) {
  const combined = combine(h, i).scale(2).rasterizeAuto().toRGBA();
}

PathBuilder ​

The PathBuilder class provides fluent methods for path transformations and rendering.

Static Factory Methods ​

These are typically accessed via the entry point functions, but can also be called directly.

PathBuilder.fromGlyph() ​

Create from a font glyph.

typescript
static fromGlyph(font: Font, glyphId: GlyphId): PathBuilder | null

PathBuilder.fromGlyphAtSize() ​

Extract a size-specific, scaled outline. "freetype-real-dim" follows FreeType/libass real-dimension sizing and 26.6 point rounding.

typescript
static fromGlyphAtSize(
  font: Font,
  glyphId: GlyphId,
  sizePx: number,
  mode?: "em" | "height" | "freetype-real-dim"
): PathBuilder | null

PathBuilder.fromGlyphWithVariation() ​

Create from a variable font glyph with axis coordinates.

typescript
static fromGlyphWithVariation(font: Font, glyphId: GlyphId, axisCoords: number[]): PathBuilder | null

PathBuilder.fromPath() ​

Create from an existing GlyphPath.

typescript
static fromPath(path: GlyphPath): PathBuilder

PathBuilder.combine() ​

Combine multiple PathBuilders into one.

typescript
static combine(...builders: PathBuilder[]): PathBuilder

Lazy Transforms ​

Transforms are accumulated as a matrix and applied lazily when rendering or when .apply() is called.

scale() ​

Scale uniformly or non-uniformly.

typescript
scale(sx: number, sy?: number): PathBuilder

translate() ​

Translate by offset.

typescript
translate(dx: number, dy: number): PathBuilder

rotate() ​

Rotate by angle in radians.

typescript
rotate(angle: number): PathBuilder

rotateDeg() ​

Rotate by angle in degrees.

typescript
rotateDeg(angleDeg: number): PathBuilder

shear() ​

Apply shear/skew transformation.

typescript
shear(shearX: number, shearY: number): PathBuilder

italic() ​

Apply italic slant (angle in degrees, typically 12-15).

typescript
italic(angleDeg: number): PathBuilder

matrix() ​

Apply custom 2D affine matrix.

typescript
matrix(m: Matrix2D): PathBuilder

perspective() ​

Apply 3D perspective matrix.

typescript
perspective(m: Matrix3x3): PathBuilder

perspectiveVanish() ​

Create perspective with vanishing point.

typescript
perspectiveVanish(vanishingPointX: number, vanishingPointY: number, strength: number): PathBuilder

resetTransform() ​

Reset transform to identity.

typescript
resetTransform(): PathBuilder

Eager Path Effects ​

These effects apply immediately (force transform application first).

apply() ​

Force application of pending transforms.

typescript
apply(): PathBuilder

Example:

typescript
const path = glyph(font, glyphId)
  ?.scale(2)
  .rotate(Math.PI / 4)
  .apply()       // Apply transforms now
  .embolden(50)  // Then embolden the transformed path
  .toPath();

embolden() ​

Synthetic bold effect.

typescript
embolden(strength: number): PathBuilder

condense() ​

Horizontal condensing (factor < 1) or expansion (factor > 1).

typescript
condense(factor: number): PathBuilder

oblique() ​

Apply oblique/slant transformation.

typescript
oblique(slant: number): PathBuilder

stroke() ​

Convert to stroked outline.

typescript
stroke(options: StrokerOptions): PathBuilder
stroke(width: number, cap?: LineCap, join?: LineJoin): PathBuilder

Example:

typescript
const stroked = glyph(font, glyphId)
  ?.stroke({ width: 20, lineCap: "round", lineJoin: "round" })
  .scale(2)
  .rasterizeAuto()
  .toRGBA();

Bounds & Metrics ​

controlBox() ​

Get bounding box of control points (after transforms).

typescript
controlBox(): { xMin: number; yMin: number; xMax: number; yMax: number }

tightBounds() ​

Get exact bounds considering curve extrema (after transforms).

typescript
tightBounds(): { xMin: number; yMin: number; xMax: number; yMax: number }

getTransformMatrix() ​

Get the accumulated 2D transform matrix.

typescript
getTransformMatrix(): Matrix2D

getTransformMatrix3D() ​

Get the accumulated 3D transform matrix.

typescript
getTransformMatrix3D(): Matrix3x3 | null

Rasterization ​

rasterize() ​

Rasterize to bitmap with explicit size.

typescript
rasterize(options: RasterOptions): BitmapBuilder

Options:

typescript
interface RasterOptions {
  width?: number;
  height?: number;
  scale?: number;
  offsetX?: number;
  offsetY?: number;
  padding?: number;
  pixelMode?: PixelMode;
  fillRule?: FillRule;
  flipY?: boolean;
}

rasterizeAuto() ​

Rasterize with auto-computed size from bounds.

typescript
rasterizeAuto(options?: AutoRasterOptions): BitmapBuilder

Options:

typescript
interface AutoRasterOptions {
  padding?: number;    // Default: 1
  scale?: number;      // Default: 1
  pixelMode?: PixelMode;
  fillRule?: FillRule;
  flipY?: boolean;
}

Example:

typescript
const rgba = glyph(font, glyphId)
  ?.scale(2)
  .rasterizeAuto({ padding: 5 })
  .toRGBA();

rasterizeWithGradient() ​

Rasterize with gradient fill.

typescript
rasterizeWithGradient(gradient: Gradient, options: RasterOptions): BitmapBuilder

Example:

typescript
import { glyph, type LinearGradient } from "text-shaper";

const gradient: LinearGradient = {
  type: "linear",
  x0: 0, y0: 0,
  x1: 100, y1: 100,
  stops: [
    { offset: 0, color: [255, 0, 0, 255] },
    { offset: 1, color: [0, 0, 255, 255] }
  ]
};

const rgba = glyph(font, glyphId)
  ?.scale(2)
  .rasterizeWithGradient(gradient, { width: 100, height: 100 })
  .toRGBA();

SDF/MSDF Rendering ​

toSdf() ​

Render as Signed Distance Field (SDF) for GPU text rendering.

typescript
toSdf(options: SdfOptions): BitmapBuilder

Options:

typescript
interface SdfOptions {
  width: number;
  height: number;
  scale: number;
  offsetX?: number;
  offsetY?: number;
  flipY?: boolean;
  spread?: number;  // Default: 8
}

Example:

typescript
const sdf = glyph(font, glyphId)
  ?.toSdf({ width: 64, height: 64, scale: 0.05, spread: 4 })
  .toGray();

toSdfAuto() ​

Render as SDF with auto-computed size from bounds.

typescript
toSdfAuto(options?: {
  padding?: number;
  scale?: number;
  spread?: number;
  flipY?: boolean;
}): BitmapBuilder

toMsdf() ​

Render as Multi-channel Signed Distance Field (MSDF) for sharper GPU text.

typescript
toMsdf(options: MsdfOptions): BitmapBuilder

Example:

typescript
const msdf = glyph(font, glyphId)
  ?.toMsdf({ width: 64, height: 64, scale: 0.05, spread: 4 })
  .toRGBA();

toMsdfAuto() ​

Render as MSDF with auto-computed size from bounds.

typescript
toMsdfAuto(options?: {
  padding?: number;
  scale?: number;
  spread?: number;
  flipY?: boolean;
}): BitmapBuilder

Asymmetric Stroke ​

strokeAsymmetric() ​

Stroke with independent X/Y border widths. Returns both outer and inner paths.

typescript
strokeAsymmetric(options: AsymmetricStrokeOptions): {
  outer: PathBuilder;
  inner: PathBuilder;
}

Options:

typescript
interface AsymmetricStrokeOptions {
  xBorder: number;
  yBorder: number;
  eps?: number;
  lineJoin?: "miter" | "round" | "bevel";
  miterLimit?: number;
}

Example:

typescript
const { outer, inner } = glyph(font, glyphId)
  ?.strokeAsymmetric({ xBorder: 10, yBorder: 5 });

// Use outer for border effect
const border = outer?.rasterizeAuto().toRGBA();

strokeAsymmetricCombined() ​

Stroke with asymmetric widths, combining inner and outer into one fillable path.

typescript
strokeAsymmetricCombined(options: AsymmetricStrokeOptions): PathBuilder

Output Methods ​

toSVG() ​

Convert to SVG path data string.

typescript
toSVG(options?: SVGOptions): string

toSVGElement() ​

Convert to complete SVG element.

typescript
toSVGElement(options?: SVGElementOptions): string

Options:

typescript
interface SVGElementOptions {
  fontSize?: number;
  fill?: string;
  stroke?: string;
  strokeWidth?: number;
}

toCanvas() ​

Render to canvas context.

typescript
toCanvas(ctx: CanvasRenderingContext2D, options?: CanvasOptions): void

Options:

typescript
interface CanvasOptions {
  flipY?: boolean;
  scale?: number;
  offsetX?: number;
  offsetY?: number;
  fill?: string;
  stroke?: string;
  strokeWidth?: number;
}

toPath2D() ​

Get Path2D object for canvas.

typescript
toPath2D(options?: { flipY?: boolean; scale?: number }): Path2D

toPath() ​

Extract the raw GlyphPath (with transforms applied).

typescript
toPath(): GlyphPath

clone() ​

Clone this builder.

typescript
clone(): PathBuilder

BitmapBuilder ​

The BitmapBuilder class provides fluent methods for bitmap manipulation.

Static Factory Methods ​

BitmapBuilder.fromBitmap() ​

Create from existing bitmap.

typescript
static fromBitmap(bitmap: Bitmap): BitmapBuilder

BitmapBuilder.fromBitmapWithBearing() ​

Create from bitmap with bearing info (used for glyph positioning).

typescript
static fromBitmapWithBearing(bitmap: Bitmap, bearingX: number, bearingY: number): BitmapBuilder

BitmapBuilder.adoptBitmapWithBearing() ​

Create from bitmap with bearing info without copying the bitmap.

typescript
static adoptBitmapWithBearing(bitmap: Bitmap, bearingX: number, bearingY: number): BitmapBuilder

This is a zero-copy hot-path helper. The builder takes ownership of the bitmap, so callers should not mutate or reuse that bitmap after adoption.

BitmapBuilder.fromRasterizedGlyph() ​

Create from a rasterized glyph result.

typescript
static fromRasterizedGlyph(glyph: RasterizedGlyph): BitmapBuilder

BitmapBuilder.adoptRasterizedGlyph() ​

Create from a rasterized glyph without copying the glyph bitmap.

typescript
static adoptRasterizedGlyph(glyph: RasterizedGlyph): BitmapBuilder

This is the zero-copy counterpart of fromRasterizedGlyph(). The builder takes ownership of glyph.bitmap.

Example:

typescript
const glyph = rasterizeGlyph(font, glyphId, options);
const builder = BitmapBuilder.fromRasterizedGlyph(glyph);
const rgba = builder.blur(3).toRGBA();

BitmapBuilder.create() ​

Create empty bitmap.

typescript
static create(width: number, height: number, pixelMode?: PixelMode): BitmapBuilder

Example:

typescript
const canvas = BitmapBuilder.create(100, 100, PixelMode.Gray);

BitmapBuilder.fromGradient() ​

Create a gradient bitmap.

typescript
static fromGradient(width: number, height: number, gradient: Gradient): BitmapBuilder

Example:

typescript
const gradientBg = BitmapBuilder.fromGradient(100, 100, {
  type: "linear",
  x0: 0, y0: 0,
  x1: 100, y1: 100,
  stops: [
    { offset: 0, color: [255, 0, 0, 255] },
    { offset: 1, color: [0, 0, 255, 255] }
  ]
});

Blur Effects ​

blur() ​

Gaussian blur.

typescript
blur(radius: number): BitmapBuilder

boxBlur() ​

Box blur (faster, less smooth).

typescript
boxBlur(radius: number): BitmapBuilder

cascadeBlur() ​

Cascade blur (fast for large radii, O(1) per pixel).

typescript
cascadeBlur(radiusX: number, radiusY?: number): BitmapBuilder

adaptiveBlur() ​

Adaptive blur (auto-selects best algorithm).

typescript
adaptiveBlur(radiusX: number, radiusY?: number): BitmapBuilder

fastBlur() ​

Fast Gaussian blur using cascade algorithm. Recommended for large radii (> 3 pixels).

typescript
fastBlur(radius: number): BitmapBuilder

Example:

typescript
// For large blur radii, fastBlur is more efficient
const blurred = glyph(font, glyphId)
  ?.rasterizeAuto({ padding: 50 })
  .fastBlur(20)  // Much faster than blur(20)
  .toRGBA();

Transform Effects ​

embolden() ​

Embolden (dilate) bitmap.

typescript
embolden(xStrength: number, yStrength?: number): BitmapBuilder

emboldenWithBearing() ​

Embolden bitmap and update bearing to avoid clipping.

typescript
emboldenWithBearing(xStrength: number, yStrength?: number): BitmapBuilder

transform2D() ​

Apply 2D affine transform to bitmap (bearing-aware).

typescript
transform2D(matrix: Matrix2D, options?: { offsetX26?: number; offsetY26?: number }): BitmapBuilder

transform3D() ​

Apply 3D perspective transform to bitmap (bearing-aware).

typescript
transform3D(matrix: Matrix3x3, options?: { offsetX26?: number; offsetY26?: number }): BitmapBuilder

shearX() / shearY() ​

Shear bitmap horizontally or vertically (bearing-aware).

typescript
shearX(amount: number, options?: { offsetX26?: number; offsetY26?: number }): BitmapBuilder
shearY(amount: number, options?: { offsetX26?: number; offsetY26?: number }): BitmapBuilder

shift() ​

Shift bitmap position.

typescript
shift(dx: number, dy: number): BitmapBuilder

resize() ​

Resize with nearest-neighbor interpolation.

typescript
resize(width: number, height: number): BitmapBuilder

resizeBilinear() ​

Resize with bilinear interpolation.

typescript
resizeBilinear(width: number, height: number): BitmapBuilder

pad() ​

Pad bitmap with empty space.

typescript
pad(left: number, top: number, right: number, bottom: number): BitmapBuilder
pad(all: number): BitmapBuilder

Metrics ​

measure() ​

Measure ascent/descent from bitmap coverage.

typescript
measure(): { ascent: number; descent: number }

metrics() ​

Get raster metrics (width/height/bearing/ascent/descent).

typescript
metrics(): RasterMetrics

expandMetrics() ​

Expand metrics for blur/border/shadow padding.

typescript
expandMetrics(options: RasterEffectOptions): RasterMetrics & {
  padLeft: number;
  padRight: number;
  padTop: number;
  padBottom: number;
}

Compositing ​

blend() ​

Alpha blend another bitmap at position.

typescript
blend(other: BitmapBuilder | Bitmap, x: number, y: number, opacity?: number): BitmapBuilder

composite() ​

Composite using Porter-Duff "over" operation.

typescript
composite(other: BitmapBuilder | Bitmap, x?: number, y?: number): BitmapBuilder

add() ​

Additive blend.

typescript
add(other: BitmapBuilder | Bitmap, x?: number, y?: number): BitmapBuilder

subtract() ​

Subtractive blend.

typescript
subtract(other: BitmapBuilder | Bitmap, x?: number, y?: number): BitmapBuilder

multiply() ​

Multiplicative blend.

typescript
multiply(other: BitmapBuilder | Bitmap, x?: number, y?: number): BitmapBuilder

max() ​

Maximum blend.

typescript
max(other: BitmapBuilder | Bitmap, x?: number, y?: number): BitmapBuilder

Output Methods ​

convert() ​

Convert to different pixel mode.

typescript
convert(targetMode: PixelMode): BitmapBuilder

toRGBA() ​

Get RGBA pixel array.

typescript
toRGBA(): Uint8Array

toGray() ​

Get grayscale array.

typescript
toGray(): Uint8Array

toBitmap() ​

Get raw bitmap (cloned).

typescript
toBitmap(): Bitmap

toRasterizedGlyph() ​

Get bitmap with bearing info.

typescript
toRasterizedGlyph(): { bitmap: Bitmap; bearingX: number; bearingY: number }

intoRasterizedGlyph() ​

Get bitmap with bearing info without copying.

typescript
intoRasterizedGlyph(): { bitmap: Bitmap; bearingX: number; bearingY: number }

This transfers ownership of the builder's internal bitmap to the caller. Do not continue using the builder after calling this method.

clone() ​

Clone this builder.

typescript
clone(): BitmapBuilder

Accessors ​

typescript
get width(): number
get height(): number
get pixelMode(): PixelMode
get bearingX(): number
get bearingY(): number

Pipe Function ​

The pipe() function enables functional composition.

typescript
function pipe<A>(a: A): A
function pipe<A, B>(a: A, ab: (a: A) => B): B
function pipe<A, B, C>(a: A, ab: (a: A) => B, bc: (b: B) => C): C
// ... up to 8 functions

Pipe Operators ​

All pipe operators are exported with a $ prefix from the main module to avoid naming conflicts.

Path Source ​

typescript
$fromGlyph(font: Font, glyphId: GlyphId): GlyphPath | null

Example:

typescript
const path = $fromGlyph(font, glyphId);
if (path) {
  const rgba = pipe(path, $scale(2), $rasterizeAuto(), $toRGBA);
}

Path Operators ​

typescript
$scale(sx: number, sy?: number): (path: GlyphPath) => GlyphPath
$translate(dx: number, dy: number): (path: GlyphPath) => GlyphPath
$rotate(angle: number): (path: GlyphPath) => GlyphPath
$rotateDeg(angleDeg: number): (path: GlyphPath) => GlyphPath
$shear(shearX: number, shearY: number): (path: GlyphPath) => GlyphPath
$italic(angleDeg: number): (path: GlyphPath) => GlyphPath
$matrix(m: Matrix2D): (path: GlyphPath) => GlyphPath
$perspective(m: Matrix3x3): (path: GlyphPath) => GlyphPath
$emboldenPath(strength: number): (path: GlyphPath) => GlyphPath
$condensePath(factor: number): (path: GlyphPath) => GlyphPath
$obliquePath(slant: number): (path: GlyphPath) => GlyphPath
$strokePath(options: StrokerOptions): (path: GlyphPath) => GlyphPath
$strokeAsymmetric(options: AsymmetricStrokeOptions): (path: GlyphPath) => { outer: GlyphPath; inner: GlyphPath }
$strokeAsymmetricCombined(options: AsymmetricStrokeOptions): (path: GlyphPath) => GlyphPath
$clone(): (path: GlyphPath) => GlyphPath

Rasterization Operators ​

typescript
$rasterize(options: RasterizeOptions): (path: GlyphPath) => Bitmap
$rasterizeAuto(options?: AutoRasterOptions): (path: GlyphPath) => Bitmap
$rasterizeWithGradient(gradient: Gradient, options: RasterizeOptions): (path: GlyphPath) => Bitmap
$renderSdf(options: SdfOptions): (path: GlyphPath) => Bitmap
$renderMsdf(options: MsdfOptions): (path: GlyphPath) => Bitmap

Bitmap Operators ​

typescript
$blur(radius: number): (bitmap: Bitmap) => Bitmap
$boxBlur(radius: number): (bitmap: Bitmap) => Bitmap
$cascadeBlur(radiusX: number, radiusY?: number): (bitmap: Bitmap) => Bitmap
$adaptiveBlur(radiusX: number, radiusY?: number): (bitmap: Bitmap) => Bitmap
$fastBlur(radius: number): (bitmap: Bitmap) => Bitmap
$embolden(xStrength: number, yStrength?: number): (bitmap: Bitmap) => Bitmap
$transformBitmap2D(matrix: Matrix2D, options?: { offsetX26?: number; offsetY26?: number }): (bitmap: Bitmap) => Bitmap
$transformBitmap3D(matrix: Matrix3x3, options?: { offsetX26?: number; offsetY26?: number }): (bitmap: Bitmap) => Bitmap
$shearBitmapX(amount: number, options?: { offsetX26?: number; offsetY26?: number }): (bitmap: Bitmap) => Bitmap
$shearBitmapY(amount: number, options?: { offsetX26?: number; offsetY26?: number }): (bitmap: Bitmap) => Bitmap
$shift(dx: number, dy: number): (bitmap: Bitmap) => Bitmap
$resize(width: number, height: number): (bitmap: Bitmap) => Bitmap
$resizeBilinear(width: number, height: number): (bitmap: Bitmap) => Bitmap
$pad(left: number, top: number, right: number, bottom: number): (bitmap: Bitmap) => Bitmap
$convert(targetMode: PixelMode): (bitmap: Bitmap) => Bitmap

Rasterized Glyph Operators ​

typescript
$emboldenGlyph(xStrength: number, yStrength?: number): (glyph: RasterizedGlyph) => RasterizedGlyph
$transformGlyph2D(matrix: Matrix2D, options?: { offsetX26?: number; offsetY26?: number }): (glyph: RasterizedGlyph) => RasterizedGlyph
$transformGlyph3D(matrix: Matrix3x3, options?: { offsetX26?: number; offsetY26?: number }): (glyph: RasterizedGlyph) => RasterizedGlyph
$shearGlyphX(amount: number, options?: { offsetX26?: number; offsetY26?: number }): (glyph: RasterizedGlyph) => RasterizedGlyph
$shearGlyphY(amount: number, options?: { offsetX26?: number; offsetY26?: number }): (glyph: RasterizedGlyph) => RasterizedGlyph

Output Operators ​

typescript
$toRGBA: (bitmap: Bitmap) => Uint8Array
$toGray: (bitmap: Bitmap) => Uint8Array
$toSVG(options?: SVGOptions): (path: GlyphPath) => string
$copy: (bitmap: Bitmap) => Bitmap

Examples ​

Basic Glyph Rendering ​

typescript
import { glyph } from "text-shaper";

// Simple: glyph -> scale -> rasterize -> RGBA
const rgba = glyph(font, glyphId)
  ?.scale(2)
  .rasterizeAuto({ padding: 2 })
  .toRGBA();

// With rotation and blur
const blurred = glyph(font, glyphId)
  ?.scale(2)
  .rotateDeg(15)
  .rasterizeAuto({ padding: 10 })
  .blur(3)
  .toRGBA();

Text Effects ​

typescript
// Synthetic bold italic
const boldItalic = glyph(font, glyphId)
  ?.embolden(50)       // Path-level bold
  .italic(12)          // 12-degree italic
  .scale(2)
  .rasterize({ width: 100, height: 100 })
  .embolden(1, 1)      // Bitmap-level additional bold
  .toRGBA();

// Stroked text
const stroked = glyph(font, glyphId)
  ?.stroke({ width: 20, lineCap: "round", lineJoin: "round" })
  .scale(2)
  .rasterizeAuto()
  .toRGBA();

Shadow/Glow Effect ​

typescript
const glyphPath = glyph(font, glyphId)?.scale(2);
if (!glyphPath) throw new Error("Glyph not found");

// Create shadow
const shadow = glyphPath
  .clone()
  .translate(4, 4)
  .rasterizeAuto({ padding: 20 })
  .cascadeBlur(8, 8);

// Create main glyph
const main = glyphPath.rasterizeAuto({ padding: 20 });

// Composite shadow + main
const result = shadow.composite(main).toRGBA();

Canvas Rendering ​

typescript
const path = glyph(font, glyphId)?.scale(2);
if (path) {
  path.toCanvas(ctx, {
    offsetX: 100,
    offsetY: 200,
    fill: "black"
  });
}

Combining Multiple Glyphs ​

typescript
import { glyph, combine } from "text-shaper";

const h = glyph(font, font.glyphId("H".charCodeAt(0))!)?.translate(0, 0);
const i = glyph(font, font.glyphId("i".charCodeAt(0))!)?.translate(50, 0);

if (h && i) {
  const combined = combine(h, i)
    .scale(2)
    .rasterizeAuto()
    .toRGBA();
}

Using Pipe Style ​

typescript
import { pipe, $scale, $rotate, $rasterizeAuto, $blur, $toRGBA, getGlyphPath } from "text-shaper";

const path = getGlyphPath(font, glyphId);
if (path) {
  const rgba = pipe(
    path,
    $scale(2, 2),
    $rotate(Math.PI / 4),
    $rasterizeAuto({ padding: 5 }),
    $blur(3),
    $toRGBA
  );
}

Lazy vs Eager Transforms ​

typescript
// Lazy: transforms accumulated, applied at render
const lazy = glyph(font, glyphId)
  ?.scale(2)           // Matrix multiply
  .rotate(0.5)         // Matrix multiply
  .translate(10, 10)   // Matrix multiply
  .rasterizeAuto();    // Apply combined matrix once

// Force immediate application with .apply()
const eager = glyph(font, glyphId)
  ?.scale(2)
  .apply()             // Apply transforms now
  .embolden(50)        // Embolden the scaled path
  .rotate(0.5)         // New lazy transform
  .rasterizeAuto();

Additional Features ​

Atlas Building ​

Build texture atlases for efficient GPU rendering.

typescript
import { buildAtlas, buildAsciiAtlas, buildStringAtlas, atlasToRGBA, getGlyphUV } from "text-shaper";

// Build atlas for ASCII characters
const atlas = buildAsciiAtlas(font, { fontSize: 32, padding: 2 });

// Build atlas for specific glyphs
const customAtlas = buildAtlas(font, [glyphId1, glyphId2], { fontSize: 32 });

// Build atlas for specific text
const textAtlas = buildStringAtlas(font, "Hello World", { fontSize: 32 });

// Get UV coordinates for rendering
const uv = getGlyphUV(atlas, glyphId);
// { u0, v0, u1, v1 }

// Convert to RGBA for GPU upload
const rgba = atlasToRGBA(atlas);

MSDF Atlas Building ​

Build multi-channel signed distance field atlases for scalable GPU text.

typescript
import { buildMsdfAtlas, buildMsdfAsciiAtlas, msdfAtlasToRGBA } from "text-shaper";

// Build MSDF atlas for ASCII
const msdfAtlas = buildMsdfAsciiAtlas(font, { fontSize: 32, spread: 4, sizeMode: "em" });

// Convert to RGBA for GPU upload
const msdfRgba = msdfAtlasToRGBA(msdfAtlas);

Direct Glyph Rasterization ​

Rasterize glyphs without building a PathBuilder.

typescript
import { rasterizeGlyph, rasterizeText } from "text-shaper";

// Rasterize single glyph
const result = rasterizeGlyph(font, glyphId, 24, { sizeMode: "em" });
if (result) {
  const { bitmap, bearingX, bearingY } = result;
}

// Rasterize text string (multiple glyphs)
const textResult = rasterizeText(font, "Hello", 24, { sizeMode: "em" });

Shaped Text Rendering ​

Render shaped text to canvas or SVG.

typescript
import { shape, renderShapedText, shapedTextToSVG, glyphBufferToShapedGlyphs } from "text-shaper";

// Shape text
const buffer = new UnicodeBuffer(text);
shape(font, buffer);

// Convert to shaped glyphs
const shapedGlyphs = glyphBufferToShapedGlyphs(buffer);

// Render to canvas
renderShapedText(ctx, font, shapedGlyphs, {
  fontSize: 24,
  fill: "black"
});

// Convert to SVG
const svg = shapedTextToSVG(font, shapedGlyphs, {
  fontSize: 24,
  fill: "currentColor"
});

SDF/MSDF for GPU Rendering ​

Generate signed distance fields for scalable GPU text.

typescript
// Single glyph SDF
const sdf = glyph(font, glyphId)
  ?.toSdfAuto({ spread: 8, scale: 0.1 })
  .toGray();

// Single glyph MSDF (better quality)
const msdf = glyph(font, glyphId)
  ?.toMsdfAuto({ spread: 8, scale: 0.1 })
  .toRGBA();

Asymmetric Stroke Effects ​

Create directional borders and shadow effects.

typescript
// Horizontal drop shadow
const { outer } = glyph(font, glyphId)
  ?.strokeAsymmetric({ xBorder: 5, yBorder: 0 });
const shadow = outer?.translate(2, 2).rasterizeAuto().blur(3);

// Vertical stretch effect
const stretched = glyph(font, glyphId)
  ?.strokeAsymmetricCombined({ xBorder: 0, yBorder: 10 })
  .rasterizeAuto()
  .toRGBA();