You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, codeSnippet = `const greet = (name) => {
return \`Hello, \${name}!\`;
};
console.log(greet('Developer'));`, language = 'javascript', theme = 'dark', fontSize = 16, position = 'bottomRight', padding = 30) {
/**
* Helper function to dynamically load a Google Font.
* It adds the font's stylesheet to the document head and waits for the font to be ready for use.
* @param {string} fontFamily The name of the Google Font (e.g., 'Source Code Pro').
* @returns {Promise<void>} A promise that resolves when the font is loaded and ready.
*/
const loadGoogleFont = async (fontFamily) => {
const fontId = `google-font-${fontFamily.replace(/\s/g, '-')}`;
if (document.getElementById(fontId)) {
await document.fonts.load(`1em "${fontFamily}"`);
return;
}
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.id = fontId;
link.rel = 'stylesheet';
link.href = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/\s/g, '+')}:wght@400&display=swap`;
link.onload = () => document.fonts.load(`1em "${fontFamily}"`).then(resolve, reject);
link.onerror = () => reject(new Error(`Failed to load Google Font: ${fontFamily}`));
document.head.appendChild(link);
});
};
/**
* Helper function to draw a rectangle with rounded corners on a canvas.
* @param {CanvasRenderingContext2D} ctx The canvas rendering context.
* @param {number} x The top-left x-coordinate.
* @param {number} y The top-left y-coordinate.
* @param {number} width The width of the rectangle.
* @param {number} height The height of the rectangle.
* @param {number} radius The corner radius.
*/
const roundRect = (ctx, x, y, width, height, radius) => {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.arcTo(x + width, y, x + width, y + height, radius);
ctx.arcTo(x + width, y + height, x, y + height, radius);
ctx.arcTo(x, y + height, x, y, radius);
ctx.arcTo(x, y, x + width, y, radius);
ctx.closePath();
};
// 1. Setup Canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
// 2. Draw Original Image
ctx.drawImage(originalImg, 0, 0);
// 3. Load Required Font
const fontFamily = 'Source Code Pro';
try {
await loadGoogleFont(fontFamily);
} catch (e) {
console.error(e);
// Fallback to a web-safe monospace font
// fontFamily = 'monospace'; // This would require changing the const to let
}
// 4. Set Theme Colors
let bgColor, textColor, headerColor, shadowColor, titleColor;
if (theme === 'light') {
bgColor = 'rgba(245, 245, 245, 0.9)';
textColor = '#333333';
headerColor = 'rgba(225, 225, 225, 0.9)';
shadowColor = 'rgba(0, 0, 0, 0.2)';
titleColor = 'rgba(51, 51, 51, 0.7)';
} else { // default to 'dark' theme
bgColor = 'rgba(40, 42, 54, 0.9)';
textColor = '#f8f8f2';
headerColor = 'rgba(30, 31, 41, 0.9)';
shadowColor = 'rgba(0, 0, 0, 0.5)';
titleColor = 'rgba(248, 248, 242, 0.6)';
}
// 5. Calculate Code Block Metrics
ctx.font = `${fontSize}px "${fontFamily}", monospace`;
ctx.textBaseline = 'top';
const lines = codeSnippet.split('\n');
const lineHeight = fontSize * 1.5;
const headerHeight = 40;
const windowButtonRadius = 7;
let maxWidth = 0;
lines.forEach(line => {
const metrics = ctx.measureText(line);
if (metrics.width > maxWidth) maxWidth = metrics.width;
});
const codeBlockWidth = maxWidth + padding * 2;
const codeBlockHeight = headerHeight + (lines.length * lineHeight) + padding;
// 6. Calculate Position of the Code Block
let verticalPos, horizontalPos;
const pos = position.toLowerCase();
if (pos.startsWith('top')) verticalPos = 'top';
else if (pos.startsWith('bottom')) verticalPos = 'bottom';
else verticalPos = 'center';
if (pos.endsWith('left')) horizontalPos = 'left';
else if (pos.endsWith('right')) horizontalPos = 'right';
else horizontalPos = 'center';
let x, y;
const margin = 40; // Margin from canvas edges
switch (horizontalPos) {
case 'right': x = canvas.width - codeBlockWidth - margin; break;
case 'left': x = margin; break;
default: x = (canvas.width - codeBlockWidth) / 2; break; // center
}
switch (verticalPos) {
case 'bottom': y = canvas.height - codeBlockHeight - margin; break;
case 'top': y = margin; break;
default: y = (canvas.height - codeBlockHeight) / 2; break; // center
}
// Clamp position to ensure the block is always visible
x = Math.max(10, Math.min(x, canvas.width - codeBlockWidth - 10));
y = Math.max(10, Math.min(y, canvas.height - codeBlockHeight - 10));
const cornerRadius = 8;
// 7. Draw the Code Block UI
ctx.save();
ctx.shadowColor = shadowColor;
ctx.shadowBlur = 30;
ctx.shadowOffsetY = 10;
roundRect(ctx, x, y, codeBlockWidth, codeBlockHeight, cornerRadius);
ctx.fillStyle = bgColor;
ctx.fill();
ctx.restore();
// Draw header area on top
ctx.fillStyle = headerColor;
ctx.beginPath();
ctx.moveTo(x, y + headerHeight);
ctx.lineTo(x, y + cornerRadius);
ctx.arcTo(x, y, x + cornerRadius, y, cornerRadius);
ctx.lineTo(x + codeBlockWidth - cornerRadius, y);
ctx.arcTo(x + codeBlockWidth, y, x + codeBlockWidth, y + cornerRadius, cornerRadius);
ctx.lineTo(x + codeBlockWidth, y + headerHeight);
ctx.closePath();
ctx.fill();
// Draw macOS-style window buttons
const buttonY = y + headerHeight / 2;
const buttonXStart = x + padding;
ctx.fillStyle = '#ff5f56';
ctx.beginPath();
ctx.arc(buttonXStart, buttonY, windowButtonRadius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#ffbd2e';
ctx.beginPath();
ctx.arc(buttonXStart + windowButtonRadius * 2.5, buttonY, windowButtonRadius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#27c93f';
ctx.beginPath();
ctx.arc(buttonXStart + windowButtonRadius * 5, buttonY, windowButtonRadius, 0, Math.PI * 2);
ctx.fill();
// Draw language title in the header
ctx.fillStyle = titleColor;
ctx.font = `13px "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(language.toLowerCase(), x + codeBlockWidth / 2, buttonY);
// 8. Draw the Code Text
ctx.fillStyle = textColor;
ctx.font = `${fontSize}px "${fontFamily}", monospace`;
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
lines.forEach((line, index) => {
const lineY = y + headerHeight + (padding / 2) + (index * lineHeight);
const lineX = x + padding;
ctx.fillText(line, lineX, lineY);
});
return canvas;
}
Apply Changes