@altano/satori-fit-text 0.1.0 → 0.1.2
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 +7 -4
- package/dist/{BrowserTextMeasurer-IY3VY5L4.js → BrowserTextMeasurer-TCSGQHDB.js} +1 -1
- package/dist/{HeadlessTextMeasurer-SN5RPZSG.js → HeadlessTextMeasurer-46CHWL7H.js} +1 -1
- package/dist/{chunk-MLBSPHAO.js → chunk-JIONCYY3.js} +41 -49
- package/dist/index.d.ts +47 -3
- package/dist/index.js +2 -2
- package/package.json +23 -21
- package/dist/getSatoriFriendlyVNode.d.ts +0 -4
package/README.md
CHANGED
|
@@ -15,13 +15,13 @@ yarn add @altano/satori-fit-text
|
|
|
15
15
|
pnpm add @altano/satori-fit-text
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
## Example
|
|
18
|
+
## Basic Example
|
|
19
19
|
|
|
20
20
|
```ts
|
|
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://
|
|
24
|
+
const interSemiBoldBuffer = await fetch(`https://files.terriblefish.com/fonts/Inter/v4/extras/otf/Inter-SemiBold.otf`).then((res) => res.arrayBuffer());
|
|
25
25
|
|
|
26
26
|
const font = {
|
|
27
27
|
name: "Inter",
|
|
@@ -41,9 +41,12 @@ const largestUsableFontSize = await findLargestUsableFontSize({
|
|
|
41
41
|
console.log(largestUsableFontSize);
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## Example Uses
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
- [React Component (sandbox)](https://codesandbox.io/s/altano-satori-fit-text-browser-example-j2gvk6)
|
|
47
|
+
- [Next.js Open Graph Card - Simple (codesandbox)](https://codesandbox.io/p/sandbox/altano-satori-fit-text-next-js-demo-sv4flc)
|
|
48
|
+
- [Next.js Open Graph Card - Complex](https://github.com/altano/alan.norbauer.com/blob/main/src/components/opengraph/image.tsx#L29-L35)
|
|
49
|
+
- [Node.js CLI App (codesandbox)](https://codesandbox.io/p/sandbox/github/altano/npm-packages/tree/main/codesandboxes/satori-fit-text-node-cli?file=%2Fpackage.json%3A1%2C1)
|
|
47
50
|
|
|
48
51
|
## Implementation
|
|
49
52
|
|
|
@@ -5,40 +5,6 @@ var log_default = log;
|
|
|
5
5
|
|
|
6
6
|
// src/TextMeasurer/TextMeasurer.ts
|
|
7
7
|
import satori from "satori";
|
|
8
|
-
|
|
9
|
-
// src/getSatoriFriendlyVNode.ts
|
|
10
|
-
import { html as htmlToVNode } from "satori-html";
|
|
11
|
-
import he from "he";
|
|
12
|
-
function walkJsxTree(vnode, callback) {
|
|
13
|
-
callback(vnode);
|
|
14
|
-
const { children } = vnode.props;
|
|
15
|
-
if (children == null || typeof children === "string") {
|
|
16
|
-
return;
|
|
17
|
-
} else if (Array.isArray(children)) {
|
|
18
|
-
children.forEach((child) => walkJsxTree(child, callback));
|
|
19
|
-
} else {
|
|
20
|
-
walkJsxTree(children, callback);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function patchJsxTreeForSatori(vnode) {
|
|
24
|
-
walkJsxTree(vnode, (vnode2) => {
|
|
25
|
-
const { style } = vnode2.props;
|
|
26
|
-
if (style != null && typeof style["lineHeight"] === "string" && style["lineHeight"] != "") {
|
|
27
|
-
const num = Number(style["lineHeight"]);
|
|
28
|
-
if (!isNaN(num)) {
|
|
29
|
-
style["lineHeight"] = num;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
function getSatoriFriendlyVNode(html) {
|
|
35
|
-
const htmlWithDecodedHtmlEntities = he.decode(html);
|
|
36
|
-
const vnode = htmlToVNode(htmlWithDecodedHtmlEntities);
|
|
37
|
-
patchJsxTreeForSatori(vnode);
|
|
38
|
-
return vnode;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// src/TextMeasurer/TextMeasurer.ts
|
|
42
8
|
var TextMeasurer = class {
|
|
43
9
|
constructor(text, font, maxWidth, maxHeight, lineHeight) {
|
|
44
10
|
this.text = text;
|
|
@@ -54,21 +20,47 @@ var TextMeasurer = class {
|
|
|
54
20
|
return width <= this.maxWidth && height <= this.maxHeight;
|
|
55
21
|
}
|
|
56
22
|
async generateSvg(fontSize) {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
23
|
+
const node = {
|
|
24
|
+
type: "div",
|
|
25
|
+
props: {
|
|
26
|
+
style: {
|
|
27
|
+
all: "initial",
|
|
28
|
+
display: "flex",
|
|
29
|
+
flexDirection: "column",
|
|
30
|
+
width: "100%",
|
|
31
|
+
height: "100%",
|
|
32
|
+
margin: 0,
|
|
33
|
+
padding: 0
|
|
34
|
+
},
|
|
35
|
+
children: [
|
|
36
|
+
{
|
|
37
|
+
type: "div",
|
|
38
|
+
props: {
|
|
39
|
+
style: {
|
|
40
|
+
// Static styles
|
|
41
|
+
margin: 0,
|
|
42
|
+
padding: 0,
|
|
43
|
+
boxSizing: "border-box",
|
|
44
|
+
// We render a border so that we measure the true dimensions of the text.
|
|
45
|
+
// Without one, the bounding box might not include significant whitespace.
|
|
46
|
+
// For example, if the last line of text has no descenders, the bounding box
|
|
47
|
+
// would only extend to the baseline of the last line. With a border, the
|
|
48
|
+
// bounding box will include all the whitespace between the baseline and the
|
|
49
|
+
// descent of the text.
|
|
50
|
+
border: "solid red 1px",
|
|
51
|
+
// Dynamic styles based on input
|
|
52
|
+
fontSize,
|
|
53
|
+
fontFamily: this.font.name,
|
|
54
|
+
maxWidth: this.maxWidth,
|
|
55
|
+
lineHeight: this.lineHeight
|
|
56
|
+
},
|
|
57
|
+
children: this.text
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
return satori(node, {
|
|
72
64
|
fonts: this.#fonts,
|
|
73
65
|
width: this.maxWidth * 2,
|
|
74
66
|
height: this.maxHeight * 2
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// src/TextMeasurer/getTextMeasurer.ts
|
|
2
2
|
async function getTextMeasurer(...args) {
|
|
3
3
|
if (typeof window !== "undefined") {
|
|
4
|
-
const Measurer = await import("./BrowserTextMeasurer-
|
|
4
|
+
const Measurer = await import("./BrowserTextMeasurer-TCSGQHDB.js");
|
|
5
5
|
return new Measurer.default(...args);
|
|
6
6
|
} else {
|
|
7
|
-
const Measurer = await import("./HeadlessTextMeasurer-
|
|
7
|
+
const Measurer = await import("./HeadlessTextMeasurer-46CHWL7H.js");
|
|
8
8
|
return new Measurer.default(...args);
|
|
9
9
|
}
|
|
10
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@altano/satori-fit-text",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Fit text to a bounding box in Node or the browser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -36,30 +36,31 @@
|
|
|
36
36
|
"@svgdotjs/svg.js": "^3.2.0",
|
|
37
37
|
"debug": "^4.3.4",
|
|
38
38
|
"he": "^1.2.0",
|
|
39
|
-
"satori": "^0.10.
|
|
39
|
+
"satori": "^0.10.13",
|
|
40
40
|
"satori-html": "^0.3.2",
|
|
41
|
-
"svgdom": "^0.1.
|
|
41
|
+
"svgdom": "^0.1.19"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@resvg/resvg-js": "^2.
|
|
45
|
-
"@types/debug": "^4.1.
|
|
46
|
-
"@types/he": "^1.2.
|
|
47
|
-
"@types/jest-image-snapshot": "^6.
|
|
48
|
-
"@types/react": "^18.2.
|
|
49
|
-
"@types/react-dom": "^18.2.
|
|
50
|
-
"@types/svgdom": "^0.1.
|
|
51
|
-
"@vitest/coverage-v8": "^
|
|
52
|
-
"@vitest/ui": "^
|
|
53
|
-
"jest-image-snapshot": "^6.
|
|
44
|
+
"@resvg/resvg-js": "^2.6.0",
|
|
45
|
+
"@types/debug": "^4.1.12",
|
|
46
|
+
"@types/he": "^1.2.3",
|
|
47
|
+
"@types/jest-image-snapshot": "^6.4.0",
|
|
48
|
+
"@types/react": "^18.2.64",
|
|
49
|
+
"@types/react-dom": "^18.2.21",
|
|
50
|
+
"@types/svgdom": "^0.1.2",
|
|
51
|
+
"@vitest/coverage-v8": "^1.3.1",
|
|
52
|
+
"@vitest/ui": "^1.3.1",
|
|
53
|
+
"jest-image-snapshot": "^6.4.0",
|
|
54
54
|
"npm-run-all": "^4.1.5",
|
|
55
|
-
"
|
|
56
|
-
"react
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
55
|
+
"prettier": "^3.2.5",
|
|
56
|
+
"react": "^18.2.0",
|
|
57
|
+
"react-dom": "^18.2.0",
|
|
58
|
+
"tsup": "^8.0.2",
|
|
59
|
+
"typescript": "^5.4.2",
|
|
60
|
+
"vite": "^5.1.5",
|
|
61
|
+
"vitest": "^1.3.1",
|
|
62
62
|
"@altano/assets": "0.0.0",
|
|
63
|
+
"@altano/tiny-async-pool": "1.0.3",
|
|
63
64
|
"@altano/tsconfig": "0.0.0"
|
|
64
65
|
},
|
|
65
66
|
"scripts": {
|
|
@@ -69,7 +70,8 @@
|
|
|
69
70
|
"lint": "eslint src/**/*.ts*",
|
|
70
71
|
"lint:timing": "TIMING=1 eslint src/**/*.ts*",
|
|
71
72
|
"lint:fix": "TIMING=1 eslint src/**/*.ts* --fix",
|
|
72
|
-
"format": "prettier --
|
|
73
|
+
"format": "prettier --check src",
|
|
74
|
+
"format:fix": "prettier --write src",
|
|
73
75
|
"clean": "rm -rf .turbo && rm -rf .tsbuildinfo && rm -rf node_modules && rm -rf dist",
|
|
74
76
|
"test": "run-s test:unit test:benchmark",
|
|
75
77
|
"test:unit": "vitest --run",
|