/**
* Image utilities using canvas for cross-platform compatibility
* Falls back to minimal implementation if canvas isn't available
*/
let canvasModule: any = null;
// Try to load canvas module
try {
canvasModule = require('canvas');
} catch (error) {
console.warn('Canvas module not available, using minimal image implementation');
}
/**
* Capture a screenshot from WebDriver and ensure it's in PNG format
*/
export async function captureScreenshot(driver: any): Promise<Buffer> {
const screenshot = await driver.takeScreenshot();
return Buffer.from(screenshot, 'base64');
}
/**
* Generate an image with text
*/
export async function generateImage(
width: number = 1280,
height: number = 720,
text?: string
): Promise<Buffer> {
if (canvasModule) {
try {
const { createCanvas } = canvasModule;
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
// Fill background
ctx.fillStyle = '#f0f0f0';
ctx.fillRect(0, 0, width, height);
// Add text if provided
if (text) {
ctx.fillStyle = '#333333';
ctx.font = '24px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(text, width / 2, height / 2);
}
// Return PNG buffer
return canvas.toBuffer('image/png');
} catch (error) {
console.warn('Canvas rendering failed:', error);
}
}
// Fallback: return a minimal 1x1 transparent PNG
return Buffer.from(
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
'base64'
);
}
/**
* Crop an image to specified area
* Note: Without image processing library, returns original
*/
export async function cropImage(
imageBuffer: Buffer,
area: { x: number; y: number; w: number; h: number }
): Promise<Buffer> {
if (canvasModule) {
try {
const { createCanvas, loadImage } = canvasModule;
const img = await loadImage(imageBuffer);
const canvas = createCanvas(area.w, area.h);
const ctx = canvas.getContext('2d');
ctx.drawImage(img, area.x, area.y, area.w, area.h, 0, 0, area.w, area.h);
return canvas.toBuffer('image/png');
} catch (error) {
console.warn('Canvas cropping failed:', error);
}
}
return imageBuffer; // Return original if can't crop
}
/**
* Get image dimensions
*/
export async function getImageDimensions(imageBuffer: Buffer): Promise<{ width: number; height: number }> {
if (canvasModule) {
try {
const { loadImage } = canvasModule;
const img = await loadImage(imageBuffer);
return {
width: img.width,
height: img.height
};
} catch (error) {
console.warn('Failed to get image dimensions:', error);
}
}
// Parse PNG header to get dimensions if possible
if (imageBuffer.length > 24 &&
imageBuffer[0] === 0x89 &&
imageBuffer[1] === 0x50 &&
imageBuffer[2] === 0x4E &&
imageBuffer[3] === 0x47) {
// PNG detected, extract dimensions from IHDR chunk
const width = imageBuffer.readUInt32BE(16);
const height = imageBuffer.readUInt32BE(20);
return { width, height };
}
// Return default dimensions
return { width: 1920, height: 1080 };
}
/**
* Apply redaction to an image (black boxes)
*/
export async function applyRedaction(
imageBuffer: Buffer,
areas: Array<{ x: number; y: number; w: number; h: number }>
): Promise<Buffer> {
if (canvasModule) {
try {
const { createCanvas, loadImage } = canvasModule;
const img = await loadImage(imageBuffer);
const canvas = createCanvas(img.width, img.height);
const ctx = canvas.getContext('2d');
// Draw original image
ctx.drawImage(img, 0, 0);
// Draw black rectangles over redacted areas
ctx.fillStyle = '#000000';
for (const area of areas) {
ctx.fillRect(area.x, area.y, area.w, area.h);
}
return canvas.toBuffer('image/png');
} catch (error) {
console.warn('Canvas redaction failed:', error);
}
}
return imageBuffer;
}
/**
* Convert image buffer to base64 string
*/
export function imageToBase64(buffer: Buffer): string {
return buffer.toString('base64');
}
/**
* Check if image processing is available
*/
export function isImageProcessingAvailable(): boolean {
return canvasModule !== null;
}