91 lines
2.2 KiB
JavaScript
91 lines
2.2 KiB
JavaScript
import { ImageResponse } from "@vercel/og";
|
|
|
|
export const config = {
|
|
runtime: "edge",
|
|
};
|
|
|
|
/**
|
|
* Generates a dynamic WebP image with text using a custom font.
|
|
* @param {Request} req
|
|
* @returns {Promise<Response>}
|
|
*/
|
|
export default async function handler(req) {
|
|
const { searchParams } = new URL(req.url, "http://localhost");
|
|
const text = searchParams.get("text")?.trim() || "Demo";
|
|
|
|
const fontUrl = "https://lib.satki.net/fonts/Inter-Bold.ttf";
|
|
|
|
let fontData;
|
|
try {
|
|
const fontResponse = await fetch(fontUrl);
|
|
if (!fontResponse.ok) throw new Error(`Font fetch failed: ${fontResponse.status}`);
|
|
fontData = await fontResponse.arrayBuffer();
|
|
} catch (error) {
|
|
return new Response(
|
|
JSON.stringify({ error: "Failed to fetch font", message: error.message }),
|
|
{ status: 500, headers: { "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
|
|
const width = 2048;
|
|
const height = 1170;
|
|
|
|
const containerStyle = {
|
|
height: `${height}px`,
|
|
width: `${width}px`,
|
|
display: "flex",
|
|
textAlign: "center",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
flexDirection: "column",
|
|
flexWrap: "nowrap",
|
|
backgroundColor: "black",
|
|
backgroundImage: [
|
|
"radial-gradient(circle at 25px 25px, #676767 2%, transparent 0%)",
|
|
"radial-gradient(circle at 75px 75px, #676767 2%, transparent 0%)",
|
|
].join(", "),
|
|
backgroundSize: "100px 100px",
|
|
};
|
|
|
|
const textStyle = {
|
|
display: "flex",
|
|
fontSize: "95px",
|
|
fontStyle: "normal",
|
|
color: "white",
|
|
lineHeight: 1.8,
|
|
whiteSpace: "pre-wrap",
|
|
};
|
|
|
|
const image = new ImageResponse(
|
|
(
|
|
<div style={containerStyle}>
|
|
<div style={textStyle}>
|
|
<b>{text}</b>
|
|
</div>
|
|
</div>
|
|
),
|
|
{
|
|
width,
|
|
height,
|
|
fonts: [
|
|
{
|
|
name: "Inter",
|
|
data: fontData,
|
|
style: "normal",
|
|
weight: 700,
|
|
},
|
|
],
|
|
// Force output to WebP
|
|
contentType: "image/webp",
|
|
quality: 80, // <- not officially documented, but works with newer versions
|
|
}
|
|
);
|
|
|
|
return new Response(image.body, {
|
|
headers: {
|
|
"Content-Type": "image/webp",
|
|
"Content-Disposition": `inline; filename="${encodeURIComponent(text)}.webp"`,
|
|
},
|
|
});
|
|
}
|