diff --git a/src/components/App.tsx b/src/components/App.tsx index 384bd7baf..f3ab968d4 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -730,18 +730,20 @@ class App extends React.Component { }; private onFontLoaded = () => { - this.scene.replaceAllElements([ - ...this.scene.getElementsIncludingDeleted().map((element) => { - if (isTextElement(element)) { - invalidateShapeForElement(element); - return newElementWith(element, { - ...refreshTextDimensions(element), - }); - } - return element; - }), - ]); - this.onSceneUpdated(); + let didUpdate = false; + this.scene.mapElements((element) => { + if (isTextElement(element)) { + invalidateShapeForElement(element); + didUpdate = true; + return newElementWith(element, { + ...refreshTextDimensions(element), + }); + } + return element; + }); + if (didUpdate) { + this.onSceneUpdated(); + } }; private resetHistory = () => { diff --git a/src/components/LibraryUnit.tsx b/src/components/LibraryUnit.tsx index aae7a8b5f..6723ac800 100644 --- a/src/components/LibraryUnit.tsx +++ b/src/components/LibraryUnit.tsx @@ -44,6 +44,7 @@ export const LibraryUnit = ({ }, null, ); + svg.querySelector(".style-fonts")?.remove(); node.innerHTML = svg.outerHTML; })(); diff --git a/src/components/PasteChartDialog.tsx b/src/components/PasteChartDialog.tsx index bb5bfc571..b71aef509 100644 --- a/src/components/PasteChartDialog.tsx +++ b/src/components/PasteChartDialog.tsx @@ -46,6 +46,7 @@ const ChartPreviewBtn = (props: { }, null, // files ); + svg.querySelector(".style-fonts")?.remove(); previewNode.replaceChildren(); previewNode.appendChild(svg); diff --git a/src/scene/Scene.ts b/src/scene/Scene.ts index 1cd29cf83..8b5b84eb4 100644 --- a/src/scene/Scene.ts +++ b/src/scene/Scene.ts @@ -79,6 +79,35 @@ class Scene { return null; } + /** + * A utility method to help with updating all scene elements, with the added + * performance optimization of not renewing the array if no change is made. + * + * Maps all current excalidraw elements, invoking the callback for each + * element. The callback should either return a new mapped element, or the + * original element if no changes are made. If no changes are made to any + * element, this results in a no-op. Otherwise, the newly mapped elements + * are set as the next scene's elements. + * + * @returns whether a change was made + */ + mapElements( + iteratee: (element: ExcalidrawElement) => ExcalidrawElement, + ): boolean { + let didChange = false; + const newElements = this.elements.map((element) => { + const nextElement = iteratee(element); + if (nextElement !== element) { + didChange = true; + } + return nextElement; + }); + if (didChange) { + this.replaceAllElements(newElements); + } + return didChange; + } + replaceAllElements(nextElements: readonly ExcalidrawElement[]) { this.elements = nextElements; this.elementsMap.clear(); diff --git a/src/scene/export.ts b/src/scene/export.ts index 6fb070b33..a6e4297ad 100644 --- a/src/scene/export.ts +++ b/src/scene/export.ts @@ -139,7 +139,7 @@ export const exportToSvg = async ( ${SVG_EXPORT_TAG} ${metadata} -