1
0
mirror of https://github.com/excalidraw/excalidraw.git synced 2024-11-10 11:35:52 +01:00

Fix embedding scene to PNG on Safari (#2235)

This commit is contained in:
David Luzar 2020-10-13 16:55:08 +02:00 committed by GitHub
parent 5950fa9a40
commit f40a2230ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 26 deletions

@ -156,7 +156,6 @@ const ExportModal = ({
</Stack.Row> </Stack.Row>
</div> </div>
{actionManager.renderAction("changeExportBackground")} {actionManager.renderAction("changeExportBackground")}
{actionManager.renderAction("changeExportEmbedScene")}
{someElementIsSelected && ( {someElementIsSelected && (
<div> <div>
<label> <label>
@ -171,6 +170,7 @@ const ExportModal = ({
</label> </label>
</div> </div>
)} )}
{actionManager.renderAction("changeExportEmbedScene")}
{actionManager.renderAction("changeShouldAddWatermark")} {actionManager.renderAction("changeShouldAddWatermark")}
</Stack.Col> </Stack.Col>
</div> </div>

@ -6,24 +6,14 @@ import { LibraryData, ImportedDataState } from "./types";
import { calculateScrollCenter } from "../scene"; import { calculateScrollCenter } from "../scene";
import { MIME_TYPES } from "../constants"; import { MIME_TYPES } from "../constants";
import { base64ToString } from "../base64"; import { base64ToString } from "../base64";
export const parseFileContents = async (blob: Blob | File) => { export const parseFileContents = async (blob: Blob | File) => {
let contents: string; let contents: string;
if (blob.type === "image/png") { if (blob.type === "image/png") {
const { default: decodePng } = await import("png-chunks-extract"); const metadata = await (await import("./png")).getTEXtChunk(blob);
const { default: tEXt } = await import("png-chunk-text"); if (metadata?.keyword === MIME_TYPES.excalidraw) {
const chunks = decodePng(new Uint8Array(await blob.arrayBuffer())); return metadata.text;
const metadataChunk = chunks.find((chunk) => chunk.name === "tEXt");
if (metadataChunk) {
const metadata = tEXt.decode(metadataChunk.data);
if (metadata.keyword === MIME_TYPES.excalidraw) {
return metadata.text;
}
throw new Error(t("alerts.imageDoesNotContainScene"));
} else {
throw new Error(t("alerts.imageDoesNotContainScene"));
} }
throw new Error(t("alerts.imageDoesNotContainScene"));
} else { } else {
if ("text" in Blob) { if ("text" in Blob) {
contents = await blob.text(); contents = await blob.text();

@ -345,17 +345,10 @@ export const exportCanvas = async (
tempCanvas.toBlob(async (blob) => { tempCanvas.toBlob(async (blob) => {
if (blob) { if (blob) {
if (appState.exportEmbedScene) { if (appState.exportEmbedScene) {
const { default: tEXt } = await import("png-chunk-text"); blob = await (await import("./png")).encodeTEXtChunk(blob, {
const { default: encodePng } = await import("png-chunks-encode"); keyword: MIME_TYPES.excalidraw,
const { default: decodePng } = await import("png-chunks-extract"); text: serializeAsJSON(elements, appState),
const chunks = decodePng(new Uint8Array(await blob.arrayBuffer())); });
const metadata = tEXt.encode(
MIME_TYPES.excalidraw,
serializeAsJSON(elements, appState),
);
// insert metadata before last chunk (iEND)
chunks.splice(-1, 0, metadata);
blob = new Blob([encodePng(chunks)], { type: "image/png" });
} }
await fileSave(blob, { await fileSave(blob, {

42
src/data/png.ts Normal file

@ -0,0 +1,42 @@
import decodePng from "png-chunks-extract";
import tEXt from "png-chunk-text";
import encodePng from "png-chunks-encode";
const blobToArrayBuffer = (blob: Blob): Promise<ArrayBuffer> => {
if ("arrayBuffer" in blob) {
return blob.arrayBuffer();
}
// Safari
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
if (!event.target?.result) {
return reject(new Error("couldn't convert blob to ArrayBuffer"));
}
resolve(event.target.result as ArrayBuffer);
};
reader.readAsArrayBuffer(blob);
});
};
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 encodeTEXtChunk = async (
blob: Blob,
chunk: { keyword: string; text: string },
): Promise<Blob> => {
const chunks = decodePng(new Uint8Array(await blobToArrayBuffer(blob)));
const metadata = tEXt.encode(chunk.keyword, chunk.text);
// insert metadata before last chunk (iEND)
chunks.splice(-1, 0, metadata);
return new Blob([encodePng(chunks)], { type: "image/png" });
};