fix: auto-rotate images based on EXIF orientation for Facebook stories

This commit is contained in:
Michael committed 2026-01-12 13:50:28 +01:00
1 parent 6c299b954a
commit 509e36a05e
1 file changed
+14 -11
+14 -11
View File
@@ -62,6 +62,7 @@ export class ImageProcessor {
const fbFeedFilename = `${baseId}-fb-feed${fileExt}`; const fbFeedFilename = `${baseId}-fb-feed${fileExt}`;
const fbFeedPath = path.join(this.uploadsDir, fbFeedFilename); const fbFeedPath = path.join(this.uploadsDir, fbFeedFilename);
await sharp(buffer) await sharp(buffer)
.rotate() // Auto-rotate based on EXIF orientation
.resize(CROP_FORMATS.facebookFeed.width, CROP_FORMATS.facebookFeed.height, { .resize(CROP_FORMATS.facebookFeed.width, CROP_FORMATS.facebookFeed.height, {
fit: "cover", fit: "cover",
position: "center", position: "center",
@@ -73,6 +74,7 @@ export class ImageProcessor {
const igFeedFilename = `${baseId}-ig-feed${fileExt}`; const igFeedFilename = `${baseId}-ig-feed${fileExt}`;
const igFeedPath = path.join(this.uploadsDir, igFeedFilename); const igFeedPath = path.join(this.uploadsDir, igFeedFilename);
await sharp(buffer) await sharp(buffer)
.rotate() // Auto-rotate based on EXIF orientation
.resize(CROP_FORMATS.instagramFeed.width, CROP_FORMATS.instagramFeed.height, { .resize(CROP_FORMATS.instagramFeed.width, CROP_FORMATS.instagramFeed.height, {
fit: "cover", fit: "cover",
position: "center", position: "center",
@@ -84,6 +86,7 @@ export class ImageProcessor {
const igStoryFilename = `${baseId}-ig-story${fileExt}`; const igStoryFilename = `${baseId}-ig-story${fileExt}`;
const igStoryPath = path.join(this.uploadsDir, igStoryFilename); const igStoryPath = path.join(this.uploadsDir, igStoryFilename);
await sharp(buffer) await sharp(buffer)
.rotate() // Auto-rotate based on EXIF orientation
.resize(CROP_FORMATS.instagramStory.width, CROP_FORMATS.instagramStory.height, { .resize(CROP_FORMATS.instagramStory.width, CROP_FORMATS.instagramStory.height, {
fit: "cover", fit: "cover",
position: "center", position: "center",
@@ -118,7 +121,7 @@ export class ImageProcessor {
const MAX_FONT_SIZE = 72; const MAX_FONT_SIZE = 72;
const PADDING = 40; const PADDING = 40;
const LINE_HEIGHT_MULTIPLIER = 1.3; const LINE_HEIGHT_MULTIPLIER = 1.3;
// Remove hashtags from text while keeping emojis // Remove hashtags from text while keeping emojis
const cleanText = removeHashtags(text); const cleanText = removeHashtags(text);
@@ -126,7 +129,7 @@ export class ImageProcessor {
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
const image = await loadImage(imageUrl); const image = await loadImage(imageUrl);
const imgWidth = image.width; const imgWidth = image.width;
const imgHeight = image.height; const imgHeight = image.height;
const targetRatio = STORY_WIDTH / STORY_HEIGHT; const targetRatio = STORY_WIDTH / STORY_HEIGHT;
@@ -165,7 +168,7 @@ export class ImageProcessor {
while (fontSize >= MIN_FONT_SIZE) { while (fontSize >= MIN_FONT_SIZE) {
ctx.font = `bold ${fontSize}px "DejaVu Sans", Arial, sans-serif`; ctx.font = `bold ${fontSize}px "DejaVu Sans", Arial, sans-serif`;
lines = await emojiRenderer.wrapText(cleanText, ctx, maxTextWidth, fontSize); lines = await emojiRenderer.wrapText(cleanText, ctx, maxTextWidth, fontSize);
const lineHeight = fontSize * LINE_HEIGHT_MULTIPLIER; const lineHeight = fontSize * LINE_HEIGHT_MULTIPLIER;
totalTextHeight = lines.length * lineHeight; totalTextHeight = lines.length * lineHeight;
@@ -183,7 +186,7 @@ export class ImageProcessor {
lines = await emojiRenderer.wrapText(cleanText, ctx, maxTextWidth, fontSize); lines = await emojiRenderer.wrapText(cleanText, ctx, maxTextWidth, fontSize);
const lineHeight = fontSize * LINE_HEIGHT_MULTIPLIER; const lineHeight = fontSize * LINE_HEIGHT_MULTIPLIER;
totalTextHeight = lines.length * lineHeight; totalTextHeight = lines.length * lineHeight;
const maxLines = Math.floor(maxTextHeight / lineHeight); const maxLines = Math.floor(maxTextHeight / lineHeight);
if (lines.length > maxLines) { if (lines.length > maxLines) {
lines = lines.slice(0, maxLines); lines = lines.slice(0, maxLines);
@@ -220,12 +223,12 @@ export class ImageProcessor {
for (const word of words) { for (const word of words) {
const testLine = currentLine ? `${currentLine} ${word}` : word; const testLine = currentLine ? `${currentLine} ${word}` : word;
const metrics = ctx.measureText(testLine); const metrics = ctx.measureText(testLine);
if (metrics.width > maxWidth && currentLine) { if (metrics.width > maxWidth && currentLine) {
// Current line would exceed width, push it and start new line // Current line would exceed width, push it and start new line
lines.push(currentLine); lines.push(currentLine);
currentLine = word; currentLine = word;
// Check if the word itself is too long for a single line // Check if the word itself is too long for a single line
const wordMetrics = ctx.measureText(word); const wordMetrics = ctx.measureText(word);
if (wordMetrics.width > maxWidth) { if (wordMetrics.width > maxWidth) {
@@ -247,7 +250,7 @@ export class ImageProcessor {
currentLine = testLine; currentLine = testLine;
} }
} }
if (currentLine) { if (currentLine) {
lines.push(currentLine); lines.push(currentLine);
} }
@@ -261,12 +264,12 @@ export class ImageProcessor {
private breakLongWord(ctx: any, word: string, maxWidth: number): string[] { private breakLongWord(ctx: any, word: string, maxWidth: number): string[] {
const chunks: string[] = []; const chunks: string[] = [];
let currentChunk = ''; let currentChunk = '';
for (let i = 0; i < word.length; i++) { for (let i = 0; i < word.length; i++) {
const char = word[i]; const char = word[i];
const testChunk = currentChunk + char; const testChunk = currentChunk + char;
const metrics = ctx.measureText(testChunk); const metrics = ctx.measureText(testChunk);
if (metrics.width > maxWidth && currentChunk) { if (metrics.width > maxWidth && currentChunk) {
chunks.push(currentChunk); chunks.push(currentChunk);
currentChunk = char; currentChunk = char;
@@ -274,11 +277,11 @@ export class ImageProcessor {
currentChunk = testChunk; currentChunk = testChunk;
} }
} }
if (currentChunk) { if (currentChunk) {
chunks.push(currentChunk); chunks.push(currentChunk);
} }
return chunks.length > 0 ? chunks : [word]; return chunks.length > 0 ? chunks : [word];
} }
} }