react-pdf icon indicating copy to clipboard operation
react-pdf copied to clipboard

Allow to use Blob as the Image source

Open davbrito opened this issue 2 years ago • 0 comments

Is your feature request related to a problem? Please describe. On web contexts it is more handy to be able to use a Blob as the data source. Even on modern versions of Node and other runtimes like Deno, have implemented this Web API.

A common use case would be taking an image from a canvas:

<Image
   src={() => {
     const canvas = document.createElement('canvas')
     renderQR(canvas, "Hello world")
     // Now what?
   }}
/>

A naive approach would be creating a Data url with canvas.toDataURL(). But it is known to have some drawbacks.

So. Why don't create a Blob and use URL.createObjectUrl?

<Image
  src={async () => {
    const canvas = document.createElement("canvas");
    renderQR(canvas, "Hello world");
    const blob = await new Promise((resolve, reject) =>
      canvas.toBlob((blob) => {
        if (blob) {
          return resolve(blob);
        } else {
          return reject(new Error("couldn't create Blob"));
        }
      })
    );
    return URL.createObjectUrl(blob);
  }}
/>

Now we have a worse problem. There is a memory leak!. Since we are not calling URL.revokeObjectUrl to release the resources. We could workaround it with a useRef and a useEffect, but the code becomes very verbose and hard to follow.

Describe the solution you'd like Allow to pass a Blob as the source for images, instead of only allowing Buffer, which is a Node-only API (we could polyfill it on other environments, but it just doesn't feels natural)

It would allow something like:

<Image
  src={() => {
    const canvas = document.createElement("canvas");
    renderQR(canvas, "Hello world");
    return new Promise((resolve, reject) =>
      canvas.toBlob((blob) => {
        if (blob) {
          return resolve(blob);
        } else {
          return reject(new Error("couldn't create Blob"));
        }
      })
    );
  }}
/>

As a sidenote I think this can be a starting point to reduce the dependencies on node specific APIs and replacing them by Web APIs, like Web Streams, the Fetch API, etc.

Describe alternatives you've considered

  • Polyfilling Node buffer and convert the blob to that.
  • Using URL.createObjectURL as pointed above.

Additional context

davbrito avatar Jun 10 '22 17:06 davbrito