Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Browser Font Loading/SVG Painting Issue #28

Open
wolfmcnally opened this issue Jan 13, 2025 · 3 comments
Open

Browser Font Loading/SVG Painting Issue #28

wolfmcnally opened this issue Jan 13, 2025 · 3 comments

Comments

@wolfmcnally
Copy link

You'll notice from my comment here that I have a field in my loaded fonts data structure for a "placeholder URL":

export class FontData {
  family: string;
  filename: string;
  url: string;
  buffer: ArrayBuffer;

  constructor(family: string, filename: string, url: string, buffer: ArrayBuffer) {
    this.family = family;
    this.filename = filename;
    this.url = url;
    this.buffer = buffer;
  }

  get placeholderUrl(): URL {
    return new URL(`file:///${this.filename}`);
  }
}

I had to do this because even though I use the browser fetch API to retrieve my font data, the DropFlow API doesn't let me use anything other an a file: URL with registerFont:

  await Promise.all(fontsData.map(fontData => flow.registerFont(fontData.buffer, fontData.placeholderUrl)));

However, I want the output SVG to include the CDN URLs for the font's I may be declaring, except DropFlow rejects these:

export const fontURLs = [
  "https://fonts.cdnfonts.com/css/noto-sans",
  "https://fonts.cdnfonts.com/css/noto-color-emoji",
  "https://fonts.cdnfonts.com/css/noto-sans-arabic",
  "https://fonts.cdnfonts.com/css/noto-sans-hebrew",
  "https://fonts.cdnfonts.com/css/noto-sans-jp",
  "https://fonts.cdnfonts.com/css/noto-sans-kr",
  "https://fonts.cdnfonts.com/css/noto-sans-sc",
  "https://fonts.cdnfonts.com/css/noto-sans-tc",
  "https://fonts.cdnfonts.com/css/noto-sans-thai",
];

So I use the placeholder URL and then have to do a textual replacement on the painted SVG:

    const divStyle: flow.DeclaredStyle = {
      direction: 'rtl',
    };

    const h = flow.h;
    const string = 'أية تقنية متقدمة بما فيه الكفاية لا يمكن تمييزها عن السحر.';

    const elem = h('div', { style: divStyle }, string);

    const root = flow.dom(elem);

    const container: flow.BlockContainer = flow.generate(root);
    flow.layout(container, 200, 200);
    let svg = flow.paintToSvg(container);

    // Replace every placeholder URL with the actual font URL from FontData
    fontsData.forEach((fontData) => {
      svg = svg
        .replace(fontData.placeholderUrl.href, fontData.url); // FONT URL REPLACEMENT HERE
    });

    console.log(beautifySvg(svg));

This is part of why I'd like to produce an SVG.js DOM instead of merely textual SVG output. I know I could use paintToSvgElements and just deal with the @font-face declarations myself, but I'd rather have a DOM to manipulate directly to incorporate multiple laid out blocks of text along with the graphics of my document.

@chearon
Copy link
Owner

chearon commented Jan 15, 2025

I had to do this because even though I use the browser fetch API to retrieve my font data, the DropFlow API doesn't let me use anything other an a file: URL with registerFont:

I know this is kind of a pain but it acts as an identifier for the ArrayBuffer to detect duplicate registrations, to log the font, caching etc. I could consider accepting strings instead of URL to ease that pain a bit.

However, I want the output SVG to include the CDN URLs for the font's I may be declaring, except DropFlow rejects these:

I might be getting sidetracked but cdnfonts seems to have only WOFF format (which dropflow doesn't support) and those URLs return CSS, so not sure how your code snippets work. Did I miss any error reporting here or did you handle that?Anyways...

This is part of why I'd like to produce an SVG.js DOM instead of merely textual SVG output.

... I do understand needing deeper hooks into dropflow. Supposing I allow an object implementing the PaintBackend to be painted to, I think that could direct calls to SVG.js. But it has to do kind of a lot, so I wonder if what you want is paint callbacks like (x, y, style, color, text)?

incorporate multiple laid out blocks of text along with the graphics of my document.

If you need content to be more finely grouped than paint output will do (painting is naturally layering stuff without any grouping) do consider painting multiple documents/layout trees if you haven't. I've optimized it to be used that way.

@wolfmcnally
Copy link
Author

I totally get the need for a string identifier for each font, the weird thing is in having to provide a file:/// URL when I'm already handing dropFlow the ArrayBuffer.

The reason I want the CDN URLs in my final output is so when someone exports my document as SVG, they can send it to someone else with reasonable assurance that the receiver's viewer can retrieve the fonts the document uses, without having to either install the fonts on their system or me having to embed font subsets in the output SVG.

See my new comment here about an interim solution for a paint back-end. I don't think I need anything finely grouped, just the in-order drawing commands. The main advantage of still having something more than a duck-type proxy like in the comment, would be not having to re-parse string structures like rgba(0, 0, 0, 1), and normal 400 normal 16px Noto Sans, etc. into my own formats.

@wolfmcnally
Copy link
Author

wolfmcnally commented Jan 15, 2025

Oh, and at least on my Mac, both the Mac SVG preview function and browser-based viewers like https://www.svgviewer.dev/ automatically retrieve and use the WOFF2 fonts just fine.

chearon added a commit that referenced this issue Mar 9, 2025
an old TODO. the way you had to pass a URL was weird, so now it
generates one (that's for logging). file:// urls load sync, and
no longer fail in Node (idk how that went uncaught so long, but
it has to do with the examples not using the public API... shame
on me).

Next up: update the examples and update loadNotoFonts

Ref #28
Also #27 (comment)
chearon added a commit that referenced this issue Mar 9, 2025
an old TODO. the way you had to pass a URL was weird, so now it
generates one (that's for logging). file:// urls load sync, and
no longer fail in Node (idk how that went uncaught so long, but
it has to do with the examples not using the public API... shame
on me).

Next up: update the examples and update loadNotoFonts

Ref #28
Also #27 (comment)
chearon added a commit that referenced this issue Mar 9, 2025
an old TODO. the way you had to pass a URL was weird, so now it
generates one (that's for logging). file:// urls load sync, and
no longer fail in Node (idk how that went uncaught so long, but
it has to do with the examples not using the public API... shame
on me).

Next up: update the examples and update loadNotoFonts

Ref #28
Also #27 (comment)
chearon added a commit that referenced this issue Mar 9, 2025
an old TODO. the way you had to pass a URL was weird, so now it
generates one (that's for logging). file:// urls load sync, and
no longer fail in Node (idk how that went uncaught so long, but
it has to do with the examples not using the public API... shame
on me).

Next up: update the examples and update loadNotoFonts

Ref #28
Also #27 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants