@artifact-kit/deckkit-pro 0.1.0

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/dist/image.js ADDED
@@ -0,0 +1,102 @@
1
+ import { mkdir } from "node:fs/promises";
2
+ import { dirname } from "node:path";
3
+ import sharp from "sharp";
4
+ async function getImageInfo(input) {
5
+ const metadata = await sharp(input).metadata();
6
+ return {
7
+ width: metadata.width,
8
+ height: metadata.height,
9
+ format: metadata.format,
10
+ channels: metadata.channels,
11
+ space: metadata.space,
12
+ hasAlpha: metadata.hasAlpha,
13
+ density: metadata.density,
14
+ size: metadata.size
15
+ };
16
+ }
17
+ async function cropImage(input, options) {
18
+ return sharp(input).extract({
19
+ left: options.x,
20
+ top: options.y,
21
+ width: options.width,
22
+ height: options.height
23
+ }).png().toBuffer();
24
+ }
25
+ async function resizeImage(input, options) {
26
+ const image = sharp(input).resize({
27
+ width: options.width,
28
+ height: options.height,
29
+ fit: options.fit ?? "contain",
30
+ background: options.background
31
+ });
32
+ return image.png().toBuffer();
33
+ }
34
+ async function sampleColor(input, x, y) {
35
+ const { data, info } = await sharp(input).ensureAlpha().extract({ left: x, top: y, width: 1, height: 1 }).raw().toBuffer({ resolveWithObject: true });
36
+ return {
37
+ x,
38
+ y,
39
+ r: data[0] ?? 0,
40
+ g: data[1] ?? 0,
41
+ b: data[2] ?? 0,
42
+ alpha: info.channels >= 4 ? data[3] : void 0
43
+ };
44
+ }
45
+ async function compareImages(reference, generated, options = {}) {
46
+ const [refBuffer, genBuffer] = await Promise.all([
47
+ sharp(reference).png().toBuffer(),
48
+ sharp(generated).png().toBuffer()
49
+ ]);
50
+ const [refInfo, genInfo] = await Promise.all([getImageInfo(refBuffer), getImageInfo(genBuffer)]);
51
+ const gap = options.gap ?? 12;
52
+ const width = (refInfo.width ?? 0) + (genInfo.width ?? 0) + gap;
53
+ const height = Math.max(refInfo.height ?? 0, genInfo.height ?? 0);
54
+ return sharp({
55
+ create: {
56
+ width,
57
+ height,
58
+ channels: 4,
59
+ background: options.background ?? { r: 255, g: 255, b: 255, alpha: 1 }
60
+ }
61
+ }).composite([
62
+ { input: refBuffer, left: 0, top: 0 },
63
+ { input: genBuffer, left: (refInfo.width ?? 0) + gap, top: 0 }
64
+ ]).png().toBuffer();
65
+ }
66
+ async function overlayImages(reference, generated, options = {}) {
67
+ const refInfo = await getImageInfo(reference);
68
+ const refWidth = refInfo.width ?? 1;
69
+ const refHeight = refInfo.height ?? 1;
70
+ const generatedBuffer = await withOpacity(
71
+ await sharp(generated).resize({ width: refWidth, height: refHeight, fit: "contain" }).ensureAlpha().png().toBuffer(),
72
+ options.opacity ?? 0.5
73
+ );
74
+ return sharp(reference).resize({ width: refWidth, height: refHeight, fit: "contain", background: options.background }).composite([{ input: generatedBuffer, left: 0, top: 0 }]).png().toBuffer();
75
+ }
76
+ async function withOpacity(input, opacity) {
77
+ const clampedOpacity = Math.max(0, Math.min(1, opacity));
78
+ const { data, info } = await sharp(input).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
79
+ for (let index = 3; index < data.length; index += info.channels) {
80
+ data[index] = Math.round((data[index] ?? 0) * clampedOpacity);
81
+ }
82
+ return sharp(data, {
83
+ raw: {
84
+ width: info.width,
85
+ height: info.height,
86
+ channels: info.channels
87
+ }
88
+ }).png().toBuffer();
89
+ }
90
+ async function writeImage(buffer, output) {
91
+ await mkdir(dirname(output), { recursive: true });
92
+ await sharp(buffer).toFile(output);
93
+ }
94
+ export {
95
+ compareImages,
96
+ cropImage,
97
+ getImageInfo,
98
+ overlayImages,
99
+ resizeImage,
100
+ sampleColor,
101
+ writeImage
102
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
+ const gradientFill = require("./gradient-fill.cjs");
4
+ const svgToCustomGeometry = require("./svg-to-custom-geometry.cjs");
5
+ const svgToPng = require("./svg-to-png.cjs");
6
+ const image = require("./image.cjs");
7
+ function deckkitPro() {
8
+ return {
9
+ name: "@artifact-kit/deckkit-pro",
10
+ setup(context) {
11
+ gradientFill.setupGradientFill(context);
12
+ svgToCustomGeometry.setupSvgCustomGeometry(context);
13
+ }
14
+ };
15
+ }
16
+ exports.renderGradientFill = gradientFill.renderGradientFill;
17
+ exports.setupGradientFill = gradientFill.setupGradientFill;
18
+ exports.handleSvgCustomGeometry = svgToCustomGeometry.handleSvgCustomGeometry;
19
+ exports.setupSvgCustomGeometry = svgToCustomGeometry.setupSvgCustomGeometry;
20
+ exports.svgToCustomGeometry = svgToCustomGeometry.svgToCustomGeometry;
21
+ exports.renderSvgToPng = svgToPng.renderSvgToPng;
22
+ exports.writeSvgToPng = svgToPng.writeSvgToPng;
23
+ exports.compareImages = image.compareImages;
24
+ exports.cropImage = image.cropImage;
25
+ exports.getImageInfo = image.getImageInfo;
26
+ exports.overlayImages = image.overlayImages;
27
+ exports.resizeImage = image.resizeImage;
28
+ exports.sampleColor = image.sampleColor;
29
+ exports.writeImage = image.writeImage;
30
+ exports.deckkitPro = deckkitPro;
31
+ exports.default = deckkitPro;
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ import { setupGradientFill } from "./gradient-fill.js";
2
+ import { renderGradientFill } from "./gradient-fill.js";
3
+ import { setupSvgCustomGeometry } from "./svg-to-custom-geometry.js";
4
+ import { handleSvgCustomGeometry, svgToCustomGeometry } from "./svg-to-custom-geometry.js";
5
+ import { renderSvgToPng, writeSvgToPng } from "./svg-to-png.js";
6
+ import { compareImages, cropImage, getImageInfo, overlayImages, resizeImage, sampleColor, writeImage } from "./image.js";
7
+ function deckkitPro() {
8
+ return {
9
+ name: "@artifact-kit/deckkit-pro",
10
+ setup(context) {
11
+ setupGradientFill(context);
12
+ setupSvgCustomGeometry(context);
13
+ }
14
+ };
15
+ }
16
+ export {
17
+ compareImages,
18
+ cropImage,
19
+ deckkitPro,
20
+ deckkitPro as default,
21
+ getImageInfo,
22
+ handleSvgCustomGeometry,
23
+ overlayImages,
24
+ renderGradientFill,
25
+ renderSvgToPng,
26
+ resizeImage,
27
+ sampleColor,
28
+ setupGradientFill,
29
+ setupSvgCustomGeometry,
30
+ svgToCustomGeometry,
31
+ writeImage,
32
+ writeSvgToPng
33
+ };
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const paper = require("paper-jsdom");
4
+ function setupSvgCustomGeometry(context) {
5
+ context.addShapeHandler(handleSvgCustomGeometry);
6
+ }
7
+ function handleSvgCustomGeometry({ target, shape, options }) {
8
+ const svg = options.svg;
9
+ if (String(shape) !== "custGeom" || typeof svg !== "string") return void 0;
10
+ const { svg: _svg, svgPrecision, ...baseOptions } = options;
11
+ const result = svgToCustomGeometry(svg, {
12
+ x: typeof options.x === "number" ? options.x : void 0,
13
+ y: typeof options.y === "number" ? options.y : void 0,
14
+ w: typeof options.w === "number" ? options.w : void 0,
15
+ h: typeof options.h === "number" ? options.h : void 0,
16
+ precision: svgPrecision
17
+ });
18
+ result.paths.forEach((path, index) => {
19
+ target.addShape("custGeom", {
20
+ ...baseOptions,
21
+ x: options.x,
22
+ y: options.y,
23
+ w: result.width,
24
+ h: result.height,
25
+ objectName: options.objectName ? `${options.objectName} ${index + 1}` : void 0,
26
+ points: path.points,
27
+ fill: path.fill,
28
+ line: path.line
29
+ });
30
+ });
31
+ return true;
32
+ }
33
+ function svgToCustomGeometry(svg, options = {}) {
34
+ const viewBox = readViewBox(svg) ?? readSvgSize(svg) ?? [0, 0, 1, 1];
35
+ const [, , viewBoxWidth, viewBoxHeight] = viewBox;
36
+ const width = options.w ?? viewBoxWidth / 100;
37
+ const height = options.h ?? viewBoxHeight / 100;
38
+ const precision = options.precision ?? 5;
39
+ paper.setup(new paper.Size(viewBoxWidth || 1, viewBoxHeight || 1));
40
+ try {
41
+ const rootItem = paper.project.importSVG(svg, {
42
+ expandShapes: true,
43
+ insert: true
44
+ });
45
+ const items = rootItem.getItems({
46
+ match: (item) => item instanceof paper.Path || item instanceof paper.CompoundPath
47
+ });
48
+ const paths = collectUniquePathItems(items).map((pathItem) => paperPathToCustomGeometry(pathItem, viewBox, width, height, precision));
49
+ return {
50
+ width,
51
+ height,
52
+ viewBox,
53
+ paths
54
+ };
55
+ } finally {
56
+ paper.project.clear();
57
+ }
58
+ }
59
+ function collectUniquePathItems(items) {
60
+ const seen = /* @__PURE__ */ new Set();
61
+ const paths = [];
62
+ for (const item of items) {
63
+ const pathItems = paperItemToPaths(item);
64
+ for (const pathItem of pathItems) {
65
+ if (seen.has(pathItem)) continue;
66
+ seen.add(pathItem);
67
+ paths.push(pathItem);
68
+ }
69
+ }
70
+ return paths;
71
+ }
72
+ function paperItemToPaths(item) {
73
+ if (item instanceof paper.Path) return [item];
74
+ if (item instanceof paper.CompoundPath) return item.children.filter((child) => child instanceof paper.Path);
75
+ return [];
76
+ }
77
+ function paperPathToCustomGeometry(pathItem, viewBox, width, height, precision) {
78
+ const stroke = inheritedStroke(pathItem);
79
+ return {
80
+ points: pathToCustomGeometryPoints(pathItem, viewBox, width, height, precision),
81
+ fill: colorToFill(inheritedValue(pathItem, "fillColor")),
82
+ line: colorToLine(stroke.color, stroke.width, viewBox, width, height)
83
+ };
84
+ }
85
+ function inheritedStroke(item) {
86
+ let cursor = item;
87
+ while (cursor) {
88
+ if (cursor.strokeColor) {
89
+ return {
90
+ color: cursor.strokeColor,
91
+ width: cursor.strokeWidth
92
+ };
93
+ }
94
+ cursor = cursor.parent;
95
+ }
96
+ return { color: void 0, width: void 0 };
97
+ }
98
+ function inheritedValue(item, key) {
99
+ let cursor = item;
100
+ while (cursor) {
101
+ const value = cursor[key];
102
+ if (value !== void 0 && value !== null) return value;
103
+ cursor = cursor.parent;
104
+ }
105
+ return void 0;
106
+ }
107
+ function pathToCustomGeometryPoints(pathItem, viewBox, width, height, precision) {
108
+ const segments = pathItem.segments;
109
+ if (!segments || segments.length === 0) return [];
110
+ const points = [movePoint(segments[0].point, viewBox, width, height, precision)];
111
+ for (let index = 1; index < segments.length; index += 1) {
112
+ points.push(segmentPoint(segments[index - 1], segments[index], viewBox, width, height, precision));
113
+ }
114
+ if (pathItem.closed) {
115
+ points.push(segmentPoint(segments[segments.length - 1], segments[0], viewBox, width, height, precision));
116
+ points.push({ close: true });
117
+ }
118
+ return points;
119
+ }
120
+ function movePoint(point, viewBox, width, height, precision) {
121
+ return {
122
+ ...scalePoint(point, viewBox, width, height, precision),
123
+ moveTo: true
124
+ };
125
+ }
126
+ function segmentPoint(from, to, viewBox, width, height, precision) {
127
+ const end = scalePoint(to.point, viewBox, width, height, precision);
128
+ const fromHandle = from.handleOut;
129
+ const toHandle = to.handleIn;
130
+ if (!fromHandle.isZero() || !toHandle.isZero()) {
131
+ const c1 = scalePoint(from.point.add(fromHandle), viewBox, width, height, precision);
132
+ const c2 = scalePoint(to.point.add(toHandle), viewBox, width, height, precision);
133
+ return {
134
+ ...end,
135
+ curve: {
136
+ type: "cubic",
137
+ x1: c1.x,
138
+ y1: c1.y,
139
+ x2: c2.x,
140
+ y2: c2.y
141
+ }
142
+ };
143
+ }
144
+ return end;
145
+ }
146
+ function scalePoint(point, viewBox, width, height, precision) {
147
+ const [minX, minY, viewBoxWidth, viewBoxHeight] = viewBox;
148
+ return {
149
+ x: round((point.x - minX) / viewBoxWidth * width, precision),
150
+ y: round((point.y - minY) / viewBoxHeight * height, precision)
151
+ };
152
+ }
153
+ function colorToFill(color) {
154
+ const hex = paperColorToHex(color);
155
+ return hex ? { color: hex } : void 0;
156
+ }
157
+ function colorToLine(color, strokeWidth, viewBox, width, height) {
158
+ const hex = paperColorToHex(color);
159
+ if (!hex) return { type: "none" };
160
+ const [, , viewBoxWidth, viewBoxHeight] = viewBox;
161
+ const scale = (width / viewBoxWidth + height / viewBoxHeight) / 2;
162
+ return {
163
+ color: hex,
164
+ width: strokeWidth ? Math.max(0.1, strokeWidth * scale * 72) : 1
165
+ };
166
+ }
167
+ function paperColorToHex(color) {
168
+ const css = color?.toCSS?.(true);
169
+ if (typeof css !== "string" || !css.startsWith("#")) return void 0;
170
+ return css.slice(1).toUpperCase();
171
+ }
172
+ function readViewBox(svg) {
173
+ const match = svg.match(/\bviewBox=["']([^"']+)["']/);
174
+ if (!match) return void 0;
175
+ const values = match[1].trim().split(/[\s,]+/).map(Number);
176
+ return values.length === 4 && values.every(Number.isFinite) ? values : void 0;
177
+ }
178
+ function readSvgSize(svg) {
179
+ const width = readNumericSvgAttr(svg, "width");
180
+ const height = readNumericSvgAttr(svg, "height");
181
+ return width && height ? [0, 0, width, height] : void 0;
182
+ }
183
+ function readNumericSvgAttr(svg, attr) {
184
+ const match = svg.match(new RegExp(`\\b${attr}=["']([0-9.]+)`));
185
+ if (!match) return void 0;
186
+ const value = Number(match[1]);
187
+ return Number.isFinite(value) ? value : void 0;
188
+ }
189
+ function round(value, precision) {
190
+ const scale = 10 ** precision;
191
+ return Math.round(value * scale) / scale;
192
+ }
193
+ exports.handleSvgCustomGeometry = handleSvgCustomGeometry;
194
+ exports.setupSvgCustomGeometry = setupSvgCustomGeometry;
195
+ exports.svgToCustomGeometry = svgToCustomGeometry;
@@ -0,0 +1,195 @@
1
+ import paper from "paper-jsdom";
2
+ function setupSvgCustomGeometry(context) {
3
+ context.addShapeHandler(handleSvgCustomGeometry);
4
+ }
5
+ function handleSvgCustomGeometry({ target, shape, options }) {
6
+ const svg = options.svg;
7
+ if (String(shape) !== "custGeom" || typeof svg !== "string") return void 0;
8
+ const { svg: _svg, svgPrecision, ...baseOptions } = options;
9
+ const result = svgToCustomGeometry(svg, {
10
+ x: typeof options.x === "number" ? options.x : void 0,
11
+ y: typeof options.y === "number" ? options.y : void 0,
12
+ w: typeof options.w === "number" ? options.w : void 0,
13
+ h: typeof options.h === "number" ? options.h : void 0,
14
+ precision: svgPrecision
15
+ });
16
+ result.paths.forEach((path, index) => {
17
+ target.addShape("custGeom", {
18
+ ...baseOptions,
19
+ x: options.x,
20
+ y: options.y,
21
+ w: result.width,
22
+ h: result.height,
23
+ objectName: options.objectName ? `${options.objectName} ${index + 1}` : void 0,
24
+ points: path.points,
25
+ fill: path.fill,
26
+ line: path.line
27
+ });
28
+ });
29
+ return true;
30
+ }
31
+ function svgToCustomGeometry(svg, options = {}) {
32
+ const viewBox = readViewBox(svg) ?? readSvgSize(svg) ?? [0, 0, 1, 1];
33
+ const [, , viewBoxWidth, viewBoxHeight] = viewBox;
34
+ const width = options.w ?? viewBoxWidth / 100;
35
+ const height = options.h ?? viewBoxHeight / 100;
36
+ const precision = options.precision ?? 5;
37
+ paper.setup(new paper.Size(viewBoxWidth || 1, viewBoxHeight || 1));
38
+ try {
39
+ const rootItem = paper.project.importSVG(svg, {
40
+ expandShapes: true,
41
+ insert: true
42
+ });
43
+ const items = rootItem.getItems({
44
+ match: (item) => item instanceof paper.Path || item instanceof paper.CompoundPath
45
+ });
46
+ const paths = collectUniquePathItems(items).map((pathItem) => paperPathToCustomGeometry(pathItem, viewBox, width, height, precision));
47
+ return {
48
+ width,
49
+ height,
50
+ viewBox,
51
+ paths
52
+ };
53
+ } finally {
54
+ paper.project.clear();
55
+ }
56
+ }
57
+ function collectUniquePathItems(items) {
58
+ const seen = /* @__PURE__ */ new Set();
59
+ const paths = [];
60
+ for (const item of items) {
61
+ const pathItems = paperItemToPaths(item);
62
+ for (const pathItem of pathItems) {
63
+ if (seen.has(pathItem)) continue;
64
+ seen.add(pathItem);
65
+ paths.push(pathItem);
66
+ }
67
+ }
68
+ return paths;
69
+ }
70
+ function paperItemToPaths(item) {
71
+ if (item instanceof paper.Path) return [item];
72
+ if (item instanceof paper.CompoundPath) return item.children.filter((child) => child instanceof paper.Path);
73
+ return [];
74
+ }
75
+ function paperPathToCustomGeometry(pathItem, viewBox, width, height, precision) {
76
+ const stroke = inheritedStroke(pathItem);
77
+ return {
78
+ points: pathToCustomGeometryPoints(pathItem, viewBox, width, height, precision),
79
+ fill: colorToFill(inheritedValue(pathItem, "fillColor")),
80
+ line: colorToLine(stroke.color, stroke.width, viewBox, width, height)
81
+ };
82
+ }
83
+ function inheritedStroke(item) {
84
+ let cursor = item;
85
+ while (cursor) {
86
+ if (cursor.strokeColor) {
87
+ return {
88
+ color: cursor.strokeColor,
89
+ width: cursor.strokeWidth
90
+ };
91
+ }
92
+ cursor = cursor.parent;
93
+ }
94
+ return { color: void 0, width: void 0 };
95
+ }
96
+ function inheritedValue(item, key) {
97
+ let cursor = item;
98
+ while (cursor) {
99
+ const value = cursor[key];
100
+ if (value !== void 0 && value !== null) return value;
101
+ cursor = cursor.parent;
102
+ }
103
+ return void 0;
104
+ }
105
+ function pathToCustomGeometryPoints(pathItem, viewBox, width, height, precision) {
106
+ const segments = pathItem.segments;
107
+ if (!segments || segments.length === 0) return [];
108
+ const points = [movePoint(segments[0].point, viewBox, width, height, precision)];
109
+ for (let index = 1; index < segments.length; index += 1) {
110
+ points.push(segmentPoint(segments[index - 1], segments[index], viewBox, width, height, precision));
111
+ }
112
+ if (pathItem.closed) {
113
+ points.push(segmentPoint(segments[segments.length - 1], segments[0], viewBox, width, height, precision));
114
+ points.push({ close: true });
115
+ }
116
+ return points;
117
+ }
118
+ function movePoint(point, viewBox, width, height, precision) {
119
+ return {
120
+ ...scalePoint(point, viewBox, width, height, precision),
121
+ moveTo: true
122
+ };
123
+ }
124
+ function segmentPoint(from, to, viewBox, width, height, precision) {
125
+ const end = scalePoint(to.point, viewBox, width, height, precision);
126
+ const fromHandle = from.handleOut;
127
+ const toHandle = to.handleIn;
128
+ if (!fromHandle.isZero() || !toHandle.isZero()) {
129
+ const c1 = scalePoint(from.point.add(fromHandle), viewBox, width, height, precision);
130
+ const c2 = scalePoint(to.point.add(toHandle), viewBox, width, height, precision);
131
+ return {
132
+ ...end,
133
+ curve: {
134
+ type: "cubic",
135
+ x1: c1.x,
136
+ y1: c1.y,
137
+ x2: c2.x,
138
+ y2: c2.y
139
+ }
140
+ };
141
+ }
142
+ return end;
143
+ }
144
+ function scalePoint(point, viewBox, width, height, precision) {
145
+ const [minX, minY, viewBoxWidth, viewBoxHeight] = viewBox;
146
+ return {
147
+ x: round((point.x - minX) / viewBoxWidth * width, precision),
148
+ y: round((point.y - minY) / viewBoxHeight * height, precision)
149
+ };
150
+ }
151
+ function colorToFill(color) {
152
+ const hex = paperColorToHex(color);
153
+ return hex ? { color: hex } : void 0;
154
+ }
155
+ function colorToLine(color, strokeWidth, viewBox, width, height) {
156
+ const hex = paperColorToHex(color);
157
+ if (!hex) return { type: "none" };
158
+ const [, , viewBoxWidth, viewBoxHeight] = viewBox;
159
+ const scale = (width / viewBoxWidth + height / viewBoxHeight) / 2;
160
+ return {
161
+ color: hex,
162
+ width: strokeWidth ? Math.max(0.1, strokeWidth * scale * 72) : 1
163
+ };
164
+ }
165
+ function paperColorToHex(color) {
166
+ const css = color?.toCSS?.(true);
167
+ if (typeof css !== "string" || !css.startsWith("#")) return void 0;
168
+ return css.slice(1).toUpperCase();
169
+ }
170
+ function readViewBox(svg) {
171
+ const match = svg.match(/\bviewBox=["']([^"']+)["']/);
172
+ if (!match) return void 0;
173
+ const values = match[1].trim().split(/[\s,]+/).map(Number);
174
+ return values.length === 4 && values.every(Number.isFinite) ? values : void 0;
175
+ }
176
+ function readSvgSize(svg) {
177
+ const width = readNumericSvgAttr(svg, "width");
178
+ const height = readNumericSvgAttr(svg, "height");
179
+ return width && height ? [0, 0, width, height] : void 0;
180
+ }
181
+ function readNumericSvgAttr(svg, attr) {
182
+ const match = svg.match(new RegExp(`\\b${attr}=["']([0-9.]+)`));
183
+ if (!match) return void 0;
184
+ const value = Number(match[1]);
185
+ return Number.isFinite(value) ? value : void 0;
186
+ }
187
+ function round(value, precision) {
188
+ const scale = 10 ** precision;
189
+ return Math.round(value * scale) / scale;
190
+ }
191
+ export {
192
+ handleSvgCustomGeometry,
193
+ setupSvgCustomGeometry,
194
+ svgToCustomGeometry
195
+ };
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ const node_fs = require("node:fs");
4
+ const promises = require("node:fs/promises");
5
+ const node_path = require("node:path");
6
+ const svgToPng = require("./svg-to-png.cjs");
7
+ const FIT_VALUES = /* @__PURE__ */ new Set(["cover", "contain", "fill", "inside", "outside"]);
8
+ async function main(args) {
9
+ const parsed = parseArgs(args);
10
+ if (!parsed.input) {
11
+ printUsage();
12
+ process.exitCode = 1;
13
+ return;
14
+ }
15
+ const svg = await readSvgInput(parsed.input);
16
+ await svgToPng.writeSvgToPng(svg, parsed.output, parsed.options);
17
+ console.log(parsed.output);
18
+ }
19
+ function parseArgs(args) {
20
+ const result = {
21
+ output: "output.png",
22
+ options: {}
23
+ };
24
+ for (let index = 0; index < args.length; index += 1) {
25
+ const arg = args[index];
26
+ switch (arg) {
27
+ case "-o":
28
+ case "--output":
29
+ result.output = readValue(args, ++index, arg);
30
+ break;
31
+ case "--width":
32
+ result.options.width = readNumber(args, ++index, arg);
33
+ break;
34
+ case "--height":
35
+ result.options.height = readNumber(args, ++index, arg);
36
+ break;
37
+ case "--density":
38
+ result.options.density = readNumber(args, ++index, arg);
39
+ break;
40
+ case "--fit": {
41
+ const fit = readValue(args, ++index, arg);
42
+ if (!FIT_VALUES.has(fit)) {
43
+ throw new Error(`Invalid --fit value "${fit}". Expected one of: ${Array.from(FIT_VALUES).join(", ")}`);
44
+ }
45
+ result.options.fit = fit;
46
+ break;
47
+ }
48
+ case "--background":
49
+ result.options.background = readValue(args, ++index, arg);
50
+ break;
51
+ case "--compression-level":
52
+ result.options.compressionLevel = readNumber(args, ++index, arg);
53
+ break;
54
+ case "-h":
55
+ case "--help":
56
+ printUsage();
57
+ process.exit(0);
58
+ default:
59
+ if (arg.startsWith("-")) {
60
+ throw new Error(`Unknown option: ${arg}`);
61
+ }
62
+ if (result.input !== void 0) {
63
+ throw new Error(`Unexpected extra argument: ${arg}`);
64
+ }
65
+ result.input = arg;
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+ async function readSvgInput(input) {
71
+ if (node_fs.existsSync(input)) {
72
+ return promises.readFile(input, "utf8");
73
+ }
74
+ return input;
75
+ }
76
+ function readValue(args, index, option) {
77
+ const value = args[index];
78
+ if (!value) {
79
+ throw new Error(`Missing value for ${option}`);
80
+ }
81
+ return value;
82
+ }
83
+ function readNumber(args, index, option) {
84
+ const value = Number(readValue(args, index, option));
85
+ if (!Number.isFinite(value)) {
86
+ throw new Error(`Invalid numeric value for ${option}`);
87
+ }
88
+ return value;
89
+ }
90
+ function printUsage() {
91
+ const command = node_path.basename(process.argv[1] ?? "svg-to-png-cli.js", node_path.extname(process.argv[1] ?? ""));
92
+ console.log(`Usage:
93
+ node ${command}.js "<svg ...>" -o output.png
94
+ node ${command}.js input.svg -o output.png
95
+
96
+ Options:
97
+ -o, --output <path> Output PNG path. Defaults to output.png.
98
+ --width <px> Resize output width.
99
+ --height <px> Resize output height.
100
+ --fit <mode> cover, contain, fill, inside, or outside. Defaults to contain.
101
+ --density <dpi> SVG render density.
102
+ --background <color> Flatten transparent background, e.g. "#ffffff".
103
+ --compression-level <0-9> PNG compression level.`);
104
+ }
105
+ main(process.argv.slice(2)).catch((error) => {
106
+ console.error(error instanceof Error ? error.message : error);
107
+ process.exitCode = 1;
108
+ });