Repository: excalidraw/excalidraw
File: packages/excalidraw/scene/Fonts.ts (TypeScript)

Code before:
  // a static member to reduce memory footprint
  private static loadedFontFaces = new Set<string>();

  /**
   * if we load a (new) font, it's likely that text elements using it have
   * already been rendered using a fallback font. Thus, we want invalidate
   * their shapes and rerender. See #637.
   *
   * Invalidates text elements and rerenders scene, provided that at least one
   * of the supplied fontFaces has not already been processed.
   */
  public onFontsLoaded = (fontFaces: readonly FontFace[]) => {
    if (
      // bail if all fonts with have been processed. We're checking just a
      // subset of the font properties (though it should be enough), so it
      // can technically bail on a false positive.
      fontFaces.every((fontFace) => {
        const sig = `${fontFace.family}-${fontFace.style}-${fontFace.weight}`;
        if (Fonts.loadedFontFaces.has(sig)) {
          return true;
        }
        Fonts.loadedFontFaces.add(sig);
        return false;
      })
    ) {
      return false;
    }

    let didUpdate = false;

    this.scene.mapElements((element) => {
      if (isTextElement(element) && !isBoundToContainer(element)) {
        ShapeCache.delete(element);
        didUpdate = true;
        return newElementWith(element, {
          ...refreshTextDimensions(
            element,
            getContainerElement(element, this.scene.getNonDeletedElementsMap()),
            this.scene.getNonDeletedElementsMap(),
          ),
        });
      }
      return element;
    });

    if (didUpdate) {
      this.onSceneUpdated();
    }
  };

  public loadFontsForElements = async (

Review comment (suggestion):
previously we were refreshing only non-containerized text elements because we were afraid (and still are) that reflowing a containerized text could end up changing the container dimensions across different browsers (clients).

Instead, I decided to not refresh text dimensions at all in this function, and just invalidate all text elements (including bound text) so as to force re-render.

@Mrazator but, we have to come up with a strategy for whether or when should we refresh text dimensions. Especially with respect to the coming virgil update which will have slightly different text dimensions. In that case, do we want to refresh dimensions, or is it ok not do it and leave it only after users edit the text later (if at all).

---

But, I'm actually not sure about this even in the current case, because it may happen the virgil doesn't load for some reason and fall back to a serif font with wildly different dimensions. Should we then refresh dimensions or leave bad bounding boxes? Tough call.

Code after:
   */
  public onFontsLoaded = (fontFaces: readonly FontFace[]) => {
    if (
      // bail if all fonts with have been processed. We're checking just a
      // subset of the font properties (though it should be enough), so it
      // can technically bail on a false positive.
      fontFaces.every((fontFace) => {
        const sig = `${fontFace.family}-${fontFace.style}-${fontFace.weight}`;
        if (Fonts.loadedFontFaces.has(sig)) {
          return true;
        }
        Fonts.loadedFontFaces.add(sig);
        return false;
      })
    ) {
      return false;
    }

    let didUpdate = false;

    this.scene.mapElements((element) => {
      if (isTextElement(element)) {
        didUpdate = true;
        ShapeCache.delete(element);
        return newElementWith(element, {}, true);
      }
      return element;
    });

    if (didUpdate) {
      this.scene.triggerUpdate();
    }
  };

  public loadFontsForElements = async (
    elements: readonly ExcalidrawElement[],
  ) => {
    const fontFaces = await Promise.all(
      [
        ...new Set(
          elements
            .filter((element) => isTextElement(element))
            .map((element) => (element as ExcalidrawTextElement).fontFamily),
        ),
      ].map((fontFamily) => {
        const fontString = getFontString({
          fontFamily,
          fontSize: 16,
        });
        if (!document.fonts?.check?.(fontString)) {
          return document.fonts?.load?.(fontString);