SVG to PNG Conversion Service
A comprehensive, agnostic TypeScript service for converting SVG content to PNG images. Extracted and enhanced from the card generator's original implementation to provide a reusable, standalone module.
Features
- ✅ Client-side Canvas-based conversion - Uses HTML5 Canvas API for high-quality rendering
- ✅ Configurable quality and scaling - Fine-tune output quality and dimensions
- ✅ Security validation - Scans SVG content for potentially malicious code
- ✅ Comprehensive error handling - Detailed error types and context
- ✅ Progress tracking - Monitor conversion progress for large files
- ✅ Batch processing - Convert multiple SVGs simultaneously
- ✅ TypeScript support - Full type safety with comprehensive interfaces
- ✅ Memory management - Prevents resource exhaustion with configurable limits
Quick Start
import { convertSvgToPng, createSvgToPngConverter } from '@lib/services/svg-conversion';
// Simple conversion (backward compatible)
const svgContent = '<svg width="100" height="100">...</svg>';
const blob = await convertSvgToPng(svgContent);
// Advanced conversion with options
const converter = createSvgToPngConverter({
enableLogging: true,
maxSvgSize: 5 * 1024 * 1024 // 5MB limit
});
const result = await converter.convertToPng(svgContent, {
scale: 2,
quality: 0.9,
backgroundColor: '#ffffff'
});
console.log(`Converted ${result.metadata.originalSize} bytes to ${result.metadata.convertedSize} bytes`);
API Reference
Types
SvgToPngConversionOptions
interface SvgToPngConversionOptions {
quality?: number; // 0-1, higher = better quality
scale?: number; // Scale factor (1 = original size)
backgroundColor?: string; // Background color or 'transparent'
maxWidth?: number; // Maximum output width
maxHeight?: number; // Maximum output height
maintainAspectRatio?: boolean; // Keep original proportions
timeout?: number; // Conversion timeout in ms
}
SvgToPngResult
interface SvgToPngResult {
blob: Blob; // The converted PNG blob
dimensions: { // Output dimensions
width: number;
height: number;
};
originalDimensions: { // Input dimensions
width: number;
height: number;
};
metadata: { // Conversion metadata
conversionTime: number; // Time taken in ms
originalSize: number; // SVG size in bytes
convertedSize: number; // PNG size in bytes
scaleApplied: number; // Actual scale factor used
};
}
Main Classes
SvgToPngConverter
The main conversion service with advanced features:
const converter = new SvgToPngConverter({
defaultOptions: {
quality: 1,
scale: 1,
timeout: 30000
},
maxSvgSize: 10 * 1024 * 1024, // 10MB
maxConcurrentConversions: 5,
enableLogging: false,
enableSecurityValidation: true
});
Methods:
validateSvg(svgContent: string): SvgValidationResult
- Validate SVG contentconvertToPng(svgContent: string, options?: SvgToPngConversionOptions): Promise<SvgToPngResult>
- Convert SVG to PNGconvertBatch(requests: BatchConversionRequest[]): Promise<BatchConversionResult[]>
- Batch conversionconvertWithProgress(svgContent: string, options?: AdvancedConversionOptions): Promise<SvgToPngResult>
- Convert with progress trackinggetStatus()
- Get current service status
Factory Functions
createSvgToPngConverter(config?: Partial<SvgToPngServiceConfig>)
Creates a converter instance with sensible defaults.
convertSvgToPng(svgContent: string, options?: SvgToPngConversionOptions)
Convenience function for simple conversions (maintains backward compatibility).
Usage Examples
Basic Conversion
import { convertSvgToPng } from '@lib/services/svg-conversion';
const svgContent = `
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="100" r="50" fill="blue"/>
</svg>`;
try {
const blob = await convertSvgToPng(svgContent);
// Create download link
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'circle.png';
link.click();
URL.revokeObjectURL(url);
} catch (error) {
console.error('Conversion failed:', error);
}
Advanced Conversion with Options
import { createSvgToPngConverter } from '@lib/services/svg-conversion';
const converter = createSvgToPngConverter({
enableLogging: true,
enableSecurityValidation: true
});
const result = await converter.convertToPng(svgContent, {
scale: 3, // 3x original size
quality: 0.95, // High quality
maxWidth: 1920, // Limit width to 1920px
backgroundColor: '#f0f0f0', // Light gray background
timeout: 60000 // 60 second timeout
});
console.log('Conversion details:', result.metadata);
Batch Conversion
const batchRequests = [
{ id: 'icon1', svgContent: icon1Svg, options: { scale: 2 } },
{ id: 'icon2', svgContent: icon2Svg, options: { scale: 2 } },
{ id: 'logo', svgContent: logoSvg, options: { quality: 1 } }
];
const results = await converter.convertBatch(batchRequests);
results.forEach(result => {
if (result.success) {
console.log(`✅ ${result.id}: ${result.result.metadata.convertedSize} bytes`);
} else {
console.error(`❌ ${result.id}: ${result.error.message}`);
}
});
Progress Tracking
const result = await converter.convertWithProgress(largeSvgContent, {
scale: 4,
onProgress: (progress) => {
console.log(`${progress.step}: ${progress.progress}% - ${progress.description}`);
}
});
SVG Validation
const validation = converter.validateSvg(svgContent);
if (!validation.isValid) {
console.error('SVG validation failed:', validation.error);
if (validation.safetyIssues) {
validation.safetyIssues.forEach(issue => {
console.warn('Security issue:', issue);
});
}
} else {
console.log('SVG is valid:', validation.dimensions);
}
Error Handling
The service provides detailed error information:
try {
await converter.convertToPng(invalidSvg);
} catch (error: ConversionError) {
switch (error.type) {
case 'INVALID_SVG':
console.error('SVG format issue:', error.message);
break;
case 'CANVAS_ERROR':
console.error('Browser canvas issue:', error.message);
break;
case 'TIMEOUT':
console.error('Conversion took too long:', error.message);
break;
case 'MEMORY_ERROR':
console.error('Resource limit exceeded:', error.message);
break;
default:
console.error('Unknown error:', error.message);
}
// Access additional context
if (error.context) {
console.log('Error context:', error.context);
}
}
Performance Considerations
Memory Usage
- Large SVGs (>5MB) may consume significant memory during rendering
- Use
maxSvgSize
config to prevent memory exhaustion - Batch processing is limited to prevent resource overuse
Browser Compatibility
- Requires HTML5 Canvas support (IE9+)
- Uses modern JavaScript features (ES2017+)
- Base64 encoding for SVG data URLs
Optimization Tips
- Simplify complex SVGs before conversion
- Use appropriate quality settings (0.8-0.9 usually sufficient)
- Consider client-side caching for repeated conversions
- Monitor conversion times and adjust timeouts accordingly
Security Features
Input Validation
- Scans for
<script>
tags and JavaScript content - Checks for iframe, object, and embed elements
- Validates SVG structure and format
- Configurable security validation levels
Safe Defaults
- XSS prevention through content sanitization
- Resource limits to prevent DoS attacks
- Timeout protection against infinite loops
- Memory usage monitoring
Migration from Card Generator
This service is extracted from the original card generator implementation in src/app/card-generator/utils/fileUtils.ts
. Key improvements:
Enhanced Features
- ✅ Configurable options (original was hardcoded)
- ✅ Batch processing (original was single-file only)
- ✅ Security validation (original had basic checks)
- ✅ Progress tracking (original had none)
- ✅ Comprehensive error handling (original was basic)
- ✅ TypeScript interfaces (original used inline types)
Backward Compatibility
The convertSvgToPng()
function maintains the same signature as the original:
// Original usage still works
const blob = await convertSvgToPng(svgContent);
Migration Path
- Update imports to use the new service
- Optionally enhance with new configuration options
- Consider using the class-based API for advanced features
Contributing
When modifying this service:
- Maintain backward compatibility - Don't break the simple
convertSvgToPng()
function - Follow type definitions - Use interfaces instead of inline types
- Add comprehensive tests - Cover security validation and error cases
- Update documentation - Keep examples current and accurate
- Consider performance - Monitor memory usage and conversion times
Dependencies
- Browser APIs: Canvas 2D, Blob, URL
- TypeScript: ES2017+ features
- No external libraries - Pure web standards implementation
Service extracted from card generator utils | Enhanced for reusability | Maintained for production use