@effing/canvas 0.23.2 → 0.24.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.
package/README.md CHANGED
@@ -93,6 +93,23 @@ Emoji characters are automatically rendered as images from CDNs. Supported style
93
93
 
94
94
  Pass `emoji: "none"` to disable emoji image rendering.
95
95
 
96
+ ## Fit Text
97
+
98
+ Find the largest font size that keeps text within a bounding box, using binary search over the built-in text layout engine:
99
+
100
+ ```typescript
101
+ import { findLargestUsableFontSize } from "@effing/canvas";
102
+
103
+ const fontSize = findLargestUsableFontSize({
104
+ text: "Hello World",
105
+ font,
106
+ maxWidth: 500,
107
+ maxHeight: 100,
108
+ });
109
+ ```
110
+
111
+ Supports optional `lineHeight` (`"normal"` or a numeric multiplier), `minFontSize` (default 1), and `maxFontSize` (default 1000).
112
+
96
113
  ## Lottie Animations
97
114
 
98
115
  Render individual frames of Lottie animations to a canvas:
@@ -265,6 +282,30 @@ function renderLottieFrame(
265
282
  ): void;
266
283
  ```
267
284
 
285
+ ### `findLargestUsableFontSize(options)`
286
+
287
+ Find the largest integer font size that keeps text within the given bounds. Uses binary search with the built-in text layout engine — no external dependencies needed.
288
+
289
+ ```typescript
290
+ function findLargestUsableFontSize(
291
+ options: FindLargestUsableFontSizeOptions,
292
+ ): number;
293
+ ```
294
+
295
+ ```typescript
296
+ import { findLargestUsableFontSize } from "@effing/canvas";
297
+
298
+ const fontSize = findLargestUsableFontSize({
299
+ text: "Hello World",
300
+ font,
301
+ maxWidth: 500,
302
+ maxHeight: 100,
303
+ lineHeight: "normal", // optional (default: "normal")
304
+ minFontSize: 1, // optional (default: 1)
305
+ maxFontSize: 200, // optional (default: 1000)
306
+ });
307
+ ```
308
+
268
309
  ### Font Helpers
269
310
 
270
311
  - `registerFont(font)` — Register a font from a `FontData` buffer (idempotent)
package/dist/index.d.ts CHANGED
@@ -133,6 +133,33 @@ declare function registerFontFromPath(path: string, nameAlias?: string): void;
133
133
  */
134
134
  declare function registeredFamilies(): string[];
135
135
 
136
+ /**
137
+ * Options for {@link findLargestUsableFontSize}.
138
+ */
139
+ type FindLargestUsableFontSizeOptions = {
140
+ /** The text to fit */
141
+ text: string;
142
+ /** Font data to use for measurement */
143
+ font: FontData;
144
+ /** Maximum width in pixels */
145
+ maxWidth: number;
146
+ /** Maximum height in pixels */
147
+ maxHeight: number;
148
+ /** Line height — `"normal"` uses font metrics, numeric values are CSS multipliers */
149
+ lineHeight?: number | "normal";
150
+ /** Minimum font size to consider (default: 1) */
151
+ minFontSize?: number;
152
+ /** Maximum font size to consider (default: 1000) */
153
+ maxFontSize?: number;
154
+ };
155
+ /**
156
+ * Find the largest integer font size that keeps text within the given bounds.
157
+ *
158
+ * Uses binary search over integer font sizes, measuring with {@link layoutText}
159
+ * at each step. Returns `minFontSize` if even the smallest size overflows.
160
+ */
161
+ declare function findLargestUsableFontSize(options: FindLargestUsableFontSizeOptions): number;
162
+
136
163
  declare function createCanvas(width: number, height: number): _napi_rs_canvas.Canvas;
137
164
 
138
- export { type EmojiStyle, type FontData, type RenderReactElementOptions, createCanvas, loadLottie, registerFont, registerFontFromPath, registeredFamilies, renderLottieFrame, renderReactElement };
165
+ export { type EmojiStyle, type FindLargestUsableFontSizeOptions, type FontData, type RenderReactElementOptions, createCanvas, findLargestUsableFontSize, loadLottie, registerFont, registerFontFromPath, registeredFamilies, renderLottieFrame, renderReactElement };
package/dist/index.js CHANGED
@@ -2564,7 +2564,7 @@ function drawStrokeWithLetterSpacing(ctx, text, x, y, letterSpacing, strokeWidth
2564
2564
  }
2565
2565
  function drawTextShadow(ctx, text, x, y, shadow) {
2566
2566
  const parts = shadow.match(
2567
- /(-?\d+(?:\.\d+)?)\s*px?\s+(-?\d+(?:\.\d+)?)\s*px?\s+(-?\d+(?:\.\d+)?)\s*px?\s+(.*)/
2567
+ /(-?\d+(?:\.\d+)?)\s*(?:px)?\s+(-?\d+(?:\.\d+)?)\s*(?:px)?\s+(-?\d+(?:\.\d+)?)\s*(?:px)?\s+(.*)/
2568
2568
  );
2569
2569
  if (!parts) return;
2570
2570
  ctx.save();
@@ -3660,6 +3660,41 @@ async function renderReactElement(ctx, element, options) {
3660
3660
  );
3661
3661
  }
3662
3662
 
3663
+ // src/fit-text.ts
3664
+ function findLargestUsableFontSize(options) {
3665
+ const {
3666
+ text,
3667
+ font,
3668
+ maxWidth,
3669
+ maxHeight,
3670
+ lineHeight = "normal",
3671
+ minFontSize = 1,
3672
+ maxFontSize = 1e3
3673
+ } = options;
3674
+ registerFont(font);
3675
+ let lo = minFontSize;
3676
+ let hi = maxFontSize;
3677
+ let best = minFontSize;
3678
+ while (lo <= hi) {
3679
+ const mid = Math.floor((lo + hi) / 2);
3680
+ const style = {
3681
+ fontSize: mid,
3682
+ fontFamily: font.name,
3683
+ fontWeight: font.weight,
3684
+ fontStyle: font.style,
3685
+ lineHeight: lineHeight === "normal" ? void 0 : lineHeight
3686
+ };
3687
+ const result = layoutText(text, style, maxWidth);
3688
+ if (result.width <= maxWidth && result.height <= maxHeight) {
3689
+ best = mid;
3690
+ lo = mid + 1;
3691
+ } else {
3692
+ hi = mid - 1;
3693
+ }
3694
+ }
3695
+ return best;
3696
+ }
3697
+
3663
3698
  // src/index.ts
3664
3699
  function createCanvas3(width, height) {
3665
3700
  const canvas = _createCanvas(width, height);
@@ -3673,6 +3708,7 @@ export {
3673
3708
  Image,
3674
3709
  LottieAnimation,
3675
3710
  createCanvas3 as createCanvas,
3711
+ findLargestUsableFontSize,
3676
3712
  loadImage3 as loadImage,
3677
3713
  loadLottie,
3678
3714
  registerFont,