Fixed minor avatar cropping bug in avatars.js (#4192) * Fixed avatar cropping bug in avatars.js Persona icons/profile pictures were forced to a 2:3 ratio despite "Never resize avatars" being enabled. Moved a misplaced line of code inside the crop condition to fix this behavior. * Fix indentation for ESLint compliance **Attempted** to correct indentation on line 49 of avatars.js to pass ESLint check. * Update avatars.js Gave my best to fix it :( At the very least the issue is known now. * Update avatars.js Removed empty characters (formatting) * Fixed avatars.js Fixed the issue of avatar's not changing after my recent revision. * Update avatars.js Accidentally left a duplicate line; removed it now * Extract reusable function for avatar resize --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

e3923a7be31748221dfc2b1ad078c2e6dd0eba60

02loka <153330008+02loka@users.noreply.github.com>

Signed
2 files changed, +21 -31Showing whitespace changes
src/endpoints/avatars.js+3 -9
@@ -3,12 +3,12 @@ import fs from 'node:fs';
33
4import express from 'express';4import express from 'express';
5import sanitize from 'sanitize-filename';5import sanitize from 'sanitize-filename';
6import { Jimp, JimpMime } from '../jimp.js';6import { Jimp } from '../jimp.js';
7import { sync as writeFileAtomicSync } from 'write-file-atomic';7import { sync as writeFileAtomicSync } from 'write-file-atomic';
88
9import { AVATAR_WIDTH, AVATAR_HEIGHT } from '../constants.js';
10import { getImages, tryParse } from '../util.js';9import { getImages, tryParse } from '../util.js';
11import { getFileNameValidationFunction } from '../middleware/validateFileName.js';10import { getFileNameValidationFunction } from '../middleware/validateFileName.js';
11import { applyAvatarCropResize } from './characters.js';
1212
13export const router = express.Router();13export const router = express.Router();
1414
@@ -42,13 +42,7 @@ router.post('/upload', getFileNameValidationFunction('overwrite_name'), async (r
42 const pathToUpload = path.join(request.file.destination, request.file.filename);42 const pathToUpload = path.join(request.file.destination, request.file.filename);
43 const crop = tryParse(request.query.crop);43 const crop = tryParse(request.query.crop);
44 const rawImg = await Jimp.read(pathToUpload);44 const rawImg = await Jimp.read(pathToUpload);
4545 const image = await applyAvatarCropResize(rawImg, crop);
46 if (typeof crop == 'object' && [crop.x, crop.y, crop.width, crop.height].every(x => typeof x === 'number')) {
47 rawImg.crop({ w: crop.width, h: crop.height, x: crop.x, y: crop.y });
48 }
49
50 rawImg.cover({ w: AVATAR_WIDTH, h: AVATAR_HEIGHT });
51 const image = await rawImg.getBuffer(JimpMime.png);
5246
53 const filename = request.body.overwrite_name || `${Date.now()}.png`;47 const filename = request.body.overwrite_name || `${Date.now()}.png`;
54 const pathToNewFile = path.join(request.user.directories.avatars, filename);48 const pathToNewFile = path.join(request.user.directories.avatars, filename);
src/endpoints/characters.js+18 -22
@@ -270,13 +270,14 @@ async function writeCharacterData(inputFile, data, outputFile, request, crop = u
270 */270 */
271271
272/**272/**
273 * Parses an image buffer and applies crop if defined.273 * Applies avatar crop and resize operations to an image.
274 * @param {Buffer} buffer Buffer of the image274 * I couldn't fix the type issue, so the first argument has {any} type.
275 * @param {object} jimp Jimp image instance
275 * @param {Crop|undefined} [crop] Crop parameters276 * @param {Crop|undefined} [crop] Crop parameters
276 * @returns {Promise<Buffer>} Image buffer277 * @returns {Promise<Buffer>} Processed image buffer
277 */278 */
278async function parseImageBuffer(buffer, crop) {279export async function applyAvatarCropResize(jimp, crop) {
279 const image = await Jimp.fromBuffer(buffer);280 const image = /** @type {InstanceType<typeof import('../jimp.js').Jimp>} */ (jimp);
280 let finalWidth = image.bitmap.width, finalHeight = image.bitmap.height;281 let finalWidth = image.bitmap.width, finalHeight = image.bitmap.height;
281282
282 // Apply crop if defined283 // Apply crop if defined
@@ -297,6 +298,17 @@ async function parseImageBuffer(buffer, crop) {
297}298}
298299
299/**300/**
301 * Parses an image buffer and applies crop if defined.
302 * @param {Buffer} buffer Buffer of the image
303 * @param {Crop|undefined} [crop] Crop parameters
304 * @returns {Promise<Buffer>} Image buffer
305 */
306async function parseImageBuffer(buffer, crop) {
307 const image = await Jimp.fromBuffer(buffer);
308 return await applyAvatarCropResize(image, crop);
309}
310
311/**
300 * Reads an image file and applies crop if defined.312 * Reads an image file and applies crop if defined.
301 * @param {string} imgPath Path to the image file313 * @param {string} imgPath Path to the image file
302 * @param {Crop|undefined} crop Crop parameters314 * @param {Crop|undefined} crop Crop parameters
@@ -305,23 +317,7 @@ async function parseImageBuffer(buffer, crop) {
305async function tryReadImage(imgPath, crop) {317async function tryReadImage(imgPath, crop) {
306 try {318 try {
307 const rawImg = await Jimp.read(imgPath);319 const rawImg = await Jimp.read(imgPath);
308 let finalWidth = rawImg.bitmap.width, finalHeight = rawImg.bitmap.height;320 return await applyAvatarCropResize(rawImg, crop);
309
310 // Apply crop if defined
311 if (typeof crop == 'object' && [crop.x, crop.y, crop.width, crop.height].every(x => typeof x === 'number')) {
312 rawImg.crop({ x: crop.x, y: crop.y, w: crop.width, h: crop.height });
313 // Apply standard resize if requested
314 if (crop.want_resize) {
315 finalWidth = AVATAR_WIDTH;
316 finalHeight = AVATAR_HEIGHT;
317 } else {
318 finalWidth = crop.width;
319 finalHeight = crop.height;
320 }
321 }
322
323 rawImg.cover({ w: finalWidth, h: finalHeight });
324 return await rawImg.getBuffer(JimpMime.png);
325 }321 }
326 // If it's an unsupported type of image (APNG) - just read the file as buffer322 // If it's an unsupported type of image (APNG) - just read the file as buffer
327 catch (error) {323 catch (error) {