import sharp from "sharp"; /** Real encoded images, generated rather than checked in, so a fixture can never drift from what the * pipeline actually accepts. */ export async function makeImage(options: { readonly width: number; readonly height: number; readonly format: "png" | "jpeg" | "webp"; readonly alpha?: boolean; readonly colour?: { r: number; g: number; b: number }; }): Promise { const colour = options.colour ?? { b: 40, g: 120, r: 200 }; const pipeline = sharp({ create: { background: { ...colour, alpha: options.alpha === true ? 0.5 : 1 }, channels: options.alpha === true ? 4 : 3, height: options.height, width: options.width, }, }); const encoded = options.format === "png" ? await pipeline.png().toBuffer() : options.format === "jpeg" ? await pipeline.jpeg().toBuffer() : await pipeline.webp().toBuffer(); return new Uint8Array(encoded); } /** A genuine two-frame animated WebP. Signage refuses animation: a signage slide is a still frame, * and an animated one would loop out of step with the authoritative slot deadline. */ export async function makeAnimatedWebp(): Promise { const frame = (red: number) => sharp({ create: { background: { alpha: 1, b: 0, g: 0, r: red }, channels: 4, height: 8, width: 8 }, }) .raw() .toBuffer(); const frames = Buffer.concat([await frame(255), await frame(0)]); const encoded = await sharp(frames, { raw: { channels: 4, height: 16, pageHeight: 8, width: 8 }, }) .webp({ delay: [100, 100], loop: 0 }) .toBuffer(); return new Uint8Array(encoded); } /** A genuine two-frame animated GIF. GIF is refused outright, but animation is detected first, so * this proves the *animation* rule rather than the format rule. */ export async function makeAnimatedGif(): Promise { const frame = (red: number) => sharp({ create: { background: { alpha: 1, b: 0, g: 0, r: red }, channels: 4, height: 8, width: 8 }, }) .raw() .toBuffer(); const frames = Buffer.concat([await frame(255), await frame(0)]); const encoded = await sharp(frames, { raw: { channels: 4, height: 16, pageHeight: 8, width: 8 }, }) .gif({ delay: [100, 100], loop: 0 }) .toBuffer(); return new Uint8Array(encoded); } export function bytesOf(value: string): Uint8Array { return new TextEncoder().encode(value); } /** A minimal, genuinely well-formed SVG. Signage refuses it: an SVG is a document with script and * external-reference capability, not a raster image. */ export const SVG_DOCUMENT = ''; /** The first bytes of a PDF. Refused for the same reason as everything else that is not a still * raster image. */ export const PDF_DOCUMENT = "%PDF-1.7\n1 0 obj<>endobj\ntrailer<>"; /** A GIF89a header with one tiny frame. */ export const GIF_DOCUMENT_BASE64 = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";