@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 ADDED
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ root: true,
3
+ env: { es2022: true, node: true, browser: true },
4
+ parserOptions: { ecmaVersion: "latest", sourceType: "module" },
5
+ extends: ["eslint:recommended", "prettier"],
6
+ rules: {}
7
+ };
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,3 @@
1
+ packages:
2
+ - "packages/*"
3
+ - "examples/*"
@@ -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
+ };