mirror of https://github.com/VERT-sh/VERT.git
parent
93c06834f7
commit
9c3aba77b0
|
@ -24,7 +24,7 @@ export class MagickConverter extends Converter {
|
||||||
new FormatInfo("jpg", true, true),
|
new FormatInfo("jpg", true, true),
|
||||||
new FormatInfo("webp", true, true),
|
new FormatInfo("webp", true, true),
|
||||||
new FormatInfo("gif", true, true),
|
new FormatInfo("gif", true, true),
|
||||||
new FormatInfo("svg", false, true), // converting from SVG unsupported my magick-wasm - suggested to let browser draw with canvas and read image to "convert" (gh issues)
|
new FormatInfo("svg", true, true),
|
||||||
new FormatInfo("jxl", true, true),
|
new FormatInfo("jxl", true, true),
|
||||||
new FormatInfo("avif", true, true),
|
new FormatInfo("avif", true, true),
|
||||||
new FormatInfo("heic", true, false), // seems to be unreliable? HEIC/HEIF is very weird if it will actually work
|
new FormatInfo("heic", true, false), // seems to be unreliable? HEIC/HEIF is very weird if it will actually work
|
||||||
|
@ -75,10 +75,7 @@ export class MagickConverter extends Converter {
|
||||||
["converters", this.name],
|
["converters", this.name],
|
||||||
`error in worker: ${message.error}`,
|
`error in worker: ${message.error}`,
|
||||||
);
|
);
|
||||||
addToast(
|
addToast("error", m["workers.errors.magick"]());
|
||||||
"error",
|
|
||||||
m["workers.errors.magick"](),
|
|
||||||
);
|
|
||||||
throw new Error(message.error);
|
throw new Error(message.error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -92,6 +89,27 @@ export class MagickConverter extends Converter {
|
||||||
): Promise<VertFile> {
|
): Promise<VertFile> {
|
||||||
const compression: number | undefined = args.at(0);
|
const compression: number | undefined = args.at(0);
|
||||||
log(["converters", this.name], `converting ${input.name} to ${to}`);
|
log(["converters", this.name], `converting ${input.name} to ${to}`);
|
||||||
|
|
||||||
|
// handle converting from SVG manually because magick-wasm doesn't support it
|
||||||
|
if (input.from === ".svg") {
|
||||||
|
try {
|
||||||
|
const blob = await this.svgToImage(input);
|
||||||
|
const pngFile = new VertFile(
|
||||||
|
new File([blob], input.name.replace(/\.svg$/i, ".png")),
|
||||||
|
input.to,
|
||||||
|
);
|
||||||
|
if (to === ".png") return pngFile; // if target is png, return it directly
|
||||||
|
return await this.convert(pngFile, to, ...args); // otherwise, recursively convert png to user's target format
|
||||||
|
} catch (err) {
|
||||||
|
error(
|
||||||
|
["converters", this.name],
|
||||||
|
`SVG conversion failed: ${err}`,
|
||||||
|
);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// every other format handled by magick worker
|
||||||
const msg = {
|
const msg = {
|
||||||
type: "convert",
|
type: "convert",
|
||||||
input: {
|
input: {
|
||||||
|
@ -150,4 +168,67 @@ export class MagickConverter extends Converter {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async svgToImage(input: VertFile): Promise<Blob> {
|
||||||
|
log(["converters", this.name], `converting SVG to image (PNG)`);
|
||||||
|
|
||||||
|
const svgText = await input.file.text();
|
||||||
|
const svgBlob = new Blob([svgText], { type: "image/svg+xml" });
|
||||||
|
const svgUrl = URL.createObjectURL(svgBlob);
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
if (!ctx) throw new Error("Failed to get canvas context");
|
||||||
|
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
// try to extract dimensions from SVG, and if not fallback to default
|
||||||
|
let width = 512;
|
||||||
|
let height = 512;
|
||||||
|
const widthMatch = svgText.match(/width=["'](\d+)["']/);
|
||||||
|
const heightMatch = svgText.match(/height=["'](\d+)["']/);
|
||||||
|
const viewBoxMatch = svgText.match(
|
||||||
|
/viewBox=["'][^"']*\s+(\d+)\s+(\d+)["']/,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (widthMatch && heightMatch) {
|
||||||
|
width = parseInt(widthMatch[1]);
|
||||||
|
height = parseInt(heightMatch[1]);
|
||||||
|
} else if (viewBoxMatch) {
|
||||||
|
width = parseInt(viewBoxMatch[1]);
|
||||||
|
height = parseInt(viewBoxMatch[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
img.onload = () => {
|
||||||
|
try {
|
||||||
|
canvas.width = img.naturalWidth || width;
|
||||||
|
canvas.height = img.naturalHeight || height;
|
||||||
|
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
URL.revokeObjectURL(svgUrl);
|
||||||
|
if (blob) {
|
||||||
|
resolve(blob);
|
||||||
|
} else {
|
||||||
|
reject(
|
||||||
|
new Error("Failed to convert canvas to Blob"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, "image/png");
|
||||||
|
} catch (err) {
|
||||||
|
URL.revokeObjectURL(svgUrl);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
img.onerror = () => {
|
||||||
|
URL.revokeObjectURL(svgUrl);
|
||||||
|
reject(new Error("Failed to load SVG image"));
|
||||||
|
};
|
||||||
|
|
||||||
|
img.src = svgUrl;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue