utils.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import {
  2. exportToCanvas as _exportToCanvas,
  3. exportToSvg as _exportToSvg,
  4. } from "../scene/export";
  5. import { getDefaultAppState } from "../appState";
  6. import { AppState, BinaryFiles } from "../types";
  7. import { ExcalidrawElement, NonDeleted } from "../element/types";
  8. import { getNonDeletedElements } from "../element";
  9. import { restore } from "../data/restore";
  10. import { MIME_TYPES } from "../constants";
  11. type ExportOpts = {
  12. elements: readonly NonDeleted<ExcalidrawElement>[];
  13. appState?: Partial<Omit<AppState, "offsetTop" | "offsetLeft">>;
  14. files: BinaryFiles | null;
  15. maxWidthOrHeight?: number;
  16. getDimensions?: (
  17. width: number,
  18. height: number,
  19. ) => { width: number; height: number; scale?: number };
  20. };
  21. export const exportToCanvas = ({
  22. elements,
  23. appState,
  24. files,
  25. maxWidthOrHeight,
  26. getDimensions,
  27. }: ExportOpts) => {
  28. const { elements: restoredElements, appState: restoredAppState } = restore(
  29. { elements, appState },
  30. null,
  31. null,
  32. );
  33. const { exportBackground, viewBackgroundColor } = restoredAppState;
  34. return _exportToCanvas(
  35. getNonDeletedElements(restoredElements),
  36. { ...restoredAppState, offsetTop: 0, offsetLeft: 0, width: 0, height: 0 },
  37. files || {},
  38. { exportBackground, viewBackgroundColor },
  39. (width: number, height: number) => {
  40. const canvas = document.createElement("canvas");
  41. if (maxWidthOrHeight) {
  42. if (typeof getDimensions === "function") {
  43. console.warn(
  44. "`getDimensions()` is ignored when `maxWidthOrHeight` is supplied.",
  45. );
  46. }
  47. const max = Math.max(width, height);
  48. const scale = maxWidthOrHeight / max;
  49. canvas.width = width * scale;
  50. canvas.height = height * scale;
  51. return {
  52. canvas,
  53. scale,
  54. };
  55. }
  56. const ret = getDimensions?.(width, height) || { width, height };
  57. canvas.width = ret.width;
  58. canvas.height = ret.height;
  59. return {
  60. canvas,
  61. scale: ret.scale ?? 1,
  62. };
  63. },
  64. );
  65. };
  66. export const exportToBlob = async (
  67. opts: ExportOpts & {
  68. mimeType?: string;
  69. quality?: number;
  70. },
  71. ): Promise<Blob | null> => {
  72. const canvas = await exportToCanvas(opts);
  73. let { mimeType = MIME_TYPES.png, quality } = opts;
  74. if (mimeType === MIME_TYPES.png && typeof quality === "number") {
  75. console.warn(`"quality" will be ignored for "${MIME_TYPES.png}" mimeType`);
  76. }
  77. // typo in MIME type (should be "jpeg")
  78. if (mimeType === "image/jpg") {
  79. mimeType = MIME_TYPES.jpg;
  80. }
  81. quality = quality ? quality : /image\/jpe?g/.test(mimeType) ? 0.92 : 0.8;
  82. return new Promise((resolve) => {
  83. canvas.toBlob(
  84. (blob: Blob | null) => {
  85. resolve(blob);
  86. },
  87. mimeType,
  88. quality,
  89. );
  90. });
  91. };
  92. export const exportToSvg = async ({
  93. elements,
  94. appState = getDefaultAppState(),
  95. files = {},
  96. exportPadding,
  97. }: Omit<ExportOpts, "getDimensions"> & {
  98. exportPadding?: number;
  99. }): Promise<SVGSVGElement> => {
  100. const { elements: restoredElements, appState: restoredAppState } = restore(
  101. { elements, appState },
  102. null,
  103. null,
  104. );
  105. return _exportToSvg(
  106. getNonDeletedElements(restoredElements),
  107. {
  108. ...restoredAppState,
  109. exportPadding,
  110. },
  111. files,
  112. );
  113. };
  114. export { serializeAsJSON } from "../data/json";
  115. export { loadFromBlob, loadLibraryFromBlob } from "../data/blob";
  116. export { getFreeDrawSvgPath } from "../renderer/renderElement";