You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, theme = "light") {
// Basic image checks
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
const isDark = theme.toLowerCase() === "dark";
const bgColor = isDark ? "#121212" : "#ffffff";
const panelBg = isDark ? "#1e1e1e" : "#f1f5f9";
const textColor = isDark ? "#e2e8f0" : "#1e293b";
const labelColor = isDark ? "#94a3b8" : "#64748b";
const borderColor = isDark ? "#334155" : "#e2e8f0";
const container = document.createElement("div");
container.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif";
container.style.backgroundColor = bgColor;
container.style.color = textColor;
container.style.padding = "24px";
container.style.borderRadius = "12px";
container.style.maxWidth = "600px";
container.style.boxShadow = isDark ? "0 4px 6px -1px rgba(0, 0, 0, 0.5)" : "0 4px 6px -1px rgba(0, 0, 0, 0.1)";
container.style.boxSizing = "border-box";
container.style.margin = "0 auto";
if (!w || !h) {
container.innerText = "Error: Image failed to load or has invalid dimensions.";
container.style.color = "#ef4444";
return container;
}
// Header setup
const header = document.createElement("div");
header.style.display = "flex";
header.style.alignItems = "center";
header.style.justifyContent = "space-between";
header.style.marginBottom = "20px";
header.style.borderBottom = `1px solid ${borderColor}`;
header.style.paddingBottom = "16px";
const title = document.createElement("h2");
title.innerText = "À propos Information";
title.style.margin = "0";
title.style.fontSize = "20px";
title.style.fontWeight = "600";
header.appendChild(title);
const thumb = document.createElement("img");
thumb.src = originalImg.src;
thumb.style.width = "48px";
thumb.style.height = "48px";
thumb.style.objectFit = "cover";
thumb.style.borderRadius = "8px";
thumb.style.border = `1px solid ${borderColor}`;
header.appendChild(thumb);
container.appendChild(header);
// Helpers
function getAspectRatioStr(width, height) {
const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));
const divisor = gcd(width, height);
const num = width / divisor;
const den = height / divisor;
const ratio = width / height;
const commonRatios = [
{ num: 16, den: 9 }, { num: 4, den: 3 }, { num: 1, den: 1 },
{ num: 3, den: 2 }, { num: 21, den: 9 }, { num: 9, den: 16 },
{ num: 3, den: 4 }, { num: 2, den: 3 }
];
for (let cr of commonRatios) {
if (Math.abs(ratio - (cr.num / cr.den)) < 0.01) {
if (num > 100 || den > 100) return `~${cr.num}:${cr.den}`;
return `${cr.num}:${cr.den}`;
}
}
if (num > 100 || den > 100) return `${(width / height).toFixed(2)}:1`;
return `${num}:${den}`;
}
function formatBytes(bytes) {
if (!+bytes) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
}
// Calculations
const mp = ((w * h) / 1000000).toFixed(2);
let format = "Unknown";
let srcType = "External / HTML";
let fileSizeStr = "Not Available";
if (originalImg.src.startsWith('data:image/')) {
srcType = "Data URI (Base64)";
const mimeMatch = originalImg.src.match(/data:image\/([a-zA-Z0-9+-]+);/);
if (mimeMatch) format = mimeMatch[1].toUpperCase();
const base64str = originalImg.src.split(",")[1] || "";
const padding = (base64str.match(/=+$/) || [""])[0].length;
const bytes = (base64str.length * 3) / 4 - padding;
fileSizeStr = formatBytes(bytes);
} else if (originalImg.src.startsWith('blob:')) {
srcType = "Blob URL";
} else {
try {
const urlObj = new URL(originalImg.src, window.location.href);
const extMatch = urlObj.pathname.match(/\.([a-zA-Z0-9]+)$/);
if (extMatch) {
format = extMatch[1].toUpperCase();
if (format === 'JPEG') format = 'JPG';
}
} catch(e) {}
}
// Analyze Canvas Details safely (Checking Transperency & Grayscale)
let hasTransparency = false;
let colorSpace = "RGB";
try {
const canvas = document.createElement("canvas");
const tw = Math.min(w, 800); // Scale down large images for performance checks
const th = Math.min(h, 800);
canvas.width = tw;
canvas.height = th;
const ctx = canvas.getContext("2d");
ctx.drawImage(originalImg, 0, 0, tw, th);
const imgData = ctx.getImageData(0, 0, tw, th).data;
let hasAlpha = false;
for (let i = 3; i < imgData.length; i += 4) {
if (imgData[i] < 255) {
hasAlpha = true;
break;
}
}
let isGrayscale = true;
for (let i = 0; i < imgData.length; i += 44) {
const r = imgData[i], g = imgData[i+1], b = imgData[i+2];
if (r !== g || g !== b) {
isGrayscale = false;
break;
}
}
hasTransparency = hasAlpha;
colorSpace = isGrayscale ? "Grayscale" : "RGB (True Color)";
} catch (e) {
hasTransparency = "Unknown (CORS)";
colorSpace = "Unknown (CORS)";
}
// Rendering Base Details
const details = [
{ label: "Dimensions", value: `${w} × ${h} px` },
{ label: "Aspect Ratio", value: getAspectRatioStr(w, h) },
{ label: "Megapixels", value: `${mp} MP` },
{ label: "File Size Estimate", value: fileSizeStr },
{ label: "Detected Format", value: format },
{ label: "Color Profiling", value: colorSpace },
{ label: "Contains Transparency", value: hasTransparency === true ? "Yes" : (hasTransparency === false ? "No" : hasTransparency) },
{ label: "Source Protocol", value: srcType }
];
const content = document.createElement("div");
content.style.backgroundColor = panelBg;
content.style.borderRadius = "8px";
content.style.padding = "16px";
details.forEach((inf, idx) => {
const row = document.createElement("div");
row.style.display = "flex";
row.style.justifyContent = "space-between";
row.style.padding = "8px 0";
if (idx !== details.length - 1) {
row.style.borderBottom = `1px solid ${borderColor}`;
}
const lbl = document.createElement("span");
lbl.innerText = inf.label;
lbl.style.color = labelColor;
lbl.style.fontWeight = "500";
lbl.style.fontSize = "14px";
const val = document.createElement("span");
val.innerText = inf.value;
val.style.fontWeight = "600";
val.style.fontSize = "14px";
val.style.textAlign = "right";
val.style.wordBreak = "break-word";
val.style.marginLeft = "12px";
row.appendChild(lbl);
row.appendChild(val);
content.appendChild(row);
});
container.appendChild(content);
// Dynamic EXIF extraction logic
try {
await new Promise((resolve, reject) => {
if (window.EXIF) return resolve();
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/exif-js";
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
await new Promise((resolve) => {
window.EXIF.getData(originalImg, function() {
const tags = window.EXIF.getAllTags(this);
if (tags && Object.keys(tags).length > 0) {
const exifSection = document.createElement("div");
exifSection.style.marginTop = "24px";
const exifTitle = document.createElement("h3");
exifTitle.innerText = "EXIF Metadata";
exifTitle.style.fontSize = "16px";
exifTitle.style.margin = "0 0 12px 0";
exifTitle.style.fontWeight = "600";
exifSection.appendChild(exifTitle);
const exifContent = document.createElement("div");
exifContent.style.backgroundColor = panelBg;
exifContent.style.borderRadius = "8px";
exifContent.style.padding = "16px";
let empty = true;
// Excluded heavily nested/binary tags
const skipTags = ["MakerNote", "thumbnail", "UserComment", "ComponentsConfiguration", "SceneType"];
for (const key in tags) {
if (skipTags.includes(key)) continue;
let val = tags[key];
if (Array.isArray(val)) {
val = val.map(v => {
if (v && v.numerator !== undefined && v.denominator !== undefined) {
return (v.numerator / (v.denominator || 1));
}
return String(v);
}).join(", ");
} else if (val && typeof val === "object") {
if (val.numerator !== undefined && val.denominator !== undefined) {
val = (val.numerator / (val.denominator || 1));
} else if (val instanceof String) {
val = val.toString();
} else {
val = JSON.stringify(val);
}
}
if (typeof val === "string" || typeof val === "number") {
if (val.toString().trim() === "") continue;
const decVal = typeof val === "number" && !Number.isInteger(val) ? val.toFixed(4) : val;
empty = false;
const row = document.createElement("div");
row.style.display = "flex";
row.style.justifyContent = "space-between";
row.style.padding = "6px 0";
row.style.borderBottom = `1px solid ${borderColor}`;
row.style.alignItems = "center";
const lbl = document.createElement("span");
lbl.innerText = key.replace(/([A-Z])/g, ' $1').trim(); // Spread CamelCase
lbl.style.color = labelColor;
lbl.style.fontSize = "13px";
lbl.style.marginRight = "10px";
lbl.style.whiteSpace = "nowrap";
const v = document.createElement("span");
v.innerText = decVal;
v.style.fontSize = "13px";
v.style.fontWeight = "500";
v.style.textAlign = "right";
v.style.wordBreak = "break-word";
row.appendChild(lbl);
row.appendChild(v);
exifContent.appendChild(row);
}
}
if (!empty) {
if (exifContent.lastChild) exifContent.lastChild.style.borderBottom = "none";
exifSection.appendChild(exifContent);
container.appendChild(exifSection);
}
}
resolve();
});
});
} catch (e) {
// Soft fail EXIF - keep the basic info view robust & clean
console.warn("EXIF processing skipped or failed.", e);
}
return container;
}
Apply Changes