Я работаю над проектом 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
Текст не рендеринг в генерации изображений на Vercel (Next.js 15) ⇐ Javascript
Форум по Javascript
-
Anonymous
1747547313
Anonymous
Я работаю над проектом 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
Подробнее здесь: [url]https://stackoverflow.com/questions/79627171/text-not-rendering-in-image-generation-on-vercel-next-js-15[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия