728x90
아래 사이트의 png2icojs.js를 사용해서 PNG를 ICO로 저장하자.
https://github.com/datvm/PNG2ICOjs
HTML 소스
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Convert PNG Files to ICO</title>
</head>
<body>
<div>
<h1>Convert PNG to ICO</h1>
<form id="fileSelect">
<div>
<input type="file" id="txt-files" accept="image/png" multiple required />
</div>
<div>
<input type="checkbox" id="chk-ignore-size">
<label for="chk-ignore-size">Ignore Image Size</label>
</div>
<p>
<button type="submit">Convert</button>
</p>
</form>
<form id="download">
<div>
<input id="txt-name" placeholder="Default download name: favicon.ico" />
<button type="submit" id="btn-download" target="_blank" disabled>Download</button>
</div>
</form>
<hr />
</div>
<script>
const MaxSize = 256; // 1 << 8
const MaxFiles = 65536; // 1 << 16
const FileHeaderSize = 6;
const ImageHeaderSize = 16;
const IcoMime = "image/x-icon";
class PngIcoConverter {
async convertToBlobAsync(inputs, mime = IcoMime) {
const arr = await this.convertAsync(inputs);
return new Blob([arr], {
type: mime,
});
}
async convertAsync(inputs) {
const inLen = inputs.length;
if (inLen > MaxFiles) {
throw new Error("TOO_MANY_FILES");
}
// File Format: https://en.wikipedia.org/wiki/ICO_(file_format)
// File Header + Image Header + Image Content
const headersLen = FileHeaderSize + ImageHeaderSize * inLen;
const totalLen = headersLen + this.sumInputLen(inputs);
const arr = new Uint8Array(totalLen);
// File Header
arr.set([0, 0, 1, 0, ...this.to2Bytes(inLen)], 0);
// Image Headers & Data
let imgPos = headersLen;
for (let i = 0; i < inputs.length; i++) {
const currPos = FileHeaderSize + ImageHeaderSize * i, input = inputs[i];
const blob = this.toBlob(input.png), img = await this.loadImageAsync(blob), w = img.naturalWidth, h = img.naturalHeight;
if (!input.ignoreSize &&
(w > MaxSize || h > MaxSize)) {
throw new Error("INVALID_SIZE");
}
// Header
arr.set([
w > MaxSize ? 0 : w,
h > MaxSize ? 0 : h,
0,
0,
0, 0,
...(input.bpp ? this.to2Bytes(input.bpp) : [0, 0]),
...this.to4Bytes(blob.size),
...this.to4Bytes(imgPos),
], currPos);
// Image
const buffer = input.png instanceof ArrayBuffer ? input.png : await input.png.arrayBuffer();
arr.set(new Uint8Array(buffer), imgPos);
imgPos += blob.size;
}
return arr;
}
loadImageAsync(png) {
return new Promise((r, rej) => {
const img = new Image();
img.onload = () => r(img);
img.onerror = () => rej("INVALID_IMAGE");
img.src = URL.createObjectURL(png);
});
}
toBlob(input, type = "image/png") {
return input instanceof Blob ? input : new Blob([input], {
type,
});
}
to2Bytes(n) {
return [n & 255, (n >> 8) & 255];
}
to4Bytes(n) {
return [n & 255, (n >> 8) & 255, (n >> 16) & 255, (n >> 24) & 255];
}
sumInputLen(inputs) {
let total = 0;
for (const i of inputs) {
const png = i.png;
if (png instanceof Blob) {
total += png.size;
}
else {
total += png.byteLength;
}
}
return total;
}
}
const btnDownload = document.getElementById("btn-download");
function init() {
document.getElementById("fileSelect").addEventListener("submit", e => {
e.preventDefault();
convert();
});
document.getElementById("download").addEventListener("submit", e => {
e.preventDefault();
onDownload();
});
}
let currBlob;
function onDownload() {
if (!currBlob) { return; }
const url = URL.createObjectURL(currBlob);
const a = document.createElement("a");
a.href = url;
const name = document.querySelector("#txt-name").value || "favicon.ico";
a.download = name;
a.click();
}
async function convert() {
const files = document.querySelector("#txt-files").files;
if (!files.length) {
alert("Please choose at least a file");
return;
}
const converter = new PngIcoConverter();
const ignoreSize = document.querySelector("#chk-ignore-size").checked;
const inputs = [...files].map(file => ({
png: file,
ignoreSize,
}));
try {
currBlob = await converter.convertToBlobAsync(inputs);
btnDownload.removeAttribute("disabled");
} catch (e) {
console.error(e);
const msg = e.message;
if (msg) {
alert("Error converting: " + (ErrorMessages[msg] ?? msg));
}
}
}
init();
</script>
</body>
</html>
728x90
반응형
'Software > JavaScript' 카테고리의 다른 글
JavaScript 시작하기 - 이미지 사물인식(1) (0) | 2024.06.18 |
---|---|
JavaScript 시작하기 - 한글 초중종분리 (1) | 2024.06.15 |
JavaScript 시작하기 - SVG to PNG (1) | 2024.06.14 |
JavaScript 시작하기 - QRcode (0) | 2024.06.14 |
Javascript 시작하기 - SVG 디지탈 날짜 및 시계 (0) | 2024.06.14 |