From c0fb5896bb2f29f53d764d3a446b38c21b2c69f1 Mon Sep 17 00:00:00 2001 From: SnapOtter Date: Fri, 8 May 2026 00:00:39 +0800 Subject: [PATCH] feat: add RAW magic bytes to image-engine detect.ts and MIME entries for 17 new RAW formats --- packages/image-engine/src/formats/detect.ts | 20 ++++++++++++ packages/image-engine/src/utils/mime.ts | 34 +++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/packages/image-engine/src/formats/detect.ts b/packages/image-engine/src/formats/detect.ts index 8b4f0561..292d6eb0 100644 --- a/packages/image-engine/src/formats/detect.ts +++ b/packages/image-engine/src/formats/detect.ts @@ -20,6 +20,20 @@ const MAGIC_BYTES: Array<{ bytes: number[]; offset: number; format: string }> = { bytes: [0x38, 0x42, 0x50, 0x53], offset: 0, format: "psd" }, // OpenEXR { bytes: [0x76, 0x2f, 0x31, 0x01], offset: 0, format: "exr" }, + // CR3 (Canon ISOBMFF RAW) - ftyp box at offset 4 + { bytes: [0x66, 0x74, 0x79, 0x70], offset: 4, format: "cr3" }, + // Fujifilm RAF: "FUJIFILMCCD-RAW" at offset 0 + { + bytes: [ + 0x46, 0x55, 0x4a, 0x49, 0x46, 0x49, 0x4c, 0x4d, 0x43, 0x43, 0x44, 0x2d, 0x52, 0x41, 0x57, + ], + offset: 0, + format: "raf", + }, + // Sigma X3F: "FOVb" at offset 0 + { bytes: [0x46, 0x4f, 0x56, 0x62], offset: 0, format: "x3f" }, + // Minolta MRW: "\x00MRM" at offset 0 + { bytes: [0x00, 0x4d, 0x52, 0x4d], offset: 0, format: "mrw" }, ]; /** @@ -67,6 +81,12 @@ function detectByMagicBytes(buffer: Buffer): string { const brand = buffer.slice(8, 12).toString("ascii"); if (brand !== "avif" && brand !== "avis") continue; } + // For ftyp, verify CR3 brand at bytes 8-11 + if (entry.format === "cr3") { + if (buffer.length < 12) continue; + const brand = buffer.slice(8, 12).toString("ascii"); + if (brand !== "crx ") continue; + } return entry.format; } } diff --git a/packages/image-engine/src/utils/mime.ts b/packages/image-engine/src/utils/mime.ts index 18c255ab..d2b0c1d8 100644 --- a/packages/image-engine/src/utils/mime.ts +++ b/packages/image-engine/src/utils/mime.ts @@ -19,6 +19,23 @@ const EXT_TO_MIME: Record = { arw: "image/x-sony-arw", orf: "image/x-olympus-orf", rw2: "image/x-panasonic-rw2", + cr3: "image/x-canon-cr3", + raf: "image/x-fuji-raf", + pef: "image/x-pentax-pef", + "3fr": "image/x-hasselblad-3fr", + iiq: "image/x-phaseone-iiq", + srw: "image/x-samsung-srw", + x3f: "image/x-sigma-x3f", + rwl: "image/x-leica-rwl", + nrw: "image/x-nikon-nrw", + gpr: "image/x-gopro-gpr", + fff: "image/x-hasselblad-fff", + mrw: "image/x-minolta-mrw", + mef: "image/x-mamiya-mef", + kdc: "image/x-kodak-kdc", + dcr: "image/x-kodak-dcr", + erf: "image/x-epson-erf", + ptx: "image/x-pentax-ptx", tga: "image/x-tga", psd: "image/vnd.adobe.photoshop", exr: "image/x-exr", @@ -44,6 +61,23 @@ const MIME_TO_EXT: Record = { "image/x-sony-arw": "arw", "image/x-olympus-orf": "orf", "image/x-panasonic-rw2": "rw2", + "image/x-canon-cr3": "cr3", + "image/x-fuji-raf": "raf", + "image/x-pentax-pef": "pef", + "image/x-hasselblad-3fr": "3fr", + "image/x-phaseone-iiq": "iiq", + "image/x-samsung-srw": "srw", + "image/x-sigma-x3f": "x3f", + "image/x-leica-rwl": "rwl", + "image/x-nikon-nrw": "nrw", + "image/x-gopro-gpr": "gpr", + "image/x-hasselblad-fff": "fff", + "image/x-minolta-mrw": "mrw", + "image/x-mamiya-mef": "mef", + "image/x-kodak-kdc": "kdc", + "image/x-kodak-dcr": "dcr", + "image/x-epson-erf": "erf", + "image/x-pentax-ptx": "ptx", "image/x-tga": "tga", "image/vnd.adobe.photoshop": "psd", "image/x-exr": "exr",