You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, subtitleText = "A beautiful scene captured in this frame.", targetLang = "es", playerTime = "01:23 / 03:45") {
// 1. Text Wrapper Helper for Canvas
const wrapText = (context, text, maxWidth) => {
const words = text.split(' ');
const lines = [];
let currentLine = words[0] || "";
for (let i = 1; i < words.length; i++) {
const word = words[i];
const width = context.measureText(currentLine + " " + word).width;
if (width < maxWidth) {
currentLine += " " + word;
} else {
lines.push(currentLine);
currentLine = word;
}
}
if (currentLine) lines.push(currentLine);
return lines;
};
// 2. Fetch language translation (Simulating the 'Dubbing Translator' aspect using a free web endpoint)
let translatedText = subtitleText;
try {
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${targetLang}&dt=t&q=${encodeURIComponent(subtitleText)}`;
const res = await fetch(url);
const data = await res.json();
if (data && data[0] && data[0][0] && data[0][0][0]) {
translatedText = data[0][0][0];
}
} catch (e) {
console.error("Translation API failed due to CORS/Network. Formatting will use original text.", e);
}
// 3. Setup Canvas ('Video Player' frame)
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// Creating a cinematic widescreen (16:9) DVDRip ratio
const w = originalImg.width;
let h = Math.floor(w * (9 / 16));
// Cap minimum resolution to ensure legible text/UI
if (w < 800) {
canvas.width = 800;
canvas.height = 450;
} else {
canvas.width = w;
canvas.height = h;
}
const cw = canvas.width;
const ch = canvas.height;
// 4. Draw Background & Image Frame
ctx.fillStyle = "#000000"; // Letterboxing background
ctx.fillRect(0, 0, cw, ch);
// Draw the image, fitting it in center while maintaining its original ratio
const scale = Math.min(cw / originalImg.width, ch / originalImg.height);
const dx = (cw - originalImg.width * scale) / 2;
const dy = (ch - originalImg.height * scale) / 2;
ctx.drawImage(originalImg, dx, dy, originalImg.width * scale, originalImg.height * scale);
// Add a dark gradient at the bottom so subtitles and UI pop
const grad = ctx.createLinearGradient(0, ch * 0.5, 0, ch);
grad.addColorStop(0, "rgba(0,0,0,0)");
grad.addColorStop(1, "rgba(0,0,0,0.85)");
ctx.fillStyle = grad;
ctx.fillRect(0, ch * 0.5, cw, ch * 0.5);
// 5. Draw Player Overlay Elements (MP4 Player Theme)
const controlH = Math.floor(ch * 0.08);
const controlY = ch - controlH;
// Top DVDRip Info Bar
ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
ctx.fillRect(0, 0, cw, ch * 0.08);
ctx.fillStyle = "#FFFFFF";
ctx.font = `bold ${Math.floor(ch * 0.03)}px sans-serif`;
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillText(`🎬 MP4 DVDRip Player - Audio Dub / Sub Translator | Target: [${targetLang.toUpperCase()}]`, cw * 0.02, ch * 0.04);
// Bottom Player Controls Bar
ctx.fillStyle = "rgba(10, 10, 10, 0.95)";
ctx.fillRect(0, controlY, cw, controlH);
// Play/Pause Button
ctx.fillStyle = "#FFFFFF";
ctx.beginPath();
const px = cw * 0.03;
const py = controlY + controlH / 2;
ctx.moveTo(px - cw * 0.005, py - controlH * 0.25);
ctx.lineTo(px - cw * 0.005, py + controlH * 0.25);
ctx.lineTo(px + controlH * 0.4, py);
ctx.closePath();
ctx.fill();
// Timeline Progress Bar
const barX = cw * 0.07;
const barY = py;
const barW = cw * 0.75;
const barH = Math.max(2, controlH * 0.08);
ctx.fillStyle = "#555555";
ctx.fillRect(barX, barY - barH / 2, barW, barH);
ctx.fillStyle = "#E50914"; // Widescreen player red tone
const progress = 0.38; // Simulated progress
ctx.fillRect(barX, barY - barH / 2, barW * progress, barH);
// Progress scrub knob
ctx.beginPath();
ctx.arc(barX + barW * progress, barY, barH * 3, 0, Math.PI * 2);
ctx.fill();
// Timestamps
ctx.fillStyle = "#FFFFFF";
ctx.font = `${Math.floor(controlH * 0.4)}px monospace`;
ctx.textAlign = "left";
ctx.textBaseline = "middle";
ctx.fillText(playerTime, barX + barW + cw * 0.02, py);
// 6. Format and Draw Subtitles
const subSize = Math.floor(ch * 0.065);
const origSize = Math.floor(ch * 0.035);
const maxSubWidth = cw * 0.85;
// Measure Translation
ctx.font = `bold ${subSize}px sans-serif`;
const transLines = wrapText(ctx, translatedText, maxSubWidth);
// Measure Original
ctx.font = `italic ${origSize}px sans-serif`;
const origLines = wrapText(ctx, subtitleText, maxSubWidth);
// Calculate Y Offset to ensure we don't bleed into the player bar
let currentY = controlY - (origLines.length * origSize * 1.3) - (transLines.length * subSize * 1.3) - (ch * 0.03);
ctx.textAlign = "center";
ctx.textBaseline = "top";
ctx.lineJoin = "round";
// Draw Translation (Primary DVD Dub format - Large & Yellow)
ctx.font = `bold ${subSize}px sans-serif`;
ctx.fillStyle = "#F5CB42"; // Classic subtitle yellow
ctx.strokeStyle = "#000000";
ctx.lineWidth = Math.max(3, subSize * 0.12);
for (const line of transLines) {
ctx.strokeText(line, cw / 2, currentY);
ctx.fillText(line, cw / 2, currentY);
currentY += subSize * 1.3;
}
// Draw Original text (Secondary reference - Smaller & White)
ctx.font = `italic ${origSize}px sans-serif`;
ctx.fillStyle = "#E0E0E0";
ctx.lineWidth = Math.max(2, origSize * 0.15);
for (const line of origLines) {
const textToDraw = `(${line})`;
ctx.strokeText(textToDraw, cw / 2, currentY);
ctx.fillText(textToDraw, cw / 2, currentY);
currentY += origSize * 1.3;
}
return canvas;
}
Apply Changes