diff --git a/src/handler/image.ts b/src/handler/image.ts index f0709e39..9935fd73 100644 --- a/src/handler/image.ts +++ b/src/handler/image.ts @@ -9,6 +9,7 @@ const AVIF = 'image/avif' const WEBP = 'image/webp' +const APNG = 'image/apng' const PNG = 'image/png' const JPEG = 'image/jpeg' const GIF = 'image/gif' @@ -63,7 +64,7 @@ type ResolvedImageData = [string, number?, number?] | readonly [] export const cache = createLRU(100) const inflightRequests = new Map>() -const ALLOWED_IMAGE_TYPES = [PNG, JPEG, GIF, SVG] +const ALLOWED_IMAGE_TYPES = [PNG, APNG, JPEG, GIF, SVG] function arrayBufferToBase64(buffer) { let binary = '' @@ -120,6 +121,7 @@ function arrayBufferToDataUri(data: ArrayBuffer) { switch (imageType) { case PNG: + case APNG: imageSize = parsePNG(data) break case GIF: @@ -198,6 +200,7 @@ export async function resolveImageData( const data = base64ToArrayBuffer(dataString) switch (imageType) { case PNG: + case APNG: imageSize = parsePNG(data) break case GIF: @@ -283,6 +286,9 @@ function detectContentType(buffer: Uint8Array) { (b, i) => buffer[i] === b ) ) { + if (detectAPNG(buffer)) { + return APNG + } return PNG } if ([0x47, 0x49, 0x46, 0x38].every((b, i) => buffer[i] === b)) { @@ -307,3 +313,19 @@ function detectContentType(buffer: Uint8Array) { } return null } + +function detectAPNG(bytes: Uint8Array) { + const dv = new DataView(bytes.buffer) + let type: string, + length: number, + off = 8, + isAPNG = false + while (!isAPNG && type !== 'IEND' && off < bytes.length) { + length = dv.getUint32(off) + const chars = bytes.subarray(off + 4, off + 8) + type = String.fromCharCode(...chars) + isAPNG = type === 'acTL' + off += 12 + length + } + return isAPNG +}