0
<!DOCTYPE html>
<html>

<body>
  <button type="button" onclick='UseLocalFontsButton()'>Click Me!</button>
  <script>

    async function UseLocalFontsButton() {
      try {
        const array = await self.queryLocalFonts();

        for (const font of array) {
          // blob() returns a Blob containing the bytes of the font.
          const bytes = await font.blob();
          console.log(`bytes.size = ${bytes.size}`);
          console.log(`bytes.type = ${bytes.type}`);
          // process font
        };
      } 
      catch (e) {
        console.warn(`Error: ${e.message}`);
      }
    };

</script>

</body>
</html>

Here I am getting local fonts and processing it. It works but I need to move font processing part to worker thread. I cant move everything because queryLocalFonts only works on main thread.

Here is my try:

<!DOCTYPE html>
<html>

<body>
  <button type="button" onclick='UseLocalFontsButton()'>Click Me!</button>

  <script>

    async function UseLocalFontsButton() {
      try {
        const array = await self.queryLocalFonts();

        let worker = new Worker('worker.js');
        worker.postMessage(JSON.stringify(array));
      } 
      catch (e) {
        console.warn(`Error: ${e.message}`);
      }
    };

</script>

</body>
</html>

And the worker:

(function () {
    onmessage = async function handleMessageFromMain(msg) 
    {
        var array = JSON.parse(msg.data);

        console.log('array = ', array);

        try {
            for (const font of array) {
                // blob() returns a Blob containing the bytes of the font.
                const bytes = await font.blob();
                console.log(`bytes.size = ${bytes.size}`);
                console.log(`bytes.type = ${bytes.type}`);
                // process font
            };
        } catch (e) {
            console.warn(`Error: ${e.message}`);
        }
    };
})();

I am getting error: Error: font.blob is not a function. Looks like font object is not properly copied to worker thread. Can you hint how that can be done?

Ashot
  • 10,807
  • 14
  • 66
  • 117
  • 1
    Why would you stringify at all? (when you postMessage) – IT goldman Oct 10 '22 at 21:42
  • Without stringify I got `Failed to execute 'postMessage' on 'Worker': FontData object could not be cloned.` – Ashot Oct 10 '22 at 21:43
  • 1
    Blobs can't be passed to workers. You can try to convert it to `ArrayBuffer` https://developer.mozilla.org/en-US/docs/Glossary/Transferable_objects – Konrad Oct 10 '22 at 21:45
  • @KonradLinkowski I was trying to do that on worker thread as well, but looks like it is not possible, I will convert it to `ArrayBuffer` and then pass.. – Ashot Oct 10 '22 at 22:02
  • 1
    @KonradLinkowski you can very well pass Blobs to Workers and you should in most cases since there is no copy involved: https://stackoverflow.com/questions/63641798/is-copying-a-large-blob-over-to-a-worker-expensive/63642296#63642296 – Kaiido Oct 11 '22 at 01:02

1 Answers1

1

You don't need to serialize your data to JSON when you post it to a Worker (or other MessagePort objects for that matter); the postMessage() method has its own cloning algorithm able to pass many different JS object types that JSON doesn't support.

And while FontData is not part of these many object types, Blob is. So what you need to do is to extract all the your FontData instances as Blob and send these Blob objects to your worker.

That would give:

// main script
async function UseLocalFontsButton() {
  try {
    const fonts = await self.queryLocalFonts();
    const toTransfer = [];
    for(const font of fonts) {
      toTransfer.push({
        // You're free to add whatever other info you need
        name: font.postscriptName,
        blob: await font.blob()
      });
    }
    const worker = new Worker("worker.js");
    worker.postMessage(toTransfer);
    worker.onmessage = ({data}) => {
      log(data);
    }
  } 
  catch (e) {
    log(`Error: ${e.message}`);
  }
};
// worker script
onmessage = async ({data}) => {
  postMessage(`Received ${data.length} Blobs`);
  for(const {blob, name} of data) {
    const buf = await blob..arrayBuffer();
    // Do your analysis on the font's data
  }
};

Live example as a Glitch project since StackSnippet's don't allow access to local fonts. (editor).

Kaiido
  • 123,334
  • 13
  • 219
  • 285