mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: add exotic format decoding to image-to-pdf tool
The image-to-pdf route used ensureSharpCompat (HEIC-only) instead of the full format decode pipeline from tool-factory. Formats like FITS, PSD, RAW, EXR, HDR, TGA, etc. passed through undecoded and crashed Sharp. Replace with validateImageBuffer + decodeToSharpCompat to match the standard tool pipeline.
This commit is contained in:
@@ -7,8 +7,10 @@ import sharp from "sharp";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { autoOrient } from "../../lib/auto-orient.js";
|
import { autoOrient } from "../../lib/auto-orient.js";
|
||||||
import { formatZodErrors } from "../../lib/errors.js";
|
import { formatZodErrors } from "../../lib/errors.js";
|
||||||
|
import { validateImageBuffer } from "../../lib/file-validation.js";
|
||||||
import { sanitizeFilename } from "../../lib/filename.js";
|
import { sanitizeFilename } from "../../lib/filename.js";
|
||||||
import { ensureSharpCompat } from "../../lib/heic-converter.js";
|
import { decodeToSharpCompat, needsCliDecode } from "../../lib/format-decoders.js";
|
||||||
|
import { decodeHeic } from "../../lib/heic-converter.js";
|
||||||
import { createWorkspace } from "../../lib/workspace.js";
|
import { createWorkspace } from "../../lib/workspace.js";
|
||||||
|
|
||||||
const targetSizeSchema = z.object({
|
const targetSizeSchema = z.object({
|
||||||
@@ -179,8 +181,21 @@ export function registerImageToPdf(app: FastifyInstance) {
|
|||||||
|
|
||||||
const preparedBuffers: Buffer[] = [];
|
const preparedBuffers: Buffer[] = [];
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const compatBuffer = await autoOrient(await ensureSharpCompat(file.buffer));
|
let buf = file.buffer;
|
||||||
preparedBuffers.push(compatBuffer);
|
|
||||||
|
const validation = await validateImageBuffer(buf, file.filename);
|
||||||
|
if (!validation.valid) {
|
||||||
|
return reply.status(400).send({ error: `Invalid image: ${validation.reason}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validation.format === "heif") {
|
||||||
|
buf = await decodeHeic(buf);
|
||||||
|
} else if (needsCliDecode(validation.format)) {
|
||||||
|
const fileExt = file.filename.split(".").pop()?.toLowerCase();
|
||||||
|
buf = await decodeToSharpCompat(buf, validation.format, fileExt);
|
||||||
|
}
|
||||||
|
|
||||||
|
preparedBuffers.push(await autoOrient(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
let imageBuffers: Buffer[];
|
let imageBuffers: Buffer[];
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ describe("image-to-pdf", () => {
|
|||||||
|
|
||||||
// ── Branch coverage: lines 143-147 (processing failure) ───────────
|
// ── Branch coverage: lines 143-147 (processing failure) ───────────
|
||||||
|
|
||||||
it("returns 422 when processing fails on corrupted image data", async () => {
|
it("returns 400 when image format is unrecognized", async () => {
|
||||||
const corruptedBuffer = Buffer.alloc(100, 0xff);
|
const corruptedBuffer = Buffer.alloc(100, 0xff);
|
||||||
const { body, contentType } = createMultipartPayload([
|
const { body, contentType } = createMultipartPayload([
|
||||||
{ name: "file", filename: "bad.png", contentType: "image/png", content: corruptedBuffer },
|
{ name: "file", filename: "bad.png", contentType: "image/png", content: corruptedBuffer },
|
||||||
@@ -230,9 +230,9 @@ describe("image-to-pdf", () => {
|
|||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(res.statusCode).toBe(422);
|
expect(res.statusCode).toBe(400);
|
||||||
const json = JSON.parse(res.body);
|
const json = JSON.parse(res.body);
|
||||||
expect(json.error).toContain("PDF creation failed");
|
expect(json.error).toContain("Invalid image");
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── HEIC input handling ───────────────────────────────────────────
|
// ── HEIC input handling ───────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user