@altano/satori-fit-text 0.1.2 → 0.1.4

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
@@ -21,7 +21,7 @@ pnpm add @altano/satori-fit-text
21
21
  import { findLargestUsableFontSize, type Font } from "@altano/satori-fit-text";
22
22
 
23
23
  async function getInter(): Promise<Font> {
24
- const interSemiBoldBuffer = await fetch(`https://files.terriblefish.com/fonts/Inter/v4/extras/otf/Inter-SemiBold.otf`).then((res) => res.arrayBuffer());
24
+ const interSemiBoldBuffer = await fetch(`https://cdn.jsdelivr.net/npm/@fontsource/inter@5.0.17/files/inter-latin-600-normal.woff`).then((res) => res.arrayBuffer());
25
25
 
26
26
  const font = {
27
27
  name: "Inter",
@@ -1,4 +1,4 @@
1
- import { TextMeasurer, type Dimensions } from "./TextMeasurer";
1
+ import { TextMeasurer, type Dimensions } from "./TextMeasurer.js";
2
2
  export default class BrowserTextMeasurer extends TextMeasurer {
3
3
  #private;
4
4
  getDimensions(fontSize: number): Promise<Dimensions>;
@@ -1,11 +1,7 @@
1
- import {
2
- TextMeasurer,
3
- getWidthAdjustedForInlineBleed,
4
- log_default
5
- } from "./chunk-JIONCYY3.js";
6
-
7
- // src/TextMeasurer/BrowserTextMeasurer.ts
8
- var BrowserTextMeasurer = class extends TextMeasurer {
1
+ import log from "../log.js";
2
+ import { TextMeasurer } from "./TextMeasurer.js";
3
+ import getWidthAdjustedForInlineBleed from "./getWidthAdjustedForInlineBleed.js";
4
+ class BrowserTextMeasurer extends TextMeasurer {
9
5
  #getSvgElement(svgText) {
10
6
  const div = document.createElement("div");
11
7
  div.style.position = "fixed";
@@ -23,7 +19,7 @@ var BrowserTextMeasurer = class extends TextMeasurer {
23
19
  document.body.appendChild(svg);
24
20
  const { width, height } = svg.getBBox();
25
21
  const widthAdjusted = getWidthAdjustedForInlineBleed(svg, width);
26
- log_default({
22
+ log({
27
23
  fontSize,
28
24
  width,
29
25
  widthAdjusted,
@@ -36,7 +32,7 @@ var BrowserTextMeasurer = class extends TextMeasurer {
36
32
  svg.parentElement?.removeChild?.(svg);
37
33
  }
38
34
  }
39
- };
35
+ }
40
36
  export {
41
37
  BrowserTextMeasurer as default
42
38
  };
@@ -1,5 +1,5 @@
1
- import type { Font } from "../types";
2
- import { TextMeasurer, type Dimensions } from "./TextMeasurer";
1
+ import type { Font } from "../types.js";
2
+ import { TextMeasurer, type Dimensions } from "./TextMeasurer.js";
3
3
  export default class HeadlessTextMeasurer extends TextMeasurer {
4
4
  #private;
5
5
  constructor(text: string, font: Font, maxWidth: number, maxHeight: number, lineHeight: number | "normal");
@@ -1,13 +1,9 @@
1
- import {
2
- TextMeasurer,
3
- getWidthAdjustedForInlineBleed,
4
- log_default
5
- } from "./chunk-JIONCYY3.js";
6
-
7
- // src/TextMeasurer/HeadlessTextMeasurer.ts
8
1
  import { SVG, registerWindow } from "@svgdotjs/svg.js";
9
2
  import { createSVGWindow } from "svgdom";
10
- var HeadlessTextMeasurer = class extends TextMeasurer {
3
+ import log from "../log.js";
4
+ import { TextMeasurer } from "./TextMeasurer.js";
5
+ import getWidthAdjustedForInlineBleed from "./getWidthAdjustedForInlineBleed.js";
6
+ class HeadlessTextMeasurer extends TextMeasurer {
11
7
  #canvas;
12
8
  constructor(text, font, maxWidth, maxHeight, lineHeight) {
13
9
  super(text, font, maxWidth, maxHeight, lineHeight);
@@ -23,7 +19,7 @@ var HeadlessTextMeasurer = class extends TextMeasurer {
23
19
  this.#canvas.node,
24
20
  width
25
21
  );
26
- log_default({
22
+ log({
27
23
  fontSize,
28
24
  width,
29
25
  widthAdjusted,
@@ -38,7 +34,7 @@ var HeadlessTextMeasurer = class extends TextMeasurer {
38
34
  this.#canvas.clear();
39
35
  this.#canvas.svg(svgText);
40
36
  }
41
- };
37
+ }
42
38
  export {
43
39
  HeadlessTextMeasurer as default
44
40
  };
@@ -1,4 +1,4 @@
1
- import type { Font } from "../types";
1
+ import type { Font } from "../types.js";
2
2
  export type Dimensions = Pick<DOMRect, "width" | "height">;
3
3
  export declare abstract class TextMeasurer {
4
4
  #private;
@@ -1,11 +1,5 @@
1
- // src/log.ts
2
- import debug from "debug";
3
- var log = debug("satori-text-fit");
4
- var log_default = log;
5
-
6
- // src/TextMeasurer/TextMeasurer.ts
7
1
  import satori from "satori";
8
- var TextMeasurer = class {
2
+ class TextMeasurer {
9
3
  constructor(text, font, maxWidth, maxHeight, lineHeight) {
10
4
  this.text = text;
11
5
  this.font = font;
@@ -66,26 +60,7 @@ var TextMeasurer = class {
66
60
  height: this.maxHeight * 2
67
61
  });
68
62
  }
69
- };
70
-
71
- // src/TextMeasurer/getWidthAdjustedForInlineBleed.ts
72
- function getWidthAdjustedForInlineBleed(svg, svgWidth) {
73
- let smallestXValue = 0;
74
- let largestYValue = svgWidth;
75
- for (const child of svg.children) {
76
- if ("getBBox" in child && typeof child["getBBox"] === "function") {
77
- const { x, y } = child.getBBox();
78
- smallestXValue = Math.min(smallestXValue, x);
79
- largestYValue = Math.max(largestYValue, y);
80
- }
81
- }
82
- const xAdjustment = Math.ceil(Math.abs(smallestXValue));
83
- const yAdjustment = largestYValue <= svgWidth ? 0 : Math.ceil(largestYValue) - svgWidth;
84
- return svgWidth - xAdjustment - yAdjustment;
85
63
  }
86
-
87
64
  export {
88
- log_default,
89
- TextMeasurer,
90
- getWidthAdjustedForInlineBleed
65
+ TextMeasurer
91
66
  };
@@ -1,3 +1,3 @@
1
- import type { TextMeasurer } from "./TextMeasurer";
1
+ import type { TextMeasurer } from "./TextMeasurer.js";
2
2
  export declare function getTextMeasurer(...args: ConstructorParameters<typeof TextMeasurer>): Promise<TextMeasurer>;
3
3
  //# sourceMappingURL=getTextMeasurer.d.ts.map
@@ -0,0 +1,12 @@
1
+ async function getTextMeasurer(...args) {
2
+ if (typeof window !== "undefined") {
3
+ const Measurer = await import("./BrowserTextMeasurer.js");
4
+ return new Measurer.default(...args);
5
+ } else {
6
+ const Measurer = await import("./HeadlessTextMeasurer.js");
7
+ return new Measurer.default(...args);
8
+ }
9
+ }
10
+ export {
11
+ getTextMeasurer
12
+ };
@@ -0,0 +1,17 @@
1
+ function getWidthAdjustedForInlineBleed(svg, svgWidth) {
2
+ let smallestXValue = 0;
3
+ let largestYValue = svgWidth;
4
+ for (const child of svg.children) {
5
+ if ("getBBox" in child && typeof child["getBBox"] === "function") {
6
+ const { x, y } = child.getBBox();
7
+ smallestXValue = Math.min(smallestXValue, x);
8
+ largestYValue = Math.max(largestYValue, y);
9
+ }
10
+ }
11
+ const xAdjustment = Math.ceil(Math.abs(smallestXValue));
12
+ const yAdjustment = largestYValue <= svgWidth ? 0 : Math.ceil(largestYValue) - svgWidth;
13
+ return svgWidth - xAdjustment - yAdjustment;
14
+ }
15
+ export {
16
+ getWidthAdjustedForInlineBleed as default
17
+ };
package/dist/fitText.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Font } from "./types";
1
+ import type { Font } from "./types.js";
2
2
  export type FindOptions = {
3
3
  /**
4
4
  * The text you want to fit
@@ -0,0 +1,41 @@
1
+ import { getTextMeasurer } from "./TextMeasurer/getTextMeasurer.js";
2
+ async function findLargestUsableFontSize({
3
+ text,
4
+ font,
5
+ maxWidth,
6
+ maxHeight,
7
+ lineHeight = "normal",
8
+ minFontSize = 1,
9
+ maxFontSize = 1e3,
10
+ maxTries = 1e3
11
+ }) {
12
+ if (maxTries < 2) {
13
+ throw new Error("`maxTries` must be >= 2");
14
+ }
15
+ const textMeasurer = await getTextMeasurer(
16
+ text,
17
+ font,
18
+ maxWidth,
19
+ maxHeight,
20
+ lineHeight
21
+ );
22
+ for (let tries = 1, lower = minFontSize, upper = maxFontSize; tries < maxTries; ++tries) {
23
+ const searchCutoffThreshold = Math.min(1, lower);
24
+ if (upper - lower <= searchCutoffThreshold) {
25
+ return Math.floor(lower);
26
+ }
27
+ const sizeCandidate = Math.floor(lower + (upper - lower) / 2);
28
+ const doesFit = await textMeasurer.doesSizeFit(sizeCandidate);
29
+ if (doesFit) {
30
+ lower = sizeCandidate;
31
+ } else {
32
+ upper = sizeCandidate;
33
+ }
34
+ }
35
+ throw new Error(
36
+ `Could not find a valid font size in ${maxTries - 1} attempts. Giving up. Please try again after constraining the search space, e.g. by restricting the min/max font size difference.`
37
+ );
38
+ }
39
+ export {
40
+ findLargestUsableFontSize
41
+ };
package/dist/index.d.ts CHANGED
@@ -1,47 +1,3 @@
1
- import { Font as Font$1 } from 'satori';
2
-
3
- type Font = Font$1;
4
-
5
- type FindOptions = {
6
- /**
7
- * The text you want to fit
8
- */
9
- text: string;
10
- /**
11
- * The font of your text. Options are the same as [those taken by
12
- * Satori](https://github.com/vercel/satori#fonts), but only one font and not
13
- * an array of fonts is accepted here.
14
- */
15
- font: Font;
16
- /**
17
- * The max width the text must be contained in
18
- */
19
- maxWidth: number;
20
- /**
21
- * The max height the text must be contained in
22
- */
23
- maxHeight: number;
24
- /**
25
- * Optional: the CSS line height factor to use, e.g. 1.1
26
- */
27
- lineHeight?: number | "normal";
28
- /**
29
- * The minimum font size that should be returned, even if smaller fonts fit
30
- */
31
- minFontSize?: number;
32
- /**
33
- * The maximum font size that should be returned, even if larger fonts fit
34
- */
35
- maxFontSize?: number;
36
- /**
37
- * The maximum number of font sizes to try before giving up. Defaults to 1000.
38
- */
39
- maxTries?: number;
40
- };
41
- /**
42
- * Given text, dimensions, and some options, returns the largest font size that
43
- * would keep the text contained within the dimensions.
44
- */
45
- declare function findLargestUsableFontSize({ text, font, maxWidth, maxHeight, lineHeight, minFontSize, maxFontSize, maxTries, }: FindOptions): Promise<number>;
46
-
47
- export { type FindOptions, type Font, findLargestUsableFontSize };
1
+ export * from "./fitText.js";
2
+ export * from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,52 +1,2 @@
1
- // src/TextMeasurer/getTextMeasurer.ts
2
- async function getTextMeasurer(...args) {
3
- if (typeof window !== "undefined") {
4
- const Measurer = await import("./BrowserTextMeasurer-TCSGQHDB.js");
5
- return new Measurer.default(...args);
6
- } else {
7
- const Measurer = await import("./HeadlessTextMeasurer-46CHWL7H.js");
8
- return new Measurer.default(...args);
9
- }
10
- }
11
-
12
- // src/fitText.ts
13
- async function findLargestUsableFontSize({
14
- text,
15
- font,
16
- maxWidth,
17
- maxHeight,
18
- lineHeight = "normal",
19
- minFontSize = 1,
20
- maxFontSize = 1e3,
21
- maxTries = 1e3
22
- }) {
23
- if (maxTries < 2) {
24
- throw new Error("`maxTries` must be >= 2");
25
- }
26
- const textMeasurer = await getTextMeasurer(
27
- text,
28
- font,
29
- maxWidth,
30
- maxHeight,
31
- lineHeight
32
- );
33
- for (let tries = 1, lower = minFontSize, upper = maxFontSize; tries < maxTries; ++tries) {
34
- const searchCutoffThreshold = Math.min(1, lower);
35
- if (upper - lower <= searchCutoffThreshold) {
36
- return Math.floor(lower);
37
- }
38
- const sizeCandidate = Math.floor(lower + (upper - lower) / 2);
39
- const doesFit = await textMeasurer.doesSizeFit(sizeCandidate);
40
- if (doesFit) {
41
- lower = sizeCandidate;
42
- } else {
43
- upper = sizeCandidate;
44
- }
45
- }
46
- throw new Error(
47
- `Could not find a valid font size in ${maxTries - 1} attempts. Giving up. Please try again after constraining the search space, e.g. by restricting the min/max font size difference.`
48
- );
49
- }
50
- export {
51
- findLargestUsableFontSize
52
- };
1
+ export * from "./fitText.js";
2
+ export * from "./types.js";
package/dist/log.js ADDED
@@ -0,0 +1,6 @@
1
+ import debug from "debug";
2
+ const log = debug("satori-text-fit");
3
+ var log_default = log;
4
+ export {
5
+ log_default as default
6
+ };
package/dist/types.js ADDED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@altano/satori-fit-text",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Fit text to a bounding box in Node or the browser",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -35,47 +35,54 @@
35
35
  "dependencies": {
36
36
  "@svgdotjs/svg.js": "^3.2.0",
37
37
  "debug": "^4.3.4",
38
- "he": "^1.2.0",
39
38
  "satori": "^0.10.13",
40
39
  "satori-html": "^0.3.2",
41
40
  "svgdom": "^0.1.19"
42
41
  },
43
42
  "devDependencies": {
43
+ "@fontsource-variable/inter": "^5.0.17",
44
+ "@fontsource/inter": "^5.0.17",
45
+ "@playwright/test": "1.40.0",
44
46
  "@resvg/resvg-js": "^2.6.0",
45
47
  "@types/debug": "^4.1.12",
46
48
  "@types/he": "^1.2.3",
47
49
  "@types/jest-image-snapshot": "^6.4.0",
50
+ "@types/node": "^20.11.25",
48
51
  "@types/react": "^18.2.64",
49
52
  "@types/react-dom": "^18.2.21",
50
53
  "@types/svgdom": "^0.1.2",
51
- "@vitest/coverage-v8": "^1.3.1",
52
- "@vitest/ui": "^1.3.1",
54
+ "@vitest/coverage-v8": "^1.4.0",
55
+ "@vitest/ui": "^1.4.0",
53
56
  "jest-image-snapshot": "^6.4.0",
54
57
  "npm-run-all": "^4.1.5",
55
58
  "prettier": "^3.2.5",
56
59
  "react": "^18.2.0",
57
60
  "react-dom": "^18.2.0",
58
- "tsup": "^8.0.2",
59
61
  "typescript": "^5.4.2",
60
- "vite": "^5.1.5",
61
- "vitest": "^1.3.1",
62
+ "vite": "^5.2.8",
63
+ "vitest": "^1.4.0",
62
64
  "@altano/assets": "0.0.0",
63
- "@altano/tiny-async-pool": "1.0.3",
65
+ "@altano/tiny-async-pool": "3.0.1",
64
66
  "@altano/tsconfig": "0.0.0"
65
67
  },
66
68
  "scripts": {
67
- "build": "tsup",
68
- "build:types": "tsc --emitDeclarationOnly --declaration",
69
- "dev": "tsup --watch",
70
- "lint": "eslint src/**/*.ts*",
71
- "lint:timing": "TIMING=1 eslint src/**/*.ts*",
72
- "lint:fix": "TIMING=1 eslint src/**/*.ts* --fix",
69
+ "build": "tsup --config build-config/tsup.config.node.ts",
70
+ "build:types": "tsc --project tsconfig.declarations.json",
71
+ "clean": "rm -rf .turbo && rm -rf .tsbuildinfo && rm -rf node_modules && rm -rf dist",
72
+ "dev": "pnpm run build --watch",
73
73
  "format": "prettier --check src",
74
74
  "format:fix": "prettier --write src",
75
- "clean": "rm -rf .turbo && rm -rf .tsbuildinfo && rm -rf node_modules && rm -rf dist",
76
- "test": "run-s test:unit test:benchmark",
77
- "test:unit": "vitest --run",
78
- "test:benchmark": "vitest --run bench",
79
- "test:watch": "vitest tests/"
75
+ "lint": "eslint package.json src/**/*.ts*",
76
+ "lint:fix": "TIMING=1 pnpm lint --fix",
77
+ "lint:timing": "TIMING=1 pnpm lint",
78
+ "test:e2e": "playwright test",
79
+ "test:e2e:server": "vite --strictPort --port 3717 ./tests/e2e",
80
+ "test:e2e:ui": "playwright test --ui",
81
+ "test:unit": "run-s test:unit:run test:unit:benchmark",
82
+ "test:unit:benchmark": "vitest --run bench",
83
+ "test:unit:run": "vitest --run",
84
+ "test:unit:watch": "vitest",
85
+ "typecheck:src": "tsc --noEmit",
86
+ "typecheck:test": "tsc --noEmit --project ./tests/tsconfig.json"
80
87
  }
81
88
  }