Я работаю над проектом Next.js 15, где динамически генерирую изображения. Реализация отлично работает на Localhost, но после развертывания в Vercel сгенерированное изображение содержит пустые ящики вместо текста.
Даже при использовании системного шрифта, подобного «Arial», проблема сохраняется.'use server'
import sharp from 'sharp'
import path from 'path'
interface CreateArgs {
nomineName: string
awardName: string
regionName: string
}
// Estimate font size so text fits in 1 line and does not exceed SVG width
function getNomineFontSize(name: string, maxFont = 48, minFont = 16, maxWidth = 380) {
// Average character width factor (adjust for your font)
const avgCharWidth = 0.6 // 0.6em per character is a good estimate for most sans-serif fonts
const estimatedFontSize = Math.floor(maxWidth / (name.length * avgCharWidth))
return Math.max(minFont, Math.min(maxFont, estimatedFontSize))
}
// Helper to wrap text at a max character length per line, maxLines = 2
function wrapText(text: string, maxChars = 20, maxLines = 2) {
const words = text.split(' ')
const lines: string[] = []
let currentLine = ''
for (const word of words) {
if ((currentLine + ' ' + word).trim().length > maxChars) {
lines.push(currentLine.trim())
currentLine = word
if (lines.length === maxLines - 1) {
// Add the rest of the words to the last line and break
currentLine += ' ' + words.slice(words.indexOf(word) + 1).join(' ')
break
}
} else {
currentLine += ' ' + word
}
}
if (currentLine) lines.push(currentLine.trim())
// Ensure no more than maxLines
return lines.slice(0, maxLines)
}
// Estimate font size for award lines so each line fits in maxWidth (e.g. 390px)
function getAwardFontSize(lines: string[], maxFont = 28, minFont = 14, maxWidth = 390) {
// Find the longest line
const longest = lines.reduce((a, b) => (a.length > b.length ? a : b), '')
const avgCharWidth = 0.6
const estimatedFontSize = Math.floor(maxWidth / (longest.length * avgCharWidth))
return Math.max(minFont, Math.min(maxFont, estimatedFontSize))
}
export const createBadge = async ({
nomineName,
awardName,
regionName
}: CreateArgs) => {
try {
const rootPath = process.cwd()
const imagePath = path.resolve(rootPath, './public/badge/badge.png') // Path to your image
const image = sharp(imagePath)
// Nominee name font size (1 line, full width)
const nomineFontSize = getNomineFontSize(nomineName)
// Award name: wrap to 2 lines, font size so each line fits maxWidth
const awardLines = wrapText(awardName, 20, 2)
const awardFontSize = getAwardFontSize(awardLines)
const awardTspans = awardLines
.map((line, i) => `${line}`)
.join('')
const svgText = `
.nomine { fill: #ba8f30; font-size: ${nomineFontSize}px; font-weight: bold; font-family: "Arial", sans-serif; }
.region { fill: #ba8f30; font-size: 40px; font-weight: bold; font-family: "Arial", sans-serif; }
.award { fill: white; font-size: ${awardFontSize}px; font-weight: bold; font-family: "Arial", sans-serif; }
${nomineName}
${regionName}
${awardTspans}
`
const buffer = await image
.composite([{ input: Buffer.from(svgText), top: 30, left: 30 }])
.toBuffer()
return { buffer}
} catch (error) {
console.error('Error generating badge:', error)
throw error // Rethrow the error to be handled by the caller
}
}
< /code>
Попытки устранения неполадок: < /p>
работает правильно на локальном < /li>
Пробое использовал системные шрифты (например, ариальные) < /li>
Проблема сохраняется только на развертывании Vercel
, я прилагал изображения, и в Vercost vercost vercost vercost vercost vercost wercost wercost wercost. /> < /ul>
сгенерированное изображение Localhost
< /p>
Сгенерированное изображением Verce
Подробнее здесь: https://stackoverflow.com/questions/796 ... next-js-15