@shotstack/shotstack-canvas 1.0.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/.eslintrc.cjs +7 -0
- package/.prettierrc +1 -0
- package/README.md +13 -0
- package/assets/wasm/hb.wasm +0 -0
- package/package.json +48 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/vendor-harfbuzz.js +64 -0
- package/src/config/canvas-constants.ts +30 -0
- package/src/core/animations.ts +570 -0
- package/src/core/colors.ts +11 -0
- package/src/core/decoration.ts +9 -0
- package/src/core/drawops.ts +206 -0
- package/src/core/font-registry.ts +77 -0
- package/src/core/gradients.ts +12 -0
- package/src/core/layout.ts +184 -0
- package/src/core/utils.ts +3 -0
- package/src/core/video-generator.ts +157 -0
- package/src/env/entry.node.ts +167 -0
- package/src/env/entry.web.ts +146 -0
- package/src/index.ts +1 -0
- package/src/io/node.ts +45 -0
- package/src/io/web.ts +5 -0
- package/src/painters/node.ts +290 -0
- package/src/painters/web.ts +224 -0
- package/src/schema/asset-schema.ts +166 -0
- package/src/types.ts +36 -0
- package/src/wasm/hb-loader.ts +31 -0
- package/tsconfig.base.json +22 -0
- package/tsconfig.json +16 -0
- package/tsup.config.ts +52 -0
package/.eslintrc.cjs
ADDED
package/.prettierrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "singleQuote": false, "semi": true, "printWidth": 100 }
|
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @shotstack/shotstack-canvas
|
|
2
|
+
|
|
3
|
+
One package → identical text shaping/wrapping/animation on Web & Node.
|
|
4
|
+
- HarfBuzz WASM for shaping and glyph outlines (via `harfbuzzjs`).
|
|
5
|
+
- Device-independent draw-ops.
|
|
6
|
+
- Painters: Canvas2D (web) and node-canvas (node).
|
|
7
|
+
- Deterministic time-driven animations.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @shotstack/shotstack-canvas
|
|
13
|
+
# or npm i / yarn add
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shotstack/shotstack-canvas",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"node": {
|
|
8
|
+
"import": "./dist/entry.node.js",
|
|
9
|
+
"require": "./dist/entry.node.cjs"
|
|
10
|
+
},
|
|
11
|
+
"browser": "./dist/entry.web.js",
|
|
12
|
+
"default": "./dist/entry.web.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"dev": "tsup --watch",
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"vendor:harfbuzz": "node scripts/vendor-harfbuzz.js",
|
|
19
|
+
"example:node": "node examples/node-example.mjs",
|
|
20
|
+
"example:video": "node examples/node-video.mjs",
|
|
21
|
+
"example:web": "vite dev examples/web-example"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"harfbuzzjs": "0.4.12",
|
|
25
|
+
"joi": "^17.13.3"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": { "access": "public" },
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/fluent-ffmpeg": "2.1.27",
|
|
30
|
+
"@types/node": "^20.14.10",
|
|
31
|
+
"canvas": "^2.11.2",
|
|
32
|
+
"ffmpeg-static": "^5.2.0",
|
|
33
|
+
"fluent-ffmpeg": "^2.1.3",
|
|
34
|
+
"tsup": "^8.2.3",
|
|
35
|
+
"typescript": "^5.5.3",
|
|
36
|
+
"vite": "^5.3.3",
|
|
37
|
+
"vite-plugin-top-level-await": "1.6.0",
|
|
38
|
+
"vite-plugin-wasm": "3.5.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"canvas": "^2.11.2"
|
|
42
|
+
},
|
|
43
|
+
"peerDependenciesMeta": {
|
|
44
|
+
"canvas": {
|
|
45
|
+
"optional": true
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import url from "node:url";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
|
|
10
|
+
function findHbWasm(pkgRoot) {
|
|
11
|
+
const candidates = [
|
|
12
|
+
"hb.wasm",
|
|
13
|
+
"harfbuzz.wasm",
|
|
14
|
+
"dist/hb.wasm",
|
|
15
|
+
"dist/harfbuzz.wasm"
|
|
16
|
+
];
|
|
17
|
+
for (const c of candidates) {
|
|
18
|
+
const p = path.join(pkgRoot, c);
|
|
19
|
+
if (fs.existsSync(p)) return p;
|
|
20
|
+
}
|
|
21
|
+
// shallow scan
|
|
22
|
+
for (const f of fs.readdirSync(pkgRoot)) {
|
|
23
|
+
if (f.endsWith(".wasm") && /hb|harfbuzz/i.test(f)) return path.join(pkgRoot, f);
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function resolveFromImportMeta(specifier) {
|
|
29
|
+
// Node >=20 supports import.meta.resolve; fallback to createRequire for Node 18/19
|
|
30
|
+
if (typeof import.meta.resolve === "function") {
|
|
31
|
+
try {
|
|
32
|
+
return await import.meta.resolve(specifier);
|
|
33
|
+
} catch {
|
|
34
|
+
// fall through
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return require.resolve(specifier);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
(async () => {
|
|
41
|
+
try {
|
|
42
|
+
// Resolve the harfbuzzjs package root
|
|
43
|
+
const hbPkgJsonUrl = await resolveFromImportMeta("harfbuzzjs/package.json");
|
|
44
|
+
const hbPkgRoot = path.dirname(
|
|
45
|
+
hbPkgJsonUrl.startsWith("file:") ? url.fileURLToPath(hbPkgJsonUrl) : hbPkgJsonUrl
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Try common paths; if not found, scan
|
|
49
|
+
const wasmSrc = findHbWasm(hbPkgRoot);
|
|
50
|
+
if (!wasmSrc) {
|
|
51
|
+
console.error("[vendor-harfbuzz] Could not locate hb.wasm inside harfbuzzjs package.");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const destDir = path.join(__dirname, "..", "assets", "wasm");
|
|
56
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
57
|
+
const wasmDest = path.join(destDir, "hb.wasm");
|
|
58
|
+
fs.copyFileSync(wasmSrc, wasmDest);
|
|
59
|
+
console.log(`[vendor-harfbuzz] Vendored ${wasmSrc} -> ${wasmDest}`);
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.error("[vendor-harfbuzz] Failed:", e);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const CANVAS_CONFIG = {
|
|
2
|
+
DEFAULTS: {
|
|
3
|
+
width: 800,
|
|
4
|
+
height: 400,
|
|
5
|
+
pixelRatio: 2,
|
|
6
|
+
fontFamily: "Roboto",
|
|
7
|
+
fontSize: 48,
|
|
8
|
+
color: "#000000",
|
|
9
|
+
textAlign: "left" as const
|
|
10
|
+
},
|
|
11
|
+
LIMITS: {
|
|
12
|
+
minWidth: 1,
|
|
13
|
+
maxWidth: 4096,
|
|
14
|
+
minHeight: 1,
|
|
15
|
+
maxHeight: 4096,
|
|
16
|
+
minFontSize: 1,
|
|
17
|
+
maxFontSize: 512,
|
|
18
|
+
minDuration: 0.1,
|
|
19
|
+
maxDuration: 120,
|
|
20
|
+
maxTextLength: 10000
|
|
21
|
+
},
|
|
22
|
+
ANIMATION_TYPES: [
|
|
23
|
+
"typewriter",
|
|
24
|
+
"fadeIn",
|
|
25
|
+
"slideIn",
|
|
26
|
+
"shift",
|
|
27
|
+
"ascend",
|
|
28
|
+
"movingLetters"
|
|
29
|
+
] as const
|
|
30
|
+
};
|