guanghulab/image-studio/renderer.js
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

89 lines
2.7 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.

/**
* ═══════════════════════════════════════════════════
* 铸渊图片工作室 · Puppeteer 渲染引擎
* ═══════════════════════════════════════════════════
*/
import puppeteer from 'puppeteer'
import { existsSync, mkdirSync } from 'fs'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const OUTPUT_DIR = join(__dirname, 'output')
/**
* 渲染 HTML 为图片
* @param {string} html - 完整 HTML 字符串
* @param {object} opts
* @param {number} opts.width - 图片宽度
* @param {number} opts.height - 图片高度
* @param {string} opts.name - 输出文件名(不含扩展名)
* @param {string} opts.format - 输出格式 png/webp/jpeg
* @param {number} opts.quality- 图片质量(仅 webp/jpeg
* @returns {Promise<string>} 输出文件路径
*/
export async function renderToImage(html, opts = {}) {
const {
width = 1080,
height = 1440,
name = `card_${Date.now()}`,
format = 'png',
quality = 90,
} = opts
if (!existsSync(OUTPUT_DIR)) mkdirSync(OUTPUT_DIR, { recursive: true })
const browser = await puppeteer.launch({
headless: true,
executablePath: '/usr/bin/google-chrome',
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-features=TranslateUI', '--font-render-hinting=none'],
})
try {
const page = await browser.newPage()
await page.setViewport({ width, height })
// 注入完整的 HTML
await page.setContent(html, { waitUntil: 'networkidle0' })
// 等待字体加载
await page.evaluate(() => document.fonts.ready)
// 额外等一会儿让渲染稳定
await new Promise(r => setTimeout(r, 500))
const outputPath = join(OUTPUT_DIR, `${name}.${format}`)
await page.screenshot({
path: outputPath,
type: format,
clip: { x: 0, y: 0, width, height },
quality: format !== 'png' ? quality : undefined,
})
return outputPath
} finally {
await browser.close()
}
}
/**
* 批量生成多页(轮播图)
* @param {string[]} pages - 每页的 HTML
* @param {object} opts
* @returns {Promise<string[]>} 输出文件路径数组
*/
export async function renderCarousel(pages, opts = {}) {
const results = []
for (let i = 0; i < pages.length; i++) {
const name = opts.baseName
? `${opts.baseName}_p${i + 1}`
: `carousel_${Date.now()}_p${i + 1}`
const path = await renderToImage(pages[i], { ...opts, name })
results.push(path)
}
return results
}