UNPKG

tldraw

Version:

A tiny little drawing editor.

8 lines (7 loc) 8.51 kB
{ "version": 3, "sources": ["../../../../src/lib/utils/export/export.ts"], "sourcesContent": ["import {\n\tEditor,\n\tFileHelpers,\n\tImage,\n\tPngHelpers,\n\tTLImageExportOptions,\n\tTLShapeId,\n\tdebugFlags,\n\texhaustiveSwitchError,\n\tsleep,\n\ttlenv,\n} from '@tldraw/editor'\nimport { clampToBrowserMaxCanvasSize } from '../../shapes/shared/getBrowserCanvasMaxSize'\nimport { TLExportType } from './exportAs'\n\n/** @public */\nexport async function getSvgAsImage(\n\teditor: Editor,\n\tsvgString: string,\n\toptions: {\n\t\ttype: 'png' | 'jpeg' | 'webp'\n\t\twidth: number\n\t\theight: number\n\t\tquality?: number\n\t\tpixelRatio?: number\n\t}\n) {\n\tconst { type, width, height, quality = 1, pixelRatio = 2 } = options\n\n\tlet [clampedWidth, clampedHeight] = await clampToBrowserMaxCanvasSize(\n\t\twidth * pixelRatio,\n\t\theight * pixelRatio\n\t)\n\tclampedWidth = Math.floor(clampedWidth)\n\tclampedHeight = Math.floor(clampedHeight)\n\tconst effectiveScale = clampedWidth / width\n\n\t// usually we would use `URL.createObjectURL` here, but chrome has a bug where `blob:` URLs of\n\t// SVGs that use <foreignObject> mark the canvas as tainted, where data: ones do not.\n\t// https://issues.chromium.org/issues/41054640\n\tconst svgUrl = await FileHelpers.blobToDataUrl(new Blob([svgString], { type: 'image/svg+xml' }))\n\n\tconst canvas = await new Promise<HTMLCanvasElement | null>((resolve) => {\n\t\tconst image = 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\tif (tlenv.isSafari) {\n\t\t\t\tawait sleep(250)\n\t\t\t}\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 = clampedWidth\n\t\t\tcanvas.height = clampedHeight\n\n\t\t\tctx.imageSmoothingEnabled = true\n\t\t\tctx.imageSmoothingQuality = 'high'\n\t\t\tctx.drawImage(image, 0, 0, clampedWidth, clampedHeight)\n\n\t\t\tURL.revokeObjectURL(svgUrl)\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 = svgUrl\n\t})\n\n\tif (!canvas) return null\n\n\tconst blob = await new Promise<Blob | null>((resolve) =>\n\t\tcanvas.toBlob(\n\t\t\t(blob) => {\n\t\t\t\tif (!blob || debugFlags.throwToBlob.get()) {\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\tif (type === 'png') {\n\t\tconst view = new DataView(await blob.arrayBuffer())\n\t\treturn PngHelpers.setPhysChunk(view, effectiveScale, {\n\t\t\ttype: 'image/' + type,\n\t\t})\n\t} else {\n\t\treturn blob\n\t}\n}\n\nasync function getSvgString(editor: Editor, ids: TLShapeId[], opts: TLImageExportOptions) {\n\tconst svg = await editor.getSvgString(ids?.length ? ids : [...editor.getCurrentPageShapeIds()], {\n\t\tscale: opts.scale ?? 1,\n\t\tbackground: editor.getInstanceState().exportBackground,\n\t\t...opts,\n\t})\n\tif (!svg) {\n\t\tthrow new Error('Could not construct SVG.')\n\t}\n\treturn svg\n}\n\nexport async function exportToString(\n\teditor: Editor,\n\tids: TLShapeId[],\n\tformat: 'svg' | 'json',\n\topts: TLImageExportOptions = {}\n) {\n\tswitch (format) {\n\t\tcase 'svg': {\n\t\t\treturn (await getSvgString(editor, ids, opts))?.svg\n\t\t}\n\t\tcase 'json': {\n\t\t\tconst data = await editor.resolveAssetsInContent(editor.getContentFromCurrentPage(ids))\n\t\t\treturn JSON.stringify(data)\n\t\t}\n\t\tdefault: {\n\t\t\texhaustiveSwitchError(format)\n\t\t}\n\t}\n}\n\n/**\n * Export the given shapes as a blob.\n * @param editor - The editor instance.\n * @param ids - The ids of the shapes to export.\n * @param format - The format to export as.\n * @param opts - Rendering options.\n * @returns A promise that resolves to a blob.\n * @public\n */\nexport async function exportToBlob({\n\teditor,\n\tids,\n\tformat,\n\topts = {},\n}: {\n\teditor: Editor\n\tids: TLShapeId[]\n\tformat: TLExportType\n\topts?: TLImageExportOptions\n}): Promise<Blob> {\n\tswitch (format) {\n\t\tcase 'svg':\n\t\t\treturn new Blob([await exportToString(editor, ids, 'svg', opts)], { type: 'text/plain' })\n\t\tcase 'json':\n\t\t\treturn new Blob([await exportToString(editor, ids, 'json', opts)], { type: 'text/plain' })\n\t\tcase 'jpeg':\n\t\tcase 'png':\n\t\tcase 'webp': {\n\t\t\tconst svgResult = await getSvgString(editor, ids, opts)\n\t\t\tif (!svgResult) throw new Error('Could not construct image.')\n\t\t\tconst image = await getSvgAsImage(editor, svgResult.svg, {\n\t\t\t\ttype: format,\n\t\t\t\tquality: opts.quality,\n\t\t\t\tpixelRatio: opts.pixelRatio,\n\t\t\t\twidth: svgResult.width,\n\t\t\t\theight: svgResult.height,\n\t\t\t})\n\t\t\tif (!image) {\n\t\t\t\tthrow new Error('Could not construct image.')\n\t\t\t}\n\t\t\treturn image\n\t\t}\n\t\tdefault: {\n\t\t\texhaustiveSwitchError(format)\n\t\t}\n\t}\n}\n\nconst mimeTypeByFormat = {\n\tjpeg: 'image/jpeg',\n\tpng: 'image/png',\n\twebp: 'image/webp',\n\tjson: 'text/plain',\n\tsvg: 'text/plain',\n}\n\nexport function exportToBlobPromise(\n\teditor: Editor,\n\tids: TLShapeId[],\n\tformat: TLExportType,\n\topts: TLImageExportOptions = {}\n): { blobPromise: Promise<Blob>; mimeType: string } {\n\treturn {\n\t\tblobPromise: exportToBlob({ editor, ids, format, opts }),\n\t\tmimeType: mimeTypeByFormat[format],\n\t}\n}\n"], "mappings": "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", "names": ["canvas", "blob"] }