feat: add bidirectional ICO image converter

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 10:09:56 +02:00
co-authored by Claude Sonnet 5
parent 39b296f5d3
commit 05ef7dfca1
5 changed files with 223 additions and 2 deletions
+45
View File
@@ -0,0 +1,45 @@
import fs from 'node:fs/promises';
import sharp from 'sharp';
import { decodeIco, encodeIco } from 'icojs';
import { register } from './registry.js';
import { sharpFormatName, buildFormatOptions } from './image.js';
const IMAGE_FORMATS = ['jpg', 'jpeg', 'png', 'webp', 'gif', 'tiff', 'avif'];
export const DEFAULT_ICON_SIZE = 256;
export async function encodeIcoFromInput(input, size = DEFAULT_ICON_SIZE) {
const pngBuffer = await sharp(input).resize(size, size, { fit: 'contain' }).png().toBuffer();
return Buffer.from(await encodeIco([{ buffer: pngBuffer }]));
}
async function decodeIcoLargestImage(inputPath) {
const buffer = await fs.readFile(inputPath);
const images = await decodeIco(buffer, 'image/png');
return images.reduce((a, b) => (a.width * a.height >= b.width * b.height ? a : b));
}
export function registerIcoConverter() {
for (const format of IMAGE_FORMATS) {
register({
family: 'image',
sourceFormat: 'ico',
targetFormat: format,
convert: async (inputPath, outputPath, { quality } = {}) => {
const largest = await decodeIcoLargestImage(inputPath);
await sharp(Buffer.from(largest.buffer))
.toFormat(sharpFormatName(format), buildFormatOptions(format, quality))
.toFile(outputPath);
},
});
register({
family: 'image',
sourceFormat: format,
targetFormat: 'ico',
convert: async (inputPath, outputPath, { iconSize } = {}) => {
const icoBuffer = await encodeIcoFromInput(inputPath, iconSize ?? DEFAULT_ICON_SIZE);
await fs.writeFile(outputPath, icoBuffer);
},
});
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ import { register } from './registry.js';
const IMAGE_FORMATS = ['jpg', 'jpeg', 'png', 'webp', 'gif', 'tiff', 'avif'];
function sharpFormatName(format) {
export function sharpFormatName(format) {
return format === 'jpg' ? 'jpeg' : format;
}