You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
title = "ПОЧТАЛЬОН ШАРИК",
location = "ПРОСТОКВАШИНО",
bgColor = "#f4ecd8"
) {
// Determine dimensions based on original image size to remain proportional
const w = originalImg.width;
const h = originalImg.height;
// Scale margins and features based on image size
const margin = Math.max(w, h) * 0.08;
const bottomMargin = margin * 2;
const holeRadius = margin * 0.2;
const cw = w + margin * 2;
const ch = h + margin + bottomMargin;
// Offscreen canvas for constructing the perforated stamp
const stampCanvas = document.createElement('canvas');
stampCanvas.width = cw;
stampCanvas.height = ch;
const sCtx = stampCanvas.getContext('2d');
// Fill stamp background color
sCtx.fillStyle = bgColor;
sCtx.fillRect(0, 0, cw, ch);
// Create perforation holes along the border using destination-out
sCtx.globalCompositeOperation = 'destination-out';
// Top and Bottom perforations
const numHolesX = Math.round(cw / (holeRadius * 3));
const spacingX = cw / numHolesX;
sCtx.fillStyle = '#000';
for (let i = 0; i <= numHolesX; i++) {
const x = i * spacingX;
sCtx.beginPath(); sCtx.arc(x, 0, holeRadius, 0, Math.PI * 2); sCtx.fill();
sCtx.beginPath(); sCtx.arc(x, ch, holeRadius, 0, Math.PI * 2); sCtx.fill();
}
// Left and Right perforations
const numHolesY = Math.round(ch / (holeRadius * 3));
const spacingY = ch / numHolesY;
for (let i = 0; i <= numHolesY; i++) {
const y = i * spacingY;
sCtx.beginPath(); sCtx.arc(0, y, holeRadius, 0, Math.PI * 2); sCtx.fill();
sCtx.beginPath(); sCtx.arc(cw, y, holeRadius, 0, Math.PI * 2); sCtx.fill();
}
// Switch back to normal drawing mode
sCtx.globalCompositeOperation = 'source-over';
// Draw the actual image inside the stamp
sCtx.drawImage(originalImg, margin, margin, w, h);
// Draw a subtle border around the image itself
sCtx.lineWidth = Math.max(1, margin * 0.05);
sCtx.strokeStyle = '#333333';
sCtx.strokeRect(margin, margin, w, h);
// Add custom text at the bottom margin (like stamp title or nominal value)
sCtx.fillStyle = '#2b2b2b';
sCtx.font = `bold ${margin * 0.75}px "Arial", sans-serif`;
sCtx.textAlign = 'center';
sCtx.textBaseline = 'middle';
sCtx.fillText(title, cw / 2, h + margin + bottomMargin / 2);
// Draw an overlay Postmark (Russian cancellation stamp)
const pr = Math.min(w, h) * 0.25; // Postmark radius
const px = margin + w - pr * 0.6; // Position near the top right logic corner
const py = margin + pr * 0.6;
sCtx.save();
sCtx.translate(px, py);
// Slight random-looking tilt
sCtx.rotate(-0.15);
sCtx.strokeStyle = 'rgba(25, 25, 25, 0.75)';
sCtx.fillStyle = 'rgba(25, 25, 25, 0.75)';
sCtx.lineWidth = Math.max(2, pr * 0.04);
// Postmark outer and inner circles
sCtx.beginPath(); sCtx.arc(0, 0, pr, 0, Math.PI * 2); sCtx.stroke();
sCtx.beginPath(); sCtx.arc(0, 0, pr * 0.75, 0, Math.PI * 2); sCtx.stroke();
// Circular text in postmark
const textStr = ` ${location} ★ ${title} ★ `;
sCtx.font = `bold ${pr * 0.22}px "Courier New", monospace`;
sCtx.textAlign = 'center';
sCtx.textBaseline = 'middle';
const angleStep = (Math.PI * 2) / textStr.length;
for (let i = 0; i < textStr.length; i++) {
sCtx.save();
sCtx.rotate(i * angleStep - Math.PI / 2);
sCtx.translate(0, -pr * 0.88);
sCtx.fillText(textStr[i], 0, 0);
sCtx.restore();
}
// Postmark center date (using 1978 as a nod to Prostokvashino's debut)
sCtx.font = `bold ${pr * 0.25}px "Courier New", monospace`;
sCtx.fillText("12.04.1978", 0, -pr * 0.15);
sCtx.fillText("ПОЧТА", 0, pr * 0.25);
// Postmark wavy cancellation lines extending to the left
sCtx.beginPath();
const waveCount = 5;
const waveLength = w * 0.35 + pr;
const waveFreq = (Math.PI * 2) / (pr * 0.6); // Based on postmark radius for clean scale
const waveAmplitude = pr * 0.08;
for (let i = 0; i < waveCount; i++) {
const startY = -pr * 0.4 + i * (pr * 0.2);
sCtx.moveTo(-pr, startY);
// Draw the wavy path
for (let wx = -pr; wx > -waveLength; wx -= 2) {
const wy = startY + Math.sin((wx + pr) * waveFreq) * waveAmplitude;
sCtx.lineTo(wx, wy);
}
}
sCtx.stroke();
sCtx.restore();
// Generate Final Canvas featuring drop shadow and proper margin fitting
const outputCanvas = document.createElement('canvas');
const shadowPadding = margin * 1.5;
outputCanvas.width = cw + shadowPadding * 2;
outputCanvas.height = ch + shadowPadding * 2;
const oCtx = outputCanvas.getContext('2d');
// Setup shadow effect for realism
oCtx.shadowColor = 'rgba(0, 0, 0, 0.4)';
oCtx.shadowBlur = margin * 0.4;
oCtx.shadowOffsetX = margin * 0.15;
oCtx.shadowOffsetY = margin * 0.25;
// Draw the completed stamp onto the shadowed canvas
oCtx.drawImage(stampCanvas, shadowPadding, shadowPadding);
return outputCanvas;
}
Apply Changes