feat: add source-only HEIC/HEIF image converter

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 10:10:41 +02:00
co-authored by Claude Sonnet 5
parent 05ef7dfca1
commit 8795f65055
4 changed files with 175 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
import fs from 'node:fs/promises';
import sharp from 'sharp';
import convert from 'heic-convert';
import { register } from './registry.js';
import { sharpFormatName, buildFormatOptions } from './image.js';
import { encodeIcoFromInput, DEFAULT_ICON_SIZE } from './ico.js';
const IMAGE_FORMATS = ['jpg', 'jpeg', 'png', 'webp', 'gif', 'tiff', 'avif'];
const HEIC_SOURCE_FORMATS = ['heic', 'heif'];
async function decodeToPng(inputPath) {
const buffer = await fs.readFile(inputPath);
return convert({ buffer, format: 'PNG' });
}
export function registerHeicConverter() {
for (const sourceFormat of HEIC_SOURCE_FORMATS) {
for (const targetFormat of IMAGE_FORMATS) {
register({
family: 'image',
sourceFormat,
targetFormat,
convert: async (inputPath, outputPath, { quality } = {}) => {
const pngBuffer = await decodeToPng(inputPath);
await sharp(pngBuffer)
.toFormat(sharpFormatName(targetFormat), buildFormatOptions(targetFormat, quality))
.toFile(outputPath);
},
});
}
register({
family: 'image',
sourceFormat,
targetFormat: 'ico',
convert: async (inputPath, outputPath, { iconSize } = {}) => {
const pngBuffer = await decodeToPng(inputPath);
const icoBuffer = await encodeIcoFromInput(pngBuffer, iconSize ?? DEFAULT_ICON_SIZE);
await fs.writeFile(outputPath, icoBuffer);
},
});
}
}