@tldraw/editor 3.8.0-canary.61db77736050 → 3.8.0-canary.80294e641ad6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/index.d.ts +11 -104
- package/dist-cjs/index.js +1 -5
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +4 -1
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/config/createTLStore.js +1 -1
- package/dist-cjs/lib/config/createTLStore.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +4 -57
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/types/SvgExportContext.js.map +2 -2
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/exports/exportToSvg.js.map +2 -2
- package/dist-cjs/lib/exports/getSvgJsx.js +3 -16
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +1 -3
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +11 -104
- package/dist-esm/index.mjs +1 -5
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +4 -1
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/config/createTLStore.mjs +1 -1
- package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +4 -57
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/types/SvgExportContext.mjs.map +2 -2
- package/dist-esm/lib/exports/exportToSvg.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgJsx.mjs +3 -16
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +1 -3
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/package.json +7 -9
- package/src/index.ts +0 -4
- package/src/lib/components/default-components/DefaultCanvas.tsx +4 -1
- package/src/lib/config/createTLStore.ts +1 -1
- package/src/lib/editor/Editor.ts +9 -68
- package/src/lib/editor/types/SvgExportContext.tsx +0 -21
- package/src/lib/editor/types/misc-types.ts +2 -55
- package/src/lib/exports/exportToSvg.tsx +2 -2
- package/src/lib/exports/getSvgJsx.tsx +2 -17
- package/src/lib/utils/sync/TLLocalSyncClient.ts +1 -3
- package/src/version.ts +3 -3
- package/dist-cjs/lib/exports/getSvgAsImage.js +0 -83
- package/dist-cjs/lib/exports/getSvgAsImage.js.map +0 -7
- package/dist-cjs/lib/utils/browserCanvasMaxSize.js +0 -75
- package/dist-cjs/lib/utils/browserCanvasMaxSize.js.map +0 -7
- package/dist-esm/lib/exports/getSvgAsImage.mjs +0 -63
- package/dist-esm/lib/exports/getSvgAsImage.mjs.map +0 -7
- package/dist-esm/lib/utils/browserCanvasMaxSize.mjs +0 -45
- package/dist-esm/lib/utils/browserCanvasMaxSize.mjs.map +0 -7
- package/src/lib/exports/getSvgAsImage.ts +0 -92
- package/src/lib/utils/browserCanvasMaxSize.ts +0 -65
|
@@ -8,70 +8,17 @@ export type RequiredKeys<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T,
|
|
|
8
8
|
export type OptionalKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
|
|
9
9
|
|
|
10
10
|
/** @public */
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
/** @public */
|
|
14
|
-
export interface TLSvgExportOptions {
|
|
15
|
-
/**
|
|
16
|
-
* The bounding box, in page coordinates, of the area being exported.
|
|
17
|
-
*/
|
|
11
|
+
export interface TLImageExportOptions {
|
|
18
12
|
bounds?: Box
|
|
19
|
-
/**
|
|
20
|
-
* The logical scale of the export. This scales the resulting size of the SVG being generated.
|
|
21
|
-
*/
|
|
22
13
|
scale?: number
|
|
23
|
-
|
|
24
|
-
* When exporting an SVG, the expected pixel ratio of the export will be passed in to
|
|
25
|
-
* {@link @tldraw/tlschema#TLAssetStore.resolve} as the `dpr` property, so that assets can be
|
|
26
|
-
* downscaled to the appropriate resolution.
|
|
27
|
-
*
|
|
28
|
-
* When exporting to a bitmap image format, the size of the resulting image will be multiplied
|
|
29
|
-
* by this number.
|
|
30
|
-
*
|
|
31
|
-
* For SVG exports, this defaults to undefined - which means we'll request original-quality
|
|
32
|
-
* assets. For bitmap exports, this defaults to 2.
|
|
33
|
-
*/
|
|
14
|
+
quality?: number
|
|
34
15
|
pixelRatio?: number
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Should the background color be included in the export? If false, the generated image will be
|
|
38
|
-
* transparent (if exporting to a format that supports transparency).
|
|
39
|
-
*/
|
|
40
16
|
background?: boolean
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* How much padding to include around the bounds of exports? Defaults to 32px.
|
|
44
|
-
*/
|
|
45
17
|
padding?: number
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Should the export be rendered in dark mode (true) or light mode (false)? Defaults to the
|
|
49
|
-
* current instance's dark mode setting.
|
|
50
|
-
*/
|
|
51
18
|
darkMode?: boolean
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* The
|
|
55
|
-
* {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio | `preserveAspectRatio` }
|
|
56
|
-
* attribute of the SVG element.
|
|
57
|
-
*/
|
|
58
19
|
preserveAspectRatio?: React.SVGAttributes<SVGSVGElement>['preserveAspectRatio']
|
|
59
20
|
}
|
|
60
21
|
|
|
61
|
-
/** @public */
|
|
62
|
-
export interface TLImageExportOptions extends TLSvgExportOptions {
|
|
63
|
-
/**
|
|
64
|
-
* If the export is being converted to a lossy bitmap format (e.g. jpeg), this is the quality of
|
|
65
|
-
* the export. This is a number between 0 and 1.
|
|
66
|
-
*/
|
|
67
|
-
quality?: number
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* The format to export as. Defaults to 'png'.
|
|
71
|
-
*/
|
|
72
|
-
format?: TLExportType
|
|
73
|
-
}
|
|
74
|
-
|
|
75
22
|
/**
|
|
76
23
|
* @public
|
|
77
24
|
* @deprecated use {@link TLImageExportOptions} instead
|
|
@@ -3,7 +3,7 @@ import { assert } from '@tldraw/utils'
|
|
|
3
3
|
import { flushSync } from 'react-dom'
|
|
4
4
|
import { createRoot } from 'react-dom/client'
|
|
5
5
|
import { Editor } from '../editor/Editor'
|
|
6
|
-
import {
|
|
6
|
+
import { TLImageExportOptions } from '../editor/types/misc-types'
|
|
7
7
|
import { StyleEmbedder } from './StyleEmbedder'
|
|
8
8
|
import { embedMedia } from './embedMedia'
|
|
9
9
|
import { getSvgJsx } from './getSvgJsx'
|
|
@@ -13,7 +13,7 @@ let idCounter = 1
|
|
|
13
13
|
export async function exportToSvg(
|
|
14
14
|
editor: Editor,
|
|
15
15
|
shapeIds: TLShapeId[],
|
|
16
|
-
opts:
|
|
16
|
+
opts: TLImageExportOptions = {}
|
|
17
17
|
) {
|
|
18
18
|
// when rendering to SVG, we start by creating a JSX representation of the SVG that we can
|
|
19
19
|
// render with react. Hopefully elements will have a `toSvg` method that renders them to SVG,
|
|
@@ -41,7 +41,7 @@ export function getSvgJsx(editor: Editor, ids: TLShapeId[], opts: TLImageExportO
|
|
|
41
41
|
const {
|
|
42
42
|
scale = 1,
|
|
43
43
|
// should we include the background in the export? or is it transparent?
|
|
44
|
-
background =
|
|
44
|
+
background = false,
|
|
45
45
|
padding = editor.options.defaultSvgPadding,
|
|
46
46
|
preserveAspectRatio,
|
|
47
47
|
} = opts
|
|
@@ -102,7 +102,6 @@ export function getSvgJsx(editor: Editor, ids: TLShapeId[], opts: TLImageExportO
|
|
|
102
102
|
editor={editor}
|
|
103
103
|
preserveAspectRatio={preserveAspectRatio}
|
|
104
104
|
scale={scale}
|
|
105
|
-
pixelRatio={opts.pixelRatio ?? null}
|
|
106
105
|
bbox={bbox}
|
|
107
106
|
background={background}
|
|
108
107
|
singleFrameShapeId={singleFrameShapeId}
|
|
@@ -122,7 +121,6 @@ function SvgExport({
|
|
|
122
121
|
editor,
|
|
123
122
|
preserveAspectRatio,
|
|
124
123
|
scale,
|
|
125
|
-
pixelRatio,
|
|
126
124
|
bbox,
|
|
127
125
|
background,
|
|
128
126
|
singleFrameShapeId,
|
|
@@ -134,7 +132,6 @@ function SvgExport({
|
|
|
134
132
|
editor: Editor
|
|
135
133
|
preserveAspectRatio?: string
|
|
136
134
|
scale: number
|
|
137
|
-
pixelRatio: number | null
|
|
138
135
|
bbox: Box
|
|
139
136
|
background: boolean
|
|
140
137
|
singleFrameShapeId: TLShapeId | null
|
|
@@ -180,20 +177,8 @@ function SvgExport({
|
|
|
180
177
|
isDarkMode,
|
|
181
178
|
waitUntil,
|
|
182
179
|
addExportDef,
|
|
183
|
-
scale,
|
|
184
|
-
pixelRatio,
|
|
185
|
-
async resolveAssetUrl(assetId, width) {
|
|
186
|
-
const asset = editor.getAsset(assetId)
|
|
187
|
-
if (!asset || (asset.type !== 'image' && asset.type !== 'video')) return null
|
|
188
|
-
|
|
189
|
-
return await editor.resolveAssetUrl(assetId, {
|
|
190
|
-
screenScale: scale * (width / asset.props.w),
|
|
191
|
-
shouldResolveToOriginal: pixelRatio === null,
|
|
192
|
-
dpr: pixelRatio ?? undefined,
|
|
193
|
-
})
|
|
194
|
-
},
|
|
195
180
|
}),
|
|
196
|
-
[isDarkMode, waitUntil, addExportDef
|
|
181
|
+
[isDarkMode, waitUntil, addExportDef]
|
|
197
182
|
)
|
|
198
183
|
|
|
199
184
|
const didRenderRef = useRef(false)
|
|
@@ -196,9 +196,7 @@ export class TLLocalSyncClient {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
if (sessionStateSnapshot) {
|
|
199
|
-
loadSessionStateSnapshotIntoStore(this.store, sessionStateSnapshot
|
|
200
|
-
forceOverwrite: true,
|
|
201
|
-
})
|
|
199
|
+
loadSessionStateSnapshotIntoStore(this.store, sessionStateSnapshot)
|
|
202
200
|
}
|
|
203
201
|
}
|
|
204
202
|
|
package/src/version.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// This file is automatically generated by internal/scripts/refresh-assets.ts.
|
|
2
2
|
// Do not edit manually. Or do, I'm a comment, not a cop.
|
|
3
3
|
|
|
4
|
-
export const version = '3.8.0-canary.
|
|
4
|
+
export const version = '3.8.0-canary.80294e641ad6'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2024-09-13T14:36:29.063Z',
|
|
7
|
-
minor: '2025-01-
|
|
8
|
-
patch: '2025-01-
|
|
7
|
+
minor: '2025-01-20T09:29:17.935Z',
|
|
8
|
+
patch: '2025-01-20T09:29:17.935Z',
|
|
9
9
|
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var getSvgAsImage_exports = {};
|
|
20
|
-
__export(getSvgAsImage_exports, {
|
|
21
|
-
getSvgAsImage: () => getSvgAsImage
|
|
22
|
-
});
|
|
23
|
-
module.exports = __toCommonJS(getSvgAsImage_exports);
|
|
24
|
-
var import_utils = require("@tldraw/utils");
|
|
25
|
-
var import_environment = require("../globals/environment");
|
|
26
|
-
var import_browserCanvasMaxSize = require("../utils/browserCanvasMaxSize");
|
|
27
|
-
var import_debug_flags = require("../utils/debug-flags");
|
|
28
|
-
async function getSvgAsImage(svgString, options) {
|
|
29
|
-
const { type, width, height, quality = 1, pixelRatio = 2 } = options;
|
|
30
|
-
let [clampedWidth, clampedHeight] = await (0, import_browserCanvasMaxSize.clampToBrowserMaxCanvasSize)(
|
|
31
|
-
width * pixelRatio,
|
|
32
|
-
height * pixelRatio
|
|
33
|
-
);
|
|
34
|
-
clampedWidth = Math.floor(clampedWidth);
|
|
35
|
-
clampedHeight = Math.floor(clampedHeight);
|
|
36
|
-
const effectiveScale = clampedWidth / width;
|
|
37
|
-
const svgUrl = await import_utils.FileHelpers.blobToDataUrl(new Blob([svgString], { type: "image/svg+xml" }));
|
|
38
|
-
const canvas = await new Promise((resolve) => {
|
|
39
|
-
const image = (0, import_utils.Image)();
|
|
40
|
-
image.crossOrigin = "anonymous";
|
|
41
|
-
image.onload = async () => {
|
|
42
|
-
if (import_environment.tlenv.isSafari) {
|
|
43
|
-
await (0, import_utils.sleep)(250);
|
|
44
|
-
}
|
|
45
|
-
const canvas2 = document.createElement("canvas");
|
|
46
|
-
const ctx = canvas2.getContext("2d");
|
|
47
|
-
canvas2.width = clampedWidth;
|
|
48
|
-
canvas2.height = clampedHeight;
|
|
49
|
-
ctx.imageSmoothingEnabled = true;
|
|
50
|
-
ctx.imageSmoothingQuality = "high";
|
|
51
|
-
ctx.drawImage(image, 0, 0, clampedWidth, clampedHeight);
|
|
52
|
-
URL.revokeObjectURL(svgUrl);
|
|
53
|
-
resolve(canvas2);
|
|
54
|
-
};
|
|
55
|
-
image.onerror = () => {
|
|
56
|
-
resolve(null);
|
|
57
|
-
};
|
|
58
|
-
image.src = svgUrl;
|
|
59
|
-
});
|
|
60
|
-
if (!canvas) return null;
|
|
61
|
-
const blob = await new Promise(
|
|
62
|
-
(resolve) => canvas.toBlob(
|
|
63
|
-
(blob2) => {
|
|
64
|
-
if (!blob2 || import_debug_flags.debugFlags.throwToBlob.get()) {
|
|
65
|
-
resolve(null);
|
|
66
|
-
}
|
|
67
|
-
resolve(blob2);
|
|
68
|
-
},
|
|
69
|
-
"image/" + type,
|
|
70
|
-
quality
|
|
71
|
-
)
|
|
72
|
-
);
|
|
73
|
-
if (!blob) return null;
|
|
74
|
-
if (type === "png") {
|
|
75
|
-
const view = new DataView(await blob.arrayBuffer());
|
|
76
|
-
return import_utils.PngHelpers.setPhysChunk(view, effectiveScale, {
|
|
77
|
-
type: "image/" + type
|
|
78
|
-
});
|
|
79
|
-
} else {
|
|
80
|
-
return blob;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
//# sourceMappingURL=getSvgAsImage.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/lib/exports/getSvgAsImage.ts"],
|
|
4
|
-
"sourcesContent": ["import { FileHelpers, Image, PngHelpers, sleep } from '@tldraw/utils'\nimport { tlenv } from '../globals/environment'\nimport { clampToBrowserMaxCanvasSize } from '../utils/browserCanvasMaxSize'\nimport { debugFlags } from '../utils/debug-flags'\n\n/** @public */\nexport async function getSvgAsImage(\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"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAsD;AACtD,yBAAsB;AACtB,kCAA4C;AAC5C,yBAA2B;AAG3B,eAAsB,cACrB,WACA,SAOC;AACD,QAAM,EAAE,MAAM,OAAO,QAAQ,UAAU,GAAG,aAAa,EAAE,IAAI;AAE7D,MAAI,CAAC,cAAc,aAAa,IAAI,UAAM;AAAA,IACzC,QAAQ;AAAA,IACR,SAAS;AAAA,EACV;AACA,iBAAe,KAAK,MAAM,YAAY;AACtC,kBAAgB,KAAK,MAAM,aAAa;AACxC,QAAM,iBAAiB,eAAe;AAKtC,QAAM,SAAS,MAAM,yBAAY,cAAc,IAAI,KAAK,CAAC,SAAS,GAAG,EAAE,MAAM,gBAAgB,CAAC,CAAC;AAE/F,QAAM,SAAS,MAAM,IAAI,QAAkC,CAAC,YAAY;AACvE,UAAM,YAAQ,oBAAM;AACpB,UAAM,cAAc;AAEpB,UAAM,SAAS,YAAY;AAK1B,UAAI,yBAAM,UAAU;AACnB,kBAAM,oBAAM,GAAG;AAAA,MAChB;AAEA,YAAMA,UAAS,SAAS,cAAc,QAAQ;AAC9C,YAAM,MAAMA,QAAO,WAAW,IAAI;AAElC,MAAAA,QAAO,QAAQ;AACf,MAAAA,QAAO,SAAS;AAEhB,UAAI,wBAAwB;AAC5B,UAAI,wBAAwB;AAC5B,UAAI,UAAU,OAAO,GAAG,GAAG,cAAc,aAAa;AAEtD,UAAI,gBAAgB,MAAM;AAE1B,cAAQA,OAAM;AAAA,IACf;AAEA,UAAM,UAAU,MAAM;AACrB,cAAQ,IAAI;AAAA,IACb;AAEA,UAAM,MAAM;AAAA,EACb,CAAC;AAED,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,OAAO,MAAM,IAAI;AAAA,IAAqB,CAAC,YAC5C,OAAO;AAAA,MACN,CAACC,UAAS;AACT,YAAI,CAACA,SAAQ,8BAAW,YAAY,IAAI,GAAG;AAC1C,kBAAQ,IAAI;AAAA,QACb;AACA,gBAAQA,KAAI;AAAA,MACb;AAAA,MACA,WAAW;AAAA,MACX;AAAA,IACD;AAAA,EACD;AAEA,MAAI,CAAC,KAAM,QAAO;AAElB,MAAI,SAAS,OAAO;AACnB,UAAM,OAAO,IAAI,SAAS,MAAM,KAAK,YAAY,CAAC;AAClD,WAAO,wBAAW,aAAa,MAAM,gBAAgB;AAAA,MACpD,MAAM,WAAW;AAAA,IAClB,CAAC;AAAA,EACF,OAAO;AACN,WAAO;AAAA,EACR;AACD;",
|
|
6
|
-
"names": ["canvas", "blob"]
|
|
7
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
var browserCanvasMaxSize_exports = {};
|
|
30
|
-
__export(browserCanvasMaxSize_exports, {
|
|
31
|
-
clampToBrowserMaxCanvasSize: () => clampToBrowserMaxCanvasSize
|
|
32
|
-
});
|
|
33
|
-
module.exports = __toCommonJS(browserCanvasMaxSize_exports);
|
|
34
|
-
var import_canvas_size = __toESM(require("canvas-size"));
|
|
35
|
-
let maxSizePromise = null;
|
|
36
|
-
function getBrowserCanvasMaxSize() {
|
|
37
|
-
if (!maxSizePromise) {
|
|
38
|
-
maxSizePromise = calculateBrowserCanvasMaxSize();
|
|
39
|
-
}
|
|
40
|
-
return maxSizePromise;
|
|
41
|
-
}
|
|
42
|
-
async function calculateBrowserCanvasMaxSize() {
|
|
43
|
-
const maxWidth = await import_canvas_size.default.maxWidth({ usePromise: true });
|
|
44
|
-
const maxHeight = await import_canvas_size.default.maxHeight({ usePromise: true });
|
|
45
|
-
const maxArea = await import_canvas_size.default.maxArea({ usePromise: true });
|
|
46
|
-
return {
|
|
47
|
-
maxWidth: maxWidth.width,
|
|
48
|
-
maxHeight: maxHeight.height,
|
|
49
|
-
maxArea: maxArea.width * maxArea.height
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
const MAX_SAFE_CANVAS_DIMENSION = 8192;
|
|
53
|
-
const MAX_SAFE_CANVAS_AREA = 4096 * 4096;
|
|
54
|
-
async function clampToBrowserMaxCanvasSize(width, height) {
|
|
55
|
-
if (width <= MAX_SAFE_CANVAS_DIMENSION && height <= MAX_SAFE_CANVAS_DIMENSION && width * height <= MAX_SAFE_CANVAS_AREA) {
|
|
56
|
-
return [width, height];
|
|
57
|
-
}
|
|
58
|
-
const { maxWidth, maxHeight, maxArea } = await getBrowserCanvasMaxSize();
|
|
59
|
-
const aspectRatio = width / height;
|
|
60
|
-
if (width > maxWidth) {
|
|
61
|
-
width = maxWidth;
|
|
62
|
-
height = width / aspectRatio;
|
|
63
|
-
}
|
|
64
|
-
if (height > maxHeight) {
|
|
65
|
-
height = maxHeight;
|
|
66
|
-
width = height * aspectRatio;
|
|
67
|
-
}
|
|
68
|
-
if (width * height > maxArea) {
|
|
69
|
-
const ratio = Math.sqrt(maxArea / (width * height));
|
|
70
|
-
width *= ratio;
|
|
71
|
-
height *= ratio;
|
|
72
|
-
}
|
|
73
|
-
return [width, height];
|
|
74
|
-
}
|
|
75
|
-
//# sourceMappingURL=browserCanvasMaxSize.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/lib/utils/browserCanvasMaxSize.ts"],
|
|
4
|
-
"sourcesContent": ["import canvasSize from 'canvas-size'\n\n/** @internal */\nexport interface CanvasMaxSize {\n\tmaxWidth: number\n\tmaxHeight: number\n\tmaxArea: number\n}\n\nlet maxSizePromise: Promise<CanvasMaxSize> | null = null\n\nfunction getBrowserCanvasMaxSize() {\n\tif (!maxSizePromise) {\n\t\tmaxSizePromise = calculateBrowserCanvasMaxSize()\n\t}\n\n\treturn maxSizePromise\n}\n\nasync function calculateBrowserCanvasMaxSize(): Promise<CanvasMaxSize> {\n\tconst maxWidth = await canvasSize.maxWidth({ usePromise: true })\n\tconst maxHeight = await canvasSize.maxHeight({ usePromise: true })\n\tconst maxArea = await canvasSize.maxArea({ usePromise: true })\n\treturn {\n\t\tmaxWidth: maxWidth.width,\n\t\tmaxHeight: maxHeight.height,\n\t\tmaxArea: maxArea.width * maxArea.height,\n\t}\n}\n\n// https://github.com/jhildenbiddle/canvas-size?tab=readme-ov-file#test-results\nconst MAX_SAFE_CANVAS_DIMENSION = 8192\nconst MAX_SAFE_CANVAS_AREA = 4096 * 4096\n\n/** @internal */\nexport async function clampToBrowserMaxCanvasSize(width: number, height: number) {\n\tif (\n\t\twidth <= MAX_SAFE_CANVAS_DIMENSION &&\n\t\theight <= MAX_SAFE_CANVAS_DIMENSION &&\n\t\twidth * height <= MAX_SAFE_CANVAS_AREA\n\t) {\n\t\treturn [width, height]\n\t}\n\n\tconst { maxWidth, maxHeight, maxArea } = await getBrowserCanvasMaxSize()\n\tconst aspectRatio = width / height\n\n\tif (width > maxWidth) {\n\t\twidth = maxWidth\n\t\theight = width / aspectRatio\n\t}\n\n\tif (height > maxHeight) {\n\t\theight = maxHeight\n\t\twidth = height * aspectRatio\n\t}\n\n\tif (width * height > maxArea) {\n\t\tconst ratio = Math.sqrt(maxArea / (width * height))\n\t\twidth *= ratio\n\t\theight *= ratio\n\t}\n\n\treturn [width, height]\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAuB;AASvB,IAAI,iBAAgD;AAEpD,SAAS,0BAA0B;AAClC,MAAI,CAAC,gBAAgB;AACpB,qBAAiB,8BAA8B;AAAA,EAChD;AAEA,SAAO;AACR;AAEA,eAAe,gCAAwD;AACtE,QAAM,WAAW,MAAM,mBAAAA,QAAW,SAAS,EAAE,YAAY,KAAK,CAAC;AAC/D,QAAM,YAAY,MAAM,mBAAAA,QAAW,UAAU,EAAE,YAAY,KAAK,CAAC;AACjE,QAAM,UAAU,MAAM,mBAAAA,QAAW,QAAQ,EAAE,YAAY,KAAK,CAAC;AAC7D,SAAO;AAAA,IACN,UAAU,SAAS;AAAA,IACnB,WAAW,UAAU;AAAA,IACrB,SAAS,QAAQ,QAAQ,QAAQ;AAAA,EAClC;AACD;AAGA,MAAM,4BAA4B;AAClC,MAAM,uBAAuB,OAAO;AAGpC,eAAsB,4BAA4B,OAAe,QAAgB;AAChF,MACC,SAAS,6BACT,UAAU,6BACV,QAAQ,UAAU,sBACjB;AACD,WAAO,CAAC,OAAO,MAAM;AAAA,EACtB;AAEA,QAAM,EAAE,UAAU,WAAW,QAAQ,IAAI,MAAM,wBAAwB;AACvE,QAAM,cAAc,QAAQ;AAE5B,MAAI,QAAQ,UAAU;AACrB,YAAQ;AACR,aAAS,QAAQ;AAAA,EAClB;AAEA,MAAI,SAAS,WAAW;AACvB,aAAS;AACT,YAAQ,SAAS;AAAA,EAClB;AAEA,MAAI,QAAQ,SAAS,SAAS;AAC7B,UAAM,QAAQ,KAAK,KAAK,WAAW,QAAQ,OAAO;AAClD,aAAS;AACT,cAAU;AAAA,EACX;AAEA,SAAO,CAAC,OAAO,MAAM;AACtB;",
|
|
6
|
-
"names": ["canvasSize"]
|
|
7
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { FileHelpers, Image, PngHelpers, sleep } from "@tldraw/utils";
|
|
2
|
-
import { tlenv } from "../globals/environment.mjs";
|
|
3
|
-
import { clampToBrowserMaxCanvasSize } from "../utils/browserCanvasMaxSize.mjs";
|
|
4
|
-
import { debugFlags } from "../utils/debug-flags.mjs";
|
|
5
|
-
async function getSvgAsImage(svgString, options) {
|
|
6
|
-
const { type, width, height, quality = 1, pixelRatio = 2 } = options;
|
|
7
|
-
let [clampedWidth, clampedHeight] = await clampToBrowserMaxCanvasSize(
|
|
8
|
-
width * pixelRatio,
|
|
9
|
-
height * pixelRatio
|
|
10
|
-
);
|
|
11
|
-
clampedWidth = Math.floor(clampedWidth);
|
|
12
|
-
clampedHeight = Math.floor(clampedHeight);
|
|
13
|
-
const effectiveScale = clampedWidth / width;
|
|
14
|
-
const svgUrl = await FileHelpers.blobToDataUrl(new Blob([svgString], { type: "image/svg+xml" }));
|
|
15
|
-
const canvas = await new Promise((resolve) => {
|
|
16
|
-
const image = Image();
|
|
17
|
-
image.crossOrigin = "anonymous";
|
|
18
|
-
image.onload = async () => {
|
|
19
|
-
if (tlenv.isSafari) {
|
|
20
|
-
await sleep(250);
|
|
21
|
-
}
|
|
22
|
-
const canvas2 = document.createElement("canvas");
|
|
23
|
-
const ctx = canvas2.getContext("2d");
|
|
24
|
-
canvas2.width = clampedWidth;
|
|
25
|
-
canvas2.height = clampedHeight;
|
|
26
|
-
ctx.imageSmoothingEnabled = true;
|
|
27
|
-
ctx.imageSmoothingQuality = "high";
|
|
28
|
-
ctx.drawImage(image, 0, 0, clampedWidth, clampedHeight);
|
|
29
|
-
URL.revokeObjectURL(svgUrl);
|
|
30
|
-
resolve(canvas2);
|
|
31
|
-
};
|
|
32
|
-
image.onerror = () => {
|
|
33
|
-
resolve(null);
|
|
34
|
-
};
|
|
35
|
-
image.src = svgUrl;
|
|
36
|
-
});
|
|
37
|
-
if (!canvas) return null;
|
|
38
|
-
const blob = await new Promise(
|
|
39
|
-
(resolve) => canvas.toBlob(
|
|
40
|
-
(blob2) => {
|
|
41
|
-
if (!blob2 || debugFlags.throwToBlob.get()) {
|
|
42
|
-
resolve(null);
|
|
43
|
-
}
|
|
44
|
-
resolve(blob2);
|
|
45
|
-
},
|
|
46
|
-
"image/" + type,
|
|
47
|
-
quality
|
|
48
|
-
)
|
|
49
|
-
);
|
|
50
|
-
if (!blob) return null;
|
|
51
|
-
if (type === "png") {
|
|
52
|
-
const view = new DataView(await blob.arrayBuffer());
|
|
53
|
-
return PngHelpers.setPhysChunk(view, effectiveScale, {
|
|
54
|
-
type: "image/" + type
|
|
55
|
-
});
|
|
56
|
-
} else {
|
|
57
|
-
return blob;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
export {
|
|
61
|
-
getSvgAsImage
|
|
62
|
-
};
|
|
63
|
-
//# sourceMappingURL=getSvgAsImage.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/lib/exports/getSvgAsImage.ts"],
|
|
4
|
-
"sourcesContent": ["import { FileHelpers, Image, PngHelpers, sleep } from '@tldraw/utils'\nimport { tlenv } from '../globals/environment'\nimport { clampToBrowserMaxCanvasSize } from '../utils/browserCanvasMaxSize'\nimport { debugFlags } from '../utils/debug-flags'\n\n/** @public */\nexport async function getSvgAsImage(\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"],
|
|
5
|
-
"mappings": "AAAA,SAAS,aAAa,OAAO,YAAY,aAAa;AACtD,SAAS,aAAa;AACtB,SAAS,mCAAmC;AAC5C,SAAS,kBAAkB;AAG3B,eAAsB,cACrB,WACA,SAOC;AACD,QAAM,EAAE,MAAM,OAAO,QAAQ,UAAU,GAAG,aAAa,EAAE,IAAI;AAE7D,MAAI,CAAC,cAAc,aAAa,IAAI,MAAM;AAAA,IACzC,QAAQ;AAAA,IACR,SAAS;AAAA,EACV;AACA,iBAAe,KAAK,MAAM,YAAY;AACtC,kBAAgB,KAAK,MAAM,aAAa;AACxC,QAAM,iBAAiB,eAAe;AAKtC,QAAM,SAAS,MAAM,YAAY,cAAc,IAAI,KAAK,CAAC,SAAS,GAAG,EAAE,MAAM,gBAAgB,CAAC,CAAC;AAE/F,QAAM,SAAS,MAAM,IAAI,QAAkC,CAAC,YAAY;AACvE,UAAM,QAAQ,MAAM;AACpB,UAAM,cAAc;AAEpB,UAAM,SAAS,YAAY;AAK1B,UAAI,MAAM,UAAU;AACnB,cAAM,MAAM,GAAG;AAAA,MAChB;AAEA,YAAMA,UAAS,SAAS,cAAc,QAAQ;AAC9C,YAAM,MAAMA,QAAO,WAAW,IAAI;AAElC,MAAAA,QAAO,QAAQ;AACf,MAAAA,QAAO,SAAS;AAEhB,UAAI,wBAAwB;AAC5B,UAAI,wBAAwB;AAC5B,UAAI,UAAU,OAAO,GAAG,GAAG,cAAc,aAAa;AAEtD,UAAI,gBAAgB,MAAM;AAE1B,cAAQA,OAAM;AAAA,IACf;AAEA,UAAM,UAAU,MAAM;AACrB,cAAQ,IAAI;AAAA,IACb;AAEA,UAAM,MAAM;AAAA,EACb,CAAC;AAED,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,OAAO,MAAM,IAAI;AAAA,IAAqB,CAAC,YAC5C,OAAO;AAAA,MACN,CAACC,UAAS;AACT,YAAI,CAACA,SAAQ,WAAW,YAAY,IAAI,GAAG;AAC1C,kBAAQ,IAAI;AAAA,QACb;AACA,gBAAQA,KAAI;AAAA,MACb;AAAA,MACA,WAAW;AAAA,MACX;AAAA,IACD;AAAA,EACD;AAEA,MAAI,CAAC,KAAM,QAAO;AAElB,MAAI,SAAS,OAAO;AACnB,UAAM,OAAO,IAAI,SAAS,MAAM,KAAK,YAAY,CAAC;AAClD,WAAO,WAAW,aAAa,MAAM,gBAAgB;AAAA,MACpD,MAAM,WAAW;AAAA,IAClB,CAAC;AAAA,EACF,OAAO;AACN,WAAO;AAAA,EACR;AACD;",
|
|
6
|
-
"names": ["canvas", "blob"]
|
|
7
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import canvasSize from "canvas-size";
|
|
2
|
-
let maxSizePromise = null;
|
|
3
|
-
function getBrowserCanvasMaxSize() {
|
|
4
|
-
if (!maxSizePromise) {
|
|
5
|
-
maxSizePromise = calculateBrowserCanvasMaxSize();
|
|
6
|
-
}
|
|
7
|
-
return maxSizePromise;
|
|
8
|
-
}
|
|
9
|
-
async function calculateBrowserCanvasMaxSize() {
|
|
10
|
-
const maxWidth = await canvasSize.maxWidth({ usePromise: true });
|
|
11
|
-
const maxHeight = await canvasSize.maxHeight({ usePromise: true });
|
|
12
|
-
const maxArea = await canvasSize.maxArea({ usePromise: true });
|
|
13
|
-
return {
|
|
14
|
-
maxWidth: maxWidth.width,
|
|
15
|
-
maxHeight: maxHeight.height,
|
|
16
|
-
maxArea: maxArea.width * maxArea.height
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
const MAX_SAFE_CANVAS_DIMENSION = 8192;
|
|
20
|
-
const MAX_SAFE_CANVAS_AREA = 4096 * 4096;
|
|
21
|
-
async function clampToBrowserMaxCanvasSize(width, height) {
|
|
22
|
-
if (width <= MAX_SAFE_CANVAS_DIMENSION && height <= MAX_SAFE_CANVAS_DIMENSION && width * height <= MAX_SAFE_CANVAS_AREA) {
|
|
23
|
-
return [width, height];
|
|
24
|
-
}
|
|
25
|
-
const { maxWidth, maxHeight, maxArea } = await getBrowserCanvasMaxSize();
|
|
26
|
-
const aspectRatio = width / height;
|
|
27
|
-
if (width > maxWidth) {
|
|
28
|
-
width = maxWidth;
|
|
29
|
-
height = width / aspectRatio;
|
|
30
|
-
}
|
|
31
|
-
if (height > maxHeight) {
|
|
32
|
-
height = maxHeight;
|
|
33
|
-
width = height * aspectRatio;
|
|
34
|
-
}
|
|
35
|
-
if (width * height > maxArea) {
|
|
36
|
-
const ratio = Math.sqrt(maxArea / (width * height));
|
|
37
|
-
width *= ratio;
|
|
38
|
-
height *= ratio;
|
|
39
|
-
}
|
|
40
|
-
return [width, height];
|
|
41
|
-
}
|
|
42
|
-
export {
|
|
43
|
-
clampToBrowserMaxCanvasSize
|
|
44
|
-
};
|
|
45
|
-
//# sourceMappingURL=browserCanvasMaxSize.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/lib/utils/browserCanvasMaxSize.ts"],
|
|
4
|
-
"sourcesContent": ["import canvasSize from 'canvas-size'\n\n/** @internal */\nexport interface CanvasMaxSize {\n\tmaxWidth: number\n\tmaxHeight: number\n\tmaxArea: number\n}\n\nlet maxSizePromise: Promise<CanvasMaxSize> | null = null\n\nfunction getBrowserCanvasMaxSize() {\n\tif (!maxSizePromise) {\n\t\tmaxSizePromise = calculateBrowserCanvasMaxSize()\n\t}\n\n\treturn maxSizePromise\n}\n\nasync function calculateBrowserCanvasMaxSize(): Promise<CanvasMaxSize> {\n\tconst maxWidth = await canvasSize.maxWidth({ usePromise: true })\n\tconst maxHeight = await canvasSize.maxHeight({ usePromise: true })\n\tconst maxArea = await canvasSize.maxArea({ usePromise: true })\n\treturn {\n\t\tmaxWidth: maxWidth.width,\n\t\tmaxHeight: maxHeight.height,\n\t\tmaxArea: maxArea.width * maxArea.height,\n\t}\n}\n\n// https://github.com/jhildenbiddle/canvas-size?tab=readme-ov-file#test-results\nconst MAX_SAFE_CANVAS_DIMENSION = 8192\nconst MAX_SAFE_CANVAS_AREA = 4096 * 4096\n\n/** @internal */\nexport async function clampToBrowserMaxCanvasSize(width: number, height: number) {\n\tif (\n\t\twidth <= MAX_SAFE_CANVAS_DIMENSION &&\n\t\theight <= MAX_SAFE_CANVAS_DIMENSION &&\n\t\twidth * height <= MAX_SAFE_CANVAS_AREA\n\t) {\n\t\treturn [width, height]\n\t}\n\n\tconst { maxWidth, maxHeight, maxArea } = await getBrowserCanvasMaxSize()\n\tconst aspectRatio = width / height\n\n\tif (width > maxWidth) {\n\t\twidth = maxWidth\n\t\theight = width / aspectRatio\n\t}\n\n\tif (height > maxHeight) {\n\t\theight = maxHeight\n\t\twidth = height * aspectRatio\n\t}\n\n\tif (width * height > maxArea) {\n\t\tconst ratio = Math.sqrt(maxArea / (width * height))\n\t\twidth *= ratio\n\t\theight *= ratio\n\t}\n\n\treturn [width, height]\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,gBAAgB;AASvB,IAAI,iBAAgD;AAEpD,SAAS,0BAA0B;AAClC,MAAI,CAAC,gBAAgB;AACpB,qBAAiB,8BAA8B;AAAA,EAChD;AAEA,SAAO;AACR;AAEA,eAAe,gCAAwD;AACtE,QAAM,WAAW,MAAM,WAAW,SAAS,EAAE,YAAY,KAAK,CAAC;AAC/D,QAAM,YAAY,MAAM,WAAW,UAAU,EAAE,YAAY,KAAK,CAAC;AACjE,QAAM,UAAU,MAAM,WAAW,QAAQ,EAAE,YAAY,KAAK,CAAC;AAC7D,SAAO;AAAA,IACN,UAAU,SAAS;AAAA,IACnB,WAAW,UAAU;AAAA,IACrB,SAAS,QAAQ,QAAQ,QAAQ;AAAA,EAClC;AACD;AAGA,MAAM,4BAA4B;AAClC,MAAM,uBAAuB,OAAO;AAGpC,eAAsB,4BAA4B,OAAe,QAAgB;AAChF,MACC,SAAS,6BACT,UAAU,6BACV,QAAQ,UAAU,sBACjB;AACD,WAAO,CAAC,OAAO,MAAM;AAAA,EACtB;AAEA,QAAM,EAAE,UAAU,WAAW,QAAQ,IAAI,MAAM,wBAAwB;AACvE,QAAM,cAAc,QAAQ;AAE5B,MAAI,QAAQ,UAAU;AACrB,YAAQ;AACR,aAAS,QAAQ;AAAA,EAClB;AAEA,MAAI,SAAS,WAAW;AACvB,aAAS;AACT,YAAQ,SAAS;AAAA,EAClB;AAEA,MAAI,QAAQ,SAAS,SAAS;AAC7B,UAAM,QAAQ,KAAK,KAAK,WAAW,QAAQ,OAAO;AAClD,aAAS;AACT,cAAU;AAAA,EACX;AAEA,SAAO,CAAC,OAAO,MAAM;AACtB;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { FileHelpers, Image, PngHelpers, sleep } from '@tldraw/utils'
|
|
2
|
-
import { tlenv } from '../globals/environment'
|
|
3
|
-
import { clampToBrowserMaxCanvasSize } from '../utils/browserCanvasMaxSize'
|
|
4
|
-
import { debugFlags } from '../utils/debug-flags'
|
|
5
|
-
|
|
6
|
-
/** @public */
|
|
7
|
-
export async function getSvgAsImage(
|
|
8
|
-
svgString: string,
|
|
9
|
-
options: {
|
|
10
|
-
type: 'png' | 'jpeg' | 'webp'
|
|
11
|
-
width: number
|
|
12
|
-
height: number
|
|
13
|
-
quality?: number
|
|
14
|
-
pixelRatio?: number
|
|
15
|
-
}
|
|
16
|
-
) {
|
|
17
|
-
const { type, width, height, quality = 1, pixelRatio = 2 } = options
|
|
18
|
-
|
|
19
|
-
let [clampedWidth, clampedHeight] = await clampToBrowserMaxCanvasSize(
|
|
20
|
-
width * pixelRatio,
|
|
21
|
-
height * pixelRatio
|
|
22
|
-
)
|
|
23
|
-
clampedWidth = Math.floor(clampedWidth)
|
|
24
|
-
clampedHeight = Math.floor(clampedHeight)
|
|
25
|
-
const effectiveScale = clampedWidth / width
|
|
26
|
-
|
|
27
|
-
// usually we would use `URL.createObjectURL` here, but chrome has a bug where `blob:` URLs of
|
|
28
|
-
// SVGs that use <foreignObject> mark the canvas as tainted, where data: ones do not.
|
|
29
|
-
// https://issues.chromium.org/issues/41054640
|
|
30
|
-
const svgUrl = await FileHelpers.blobToDataUrl(new Blob([svgString], { type: 'image/svg+xml' }))
|
|
31
|
-
|
|
32
|
-
const canvas = await new Promise<HTMLCanvasElement | null>((resolve) => {
|
|
33
|
-
const image = Image()
|
|
34
|
-
image.crossOrigin = 'anonymous'
|
|
35
|
-
|
|
36
|
-
image.onload = async () => {
|
|
37
|
-
// safari will fire `onLoad` before the fonts in the SVG are
|
|
38
|
-
// actually loaded. just waiting around a while is brittle, but
|
|
39
|
-
// there doesn't seem to be any better solution for now :( see
|
|
40
|
-
// https://bugs.webkit.org/show_bug.cgi?id=219770
|
|
41
|
-
if (tlenv.isSafari) {
|
|
42
|
-
await sleep(250)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const canvas = document.createElement('canvas') as HTMLCanvasElement
|
|
46
|
-
const ctx = canvas.getContext('2d')!
|
|
47
|
-
|
|
48
|
-
canvas.width = clampedWidth
|
|
49
|
-
canvas.height = clampedHeight
|
|
50
|
-
|
|
51
|
-
ctx.imageSmoothingEnabled = true
|
|
52
|
-
ctx.imageSmoothingQuality = 'high'
|
|
53
|
-
ctx.drawImage(image, 0, 0, clampedWidth, clampedHeight)
|
|
54
|
-
|
|
55
|
-
URL.revokeObjectURL(svgUrl)
|
|
56
|
-
|
|
57
|
-
resolve(canvas)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
image.onerror = () => {
|
|
61
|
-
resolve(null)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
image.src = svgUrl
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
if (!canvas) return null
|
|
68
|
-
|
|
69
|
-
const blob = await new Promise<Blob | null>((resolve) =>
|
|
70
|
-
canvas.toBlob(
|
|
71
|
-
(blob) => {
|
|
72
|
-
if (!blob || debugFlags.throwToBlob.get()) {
|
|
73
|
-
resolve(null)
|
|
74
|
-
}
|
|
75
|
-
resolve(blob)
|
|
76
|
-
},
|
|
77
|
-
'image/' + type,
|
|
78
|
-
quality
|
|
79
|
-
)
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
if (!blob) return null
|
|
83
|
-
|
|
84
|
-
if (type === 'png') {
|
|
85
|
-
const view = new DataView(await blob.arrayBuffer())
|
|
86
|
-
return PngHelpers.setPhysChunk(view, effectiveScale, {
|
|
87
|
-
type: 'image/' + type,
|
|
88
|
-
})
|
|
89
|
-
} else {
|
|
90
|
-
return blob
|
|
91
|
-
}
|
|
92
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import canvasSize from 'canvas-size'
|
|
2
|
-
|
|
3
|
-
/** @internal */
|
|
4
|
-
export interface CanvasMaxSize {
|
|
5
|
-
maxWidth: number
|
|
6
|
-
maxHeight: number
|
|
7
|
-
maxArea: number
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
let maxSizePromise: Promise<CanvasMaxSize> | null = null
|
|
11
|
-
|
|
12
|
-
function getBrowserCanvasMaxSize() {
|
|
13
|
-
if (!maxSizePromise) {
|
|
14
|
-
maxSizePromise = calculateBrowserCanvasMaxSize()
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return maxSizePromise
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function calculateBrowserCanvasMaxSize(): Promise<CanvasMaxSize> {
|
|
21
|
-
const maxWidth = await canvasSize.maxWidth({ usePromise: true })
|
|
22
|
-
const maxHeight = await canvasSize.maxHeight({ usePromise: true })
|
|
23
|
-
const maxArea = await canvasSize.maxArea({ usePromise: true })
|
|
24
|
-
return {
|
|
25
|
-
maxWidth: maxWidth.width,
|
|
26
|
-
maxHeight: maxHeight.height,
|
|
27
|
-
maxArea: maxArea.width * maxArea.height,
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// https://github.com/jhildenbiddle/canvas-size?tab=readme-ov-file#test-results
|
|
32
|
-
const MAX_SAFE_CANVAS_DIMENSION = 8192
|
|
33
|
-
const MAX_SAFE_CANVAS_AREA = 4096 * 4096
|
|
34
|
-
|
|
35
|
-
/** @internal */
|
|
36
|
-
export async function clampToBrowserMaxCanvasSize(width: number, height: number) {
|
|
37
|
-
if (
|
|
38
|
-
width <= MAX_SAFE_CANVAS_DIMENSION &&
|
|
39
|
-
height <= MAX_SAFE_CANVAS_DIMENSION &&
|
|
40
|
-
width * height <= MAX_SAFE_CANVAS_AREA
|
|
41
|
-
) {
|
|
42
|
-
return [width, height]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const { maxWidth, maxHeight, maxArea } = await getBrowserCanvasMaxSize()
|
|
46
|
-
const aspectRatio = width / height
|
|
47
|
-
|
|
48
|
-
if (width > maxWidth) {
|
|
49
|
-
width = maxWidth
|
|
50
|
-
height = width / aspectRatio
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (height > maxHeight) {
|
|
54
|
-
height = maxHeight
|
|
55
|
-
width = height * aspectRatio
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (width * height > maxArea) {
|
|
59
|
-
const ratio = Math.sqrt(maxArea / (width * height))
|
|
60
|
-
width *= ratio
|
|
61
|
-
height *= ratio
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return [width, height]
|
|
65
|
-
}
|