From d3d1f6e9e63dc78f994458cb6c9e5323816480c9 Mon Sep 17 00:00:00 2001 From: not-nullptr <62841684+not-nullptr@users.noreply.github.com> Date: Tue, 15 Apr 2025 15:11:13 +0100 Subject: [PATCH] feat: oops wait no THIS is icns support --- bun.lock | 13 ++++--- package.json | 3 +- src/lib/converters/index.ts | 1 - src/lib/converters/vips.svelte.ts | 3 +- src/lib/parse/icns/index.ts | 0 src/lib/workers/vips.ts | 58 +++++++++++++++++++++++++++---- vite.config.ts | 2 ++ 7 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 src/lib/parse/icns/index.ts diff --git a/bun.lock b/bun.lock index 1a41b8b..805d549 100644 --- a/bun.lock +++ b/bun.lock @@ -7,7 +7,6 @@ "@bjorn3/browser_wasi_shim": "^0.4.1", "@ffmpeg/ffmpeg": "^0.12.15", "@ffmpeg/util": "^0.12.2", - "@fiahfy/icns": "^0.0.7", "@fontsource/azeret-mono": "^5.1.1", "@fontsource/lexend": "^5.1.2", "@fontsource/radio-canada-big": "^5.1.1", @@ -19,7 +18,9 @@ "music-metadata": "^11.0.0", "p-queue": "^8.1.0", "riff-file": "^1.0.3", + "vert-wasm": "^0.0.2", "vite-plugin-static-copy": "^2.2.0", + "vite-plugin-wasm": "^3.4.1", "wasm-vips": "^0.0.11", }, "devDependencies": { @@ -121,10 +122,6 @@ "@ffmpeg/util": ["@ffmpeg/util@0.12.2", "", {}, "sha512-ouyoW+4JB7WxjeZ2y6KpRvB+dLp7Cp4ro8z0HIVpZVCM7AwFlHa0c4R8Y/a4M3wMqATpYKhC7lSFHQ0T11MEDw=="], - "@fiahfy/icns": ["@fiahfy/icns@0.0.7", "", { "dependencies": { "@fiahfy/packbits": "^0.0.6", "pngjs": "^6.0.0" } }, "sha512-0apAtbUXTU3Opy/Z4h69o53voBa+am8FmdZauyagUMskAVYN1a5yIRk48Sf+tEdBLlefbvqLWPJ4pxr/Y/QtTg=="], - - "@fiahfy/packbits": ["@fiahfy/packbits@0.0.6", "", {}, "sha512-XuhF/edg+iIvXjkCWgfj6fWtRi/KrEPg2ILXj1l86EN4EssuOiPcLKgkMDr9cL8jTGtVd/MKUWW6Y0/ZVf1PGA=="], - "@fontsource/azeret-mono": ["@fontsource/azeret-mono@5.2.5", "", {}, "sha512-GRzKYuD1CVOS6Jag/ohDCycLV9a3TK6y1T73A8q0JoDZTVO85DNapqLK+SV2gYtTFldahNAlDSIaizv9MLhR1A=="], "@fontsource/lexend": ["@fontsource/lexend@5.2.5", "", {}, "sha512-Mv2XQ+B4ek2lNCGRW5ddLTW8T3xTT17AnCk1IETpoef57XHz+e42fUfLAYMrmiJLOGpR44qnyJ5S6D323A5EIw=="], @@ -613,8 +610,6 @@ "pirates": ["pirates@4.0.6", "", {}, "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg=="], - "pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="], - "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], @@ -739,10 +734,14 @@ "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "vert-wasm": ["vert-wasm@0.0.2", "", {}, "sha512-QCkhkATZkWp3OPpIrlcLVd5S4DQXeDEWIPI1eV+Ku+sUVoCM4s9DQbNKtnJ8s4leMW3AD4E7cY/gXB/9TsI3WA=="], + "vite": ["vite@5.4.14", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA=="], "vite-plugin-static-copy": ["vite-plugin-static-copy@2.3.0", "", { "dependencies": { "chokidar": "^3.5.3", "fast-glob": "^3.2.11", "fs-extra": "^11.1.0", "p-map": "^7.0.3", "picocolors": "^1.0.0" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0" } }, "sha512-LLKwhhHetGaCnWz4mas4qqjjguDka6/6b4+SeIohRroj8aCE7QTfiZECfPecslFQkWZ3HdQuq5kOPmWZjNYlKA=="], + "vite-plugin-wasm": ["vite-plugin-wasm@3.4.1", "", { "peerDependencies": { "vite": "^2 || ^3 || ^4 || ^5 || ^6" } }, "sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA=="], + "vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="], "wasm-vips": ["wasm-vips@0.0.11", "", {}, "sha512-bzFU7WcimMY4WeqnZk7whKVpSXxpagISXPJwsk2VHF4lgIN9rl4uUo5sF9x6jOlACuCH6ITZUJ7QPTYmy60NCQ=="], diff --git a/package.json b/package.json index 6eeb872..384b169 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "@bjorn3/browser_wasi_shim": "^0.4.1", "@ffmpeg/ffmpeg": "^0.12.15", "@ffmpeg/util": "^0.12.2", - "@fiahfy/icns": "^0.0.7", "@fontsource/azeret-mono": "^5.1.1", "@fontsource/lexend": "^5.1.2", "@fontsource/radio-canada-big": "^5.1.1", @@ -49,7 +48,9 @@ "music-metadata": "^11.0.0", "p-queue": "^8.1.0", "riff-file": "^1.0.3", + "vert-wasm": "^0.0.2", "vite-plugin-static-copy": "^2.2.0", + "vite-plugin-wasm": "^3.4.1", "wasm-vips": "^0.0.11" } } diff --git a/src/lib/converters/index.ts b/src/lib/converters/index.ts index 3ccb073..d5e60a0 100644 --- a/src/lib/converters/index.ts +++ b/src/lib/converters/index.ts @@ -1,4 +1,3 @@ -import type { Converter } from "./converter.svelte"; import { FFmpegConverter } from "./ffmpeg.svelte"; import { PandocConverter } from "./pandoc.svelte"; import { VertdConverter } from "./vertd.svelte"; diff --git a/src/lib/converters/vips.svelte.ts b/src/lib/converters/vips.svelte.ts index 46e7022..c874add 100644 --- a/src/lib/converters/vips.svelte.ts +++ b/src/lib/converters/vips.svelte.ts @@ -22,10 +22,11 @@ export class VipsConverter extends Converter { new FormatInfo("jpg"), new FormatInfo("webp"), new FormatInfo("gif"), + new FormatInfo("heic", true, false), new FormatInfo("ico", true, false), new FormatInfo("cur", true, false), new FormatInfo("ani", true, false), - new FormatInfo("heic", true, false), + new FormatInfo("icns", true, false), new FormatInfo("nef", true, false), new FormatInfo("cr2", true, false), new FormatInfo("hdr"), diff --git a/src/lib/parse/icns/index.ts b/src/lib/parse/icns/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/workers/vips.ts b/src/lib/workers/vips.ts index 8c76276..f38bede 100644 --- a/src/lib/workers/vips.ts +++ b/src/lib/workers/vips.ts @@ -10,7 +10,7 @@ import { import { makeZip } from "client-zip"; import wasm from "@imagemagick/magick-wasm/magick.wasm?url"; import { parseAni } from "$lib/parse/ani"; -import { Icns } from "@fiahfy/icns/dist"; +import { parseIcns } from "vert-wasm"; const vipsPromise = Vips({ dynamicLibraries: [], @@ -158,8 +158,6 @@ const handleMessage = async (message: any): Promise => { } } - console.log(message.input.from); - const img = MagickImage.create( new Uint8Array(buffer), new MagickReadSettings({ @@ -178,11 +176,59 @@ const handleMessage = async (message: any): Promise => { } if (message.input.from === ".icns") { - const icns = Icns.from(new Uint8Array(buffer)); - console.log(icns); + const icns: Uint8Array[] = parseIcns(new Uint8Array(buffer)); + // Result in vert-wasm maps to a string in JS + if (typeof icns === "string") { + return { + type: "error", + error: `Failed to read ICNS -- ${icns}`, + }; + } + const formats = [ + MagickFormat.Png, + MagickFormat.Jpeg, + MagickFormat.Rgba, + MagickFormat.Rgb, + ]; + const outputs: Uint8Array[] = []; + for (const file of icns) { + for (const format of formats) { + try { + const img = MagickImage.create( + file, + new MagickReadSettings({ + format: format, + }), + ); + const converted = await magickConvert( + img, + message.to, + ); + outputs.push(converted); + break; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (_) { + continue; + } + } + } + + const zip = makeZip( + outputs.map( + (img, i) => + new File([img], `image${i}.${message.to.slice(1)}`), + ), + "images.zip", + ); + const zipBytes = await readToEnd(zip.getReader()); + return { + type: "finished", + output: zipBytes, + zip: true, + }; } - let image = vips.Image.newFromBuffer(buffer, ""); + let image = vips.Image.newFromBuffer(buffer); // check if animated image & keep it animated when converting if (image.getTypeof("n-pages") > 0) { diff --git a/vite.config.ts b/vite.config.ts index c4ec1e8..47d9978 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,6 +2,7 @@ import { sveltekit } from "@sveltejs/kit/vite"; import { defineConfig } from "vite"; import { viteStaticCopy } from "vite-plugin-static-copy"; import svg from "@poppanator/sveltekit-svg"; +import wasm from "vite-plugin-wasm"; export default defineConfig({ plugins: [ @@ -44,6 +45,7 @@ export default defineConfig({ }, ], }), + wasm(), ], optimizeDeps: { exclude: [