@sync-in/server
Version:
The secure, open-source platform for file storage, sharing, collaboration, and sync
134 lines (133 loc) • 4.54 kB
JavaScript
/*
* Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com>
* This file is part of Sync-in | The open source file sync and share solution
* See the LICENSE file for licensing details
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: Object.getOwnPropertyDescriptor(all, name).get
});
}
_export(exports, {
get convertImageToBase64 () {
return convertImageToBase64;
},
get generateAvatar () {
return generateAvatar;
},
get generateThumbnail () {
return generateThumbnail;
},
get pngMimeType () {
return pngMimeType;
},
get svgMimeType () {
return svgMimeType;
},
get webpMimeType () {
return webpMimeType;
}
});
const _promises = /*#__PURE__*/ _interop_require_default(require("node:fs/promises"));
const _nodeos = /*#__PURE__*/ _interop_require_default(require("node:os"));
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
const _nodeutil = require("node:util");
const _sharp = /*#__PURE__*/ _interop_require_default(require("sharp"));
const _texttosvg = /*#__PURE__*/ _interop_require_default(require("text-to-svg" // Sharp settings
));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
// Sharp settings
_sharp.default.cache(false);
_sharp.default.concurrency(Math.min(2, _nodeos.default.cpus()?.length || 1));
const pngMimeType = 'image/png';
const svgMimeType = 'image/svg+xml';
const webpMimeType = 'image/webp';
const avatarSize = 256;
const fontPath = _nodepath.default.join(__dirname, 'fonts', 'avatar.ttf');
const loadTextToSVG = (0, _nodeutil.promisify)(_texttosvg.default.load.bind(_texttosvg.default));
let textToSvgCache = null;
async function generateThumbnail(filePath, size) {
return (0, _sharp.default)(filePath, {
failOn: 'none',
sequentialRead: true,
limitInputPixels: 268e6 // protects against extremely large images
}).rotate().resize({
width: size,
height: size,
fit: 'inside',
kernel: 'nearest',
withoutEnlargement: true,
fastShrinkOnLoad: true // true by default, added for clarity
}).webp({
quality: 80,
effort: 0,
alphaQuality: 90
});
}
async function generateAvatar(initials) {
const tts = await getTextToSvg();
const { backgroundColor, foregroundColor } = randomColor();
const fontSize = fitFontSize(tts, initials, avatarSize * 0.8, 170);
const d = tts.getD(initials, {
x: avatarSize / 2,
y: avatarSize / 2.1,
fontSize,
anchor: 'center middle'
});
const svg = `
<svg width="${avatarSize}" height="${avatarSize}" viewBox="0 0 ${avatarSize} ${avatarSize}"
xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="${backgroundColor}"/>
<path d="${d}" fill="${foregroundColor}" />
</svg>`.trim();
return (0, _sharp.default)(Buffer.from(svg, 'utf8')).png();
}
async function convertImageToBase64(imgPath) {
const base64String = await _promises.default.readFile(imgPath, {
encoding: 'base64'
});
return `data:image/png;base64,${base64String}`;
}
function randomColor() {
let color = '';
while(color.length < 6){
/* sometimes the returned value does not have
* the 6 digits needed, so we do it again until
* it does
*/ color = Math.floor(Math.random() * 16777215).toString(16);
}
const red = parseInt(color.substring(0, 2), 16);
const green = parseInt(color.substring(2, 4), 16);
const blue = parseInt(color.substring(4, 6), 16);
const brightness = red * 0.299 + green * 0.587 + blue * 0.114;
return {
backgroundColor: `#${color}`,
foregroundColor: brightness > 180 ? '#000000' : '#ffffff'
};
}
function fitFontSize(tts, text, box, start = 170) {
// Heuristic to make the text occupy ~80% of the available width
let size = start;
// Lower bound to prevent infinite loops when the font renders very small
while(size > 20){
const m = tts.getMetrics(text, {
fontSize: size,
anchor: 'center middle'
});
if (m.width <= box) break;
size -= 4;
}
return size;
}
function getTextToSvg() {
return textToSvgCache ??= loadTextToSVG(fontPath);
}
//# sourceMappingURL=image.js.map