@nutshelllabs/textkit
v6.1.2
Published
An advanced text layout framework
Readme
@nutshelllabs/textkit
An advanced text layout framework
A comprehensive text layout engine for react-pdf. Handles complex text rendering including bidirectional text, line breaking, hyphenation, justification, font substitution, and text decoration.
Acknowledges
This project is a fork of textkit by @devongovett and continued under the scope of this project since it has react-pdf specific features. Any recongnition should go to him and the original project mantainers.
Installation
yarn add @nutshelllabs/textkitUsage
import layoutEngine, {
bidi,
linebreaker,
justification,
textDecoration,
scriptItemizer,
wordHyphenation,
fontSubstitution,
fromFragments,
} from '@nutshelllabs/textkit';
// Create engines configuration
const engines = {
bidi: bidi(),
linebreaker: linebreaker({}),
justification: justification({}),
textDecoration: textDecoration(),
scriptItemizer: scriptItemizer(),
wordHyphenation: wordHyphenation(),
fontSubstitution: fontSubstitution(),
};
// Create attributed string from fragments
const attributedString = fromFragments([
{ string: 'Hello ', attributes: { fontSize: 12, font: [myFont] } },
{ string: 'World!', attributes: { fontSize: 12, font: [myFont] } },
]);
// Define container
const container = {
x: 0,
y: 0,
width: 400,
height: 600,
};
// Layout text
const layout = layoutEngine(engines);
const paragraphs = layout(attributedString, container, {});Layout Process
The layout engine processes text through the following steps:
- Split into paragraphs
- Get bidi runs and paragraph direction
- Font substitution - map to resolved font runs
- Script itemization
- Font shaping - text to glyphs
- Line breaking
- Bidi reordering
- Justification
- Get a list of rectangles by intersecting path, line, and exclusion paths
- Perform line breaking to get acceptable break points for each fragment
- Ellipsize line if necessary
- Bidi reordering
- Justification
Engines
The layout engine uses several specialized engines that can be customized:
bidi
Handles bidirectional text analysis using the Unicode Bidirectional Algorithm. Determines text direction for mixed LTR/RTL content.
import { bidi } from '@nutshelllabs/textkit';
const bidiEngine = bidi();
const result = bidiEngine(attributedString);linebreaker
Performs line breaking using the Knuth-Plass algorithm with fallback to best-fit. Handles hyphenation points and produces optimal line breaks.
import { linebreaker } from '@nutshelllabs/textkit';
const linebreakerEngine = linebreaker({
tolerance: 4,
hyphenationPenalty: 100,
});justification
Adjusts character and word spacing to achieve justified text alignment. Based on Apple's justification algorithm.
import { justification } from '@nutshelllabs/textkit';
const justificationEngine = justification({
expandCharFactor: { before: 0, after: 0 },
shrinkCharFactor: { before: 0, after: 0 },
expandWhitespaceFactor: { before: 0.5, after: 0.5 },
shrinkWhitespaceFactor: { before: 0.5, after: 0.5 },
});fontSubstitution
Automatically substitutes fonts when the primary font doesn't have glyphs for certain characters. Picks the best font from the font stack.
import { fontSubstitution } from '@nutshelllabs/textkit';
const fontSubstitutionEngine = fontSubstitution();scriptItemizer
Identifies Unicode script runs in text (Latin, Arabic, Han, etc.) to enable proper font selection and shaping.
import { scriptItemizer } from '@nutshelllabs/textkit';
const scriptItemizerEngine = scriptItemizer();wordHyphenation
Provides word hyphenation using language-specific patterns. Supports soft hyphens and custom hyphenation callbacks.
import { wordHyphenation } from '@nutshelllabs/textkit';
const wordHyphenationEngine = wordHyphenation();
const syllables = wordHyphenationEngine('hyphenation'); // ['hy', 'phen', 'a', 'tion']textDecoration
Generates decoration lines (underline, strikethrough) for styled text runs.
import { textDecoration } from '@nutshelllabs/textkit';
const textDecorationEngine = textDecoration();API Reference
layoutEngine(engines)
Creates a layout function with the specified engines.
const layout = layoutEngine(engines);
const paragraphs = layout(attributedString, container, options);fromFragments(fragments)
Creates an AttributedString from text fragments.
import { fromFragments } from '@nutshelllabs/textkit';
const attributedString = fromFragments([
{ string: 'Hello ', attributes: { fontSize: 14 } },
{ string: 'World!', attributes: { fontSize: 14, color: 'blue' } },
]);Types
AttributedString
The main data structure representing styled text:
type AttributedString = {
string: string;
runs: Run[];
syllables?: string[];
box?: Rect;
decorationLines?: DecorationLine[];
};Run
A styled segment of text:
type Run = {
start: number;
end: number;
attributes: Attributes;
glyphs?: Glyph[];
positions?: Position[];
glyphIndices?: number[];
};Attributes
Style attributes for text runs:
type Attributes = {
align?: string;
alignLastLine?: string;
attachment?: Attachment;
backgroundColor?: string;
bidiLevel?: number;
characterSpacing?: number;
color?: string;
direction?: 'rtl' | 'ltr';
features?: unknown[];
fill?: boolean;
font?: Font[];
fontSize?: number;
hangingPunctuation?: boolean;
hyphenationFactor?: number;
indent?: number;
justificationFactor?: number;
lineHeight?: number;
lineSpacing?: number;
link?: string;
margin?: number;
marginLeft?: number;
marginRight?: number;
opacity?: number;
padding?: number;
paddingTop?: number;
paragraphSpacing?: number;
scale?: number;
script?: unknown;
shrinkFactor?: number;
strike?: boolean;
strikeColor?: string;
strikeStyle?: string;
stroke?: boolean;
underline?: boolean;
underlineColor?: string;
underlineStyle?: string;
verticalAlign?: string;
wordSpacing?: number;
yOffset?: number;
};Container
The area where text will be laid out:
type Container = {
x: number;
y: number;
width: number;
height: number;
truncateMode?: 'ellipsis';
maxLines?: number;
excludeRects?: Rect[];
};Rect
A rectangle definition:
type Rect = {
x: number;
y: number;
width: number;
height: number;
};Fragment
Input format for creating attributed strings:
type Fragment = {
string: string;
attributes?: Attributes;
};LayoutOptions
Options for the layout process:
type LayoutOptions = {
hyphenationCallback?: (
word: string | null,
fallback: (word: string | null) => string[],
) => string[];
tolerance?: number;
hyphenationPenalty?: number;
expandCharFactor?: JustificationFactor;
shrinkCharFactor?: JustificationFactor;
expandWhitespaceFactor?: JustificationFactor;
shrinkWhitespaceFactor?: JustificationFactor;
};Engines
The engines configuration object:
type Engines = {
bidi: ReturnType<typeof bidi>;
linebreaker: ReturnType<typeof linebreaker>;
justification: ReturnType<typeof justification>;
fontSubstitution: ReturnType<typeof fontSubstitution>;
scriptItemizer: ReturnType<typeof scriptItemizer>;
textDecoration: ReturnType<typeof textDecoration>;
wordHyphenation?: ReturnType<typeof wordHyphenation>;
};License
MIT
