{ "version": 3, "sources": ["../../../src/lib/utils/export.ts"], "sourcesContent": ["import { PngHelpers, debugFlags } from '@tldraw/editor'\nimport { getBrowserCanvasMaxSize } from '../shapes/shared/getBrowserCanvasMaxSize'\n\n/** @public */\nexport type TLCopyType = 'svg' | 'png' | 'jpeg' | 'json'\n\n/** @public */\nexport type TLExportType = 'svg' | 'png' | 'jpeg' | 'webp' | 'json'\n\n/** @public */\nexport function getSvgAsString(svg: SVGElement) {\n\tconst clone = svg.cloneNode(true) as SVGGraphicsElement\n\n\tsvg.setAttribute('width', +svg.getAttribute('width')! + '')\n\tsvg.setAttribute('height', +svg.getAttribute('height')! + '')\n\n\tconst out = new XMLSerializer()\n\t\t.serializeToString(clone)\n\t\t.replaceAll(' ', '')\n\t\t.replaceAll(/((\\s|\")[0-9]*\\.[0-9]{2})([0-9]*)(\\b|\"|\\))/g, '$1')\n\n\treturn out\n}\n\n/** @public */\nexport async function getSvgAsImage(\n\tsvg: SVGElement,\n\toptions: {\n\t\ttype: TLCopyType | TLExportType\n\t\tquality: number\n\t\tscale: number\n\t}\n) {\n\tconst { type, quality, scale } = options\n\n\tconst width = +svg.getAttribute('width')!\n\tconst height = +svg.getAttribute('height')!\n\tlet scaledWidth = width * scale\n\tlet scaledHeight = height * scale\n\n\tconst dataUrl = await getSvgAsDataUrl(svg)\n\n\tconst canvasSizes = await getBrowserCanvasMaxSize()\n\tif (width > canvasSizes.maxWidth) {\n\t\tscaledWidth = canvasSizes.maxWidth\n\t\tscaledHeight = (scaledWidth / width) * height\n\t}\n\tif (height > canvasSizes.maxHeight) {\n\t\tscaledHeight = canvasSizes.maxHeight\n\t\tscaledWidth = (scaledHeight / height) * width\n\t}\n\tif (scaledWidth * scaledHeight > canvasSizes.maxArea) {\n\t\tconst ratio = Math.sqrt(canvasSizes.maxArea / (scaledWidth * scaledHeight))\n\t\tscaledWidth *= ratio\n\t\tscaledHeight *= ratio\n\t}\n\n\tscaledWidth = Math.floor(scaledWidth)\n\tscaledHeight = Math.floor(scaledHeight)\n\tconst effectiveScale = scaledWidth / width\n\n\tconst canvas = await new Promise((resolve) => {\n\t\tconst image = new Image()\n\t\timage.crossOrigin = 'anonymous'\n\n\t\timage.onload = async () => {\n\t\t\t// safari will fire `onLoad` before the fonts in the SVG are\n\t\t\t// actually loaded. just waiting around a while is brittle, but\n\t\t\t// there doesn't seem to be any better solution for now :( see\n\t\t\t// https://bugs.webkit.org/show_bug.cgi?id=219770\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 250))\n\n\t\t\tconst canvas = document.createElement('canvas') as HTMLCanvasElement\n\t\t\tconst ctx = canvas.getContext('2d')!\n\n\t\t\tcanvas.width = scaledWidth\n\t\t\tcanvas.height = scaledHeight\n\n\t\t\tctx.imageSmoothingEnabled = true\n\t\t\tctx.imageSmoothingQuality = 'high'\n\t\t\tctx.drawImage(image, 0, 0, scaledWidth, scaledHeight)\n\n\t\t\tURL.revokeObjectURL(dataUrl)\n\n\t\t\tresolve(canvas)\n\t\t}\n\n\t\timage.onerror = () => {\n\t\t\tresolve(null)\n\t\t}\n\n\t\timage.src = dataUrl\n\t})\n\n\tif (!canvas) return null\n\n\tconst blob = await new Promise((resolve) =>\n\t\tcanvas.toBlob(\n\t\t\t(blob) => {\n\t\t\t\tif (!blob || debugFlags.throwToBlob.value) {\n\t\t\t\t\tresolve(null)\n\t\t\t\t}\n\t\t\t\tresolve(blob)\n\t\t\t},\n\t\t\t'image/' + type,\n\t\t\tquality\n\t\t)\n\t)\n\n\tif (!blob) return null\n\n\tconst view = new DataView(await blob.arrayBuffer())\n\treturn PngHelpers.setPhysChunk(view, effectiveScale, {\n\t\ttype: 'image/' + type,\n\t})\n}\n\n/** @public */\nexport async function getSvgAsDataUrl(svg: SVGElement) {\n\tconst clone = svg.cloneNode(true) as SVGGraphicsElement\n\tclone.setAttribute('encoding', 'UTF-8\"')\n\n\tconst fileReader = new FileReader()\n\tconst imgs = Array.from(clone.querySelectorAll('image')) as SVGImageElement[]\n\n\tfor (const img of imgs) {\n\t\tconst src = img.getAttribute('xlink:href')\n\t\tif (src) {\n\t\t\tif (!src.startsWith('data:')) {\n\t\t\t\tconst blob = await (await fetch(src)).blob()\n\t\t\t\tconst base64 = await new Promise((resolve, reject) => {\n\t\t\t\t\tfileReader.onload = () => resolve(fileReader.result as string)\n\t\t\t\t\tfileReader.onerror = () => reject(fileReader.error)\n\t\t\t\t\tfileReader.readAsDataURL(blob)\n\t\t\t\t})\n\t\t\t\timg.setAttribute('xlink:href', base64)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn getSvgAsDataUrlSync(clone)\n}\n\n/** @public */\nexport function getSvgAsDataUrlSync(node: SVGElement) {\n\tconst svgStr = new XMLSerializer().serializeToString(node)\n\t// NOTE: `unescape` works everywhere although deprecated\n\tconst base64SVG = window.btoa(unescape(encodeURIComponent(svgStr)))\n\treturn `data:image/svg+xml;base64,${base64SVG}`\n}\n\n/** @public */\nexport function downloadDataURLAsFile(dataUrl: string, filename: string) {\n\tconst link = document.createElement('a')\n\tlink.href = dataUrl\n\tlink.download = filename\n\tlink.click()\n}\n\n/** @public */\nexport function getTextBoundingBox(text: SVGTextElement) {\n\tconst svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n\tsvg.appendChild(text)\n\n\tdocument.body.appendChild(svg)\n\tconst bbox = text.getBoundingClientRect()\n\tdocument.body.removeChild(svg)\n\n\treturn bbox\n}\n"], "mappings": "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", "names": ["resolve", "canvas", "blob"] }