diff --git a/src/converters/main.ts b/src/converters/main.ts index 8c59e55..fc33318 100644 --- a/src/converters/main.ts +++ b/src/converters/main.ts @@ -21,6 +21,8 @@ import { convert as convertPotrace, properties as propertiesPotrace } from "./po import { convert as convertresvg, properties as propertiesresvg } from "./resvg"; import { convert as convertImage, properties as propertiesImage } from "./vips"; import { convert as convertxelatex, properties as propertiesxelatex } from "./xelatex"; +import {convert as convertVtracer, properties as propertiesVtracer} from './vtracer'; + // This should probably be reconstructed so that the functions are not imported instead the functions hook into this to make the converters more modular @@ -117,6 +119,10 @@ const properties: Record< properties: propertiesPotrace, converter: convertPotrace, }, + vtracer: { + properties: propertiesVtracer, + converter: convertVtracer, + } }; function chunks(arr: T[], size: number): T[][] { diff --git a/src/converters/vtracer.ts b/src/converters/vtracer.ts new file mode 100644 index 0000000..b81febd --- /dev/null +++ b/src/converters/vtracer.ts @@ -0,0 +1,91 @@ +import { execFile as execFileOriginal } from "node:child_process"; +import { ExecFileFn } from "./types"; + +export const properties = { + from: { + images: ["jpg", "jpeg", "png", "bmp", "gif", "tiff", "tif", "webp"], + }, + to: { + images: ["svg"], + }, +}; + +export function convert( + filePath: string, + fileType: string, + convertTo: string, + targetPath: string, + options?: unknown, + execFile: ExecFileFn = execFileOriginal, // to make it mockable +): Promise { + return new Promise((resolve, reject) => { + // Build vtracer arguments + const args = ["--input", filePath, "--output", targetPath]; + + // Add option parameter if provided + if (options && typeof options === "object") { + const opts = options as Record; + + if (opts.colormode) { + args.push("--colormode", opts.colormode); + } + + if (opts.hierarchical) { + args.push("--hierarchical", opts.hierarchical); + } + + if (opts.mode) { + args.push("--mode", opts.mode); + } + + if (opts.filter_speckle) { + args.push("--filter_speckle", opts.filter_speckle); + } + + if (opts.color_precision) { + args.push("--color_precision", opts.color_precision); + } + + if (opts.layer_difference) { + args.push("--layer_difference", opts.layer_difference); + } + + if (opts.corner_threshold) { + args.push("--corner_threshold", opts.corner_threshold); + } + + if (opts.length_threshold) { + args.push("--length_threshold", opts.length_threshold); + } + + if (opts.max_iterations) { + args.push("--max_iterations", opts.max_iterations); + } + + if (opts.splice_threshold) { + args.push("--splice_threshold", opts.splice_threshold); + } + + if (opts.path_precision) { + args.push("--path_precision", opts.path_precision); + } + + execFile("vtracer", args, (error, stdout, stderr) => { + if (error) { + reject(`error: ${error}`); + return; + } + + if (stdout) { + console.log(`stdout: ${stdout}`); + } + + if (stderr) { + console.log(`stderr: ${stderr}`); + } + + resolve("Done"); + }); + } + }); +}