31 lines · 1.3 KB
import { readFileSync } from "node:fs";
import { join } from "node:path";
import type { OgRuntime } from "./render";

/** Bun counterpart of runtime.ts for tests and scripts/og-samples.tsx: the same precompiled modules, read from disk. */
export function loadRuntimeFromDisk(): OgRuntime {
  const root = join(import.meta.dir, "../../..");
  const bytes = (path: string) => {
    const buffer = readFileSync(join(root, path));
    return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) as ArrayBuffer;
  };
  return {
    yoga: new WebAssembly.Module(bytes("node_modules/satori/yoga.wasm")),
    resvg: new WebAssembly.Module(bytes("node_modules/@resvg/resvg-wasm/index_bg.wasm")),
    fonts: {
      regular: bytes("src/server/og/fonts/plex-sans-400.bin"),
      semibold: bytes("src/server/og/fonts/plex-sans-600.bin"),
    },
  };
}

const IHDR_END = 24;

/** Width and height from a PNG's IHDR chunk, or null when the bytes are not a PNG. */
export function pngSize(png: Uint8Array): { width: number; height: number } | null {
  const signature = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
  if (png.length < IHDR_END || signature.some((byte, index) => png[index] !== byte)) return null;
  const view = new DataView(png.buffer, png.byteOffset, png.byteLength);
  return { width: view.getUint32(16), height: view.getUint32(20) };
}