Skip to content

OpenType Features

OpenType features control typographic refinements like ligatures, kerning, small caps, and more. TextShaper provides helper functions for common features and a flexible API for custom feature tags.

Applying Features

Pass features to the shape() function:

typescript
import { Font, shape, UnicodeBuffer, feature } from "text-shaper";

const font = await Font.fromFile("font.ttf");
const buffer = new UnicodeBuffer().addStr("Hello");

const result = shape(font, buffer, {
	features: [
		feature("liga"),  // Standard ligatures
		feature("kern"),  // Kerning
	]
});

Feature Helpers

TextShaper provides helper functions for common OpenType features:

Ligatures

typescript
import {
	standardLigatures,
	discretionaryLigatures,
	historicalLigatures,
	contextualAlternates
} from "text-shaper";

const result = shape(font, buffer, {
	features: [
		standardLigatures(),        // liga: fi, fl
		discretionaryLigatures(),   // dlig: ct, st
		historicalLigatures(),      // hlig
		contextualAlternates(),     // calt
	]
});

Small Caps

typescript
import {
	smallCaps,
	capsToSmallCaps,
	petiteCaps,
	allSmallCaps
} from "text-shaper";

const result = shape(font, buffer, {
	features: [
		smallCaps(),          // smcp: lowercase to small caps
		capsToSmallCaps(),    // c2sc: uppercase to small caps
		petiteCaps(),         // pcap: petite caps
		allSmallCaps(),       // smcp + c2sc
	]
});

Number Styles

typescript
import {
	oldstyleFigures,
	liningFigures,
	proportionalFigures,
	tabularFigures
} from "text-shaper";

const result = shape(font, buffer, {
	features: [
		oldstyleFigures(),         // onum: 0123
		liningFigures(),           // lnum: 0123
		proportionalFigures(),     // pnum
		tabularFigures(),          // tnum
	]
});

Stylistic Alternates

typescript
import {
	stylisticAlternates,
	stylisticSet,
	characterVariant,
	swash
} from "text-shaper";

const result = shape(font, buffer, {
	features: [
		stylisticAlternates(),     // salt
		stylisticSet(1),           // ss01
		stylisticSet(2),           // ss02
		characterVariant(1),       // cv01
		swash(),                   // swsh
	]
});

Positioning

typescript
import {
	superscript,
	subscript,
	scientificInferiors
} from "text-shaper";

const result = shape(font, buffer, {
	features: [
		superscript(),             // sups: H²O
		subscript(),               // subs: H₂O
		scientificInferiors(),     // sinf
	]
});

Fractions

typescript
import { fractions, ordinals, slashedZero } from "text-shaper";

const result = shape(font, buffer, {
	features: [
		fractions(),               // frac: 1/2 → ½
		ordinals(),                // ordn: 1st, 2nd
		slashedZero(),             // zero: 0 with slash
	]
});

Kerning

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

const result = shape(font, buffer, {
	features: [
		kerning(),                 // kern: adjust spacing
	]
});

Case-Sensitive Forms

typescript
import { caseSensitiveForms, capitalSpacing } from "text-shaper";

const result = shape(font, buffer, {
	features: [
		caseSensitiveForms(),      // case: adjust punctuation for caps
		capitalSpacing(),          // cpsp: add spacing in all caps
	]
});

CJK Features

Japanese Forms

typescript
import {
	jis78Forms,
	jis83Forms,
	jis90Forms,
	jis2004Forms
} from "text-shaper";

const result = shape(font, buffer, {
	features: [
		jis78Forms(),              // jp78
		jis83Forms(),              // jp83
		jis90Forms(),              // jp90
		jis2004Forms(),            // jp04
	]
});

Chinese Forms

typescript
import { simplifiedForms, traditionalForms } from "text-shaper";

const result = shape(font, buffer, {
	features: [
		simplifiedForms(),         // smpl
		traditionalForms(),        // trad
	]
});

Width Variants

typescript
import {
	halfWidthForms,
	fullWidthForms,
	proportionalWidthForms,
	quarterWidthForms
} from "text-shaper";

const result = shape(font, buffer, {
	features: [
		halfWidthForms(),          // hwid
		fullWidthForms(),          // fwid
		proportionalWidthForms(),  // pwid
		quarterWidthForms(),       // qwid
	]
});

Vertical Layout

typescript
import {
	verticalForms,
	verticalAlternatesRotation,
	verticalKanaAlternates,
	verticalLayoutFeatures
} from "text-shaper";

const result = shape(font, buffer, {
	features: [
		verticalForms(),                  // vert
		verticalAlternatesRotation(),     // vrt2
		verticalKanaAlternates(),         // vkna
		verticalLayoutFeatures(),         // all vertical features
	]
});

Ruby

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

const result = shape(font, buffer, {
	features: [
		ruby(),                    // ruby: ruby characters
	]
});

Custom Features

Use the feature() function for any OpenType feature tag:

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

const result = shape(font, buffer, {
	features: [
		feature("liga"),           // Enable liga
		feature("dlig", true),     // Explicitly enable dlig
		feature("kern", false),    // Disable kern
	]
});

Combining Features

Combine multiple features using the combineFeatures() helper or by passing an array:

typescript
import {
	standardLigatures,
	kerning,
	oldstyleFigures,
	smallCaps,
	combineFeatures
} from "text-shaper";

// Using array
const result = shape(font, buffer, {
	features: [
		standardLigatures(),
		kerning(),
		oldstyleFigures(),
		smallCaps(),
	]
});

// Using combineFeatures
const myFeatures = combineFeatures([
	standardLigatures(),
	kerning(),
	oldstyleFigures(),
]);

const result2 = shape(font, buffer, {
	features: myFeatures
});

Feature Sets

Create reusable feature sets for consistent typography:

typescript
import {
	standardLigatures,
	kerning,
	contextualAlternates
} from "text-shaper";

// Default text features
const defaultFeatures = [
	standardLigatures(),
	kerning(),
	contextualAlternates(),
];

// Heading features
const headingFeatures = [
	kerning(),
	capitalSpacing(),
	caseSensitiveForms(),
];

// Number features
const numberFeatures = [
	tabularFigures(),
	liningFigures(),
	slashedZero(),
];

// Apply to different text
const bodyResult = shape(font, bodyBuffer, { features: defaultFeatures });
const headingResult = shape(font, headingBuffer, { features: headingFeatures });
const numberResult = shape(font, numberBuffer, { features: numberFeatures });

Feature Values

Some features accept numeric values:

typescript
import { stylisticSet, characterVariant } from "text-shaper";

const result = shape(font, buffer, {
	features: [
		stylisticSet(1),       // ss01
		stylisticSet(5),       // ss05
		characterVariant(3),   // cv03
	]
});

Checking Available Features

Check which features a font supports:

typescript
const font = await Font.fromFile("font.ttf");

// Check if font has GSUB or GPOS tables
const hasGSUB = font.hasTable("GSUB");
const hasGPOS = font.hasTable("GPOS");

console.log(`Font supports substitution: ${hasGSUB}`);
console.log(`Font supports positioning: ${hasGPOS}`);

Complete Example

typescript
import {
	Font,
	shape,
	UnicodeBuffer,
	standardLigatures,
	discretionaryLigatures,
	kerning,
	oldstyleFigures,
	smallCaps
} from "text-shaper";

const font = await Font.fromFile("font.ttf");

const buffer = new UnicodeBuffer()
	.addStr("The Office: 1234")
	.setScript("Latn")
	.setLanguage("en");

const result = shape(font, buffer, {
	features: [
		standardLigatures(),      // fi, fl ligatures
		discretionaryLigatures(), // ct, st ligatures
		kerning(),                // spacing adjustments
		oldstyleFigures(),        // old-style numbers
		smallCaps(),              // small caps
	]
});

// Render glyphs
for (let i = 0; i < result.length; i++) {
	const info = result.infos[i];
	const pos = result.positions[i];

	console.log(`Glyph ${info.glyphId}: advance ${pos.xAdvance}`);
}