Files
data-viz-elements-ui/src/services/viz-export.service.ts
Giuliano Silvestro 0ce172bfc1 Initial commit: data-viz-elements-ui library
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 22:06:35 +10:00

67 lines
2.0 KiB
TypeScript

import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class VizExportService {
exportSvg(element: SVGSVGElement): string {
const serializer = new XMLSerializer();
const svgString = serializer.serializeToString(element);
return `<?xml version="1.0" encoding="UTF-8"?>\n${svgString}`;
}
async exportPng(element: SVGSVGElement, width: number, height: number): Promise<Blob> {
const svgString = this.exportSvg(element);
const svgBlob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });
const url = URL.createObjectURL(svgBlob);
try {
const img = new Image();
img.width = width;
img.height = height;
await new Promise<void>((resolve, reject) => {
img.onload = () => resolve();
img.onerror = reject;
img.src = url;
});
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d')!;
ctx.drawImage(img, 0, 0, width, height);
return new Promise<Blob>((resolve, reject) => {
canvas.toBlob((blob) => {
if (blob) {
resolve(blob);
} else {
reject(new Error('Failed to create PNG blob'));
}
}, 'image/png');
});
} finally {
URL.revokeObjectURL(url);
}
}
downloadSvg(element: SVGSVGElement, filename = 'chart.svg'): void {
const svgString = this.exportSvg(element);
const blob = new Blob([svgString], { type: 'image/svg+xml;charset=utf-8' });
this.downloadBlob(blob, filename);
}
async downloadPng(element: SVGSVGElement, width: number, height: number, filename = 'chart.png'): Promise<void> {
const blob = await this.exportPng(element, width, height);
this.downloadBlob(blob, filename);
}
private downloadBlob(blob: Blob, filename: string): void {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
}