26 |
Convert PNG to ICO
27 |
28 |
29 |
30 | You can use this web app to quickly make a ICO file from one or multiple PNG files.
31 | This is especially useful when you want to make favicon.ico
file for your website.
32 |
33 |
34 |
35 | With this Javascript library, all processing happen in your browser.
36 | You do not need to upload any files to any server so it's fast and secure.
37 |
38 |
39 |
40 |
62 |
63 |
70 |
71 |
72 |
73 |
74 | This app is a demo for PNG2ICOjs
. For feedback, contribution and donations please visit
75 | the Github page
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/js/convert.js:
--------------------------------------------------------------------------------
1 | import { PngIcoConverter } from "../src/png2icojs.js";
2 |
3 | const ErrorMessages = {
4 | "INVALID_IMAGE": "Cannot open the PNG file, please make sure it's a valid PNG file",
5 | "INVALID_SIZE": "The PNG file is larger than 256px. Please check Ignore Size to proceed anyway.",
6 | };
7 |
8 | class ConvertApp {
9 |
10 | btnDownload = document.querySelector("#btn-download");
11 |
12 | init() {
13 | document.querySelector(".frm-convert").addEventListener("submit", e => {
14 | e.preventDefault();
15 | void this.convert();
16 | });
17 |
18 | document.querySelector(".frm-download").addEventListener("submit", e => {
19 | e.preventDefault();
20 | void this.onDownload();
21 | });
22 | }
23 |
24 | onDownload() {
25 | if (!this.currBlob) { return; }
26 |
27 | const url = URL.createObjectURL(this.currBlob);
28 | const a = document.createElement("a");
29 | a.href = url;
30 |
31 | const name = document.querySelector("#txt-name").value || "favicon.ico";
32 | a.download = name;
33 |
34 | a.click();
35 | }
36 |
37 | async convert() {
38 | const files = document.querySelector("#txt-files").files;
39 | if (!files.length) {
40 | alert("Please choose at least a file");
41 | return;
42 | }
43 |
44 | const converter = new PngIcoConverter();
45 | const ignoreSize = document.querySelector("#chk-ignore-size").checked;
46 | const inputs = [...files].map(file => ({
47 | png: file,
48 | ignoreSize,
49 | }));
50 |
51 | try {
52 | this.currBlob = await converter.convertToBlobAsync(inputs);
53 | this.btnDownload.removeAttribute("disabled");
54 | } catch (e) {
55 | console.error(e);
56 |
57 | const msg = e.message;
58 | if (msg) {
59 | alert("Error converting: " + (ErrorMessages[msg] ?? msg));
60 | }
61 | }
62 | }
63 |
64 | }
65 | new ConvertApp().init();
--------------------------------------------------------------------------------
/src/png2icojs.js:
--------------------------------------------------------------------------------
1 | const MaxSize = 256; // 1 << 8
2 | const MaxFiles = 65536; // 1 << 16
3 | const FileHeaderSize = 6;
4 | const ImageHeaderSize = 16;
5 | const IcoMime = "image/x-icon";
6 | export class PngIcoConverter {
7 | async convertToBlobAsync(inputs, mime = IcoMime) {
8 | const arr = await this.convertAsync(inputs);
9 | return new Blob([arr], {
10 | type: mime,
11 | });
12 | }
13 | async convertAsync(inputs) {
14 | const inLen = inputs.length;
15 | if (inLen > MaxFiles) {
16 | throw new Error("TOO_MANY_FILES");
17 | }
18 | // File Format: https://en.wikipedia.org/wiki/ICO_(file_format)
19 | // File Header + Image Header + Image Content
20 | const headersLen = FileHeaderSize + ImageHeaderSize * inLen;
21 | const totalLen = headersLen + this.sumInputLen(inputs);
22 | const arr = new Uint8Array(totalLen);
23 | // File Header
24 | arr.set([0, 0, 1, 0, ...this.to2Bytes(inLen)], 0);
25 | // Image Headers & Data
26 | let imgPos = headersLen;
27 | for (let i = 0; i < inputs.length; i++) {
28 | const currPos = FileHeaderSize + ImageHeaderSize * i, input = inputs[i];
29 | const blob = this.toBlob(input.png), img = await this.loadImageAsync(blob), w = img.naturalWidth, h = img.naturalHeight;
30 | if (!input.ignoreSize &&
31 | (w > MaxSize || h > MaxSize)) {
32 | throw new Error("INVALID_SIZE");
33 | }
34 | // Header
35 | arr.set([
36 | w > MaxSize ? 0 : w,
37 | h > MaxSize ? 0 : h,
38 | 0,
39 | 0,
40 | 0, 0,
41 | ...(input.bpp ? this.to2Bytes(input.bpp) : [0, 0]),
42 | ...this.to4Bytes(blob.size),
43 | ...this.to4Bytes(imgPos),
44 | ], currPos);
45 | // Image
46 | const buffer = input.png instanceof ArrayBuffer ? input.png : await input.png.arrayBuffer();
47 | arr.set(new Uint8Array(buffer), imgPos);
48 | imgPos += blob.size;
49 | }
50 | return arr;
51 | }
52 | loadImageAsync(png) {
53 | return new Promise((r, rej) => {
54 | const img = new Image();
55 | img.onload = () => r(img);
56 | img.onerror = () => rej("INVALID_IMAGE");
57 | img.src = URL.createObjectURL(png);
58 | });
59 | }
60 | toBlob(input, type = "image/png") {
61 | return input instanceof Blob ? input : new Blob([input], {
62 | type,
63 | });
64 | }
65 | to2Bytes(n) {
66 | return [n & 255, (n >> 8) & 255];
67 | }
68 | to4Bytes(n) {
69 | return [n & 255, (n >> 8) & 255, (n >> 16) & 255, (n >> 24) & 255];
70 | }
71 | sumInputLen(inputs) {
72 | let total = 0;
73 | for (const i of inputs) {
74 | const png = i.png;
75 | if (png instanceof Blob) {
76 | total += png.size;
77 | }
78 | else {
79 | total += png.byteLength;
80 | }
81 | }
82 | return total;
83 | }
84 | }
85 | //# sourceMappingURL=png2icojs.js.map
--------------------------------------------------------------------------------
/src/png2icojs.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"png2icojs.js","sourceRoot":"","sources":["png2icojs.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/src/png2icojs.min.js:
--------------------------------------------------------------------------------
1 | const MaxSize=256,MaxFiles=65536,FileHeaderSize=6,ImageHeaderSize=16,IcoMime="image/x-icon";export class PngIcoConverter{async convertToBlobAsync(e,t=IcoMime){const n=await this.convertAsync(e);return new Blob([n],{type:t})}async convertAsync(e){const t=e.length;if(t>MaxFiles)throw new Error("TOO_MANY_FILES");const n=FileHeaderSize+ImageHeaderSize*t,r=n+this.sumInputLen(e),o=new Uint8Array(r);o.set([0,0,1,0,...this.to2Bytes(t)],0);let i=n;for(let t=0;t