text-shaper 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Exact bounding box calculation for paths with bezier curves
3
+ *
4
+ * Bezier curves can extend beyond their control points, so we need to find
5
+ * the actual extrema by solving for where the derivative equals zero.
6
+ */
7
+ import type { GlyphPath } from "../render/path.ts";
8
+ /**
9
+ * Bounding box in 2D space
10
+ */
11
+ export interface BBox {
12
+ xMin: number;
13
+ yMin: number;
14
+ xMax: number;
15
+ yMax: number;
16
+ }
17
+ /**
18
+ * Calculate exact bounding box for a path
19
+ *
20
+ * For line segments: min/max of endpoints
21
+ * For quadratic bezier: find t where derivative = 0, evaluate curve at those t values
22
+ * For cubic bezier: solve quadratic for extrema, evaluate curve at those t values
23
+ *
24
+ * @param path - Path to calculate bounds for
25
+ * @returns Bounding box or null for empty path
26
+ */
27
+ export declare function getExactBounds(path: GlyphPath): BBox | null;
28
+ /**
29
+ * Find t values where a quadratic bezier has extrema
30
+ *
31
+ * For quadratic bezier B(t) = (1-t)²p0 + 2(1-t)t*p1 + t²p2
32
+ * Derivative: B'(t) = 2(1-t)(p1-p0) + 2t(p2-p1)
33
+ * = 2(p1-p0) + 2t(p0-2p1+p2)
34
+ * = 2[(p1-p0) + t(p0-2p1+p2)]
35
+ *
36
+ * Set B'(t) = 0:
37
+ * (p1-p0) + t(p0-2p1+p2) = 0
38
+ * t = -(p1-p0)/(p0-2p1+p2)
39
+ * = (p0-p1)/(p0-2p1+p2)
40
+ *
41
+ * @param p0 - Start point
42
+ * @param p1 - Control point
43
+ * @param p2 - End point
44
+ * @returns Array of t values in [0,1] where extrema occur
45
+ */
46
+ export declare function getQuadraticExtrema(p0: number, p1: number, p2: number): number[];
47
+ /**
48
+ * Find t values where a cubic bezier has extrema
49
+ *
50
+ * For cubic bezier B(t) = (1-t)³p0 + 3(1-t)²t*p1 + 3(1-t)t²p2 + t³p3
51
+ * Derivative: B'(t) = 3(1-t)²(p1-p0) + 6(1-t)t(p2-p1) + 3t²(p3-p2)
52
+ *
53
+ * Simplified: B'(t) = at² + bt + c where:
54
+ * a = 3(p3 - 3p2 + 3p1 - p0)
55
+ * b = 6(p2 - 2p1 + p0)
56
+ * c = 3(p1 - p0)
57
+ *
58
+ * Solve using quadratic formula: t = (-b ± √(b²-4ac)) / 2a
59
+ *
60
+ * @param p0 - Start point
61
+ * @param p1 - First control point
62
+ * @param p2 - Second control point
63
+ * @param p3 - End point
64
+ * @returns Array of t values in [0,1] where extrema occur
65
+ */
66
+ export declare function getCubicExtrema(p0: number, p1: number, p2: number, p3: number): number[];
67
+ /**
68
+ * Evaluate quadratic bezier at parameter t
69
+ *
70
+ * B(t) = (1-t)²p0 + 2(1-t)t*p1 + t²p2
71
+ *
72
+ * @param p0 - Start point
73
+ * @param p1 - Control point
74
+ * @param p2 - End point
75
+ * @param t - Parameter in [0,1]
76
+ * @returns Value at t
77
+ */
78
+ export declare function evaluateQuadratic(p0: number, p1: number, p2: number, t: number): number;
79
+ /**
80
+ * Evaluate cubic bezier at parameter t
81
+ *
82
+ * B(t) = (1-t)³p0 + 3(1-t)²t*p1 + 3(1-t)t²p2 + t³p3
83
+ *
84
+ * @param p0 - Start point
85
+ * @param p1 - First control point
86
+ * @param p2 - Second control point
87
+ * @param p3 - End point
88
+ * @param t - Parameter in [0,1]
89
+ * @returns Value at t
90
+ */
91
+ export declare function evaluateCubic(p0: number, p1: number, p2: number, p3: number, t: number): number;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Bitmap manipulation utilities
3
+ */
4
+ import { type Bitmap, PixelMode } from "./types.ts";
5
+ /**
6
+ * Embolden a bitmap by dilating pixel values
7
+ * Makes text bolder by spreading coverage in x and y directions
8
+ */
9
+ export declare function emboldenBitmap(bitmap: Bitmap, xStrength: number, yStrength: number): Bitmap;
10
+ /**
11
+ * Convert bitmap between pixel modes
12
+ */
13
+ export declare function convertBitmap(bitmap: Bitmap, targetMode: PixelMode): Bitmap;
14
+ /**
15
+ * Alpha blend src bitmap onto dst bitmap at position (x, y)
16
+ */
17
+ export declare function blendBitmap(dst: Bitmap, src: Bitmap, x: number, y: number, opacity: number): void;
18
+ /**
19
+ * Create a deep copy of a bitmap
20
+ */
21
+ export declare function copyBitmap(bitmap: Bitmap): Bitmap;
22
+ /**
23
+ * Resize bitmap using nearest-neighbor interpolation
24
+ */
25
+ export declare function resizeBitmap(bitmap: Bitmap, newWidth: number, newHeight: number): Bitmap;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Bitmap blur filters - Gaussian and Box blur
3
+ */
4
+ import { type Bitmap } from "./types.ts";
5
+ /**
6
+ * Generate 1D Gaussian kernel
7
+ * Uses Gaussian function: exp(-x²/(2σ²))
8
+ */
9
+ export declare function createGaussianKernel(radius: number): Float32Array;
10
+ /**
11
+ * Gaussian blur implementation using separable 2-pass algorithm
12
+ * Modifies bitmap in-place and returns it
13
+ */
14
+ export declare function gaussianBlur(bitmap: Bitmap, radius: number): Bitmap;
15
+ /**
16
+ * Box blur using running sum for O(1) per pixel
17
+ * Modifies bitmap in-place and returns it
18
+ */
19
+ export declare function boxBlur(bitmap: Bitmap, radius: number): Bitmap;
20
+ /**
21
+ * Apply blur filter to a bitmap in-place
22
+ * @param bitmap - Bitmap to blur (modified in-place)
23
+ * @param radius - Blur radius in pixels (can be fractional)
24
+ * @param type - Blur type: 'gaussian' (default) or 'box'
25
+ * @returns The modified bitmap
26
+ */
27
+ export declare function blurBitmap(bitmap: Bitmap, radius: number, type?: "gaussian" | "box"): Bitmap;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Gradient fill rendering for paths
3
+ */
4
+ import type { GlyphPath } from "../render/path.ts";
5
+ import { type Bitmap, type RasterizeOptions } from "./types.ts";
6
+ /**
7
+ * Color stop in a gradient (RGBA, 0-255 each)
8
+ */
9
+ export interface ColorStop {
10
+ /** Position along gradient, 0.0 to 1.0 */
11
+ offset: number;
12
+ /** RGBA color values, 0-255 each */
13
+ color: [number, number, number, number];
14
+ }
15
+ /**
16
+ * Linear gradient from point (x0,y0) to (x1,y1)
17
+ */
18
+ export interface LinearGradient {
19
+ type: "linear";
20
+ /** Start X coordinate */
21
+ x0: number;
22
+ /** Start Y coordinate */
23
+ y0: number;
24
+ /** End X coordinate */
25
+ x1: number;
26
+ /** End Y coordinate */
27
+ y1: number;
28
+ /** Color stops */
29
+ stops: ColorStop[];
30
+ }
31
+ /**
32
+ * Radial gradient from center point with radius
33
+ */
34
+ export interface RadialGradient {
35
+ type: "radial";
36
+ /** Center X coordinate */
37
+ cx: number;
38
+ /** Center Y coordinate */
39
+ cy: number;
40
+ /** Gradient radius */
41
+ radius: number;
42
+ /** Color stops */
43
+ stops: ColorStop[];
44
+ }
45
+ /**
46
+ * Gradient type union
47
+ */
48
+ export type Gradient = LinearGradient | RadialGradient;
49
+ /**
50
+ * Get color at position in gradient
51
+ */
52
+ export declare function interpolateGradient(gradient: Gradient, x: number, y: number): [number, number, number, number];
53
+ /**
54
+ * Create a bitmap filled with gradient (no path)
55
+ */
56
+ export declare function createGradientBitmap(width: number, height: number, gradient: Gradient): Bitmap;
57
+ /**
58
+ * Rasterize path with gradient fill
59
+ * First rasterizes path to get coverage mask, then fills with gradient
60
+ */
61
+ export declare function rasterizePathWithGradient(path: GlyphPath, gradient: Gradient, options: RasterizeOptions): Bitmap;
@@ -33,4 +33,11 @@ export declare function bitmapToRGBA(bitmap: Bitmap): Uint8Array;
33
33
  * Export bitmap to grayscale array
34
34
  */
35
35
  export declare function bitmapToGray(bitmap: Bitmap): Uint8Array;
36
+ export { type BBox, evaluateCubic, evaluateQuadratic, getCubicExtrema, getExactBounds, getQuadraticExtrema, } from "./bbox.ts";
37
+ export { blendBitmap, convertBitmap, copyBitmap, emboldenBitmap, resizeBitmap, } from "./bitmap-utils.ts";
38
+ export { blurBitmap, boxBlur, createGaussianKernel, gaussianBlur, } from "./blur.ts";
39
+ export { type ColorStop, createGradientBitmap, type Gradient, interpolateGradient, type LinearGradient, type RadialGradient, rasterizePathWithGradient, } from "./gradient.ts";
40
+ export { renderSdf, type SdfOptions } from "./sdf.ts";
41
+ export { type LineCap, type LineJoin, type StrokerOptions, strokePath, } from "./stroker.ts";
42
+ export { condensePath, emboldenPath, obliquePath, transformPath, } from "./synth.ts";
36
43
  export { type Bitmap, clearBitmap, createBitmap, FillRule, PixelMode, type RasterizedGlyph, type RasterizeOptions, type Span, } from "./types.ts";
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Signed Distance Field (SDF) rasterizer
3
+ *
4
+ * For each pixel, compute the shortest distance to the outline.
5
+ * Positive values are inside the outline, negative are outside.
6
+ * This allows GPU text rendering at any scale.
7
+ *
8
+ * Algorithm:
9
+ * 1. For each pixel center, find the minimum distance to all outline edges
10
+ * 2. Determine sign based on whether point is inside or outside the outline
11
+ * 3. Normalize and encode to 0-255 (128 = on the edge)
12
+ */
13
+ import type { GlyphPath } from "../render/path.ts";
14
+ import { type Bitmap } from "./types.ts";
15
+ /**
16
+ * Options for SDF rendering
17
+ */
18
+ export interface SdfOptions {
19
+ /** Width in pixels */
20
+ width: number;
21
+ /** Height in pixels */
22
+ height: number;
23
+ /** Scale factor (font units to pixels) */
24
+ scale: number;
25
+ /** X offset in pixels */
26
+ offsetX?: number;
27
+ /** Y offset in pixels */
28
+ offsetY?: number;
29
+ /** Flip Y axis (font coords are Y-up, bitmap is Y-down) */
30
+ flipY?: boolean;
31
+ /** Spread/radius - how far the distance field extends in pixels (default: 8) */
32
+ spread?: number;
33
+ }
34
+ /**
35
+ * Render a glyph path as a signed distance field
36
+ */
37
+ export declare function renderSdf(path: GlyphPath, options: SdfOptions): Bitmap;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Path Stroker
3
+ *
4
+ * Converts a path outline into a stroked outline that can be filled.
5
+ * Based on FreeType's ftstroke.c algorithm.
6
+ *
7
+ * The stroker generates two borders (inside and outside) by offsetting
8
+ * the original path by half the stroke width in both directions.
9
+ */
10
+ import type { GlyphPath } from "../render/path.ts";
11
+ /** Line cap styles */
12
+ export type LineCap = "butt" | "round" | "square";
13
+ /** Line join styles */
14
+ export type LineJoin = "miter" | "round" | "bevel";
15
+ /** Stroker options */
16
+ export interface StrokerOptions {
17
+ /** Stroke width in font units */
18
+ width: number;
19
+ /** Line cap style (default: "butt") */
20
+ lineCap?: LineCap;
21
+ /** Line join style (default: "miter") */
22
+ lineJoin?: LineJoin;
23
+ /** Miter limit for miter joins (default: 4) */
24
+ miterLimit?: number;
25
+ }
26
+ /**
27
+ * Stroke a glyph path, producing a new path that represents the stroked outline
28
+ */
29
+ export declare function strokePath(path: GlyphPath, options: StrokerOptions): GlyphPath;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Synthetic Font Effects
3
+ *
4
+ * Provides transformations for creating synthetic bold, italic, and other effects
5
+ * on glyph outlines. These are useful for fonts that don't have native bold/italic
6
+ * variants.
7
+ */
8
+ import type { GlyphPath } from "../render/path.ts";
9
+ /**
10
+ * Apply oblique (slant/italic) transformation to a path
11
+ *
12
+ * @param path - The glyph path to transform
13
+ * @param slant - Tangent of the slant angle (0.2 = ~12 degrees, typical italic)
14
+ * @returns New path with slant applied
15
+ *
16
+ * Transform: x' = x + y * slant, y' = y
17
+ */
18
+ export declare function obliquePath(path: GlyphPath, slant: number): GlyphPath;
19
+ /**
20
+ * Apply general 2D affine transformation to a path
21
+ *
22
+ * @param path - The glyph path to transform
23
+ * @param matrix - 2D transformation matrix [a, b, c, d, e, f]
24
+ * @returns New path with transformation applied
25
+ *
26
+ * Transform: x' = a*x + c*y + e, y' = b*x + d*y + f
27
+ */
28
+ export declare function transformPath(path: GlyphPath, matrix: [number, number, number, number, number, number]): GlyphPath;
29
+ /**
30
+ * Apply horizontal scaling (condensing/expanding) to a path
31
+ *
32
+ * @param path - The glyph path to transform
33
+ * @param factor - Horizontal scale factor (< 1 = narrower, > 1 = wider)
34
+ * @returns New path with horizontal scaling applied
35
+ *
36
+ * Transform: x' = x * factor, y' = y
37
+ */
38
+ export declare function condensePath(path: GlyphPath, factor: number): GlyphPath;
39
+ /**
40
+ * Embolden (make bolder) a path by offsetting the outline
41
+ *
42
+ * This implementation uses a simplified approach that offsets each contour
43
+ * outward by moving points along the normal direction. For production use,
44
+ * a proper stroking algorithm would be more accurate.
45
+ *
46
+ * @param path - The glyph path to embolden
47
+ * @param strength - Offset strength in font units (positive = bolder, negative = thinner)
48
+ * @returns New path with emboldening applied
49
+ */
50
+ export declare function emboldenPath(path: GlyphPath, strength: number): GlyphPath;
@@ -13,7 +13,9 @@ export declare enum PixelMode {
13
13
  /** 24-bit LCD subpixel RGB, 3 bytes per pixel */
14
14
  LCD = 2,
15
15
  /** 24-bit LCD subpixel vertical RGB */
16
- LCD_V = 3
16
+ LCD_V = 3,
17
+ /** 32-bit RGBA, 4 bytes per pixel */
18
+ RGBA = 4
17
19
  }
18
20
  /**
19
21
  * Fill rule for outline rendering
@@ -73,6 +73,21 @@ export declare function pathToSVG(path: GlyphPath, options?: {
73
73
  flipY?: boolean;
74
74
  scale?: number;
75
75
  }): string;
76
+ /**
77
+ * Render options for stroke/fill
78
+ */
79
+ export interface RenderOptions {
80
+ flipY?: boolean;
81
+ scale?: number;
82
+ offsetX?: number;
83
+ offsetY?: number;
84
+ fill?: string;
85
+ stroke?: string;
86
+ strokeWidth?: number;
87
+ lineCap?: CanvasLineCap;
88
+ lineJoin?: CanvasLineJoin;
89
+ miterLimit?: number;
90
+ }
76
91
  /**
77
92
  * Render path commands to a Canvas 2D context
78
93
  */
@@ -88,6 +103,8 @@ export declare function pathToCanvas(ctx: CanvasRenderingContext2D | Path2D, pat
88
103
  export declare function glyphToSVG(font: Font, glyphId: GlyphId, options?: {
89
104
  fontSize?: number;
90
105
  fill?: string;
106
+ stroke?: string;
107
+ strokeWidth?: number;
91
108
  }): string | null;
92
109
  /**
93
110
  * Render shaped text to Canvas
@@ -104,6 +121,10 @@ export declare function renderShapedText(ctx: CanvasRenderingContext2D, font: Fo
104
121
  x?: number;
105
122
  y?: number;
106
123
  fill?: string;
124
+ stroke?: string;
125
+ strokeWidth?: number;
126
+ lineCap?: CanvasLineCap;
127
+ lineJoin?: CanvasLineJoin;
107
128
  }): void;
108
129
  /**
109
130
  * Generate SVG for shaped text
@@ -111,6 +132,10 @@ export declare function renderShapedText(ctx: CanvasRenderingContext2D, font: Fo
111
132
  export declare function shapedTextToSVG(font: Font, glyphs: ShapedGlyph[], options?: {
112
133
  fontSize?: number;
113
134
  fill?: string;
135
+ stroke?: string;
136
+ strokeWidth?: number;
137
+ lineCap?: "butt" | "round" | "square";
138
+ lineJoin?: "miter" | "round" | "bevel";
114
139
  }): string;
115
140
  /**
116
141
  * Convert GlyphBuffer output to ShapedGlyph array
@@ -128,6 +153,10 @@ export declare function renderShapedTextWithVariation(ctx: CanvasRenderingContex
128
153
  x?: number;
129
154
  y?: number;
130
155
  fill?: string;
156
+ stroke?: string;
157
+ strokeWidth?: number;
158
+ lineCap?: CanvasLineCap;
159
+ lineJoin?: CanvasLineJoin;
131
160
  }): void;
132
161
  /**
133
162
  * Generate SVG for shaped text with variable font support
@@ -135,6 +164,10 @@ export declare function renderShapedTextWithVariation(ctx: CanvasRenderingContex
135
164
  export declare function shapedTextToSVGWithVariation(font: Font, glyphs: ShapedGlyph[], axisCoords: number[], options?: {
136
165
  fontSize?: number;
137
166
  fill?: string;
167
+ stroke?: string;
168
+ strokeWidth?: number;
169
+ lineCap?: "butt" | "round" | "square";
170
+ lineJoin?: "miter" | "round" | "bevel";
138
171
  }): string;
139
172
  /**
140
173
  * Calculate the total advance width of shaped text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "text-shaper",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "devDependencies": {