diff options
| author | kj_sh604 | 2026-03-15 16:19:35 -0400 |
|---|---|---|
| committer | kj_sh604 | 2026-03-15 16:19:35 -0400 |
| commit | 6ec259a0e71174651bae95d4628138bf6fd68742 (patch) | |
| tree | 5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/data/image.ts | |
| parent | 16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff) | |
refactor: packages/
Diffstat (limited to 'packages/excalidraw/data/image.ts')
| -rw-r--r-- | packages/excalidraw/data/image.ts | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/packages/excalidraw/data/image.ts b/packages/excalidraw/data/image.ts new file mode 100644 index 0000000..0359a9c --- /dev/null +++ b/packages/excalidraw/data/image.ts @@ -0,0 +1,69 @@ +import decodePng from "png-chunks-extract"; +import tEXt from "png-chunk-text"; +import encodePng from "png-chunks-encode"; +import { encode, decode } from "./encode"; +import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants"; +import { blobToArrayBuffer } from "./blob"; + +// ----------------------------------------------------------------------------- +// PNG +// ----------------------------------------------------------------------------- + +export const getTEXtChunk = async ( + blob: Blob, +): Promise<{ keyword: string; text: string } | null> => { + const chunks = decodePng(new Uint8Array(await blobToArrayBuffer(blob))); + const metadataChunk = chunks.find((chunk) => chunk.name === "tEXt"); + if (metadataChunk) { + return tEXt.decode(metadataChunk.data); + } + return null; +}; + +export const encodePngMetadata = async ({ + blob, + metadata, +}: { + blob: Blob; + metadata: string; +}) => { + const chunks = decodePng(new Uint8Array(await blobToArrayBuffer(blob))); + + const metadataChunk = tEXt.encode( + MIME_TYPES.excalidraw, + JSON.stringify( + encode({ + text: metadata, + compress: true, + }), + ), + ); + // insert metadata before last chunk (iEND) + chunks.splice(-1, 0, metadataChunk); + + return new Blob([encodePng(chunks)], { type: MIME_TYPES.png }); +}; + +export const decodePngMetadata = async (blob: Blob) => { + const metadata = await getTEXtChunk(blob); + if (metadata?.keyword === MIME_TYPES.excalidraw) { + try { + const encodedData = JSON.parse(metadata.text); + if (!("encoded" in encodedData)) { + // legacy, un-encoded scene JSON + if ( + "type" in encodedData && + encodedData.type === EXPORT_DATA_TYPES.excalidraw + ) { + return metadata.text; + } + throw new Error("FAILED"); + } + return decode(encodedData); + } catch (error: any) { + console.error(error); + throw new Error("FAILED"); + } + } + throw new Error("INVALID"); +}; |
