forked from SawyerHood/draw-a-ui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgetSvgAsImage.ts
132 lines (108 loc) · 3.85 KB
/
getSvgAsImage.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { getBrowserCanvasMaxSize } from "./getBrowserCanvasMaxSize";
import { PngHelpers } from "./png";
type TLCopyType = "jpeg" | "json" | "png" | "svg";
type TLExportType = "jpeg" | "json" | "png" | "svg" | "webp";
/**
* This is all copied from node_modules/@tldraw/tldraw/src/lib/utils/export.ts
*/
export async function getSvgAsImage(
svg: SVGElement,
options: {
type: TLCopyType | TLExportType;
quality: number;
scale: number;
}
) {
const { type, quality, scale } = options;
const width = +svg.getAttribute("width")!;
const height = +svg.getAttribute("height")!;
let scaledWidth = width * scale;
let scaledHeight = height * scale;
const dataUrl = await getSvgAsDataUrl(svg);
const canvasSizes = await getBrowserCanvasMaxSize();
if (width > canvasSizes.maxWidth) {
scaledWidth = canvasSizes.maxWidth;
scaledHeight = (scaledWidth / width) * height;
}
if (height > canvasSizes.maxHeight) {
scaledHeight = canvasSizes.maxHeight;
scaledWidth = (scaledHeight / height) * width;
}
if (scaledWidth * scaledHeight > canvasSizes.maxArea) {
const ratio = Math.sqrt(canvasSizes.maxArea / (scaledWidth * scaledHeight));
scaledWidth *= ratio;
scaledHeight *= ratio;
}
scaledWidth = Math.floor(scaledWidth);
scaledHeight = Math.floor(scaledHeight);
const effectiveScale = scaledWidth / width;
const canvas = await new Promise<HTMLCanvasElement | null>((resolve) => {
const image = new Image();
image.crossOrigin = "anonymous";
image.onload = async () => {
// safari will fire `onLoad` before the fonts in the SVG are
// actually loaded. just waiting around a while is brittle, but
// there doesn't seem to be any better solution for now :( see
// https://bugs.webkit.org/show_bug.cgi?id=219770
await new Promise((resolve) => setTimeout(resolve, 250));
const canvas = document.createElement("canvas") as HTMLCanvasElement;
const ctx = canvas.getContext("2d")!;
canvas.width = scaledWidth;
canvas.height = scaledHeight;
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = "high";
ctx.drawImage(image, 0, 0, scaledWidth, scaledHeight);
URL.revokeObjectURL(dataUrl);
resolve(canvas);
};
image.onerror = () => {
resolve(null);
};
image.src = dataUrl;
});
if (!canvas) return null;
const blob = await new Promise<Blob | null>((resolve) =>
canvas.toBlob(
(blob) => {
if (!blob) {
resolve(null);
}
resolve(blob);
},
"image/" + type,
quality
)
);
if (!blob) return null;
const view = new DataView(await blob.arrayBuffer());
return PngHelpers.setPhysChunk(view, effectiveScale, {
type: "image/" + type,
});
}
export async function getSvgAsDataUrl(svg: SVGElement) {
const clone = svg.cloneNode(true) as SVGGraphicsElement;
clone.setAttribute("encoding", 'UTF-8"');
const fileReader = new FileReader();
const imgs = Array.from(clone.querySelectorAll("image")) as SVGImageElement[];
for (const img of imgs) {
const src = img.getAttribute("xlink:href");
if (src) {
if (!src.startsWith("data:")) {
const blob = await (await fetch(src)).blob();
const base64 = await new Promise<string>((resolve, reject) => {
fileReader.onload = () => resolve(fileReader.result as string);
fileReader.onerror = () => reject(fileReader.error);
fileReader.readAsDataURL(blob);
});
img.setAttribute("xlink:href", base64);
}
}
}
return getSvgAsDataUrlSync(clone);
}
export function getSvgAsDataUrlSync(node: SVGElement) {
const svgStr = new XMLSerializer().serializeToString(node);
// NOTE: `unescape` works everywhere although deprecated
const base64SVG = window.btoa(unescape(encodeURIComponent(svgStr)));
return `data:image/svg+xml;base64,${base64SVG}`;
}