Files
nobat724_front/scripts/make-blog-cover.mjs
T
hamed cea8982e6d feat: add script to generate default blog cover image
- Introduced a new script `make-blog-cover.mjs` to create a default cover image for blog posts.
- The image is generated in PNG format with dimensions 1200x630, suitable for Open Graph.
- Utilizes the site's branding colors and logo from `public/nobat724.svg`.
- Includes custom font styling using the Vazirmatn font.
- The generated image is saved to `public/assets/images/blog-default-cover.png`.
2026-07-27 19:07:30 +03:30

61 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* تولید تصویر پیش‌فرض مقاله‌ها — `public/assets/images/blog-default-cover.png`.
*
* یک‌بار اجرا می‌شود و خروجی PNG کامیت می‌گردد؛ اسکریپت می‌ماند تا اگر برند یا
* ابعاد عوض شد، تصویر قابل بازتولید باشد:
*
* node scripts/make-blog-cover.mjs
*
* ابعاد ۱۲۰۰×۶۳۰ استاندارد Open Graph است و با `aspect-video` کارت‌ها هم می‌خواند.
* رنگ‌ها و نشان از `public/nobat724.svg` می‌آیند تا با لوگوی سایت یکی بماند.
*/
import sharp from 'sharp';
import { readFileSync, writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
const W = 1200;
const H = 630;
const BRAND = '#5559CE'; // stroke نشان در public/nobat724.svg
const ACCENT = '#FF8848'; // پلاس نارنجی همان نشان
const OUT = join(ROOT, 'public/assets/images/blog-default-cover.png');
const mark = readFileSync(join(ROOT, 'public/nobat724.svg'), 'utf8')
.replace(/<\?xml[^>]*\?>/, '')
.replace(/width="\d+"\s+height="\d+"/, 'width="200" height="200"');
const fontPath = join(ROOT, 'public/fonts/vazir/Vazirmatn-RD-FD-Bold.ttf');
const fontData = readFileSync(fontPath).toString('base64');
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}">
<defs>
<style>
@font-face {
font-family: 'VazirmatnCover';
src: url('data:font/ttf;base64,${fontData}') format('truetype');
}
.brand { font-family: 'VazirmatnCover'; font-size: 68px; fill: #1F2247; }
.sub { font-family: 'VazirmatnCover'; font-size: 32px; fill: #6B6E8F; }
</style>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#F7F8FE"/>
<stop offset="100%" stop-color="#ECEDFB"/>
</linearGradient>
</defs>
<rect width="${W}" height="${H}" fill="url(#bg)"/>
<rect x="0" y="0" width="${W}" height="10" fill="${BRAND}"/>
<rect x="0" y="${H - 10}" width="${W}" height="10" fill="${ACCENT}"/>
<g transform="translate(${W / 2 - 100}, 130)">${mark}</g>
<text class="brand" x="${W / 2}" y="420" text-anchor="middle" direction="rtl">نوبت ۷۲۴</text>
<text class="sub" x="${W / 2}" y="480" text-anchor="middle" direction="rtl">مقالات و اخبار پزشکی</text>
</svg>`;
// density بالا برای متن تیز، سپس resize به ابعاد دقیق OG.
const png = await sharp(Buffer.from(svg), { density: 144 }).resize(W, H).png().toBuffer();
writeFileSync(OUT, png);
const meta = await sharp(png).metadata();
console.log(`✓ ${OUT}${meta.width}×${meta.height}, ${(png.length / 1024).toFixed(0)}KB`);