| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import { readFileSync, writeFileSync } from "node:fs"; |
| import { dirname, join } from "node:path"; |
| import { fileURLToPath, pathToFileURL } from "node:url"; |
| import png2icons from "png2icons"; |
|
|
| |
| |
| export const REQUIRED_ICO_SIZES = [16, 24, 32, 48, 64, 128, 256]; |
|
|
| |
| |
| |
| export const REQUIRED_ICNS_TYPES = [ |
| "is32", |
| "il32", |
| "ic07", |
| "ic08", |
| "ic09", |
| "ic10", |
| "ic11", |
| "ic12", |
| "ic13", |
| "ic14", |
| ]; |
|
|
| |
| |
| |
| |
| export function listIcoEntries(ico) { |
| const entries = []; |
| for (let i = 0; i < ico.readUInt16LE(4); i++) { |
| const entry = 6 + i * 16; |
| const width = ico[entry]; |
| const offset = ico.readUInt32LE(entry + 12); |
| entries.push({ |
| size: width === 0 ? 256 : width, |
| isPng: ico.readUInt32BE(offset) === 0x89504e47, |
| }); |
| } |
| return entries; |
| } |
|
|
| |
| export function listIcnsTypes(icns) { |
| const types = []; |
| for (let p = 8; p < icns.length; p += icns.readUInt32BE(p + 4)) { |
| types.push(icns.toString("ascii", p, p + 4)); |
| } |
| return types; |
| } |
|
|
| |
| |
| |
| |
| export function generateIcons(input) { |
| |
| |
| const ico = png2icons.createICO(input, png2icons.BICUBIC2, 0, false, true); |
| const icns = png2icons.createICNS(input, png2icons.BICUBIC2, 0); |
| if (!ico || !icns) throw new Error("png2icons failed to convert icon.png"); |
|
|
| const icoSizes = listIcoEntries(ico).map((entry) => entry.size); |
| for (const size of REQUIRED_ICO_SIZES) { |
| if (!icoSizes.includes(size)) throw new Error(`icon.ico missing ${size}px`); |
| } |
| const icnsTypes = listIcnsTypes(icns); |
| for (const type of REQUIRED_ICNS_TYPES) { |
| if (!icnsTypes.includes(type)) throw new Error(`icon.icns missing ${type}`); |
| } |
|
|
| return { ico, icns }; |
| } |
|
|
| const isMainModule = |
| process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href; |
|
|
| if (isMainModule) { |
| const resDir = join( |
| dirname(fileURLToPath(import.meta.url)), |
| "..", |
| "electron", |
| "build-resources", |
| ); |
| const { ico, icns } = generateIcons(readFileSync(join(resDir, "icon.png"))); |
| writeFileSync(join(resDir, "icon.ico"), ico); |
| writeFileSync(join(resDir, "icon.icns"), icns); |
| console.log( |
| `icon.ico (${listIcoEntries(ico) |
| .map((entry) => entry.size) |
| .join("/")}px, ${ico.length} B), ` + |
| `icon.icns (${listIcnsTypes(icns).join(",")}, ${icns.length} B)`, |
| ); |
| } |
|
|