72

I have a project where I need to display djvu schemas in browser.

I found this old library on Github which, as far as I understood, converts djvu files to bmp and then puts them into canvas element.

As I said, library is old(last commit was 5 years ago), so I need to make some corrections. Main problem is that lib uses obsolete BlobBuilder.

Steps I made to solve this problem:

  1. Decompress this library via Chrome DevTools
  2. Initial error is at line 3774 var c = "undefined" != typeof MozBlobBuilder ? MozBlobBuilder : "undefined" != typeof WebKitBlobBuilder ? WebKitBlobBuilder : console.log("warning: cannot build blobs")
  3. I commented out this line
  4. Next, I commented out line c = new c; and some of following lines too.

So, now it looks this way(variable I is array buffer, and ololo1 and ololo2 are some kind of offset and limit)

var c = new Blob(new Uint8Array(new Uint8Array(I,ololo1,ololo2)))
              , b = b.createObjectURL(c)
              , c = document.getElementById(kb)
              , f = c.getContext("2d")
              , h = new Image
              , g = a[Ea >> 2]
              , i = a[Fa >> 2]
              , j = c.width
              , k = Math.round(i * j / g);

            h.onload = function()
            {
                var a = g / j;
                4 < a && (a = 4);
                1 > a && (a = 1);
                f.globalAlpha = 1;

                for (N = 0; N < a; N++)
                    f.drawImage(h, N, N, g - a + N, i - a + N, 0, 0, j, k),
                    f.globalAlpha *= 1 - 1 / a;
                R(h.complete, "Image /bmp.bmp could not be decoded")
            }
            ;
            h.onerror = function(errorMsg, url, lineNumber, column, errorObj) {
                console.log(errorMsg, url, lineNumber, column, errorObj);
                console.log("Image /bmp.bmp could not be decoded!")
            }           
            ;

And now I stuck at error "Image /bmp.bmp could not be decoded!"(throwed in h.onerror handler).

So, my question is: what I am doing wrong?

Rulisp
  • 1,586
  • 1
  • 18
  • 30
  • 1
    The original was `c.append((new Uint8Array(new Uint8Array(I,ololo1,ololo2))).buffer)`. I don't see why the author nested his Uint8array inside an new one... You should be fine with just new `Blob([new Uint8Array(I,ololo1,ololo2)])`; – Kaiido May 24 '17 at 03:45
  • @Kaiido many thanks, works like a charm. Could you post it as an answer? – Rulisp May 24 '17 at 03:54

2 Answers2

134

I don't know why the author did wrap his Uint8Array in a new one... note that I don't really know either the deprecated BlobBuilder API, but one typo I can see in your code is that you need to wrap your TypedArray in a normal Array:

new Blob([new Uint8Array(buffer, byteOffset, length)]);

The Blob() constructor takes a blobParts sequence as first parameter, and then searches for BufferSource, USVStrings and Blob elements in this sequence. So when you pass a TypedArray, it will actually iterate over all the entries of this TypedArray and treat these as USVString (and thus convert their numerical value to UTF-8 strings in the Blob). That's rarely what you want, so better always pass an Array in this constructor.

Note that if you don't need to slice the buffer, it's probably better to directly pass it instead of using an intermediary Uint8Array (but still in a normal Array):

new Blob([buffer]);
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • 3
    Saved my day as well (I passed in an arrayBuffer to work with binary data, but needed that wrapped as Array). However, I think Uint8Array should be passed in without the brackets as it's already an array, and therefore wasn't the OP's problem. You need the brackets around an ArrayBuffer. – aamarks Apr 29 '18 at 16:49
  • 2
    @aamarks No, if you pass an Uint8Array, you will save all its 0~255 values as utf8 strings since the Blob constructor won't access it `.buffer` property and that it's probably what is wanted to save the contained data as binary. – Kaiido Apr 29 '18 at 22:55
  • I see, thanks. It seems passing in the buffer the Uint8Array view is based on, or the Uint8Array, both work as long as either is in brackets. – aamarks May 01 '18 at 17:47
  • 20
    Uint8Array is a waste. You do not need it. Just `new Blob([buffer])`. ArrayBuffer is a byte array. – Domske Aug 23 '18 at 10:36
  • ^ it's only a view. Not certain if the starting point was the ArrayBuffer or another typed view? This could derail the blob builder in new Blob(...) – mattdlockyer Jul 08 '19 at 14:39
  • @Dominik is right that normally you don't need to wrap a buffer inside a TypedArray, but here OP's code was actually slicing the ArrayBuffer, hence their need to use that Uint8Array constructor here. – Kaiido Jul 09 '19 at 02:02
  • Is it normal that typescript complains with this syntax: `Type 'Uint8Array' is not assignable to type 'string | Blob'` – Sharcoux Mar 09 '23 at 08:37
  • @Sharcoux could you show your TS? – Kaiido Mar 09 '23 at 09:06
  • Actually, it's because there is a conflict between @types/react-native Blob and @types/web Blob definitions... I don't know how to solve this kind of thing, though. But I'll open an issue maybe – Sharcoux Mar 09 '23 at 13:43
57
var buffer = new ArrayBuffer(32);

new Blob([buffer]);

so the Uint8Array should be

new Blob([new Uint8Array([1, 2, 3, 4]).buffer]);
xsilen T
  • 1,515
  • 14
  • 10