Files
ldap-auth-service/lib/favicon.js
2026-08-24 17:46:07 +05:00

77 lines
2.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const logoPath = path.join(__dirname, '..', 'logo.png');
let faviconBuffer = null;
// Оборачивает PNG-изображения в ICO-контейнер (PNG-записи внутри ICO
// понимаются современными браузерами). entries: [{ png: Buffer, width }]
function buildIcoFromPngs(entries) {
const header = Buffer.alloc(6);
header.writeUInt16LE(0, 0); // reserved
header.writeUInt16LE(1, 2); // type: icon
header.writeUInt16LE(entries.length, 4);
const dirSize = 16 * entries.length;
let offset = 6 + dirSize;
const parts = [];
for (const e of entries) {
const dir = Buffer.alloc(16);
const w = e.width >= 256 ? 0 : e.width; // 0 означает 256
const h = e.height >= 256 ? 0 : e.height;
dir.writeUInt8(w, 0);
dir.writeUInt8(h, 1);
dir.writeUInt8(0, 2); // color count
dir.writeUInt8(0, 3); // reserved
dir.writeUInt16LE(1, 4); // planes
dir.writeUInt16LE(32, 6); // bit count
dir.writeUInt32LE(e.png.length, 8); // bytes in resource
dir.writeUInt32LE(offset, 12); // image offset
parts.push(dir);
offset += e.png.length;
}
return Buffer.concat([header, ...parts, ...entries.map((e) => e.png)]);
}
// Читает logo.png, уменьшает до 32x32 и 256x256 (с сохранением пропорций,
// без обрезки) и собирает favicon.ico. Возвращает Buffer или null.
async function loadFavicon() {
faviconBuffer = null;
try {
const png = fs.readFileSync(logoPath);
if (!png || png.length < 8 || png.toString('latin1', 0, 8) !== '\x89PNG\r\n\x1a\n') {
return null;
}
const meta = await sharp(png).metadata();
const baseWidth = meta.width || 256;
const baseHeight = meta.height || 256;
const smallPng = await sharp(png)
.resize({ width: 32, height: 32, fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
.png()
.toBuffer();
const largePng = await sharp(png)
.resize({ width: 256, height: 256, fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
.png()
.toBuffer();
faviconBuffer = buildIcoFromPngs([
{ png: smallPng, width: 32, height: 32 },
{ png: largePng, width: baseWidth, height: baseHeight }
]);
return faviconBuffer;
} catch (err) {
console.warn(`⚠ Favicon: не удалось подготовить фавикон из logo.png: ${err.message}`);
return null;
}
}
function getFavicon() {
return faviconBuffer;
}
module.exports = { loadFavicon, getFavicon, buildIcoFromPngs, logoPath };