fixup file helpers (#3130)
We had a couple regressions in #3110: first a missing `await` was causing fonts not to get properly embedded in exports. second, some `readAsText` calls were replaced with `readAsDataURL` calls. ### Change Type <!-- ❗ Please select a 'Scope' label ❗️ --> - [x] `sdk` — Changes the tldraw SDK - [ ] `dotcom` — Changes the tldraw.com web app - [ ] `docs` — Changes to the documentation, examples, or templates. - [ ] `vs code` — Changes to the vscode plugin - [ ] `internal` — Does not affect user-facing stuff <!-- ❗ Please select a 'Type' label ❗️ --> - [x] `bugfix` — Bug fix - [ ] `feature` — New feature - [ ] `improvement` — Improving existing features - [ ] `chore` — Updating dependencies, other boring stuff - [ ] `galaxy brain` — Architectural changes - [ ] `tests` — Changes to any test code - [ ] `tools` — Changes to infrastructure, CI, internal scripts, debugging tools, etc. - [ ] `dunno` — I don't know
This commit is contained in:
parent
adebb680e5
commit
0a48aea7bb
10 changed files with 171 additions and 93 deletions
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
AssetRecordType,
|
||||
Editor,
|
||||
FileHelpers,
|
||||
MediaHelpers,
|
||||
TLAsset,
|
||||
TLAssetId,
|
||||
|
@ -96,7 +97,7 @@ export function registerDefaultExternalContentHandlers(
|
|||
typeName: 'asset',
|
||||
props: {
|
||||
name,
|
||||
src: await MediaHelpers.blobToDataUrl(file),
|
||||
src: await FileHelpers.blobToDataUrl(file),
|
||||
w: size.w,
|
||||
h: size.h,
|
||||
mimeType: file.type,
|
||||
|
|
|
@ -20,7 +20,7 @@ import { usePrefersReducedMotion } from '../shared/usePrefersReducedMotion'
|
|||
async function getDataURIFromURL(url: string): Promise<string> {
|
||||
const response = await fetch(url)
|
||||
const blob = await response.blob()
|
||||
return FileHelpers.fileToBase64(blob)
|
||||
return FileHelpers.blobToDataUrl(blob)
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
|
|
@ -23,12 +23,12 @@ export function getFontDefForExport(fontStyle: TLDefaultFontStyle): SvgExportDef
|
|||
const font = findFont(fontStyle)
|
||||
if (!font) return null
|
||||
|
||||
const url = (font as any).$$_url
|
||||
const fontFaceRule = (font as any).$$_fontface
|
||||
const url: string = (font as any).$$_url
|
||||
const fontFaceRule: string = (font as any).$$_fontface
|
||||
if (!url || !fontFaceRule) return null
|
||||
|
||||
const fontFile = await (await fetch(url)).blob()
|
||||
const base64FontFile = FileHelpers.fileToBase64(fontFile)
|
||||
const base64FontFile = await FileHelpers.blobToDataUrl(fontFile)
|
||||
|
||||
const newFontFaceRule = fontFaceRule.replace(url, base64FontFile)
|
||||
const style = document.createElementNS('http://www.w3.org/2000/svg', 'style')
|
||||
|
|
|
@ -250,27 +250,30 @@ const handlePasteFromClipboardApi = async (
|
|||
if (item.types.includes('text/html')) {
|
||||
things.push({
|
||||
type: 'html',
|
||||
source: new Promise<string>((r) =>
|
||||
item.getType('text/html').then((blob) => FileHelpers.fileToBase64(blob).then(r))
|
||||
),
|
||||
source: (async () => {
|
||||
const blob = await item.getType('text/html')
|
||||
return await FileHelpers.blobToText(blob)
|
||||
})(),
|
||||
})
|
||||
}
|
||||
|
||||
if (item.types.includes('text/uri-list')) {
|
||||
things.push({
|
||||
type: 'url',
|
||||
source: new Promise<string>((r) =>
|
||||
item.getType('text/uri-list').then((blob) => FileHelpers.fileToBase64(blob).then(r))
|
||||
),
|
||||
source: (async () => {
|
||||
const blob = await item.getType('text/uri-list')
|
||||
return await FileHelpers.blobToText(blob)
|
||||
})(),
|
||||
})
|
||||
}
|
||||
|
||||
if (item.types.includes('text/plain')) {
|
||||
things.push({
|
||||
type: 'text',
|
||||
source: new Promise<string>((r) =>
|
||||
item.getType('text/plain').then((blob) => FileHelpers.fileToBase64(blob).then(r))
|
||||
),
|
||||
source: (async () => {
|
||||
const blob = await item.getType('text/plain')
|
||||
return await FileHelpers.blobToText(blob)
|
||||
})(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ export async function getSvgAsString(svg: SVGElement) {
|
|||
if (src) {
|
||||
if (!src.startsWith('data:')) {
|
||||
const blob = await (await fetch(src)).blob()
|
||||
const base64 = await FileHelpers.fileToBase64(blob)
|
||||
const base64 = await FileHelpers.blobToDataUrl(blob)
|
||||
img.setAttribute('xlink:href', base64)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ export async function serializeTldrawJson(store: TLStore): Promise<string> {
|
|||
let assetSrcToSave
|
||||
try {
|
||||
// try to save the asset as a base64 string
|
||||
assetSrcToSave = await FileHelpers.fileToBase64(
|
||||
assetSrcToSave = await FileHelpers.blobToDataUrl(
|
||||
await (await fetch(record.props.src)).blob()
|
||||
)
|
||||
} catch {
|
||||
|
|
|
@ -62,9 +62,10 @@ export type Expand<T> = T extends infer O ? {
|
|||
|
||||
// @public
|
||||
export class FileHelpers {
|
||||
// @internal (undocumented)
|
||||
static base64ToFile(dataURL: string): Promise<ArrayBuffer>;
|
||||
static fileToBase64(file: Blob): Promise<string>;
|
||||
static blobToDataUrl(file: Blob): Promise<string>;
|
||||
static blobToText(file: Blob): Promise<string>;
|
||||
// (undocumented)
|
||||
static dataUrlToArrayBuffer(dataURL: string): Promise<ArrayBuffer>;
|
||||
}
|
||||
|
||||
// @internal
|
||||
|
@ -177,7 +178,6 @@ export function mapObjectMapValues<Key extends string, ValueBefore, ValueAfter>(
|
|||
|
||||
// @public
|
||||
export class MediaHelpers {
|
||||
static blobToDataUrl(blob: Blob): Promise<string>;
|
||||
static getImageSize(blob: Blob): Promise<{
|
||||
w: number;
|
||||
h: number;
|
||||
|
|
|
@ -519,12 +519,12 @@
|
|||
"members": [
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/utils!FileHelpers.fileToBase64:member(1)",
|
||||
"docComment": "/**\n * Convert a file to base64.\n *\n * @param value - The file as a blob.\n *\n * @example\n * ```ts\n * const A = fileToBase64('./test.png')\n * ```\n *\n * @public\n */\n",
|
||||
"canonicalReference": "@tldraw/utils!FileHelpers.blobToDataUrl:member(1)",
|
||||
"docComment": "/**\n * Convert a file to a base64 encoded data url.\n *\n * @param value - The file as a blob.\n *\n * @example\n * ```ts\n * const A = FileHelpers.toDataUrl(myImageFile)\n * ```\n *\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "static fileToBase64(file: "
|
||||
"text": "static blobToDataUrl(file: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
|
@ -569,7 +569,123 @@
|
|||
],
|
||||
"isOptional": false,
|
||||
"isAbstract": false,
|
||||
"name": "fileToBase64"
|
||||
"name": "blobToDataUrl"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/utils!FileHelpers.blobToText:member(1)",
|
||||
"docComment": "/**\n * Convert a file to a unicode text string.\n *\n * @param value - The file as a blob.\n *\n * @example\n * ```ts\n * const A = FileHelpers.fileToDataUrl(myTextFile)\n * ```\n *\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "static blobToText(file: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Blob",
|
||||
"canonicalReference": "!Blob:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Promise",
|
||||
"canonicalReference": "!Promise:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<string>"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isStatic": true,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 5
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "file",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"isOptional": false,
|
||||
"isAbstract": false,
|
||||
"name": "blobToText"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/utils!FileHelpers.dataUrlToArrayBuffer:member(1)",
|
||||
"docComment": "/**\n * @param dataURL - The file as a string.\n *\n * from https://stackoverflow.com/a/53817185\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "static dataUrlToArrayBuffer(dataURL: "
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "string"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Promise",
|
||||
"canonicalReference": "!Promise:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<"
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "ArrayBuffer",
|
||||
"canonicalReference": "!ArrayBuffer:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ">"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isStatic": true,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 7
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "dataURL",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"isOptional": false,
|
||||
"isAbstract": false,
|
||||
"name": "dataUrlToArrayBuffer"
|
||||
}
|
||||
],
|
||||
"implementsTokenRanges": []
|
||||
|
@ -1844,60 +1960,6 @@
|
|||
"name": "MediaHelpers",
|
||||
"preserveMemberOrder": false,
|
||||
"members": [
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/utils!MediaHelpers.blobToDataUrl:member(1)",
|
||||
"docComment": "/**\n * Read a blob into a data url\n *\n * @public\n */\n",
|
||||
"excerptTokens": [
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "static blobToDataUrl(blob: "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Blob",
|
||||
"canonicalReference": "!Blob:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "): "
|
||||
},
|
||||
{
|
||||
"kind": "Reference",
|
||||
"text": "Promise",
|
||||
"canonicalReference": "!Promise:interface"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": "<string>"
|
||||
},
|
||||
{
|
||||
"kind": "Content",
|
||||
"text": ";"
|
||||
}
|
||||
],
|
||||
"isStatic": true,
|
||||
"returnTypeTokenRange": {
|
||||
"startIndex": 3,
|
||||
"endIndex": 5
|
||||
},
|
||||
"releaseTag": "Public",
|
||||
"isProtected": false,
|
||||
"overloadIndex": 1,
|
||||
"parameters": [
|
||||
{
|
||||
"parameterName": "blob",
|
||||
"parameterTypeTokenRange": {
|
||||
"startIndex": 1,
|
||||
"endIndex": 2
|
||||
},
|
||||
"isOptional": false
|
||||
}
|
||||
],
|
||||
"isOptional": false,
|
||||
"isAbstract": false,
|
||||
"name": "blobToDataUrl"
|
||||
},
|
||||
{
|
||||
"kind": "Method",
|
||||
"canonicalReference": "@tldraw/utils!MediaHelpers.getImageSize:member(1)",
|
||||
|
|
|
@ -6,29 +6,27 @@
|
|||
export class FileHelpers {
|
||||
/**
|
||||
* @param dataURL - The file as a string.
|
||||
* @internal
|
||||
*
|
||||
* from https://stackoverflow.com/a/53817185
|
||||
*/
|
||||
static async base64ToFile(dataURL: string) {
|
||||
static async dataUrlToArrayBuffer(dataURL: string) {
|
||||
return fetch(dataURL).then(function (result) {
|
||||
return result.arrayBuffer()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a file to base64.
|
||||
* Convert a file to a base64 encoded data url.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const A = fileToBase64('./test.png')
|
||||
* const A = FileHelpers.toDataUrl(myImageFile)
|
||||
* ```
|
||||
*
|
||||
* @param value - The file as a blob.
|
||||
* @public
|
||||
*/
|
||||
static async fileToBase64(file: Blob): Promise<string> {
|
||||
static async blobToDataUrl(file: Blob): Promise<string> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
if (file) {
|
||||
const reader = new FileReader()
|
||||
|
@ -39,4 +37,27 @@ export class FileHelpers {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a file to a unicode text string.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const A = FileHelpers.fileToDataUrl(myTextFile)
|
||||
* ```
|
||||
*
|
||||
* @param value - The file as a blob.
|
||||
*/
|
||||
static async blobToText(file: Blob): Promise<string> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
if (file) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => resolve(reader.result as string)
|
||||
reader.onerror = (error) => reject(error)
|
||||
reader.onabort = (error) => reject(error)
|
||||
reader.readAsText(file)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { FileHelpers } from './file'
|
||||
import { PngHelpers } from './png'
|
||||
|
||||
/**
|
||||
|
@ -41,14 +40,6 @@ export class MediaHelpers {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a blob into a data url
|
||||
* @public
|
||||
*/
|
||||
static blobToDataUrl(blob: Blob) {
|
||||
return FileHelpers.fileToBase64(blob)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of a video blob
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue