You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, artist = "AI Artist", copyright = "Public Domain", cameraMake = "PixelCam", cameraModel = "AI-100", description = "An AI generated image.", lat = "40.7128", lng = "-74.0060", software = "JS Web Tool") {
// 1. DYNAMICALLY IMPORT piexifjs library
let piexif;
try {
// Use a reliable CDN like jsDelivr for the ES Module version of the library
const piexifModule = await import('https://cdn.jsdelivr.net/npm/piexifjs@1.0.6/+esm');
piexif = piexifModule.default; // piexifjs ES module exports its methods on the 'default' object
if (!piexif || !piexif.insert) {
throw new Error("piexifjs module loaded incorrectly.");
}
} catch (e) {
console.error("Failed to load piexifjs library:", e);
const errorElement = document.createElement('div');
errorElement.textContent = "Error: Could not load the required metadata library. Please check your internet connection and try again.";
errorElement.style.color = "red";
errorElement.style.fontWeight = "bold";
errorElement.style.padding = "1em";
errorElement.style.border = "1px solid red";
errorElement.style.borderRadius = "5px";
return errorElement;
}
// 2. PREPARE IMAGE DATA
// Draw the image on a canvas to get its data URL in JPEG format, which supports EXIF.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
ctx.drawImage(originalImg, 0, 0);
// Get image data as a base64 JPEG string.
const imageData = canvas.toDataURL("image/jpeg", 0.9);
// 3. GENERATE ARTIFICIAL METADATA
const exifObj = {};
const now = new Date();
// EXIF date format is YYYY:MM:DD HH:MM:SS
const exifDate = `${now.getFullYear()}:${(now.getMonth() + 1).toString().padStart(2, '0')}:${now.getDate().toString().padStart(2, '0')} ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
// Generate some plausible random camera settings for realism
const apertureValue = 4.0 + Math.random() * 4.0; // Random f-stop between f/4.0 and f/8.0
const exposureTime = 1 / (100 + Math.floor(Math.random() * 400)); // Random shutter between 1/100s and 1/500s
const iso = 100 * (1 + Math.floor(Math.random() * 8)); // Random ISO between 100 and 800
// Zeroth IFD (Image File Directory) - Main image information
exifObj["0th"] = {
[piexif.ImageIFD.Make]: cameraMake,
[piexif.ImageIFD.Model]: cameraModel,
[piexif.ImageIFD.Software]: software,
[piexif.ImageIFD.ImageDescription]: description,
[piexif.ImageIFD.Artist]: artist,
[piexif.ImageIFD.Copyright]: copyright,
[piexif.ImageIFD.DateTime]: exifDate,
};
// Exif IFD - Detailed camera and capture information
exifObj["Exif"] = {
[piexif.ExifIFD.DateTimeOriginal]: exifDate,
[piexif.ExifIFD.DateTimeDigitized]: exifDate,
[piexif.ExifIFD.PixelXDimension]: canvas.width,
[piexif.ExifIFD.PixelYDimension]: canvas.height,
[piexif.ExifIFD.FNumber]: [Math.round(apertureValue * 10), 10], // Stored as a rational number, e.g., f/4.0 is [40, 10]
[piexif.ExifIFD.ExposureTime]: [1, Math.round(1 / exposureTime)], // e.g., 1/125s is [1, 125]
[piexif.ExifIFD.ISOSpeedRatings]: iso,
[piexif.ExifIFD.FocalLength]: [50, 1], // Fixed 50mm lens
[piexif.ExifIFD.LensModel]: `${cameraModel} Kit Lens`,
};
// GPS IFD - Geolocation information
const latitude = parseFloat(lat);
const longitude = parseFloat(lng);
if (!isNaN(latitude) && !isNaN(longitude)) {
exifObj["GPS"] = {
[piexif.GPSIFD.GPSLatitudeRef]: latitude >= 0 ? 'N' : 'S',
[piexif.GPSIFD.GPSLatitude]: piexif.GPSHelper.degToDms(Math.abs(latitude)),
[piexif.GPSIFD.GPSLongitudeRef]: longitude >= 0 ? 'E' : 'W',
[piexif.GPSIFD.GPSLongitude]: piexif.GPSHelper.degToDms(Math.abs(longitude)),
};
}
// 4. EMBED METADATA INTO IMAGE DATA
const exifBytes = piexif.dump(exifObj);
const newImageData = piexif.insert(exifBytes, imageData);
// 5. CREATE A DISPLAYABLE RESULT ELEMENT
const container = document.createElement('div');
container.style.fontFamily = "Arial, sans-serif";
container.style.padding = "1em";
container.style.border = "1px solid #ccc";
container.style.borderRadius = "8px";
container.style.backgroundColor = "#fff";
container.style.maxWidth = "100%";
container.style.boxSizing = "border-box";
const title = document.createElement('h3');
title.textContent = "Image with Artificial Metadata";
title.style.marginTop = '0';
title.style.borderBottom = "1px solid #eee";
title.style.paddingBottom = "0.5em";
container.appendChild(title);
const previewWrapper = document.createElement('div');
previewWrapper.style.marginBottom = "1em";
previewWrapper.style.textAlign = "center";
const previewImg = new Image();
previewImg.src = newImageData; // The new image with embedded data can be previewed directly
previewImg.style.maxWidth = '100%';
previewImg.style.maxHeight = '400px';
previewImg.style.border = '1px solid #eee';
previewImg.style.borderRadius = '4px';
previewWrapper.appendChild(previewImg);
container.appendChild(previewWrapper);
const downloadLink = document.createElement('a');
downloadLink.href = newImageData;
downloadLink.download = "image_with_metadata.jpg";
downloadLink.textContent = "Download Image with Embedded Metadata";
downloadLink.style.display = "inline-block";
downloadLink.style.textAlign = "center";
downloadLink.style.padding = "10px 15px";
downloadLink.style.backgroundColor = "#007bff";
downloadLink.style.color = "white";
downloadLink.style.textDecoration = "none";
downloadLink.style.borderRadius = "5px";
downloadLink.style.fontWeight = "bold";
container.appendChild(downloadLink);
const metadataTitle = document.createElement('h4');
metadataTitle.textContent = "Generated Metadata (for review):";
metadataTitle.style.marginTop = "1.5em";
metadataTitle.style.marginBottom = "0.5em";
container.appendChild(metadataTitle);
const metadataDisplay = document.createElement('pre');
metadataDisplay.style.padding = "1em";
metadataDisplay.style.backgroundColor = "#f5f5f5";
metadataDisplay.style.border = "1px solid #ddd";
metadataDisplay.style.borderRadius = "5px";
metadataDisplay.style.whiteSpace = "pre-wrap";
metadataDisplay.style.wordBreak = "break-all";
metadataDisplay.style.maxHeight = "200px";
metadataDisplay.style.overflowY = "auto";
metadataDisplay.style.lineHeight = "1.5";
metadataDisplay.style.fontSize = "0.9em";
// Format the generated metadata for display
let metadataText = `--- General ---\n`;
metadataText += ` Artist: ${artist}\n`;
metadataText += ` Copyright: ${copyright}\n`;
metadataText += ` Description: ${description}\n`;
metadataText += `--- Camera ---\n`;
metadataText += ` Make: ${cameraMake}\n`;
metadataText += ` Model: ${cameraModel}\n`;
metadataText += ` Software: ${software}\n`;
metadataText += ` Date/Time: ${exifDate}\n`;
metadataText += `--- Capture Settings ---\n`;
metadataText += ` Aperture: f/${apertureValue.toFixed(1)}\n`;
metadataText += ` Exposure Time: 1/${Math.round(1/exposureTime)}s\n`;
metadataText += ` ISO Speed: ${iso}\n`;
metadataText += ` Focal Length: 50mm\n`;
if ("GPS" in exifObj) {
metadataText += `--- Geolocation ---\n`;
metadataText += ` Latitude: ${latitude.toFixed(6)}\n`;
metadataText += ` Longitude: ${longitude.toFixed(6)}\n`;
}
metadataDisplay.textContent = metadataText;
container.appendChild(metadataDisplay);
return container;
}
Apply Changes