mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-11 17:26:45 +02:00
Improve text rendering on images by removing emojis and handling long words
Add emoji removal from text before rendering on images. Implement logic to break long words that exceed the maximum text width. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: d948b4ab-8a36-4043-aed1-fc4629c72817 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/N0tTJcl
This commit is contained in:
1 parent
4a2ff72072
commit
5053cca73e
2 files changed
+66
-2
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 509 KiB |
@@ -108,6 +108,21 @@ export class ImageProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes emojis from text since canvas doesn't support them natively
|
||||
*/
|
||||
private removeEmojis(text: string): string {
|
||||
// Remove emojis and other unicode symbols that canvas can't render
|
||||
// Using a regex that removes emoji-like characters
|
||||
return text
|
||||
.replace(/[\u2600-\u26FF]/g, '') // Misc symbols
|
||||
.replace(/[\u2700-\u27BF]/g, '') // Dingbats
|
||||
.replace(/[\uD800-\uDFFF]/g, '') // Surrogate pairs (emoji range)
|
||||
.replace(/[\uFE00-\uFE0F]/g, '') // Variation selectors
|
||||
.replace(/\s+/g, ' ') // Normalize whitespace
|
||||
.trim();
|
||||
}
|
||||
|
||||
async addTextToStoryImage(imageUrl: string, text: string): Promise<Buffer> {
|
||||
const STORY_WIDTH = 1080;
|
||||
const STORY_HEIGHT = 1920;
|
||||
@@ -117,6 +132,9 @@ export class ImageProcessor {
|
||||
const PADDING = 40;
|
||||
const LINE_HEIGHT_MULTIPLIER = 1.3;
|
||||
|
||||
// Remove emojis from text as canvas doesn't support them
|
||||
const cleanText = this.removeEmojis(text);
|
||||
|
||||
const canvas = createCanvas(STORY_WIDTH, STORY_HEIGHT);
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
@@ -161,7 +179,7 @@ export class ImageProcessor {
|
||||
while (fontSize >= MIN_FONT_SIZE) {
|
||||
ctx.font = `bold ${fontSize}px "DejaVu Sans", Arial, sans-serif`;
|
||||
|
||||
lines = this.wrapText(ctx, text, maxTextWidth);
|
||||
lines = this.wrapText(ctx, cleanText, maxTextWidth);
|
||||
const lineHeight = fontSize * LINE_HEIGHT_MULTIPLIER;
|
||||
totalTextHeight = lines.length * lineHeight;
|
||||
|
||||
@@ -175,7 +193,7 @@ export class ImageProcessor {
|
||||
if (fontSize < MIN_FONT_SIZE) {
|
||||
fontSize = MIN_FONT_SIZE;
|
||||
ctx.font = `bold ${fontSize}px "DejaVu Sans", Arial, sans-serif`;
|
||||
lines = this.wrapText(ctx, text, maxTextWidth);
|
||||
lines = this.wrapText(ctx, cleanText, maxTextWidth);
|
||||
const lineHeight = fontSize * LINE_HEIGHT_MULTIPLIER;
|
||||
totalTextHeight = lines.length * lineHeight;
|
||||
|
||||
@@ -214,8 +232,27 @@ export class ImageProcessor {
|
||||
const metrics = ctx.measureText(testLine);
|
||||
|
||||
if (metrics.width > maxWidth && currentLine) {
|
||||
// Current line would exceed width, push it and start new line
|
||||
lines.push(currentLine);
|
||||
currentLine = word;
|
||||
|
||||
// Check if the word itself is too long for a single line
|
||||
const wordMetrics = ctx.measureText(word);
|
||||
if (wordMetrics.width > maxWidth) {
|
||||
// Word is too long, need to break it into chunks
|
||||
const chunks = this.breakLongWord(ctx, word, maxWidth);
|
||||
for (let i = 0; i < chunks.length - 1; i++) {
|
||||
lines.push(chunks[i]);
|
||||
}
|
||||
currentLine = chunks[chunks.length - 1];
|
||||
}
|
||||
} else if (metrics.width > maxWidth && !currentLine) {
|
||||
// First word is too long, need to break it
|
||||
const chunks = this.breakLongWord(ctx, word, maxWidth);
|
||||
for (let i = 0; i < chunks.length - 1; i++) {
|
||||
lines.push(chunks[i]);
|
||||
}
|
||||
currentLine = chunks[chunks.length - 1];
|
||||
} else {
|
||||
currentLine = testLine;
|
||||
}
|
||||
@@ -227,6 +264,33 @@ export class ImageProcessor {
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks a long word into chunks that fit within maxWidth
|
||||
*/
|
||||
private breakLongWord(ctx: any, word: string, maxWidth: number): string[] {
|
||||
const chunks: string[] = [];
|
||||
let currentChunk = '';
|
||||
|
||||
for (let i = 0; i < word.length; i++) {
|
||||
const char = word[i];
|
||||
const testChunk = currentChunk + char;
|
||||
const metrics = ctx.measureText(testChunk);
|
||||
|
||||
if (metrics.width > maxWidth && currentChunk) {
|
||||
chunks.push(currentChunk);
|
||||
currentChunk = char;
|
||||
} else {
|
||||
currentChunk = testChunk;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentChunk) {
|
||||
chunks.push(currentChunk);
|
||||
}
|
||||
|
||||
return chunks.length > 0 ? chunks : [word];
|
||||
}
|
||||
}
|
||||
|
||||
export const imageProcessor = new ImageProcessor();
|
||||
Reference in new issue
Block a user