index.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import {
  2. copyBlobToClipboardAsPng,
  3. copyTextToSystemClipboard,
  4. } from "../clipboard";
  5. import { DEFAULT_EXPORT_PADDING, MIME_TYPES } from "../constants";
  6. import { NonDeletedExcalidrawElement } from "../element/types";
  7. import { t } from "../i18n";
  8. import { exportToCanvas, exportToSvg } from "../scene/export";
  9. import { ExportType } from "../scene/types";
  10. import { AppState, BinaryFiles } from "../types";
  11. import { canvasToBlob } from "./blob";
  12. import { fileSave, FileSystemHandle } from "./filesystem";
  13. import { serializeAsJSON } from "./json";
  14. export { loadFromBlob } from "./blob";
  15. export { loadFromJSON, saveAsJSON } from "./json";
  16. export const exportCanvas = async (
  17. type: Omit<ExportType, "backend">,
  18. elements: readonly NonDeletedExcalidrawElement[],
  19. appState: AppState,
  20. files: BinaryFiles,
  21. {
  22. exportBackground,
  23. exportPadding = DEFAULT_EXPORT_PADDING,
  24. viewBackgroundColor,
  25. name,
  26. fileHandle = null,
  27. }: {
  28. exportBackground: boolean;
  29. exportPadding?: number;
  30. viewBackgroundColor: string;
  31. name: string;
  32. fileHandle?: FileSystemHandle | null;
  33. },
  34. ) => {
  35. if (elements.length === 0) {
  36. throw new Error(t("alerts.cannotExportEmptyCanvas"));
  37. }
  38. if (type === "svg" || type === "clipboard-svg") {
  39. const tempSvg = await exportToSvg(
  40. elements,
  41. {
  42. exportBackground,
  43. exportWithDarkMode: appState.exportWithDarkMode,
  44. viewBackgroundColor,
  45. exportPadding,
  46. exportScale: appState.exportScale,
  47. exportEmbedScene: appState.exportEmbedScene && type === "svg",
  48. },
  49. files,
  50. );
  51. if (type === "svg") {
  52. return await fileSave(
  53. new Blob([tempSvg.outerHTML], { type: MIME_TYPES.svg }),
  54. {
  55. description: "Export to SVG",
  56. name,
  57. extension: appState.exportEmbedScene ? "excalidraw.svg" : "svg",
  58. fileHandle,
  59. },
  60. );
  61. } else if (type === "clipboard-svg") {
  62. await copyTextToSystemClipboard(tempSvg.outerHTML);
  63. return;
  64. }
  65. }
  66. const tempCanvas = await exportToCanvas(elements, appState, files, {
  67. exportBackground,
  68. viewBackgroundColor,
  69. exportPadding,
  70. });
  71. tempCanvas.style.display = "none";
  72. document.body.appendChild(tempCanvas);
  73. if (type === "png") {
  74. let blob = await canvasToBlob(tempCanvas);
  75. tempCanvas.remove();
  76. if (appState.exportEmbedScene) {
  77. blob = await (
  78. await import(/* webpackChunkName: "image" */ "./image")
  79. ).encodePngMetadata({
  80. blob,
  81. metadata: serializeAsJSON(elements, appState, files, "local"),
  82. });
  83. }
  84. return await fileSave(blob, {
  85. description: "Export to PNG",
  86. name,
  87. extension: appState.exportEmbedScene ? "excalidraw.png" : "png",
  88. fileHandle,
  89. });
  90. } else if (type === "clipboard") {
  91. try {
  92. const blob = canvasToBlob(tempCanvas);
  93. await copyBlobToClipboardAsPng(blob);
  94. } catch (error: any) {
  95. if (error.name === "CANVAS_POSSIBLY_TOO_BIG") {
  96. throw error;
  97. }
  98. throw new Error(t("alerts.couldNotCopyToClipboard"));
  99. } finally {
  100. tempCanvas.remove();
  101. }
  102. } else {
  103. tempCanvas.remove();
  104. // shouldn't happen
  105. throw new Error("Unsupported export type");
  106. }
  107. };