feat: initial Signage 0.1.0
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
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<Uint8Array> {
|
||||
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<Uint8Array> {
|
||||
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<Uint8Array> {
|
||||
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 =
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><rect width="10" height="10"/></svg>';
|
||||
|
||||
/** 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<</Type/Catalog>>endobj\ntrailer<</Root 1 0 R>>";
|
||||
|
||||
/** A GIF89a header with one tiny frame. */
|
||||
export const GIF_DOCUMENT_BASE64 = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||
Reference in New Issue
Block a user