6

I'm trying to put image in clipboard when user copies canvas selection:

canvas selection

So I thought the right way would be to convert canvas tu dataURL, dataURL to blob and blob to binary string.

Theoretically it should be possible to skip the blob, but I don't know why.

So this is what I did:

  function copy(event) {
    console.log("copy");
    console.log(event);

    //Get DataTransfer object
    var items = (event.clipboardData || event.originalEvent.clipboardData);
    //Canvas to blob
    var blob = Blob.fromDataURL(_this.editor.selection.getSelectedImage().toDataURL("image/png"));
    //File reader to convert blob to binary string
    var reader = new FileReader();
    //File reader is for some reason asynchronous
    reader.onloadend = function () {
      items.setData(reader.result, "image/png");
    }
    //This starts the conversion
    reader.readAsBinaryString(blob);

    //Prevent default copy operation
    event.preventDefault();
    event.cancelBubble = true;
    return false;
  }
  div.addEventListener('copy', copy);

But when the DataTransfer object is used out of the paste event thread the setData has no longer any chance to take effect.

How can I do the conversion in the same function thread?

3
  • parameter order in setData should be reversed -> items.setData("image/png", reader.result) Commented Jul 16, 2015 at 7:39
  • I have to admit that I'm really struggling to get the canvas into the clipboardData object and paste it let's say in MS Outlook message or elsewhere. Tomas, could you please outline what was your final solution to copy canvas into clipboard. I've tried nearly everything I could either using the setData("image/png"...) or items.filed.add(file) etc.. what should be the value to be passed to setData("image/url", ??) ? Thank you. Commented Jul 17, 2015 at 2:31
  • @belzebu Unless something changes, I have bad news. I posted question subsequent to this one and it's still not answered because it's simply not possible: stackoverflow.com/q/27262879/607407 Commented Jul 21, 2015 at 19:18

2 Answers 2

8

Here is a hacky-way to get you synchronously from a blob to its bytes. I'm not sure how well this works for any binary data.

function blobToUint8Array(b) {
    var uri = URL.createObjectURL(b),
        xhr = new XMLHttpRequest(),
        i,
        ui8;
    
    xhr.open('GET', uri, false);
    xhr.send();
    
    URL.revokeObjectURL(uri);
    
    ui8 = new Uint8Array(xhr.response.length);
    
    for (i = 0; i < xhr.response.length; ++i) {
        ui8[i] = xhr.response.charCodeAt(i);
    }
    
    return ui8;
}

var b = new Blob(['abc'], {type: 'application/octet-stream'});
blobToUint8Array(b); // [97, 98, 99]

You should consider keeping it async but making it two-stage, though, as you may end up locking up the browser.

Additionally, you can skip Blobs entirely by including a binary-safe Base64 decoder, and you probably don't need to go via Base64 AND Blob, just one of them.

Sign up to request clarification or add additional context in comments.

5 Comments

I think I'll just skip the blob phase. I'm curious though - how come that pseudo-xhr is synchronous?
@TomášZato Passed false as the third parameter of the open method. This is not considered best practice.
For me, this is a clever solution. But a synchronously XMLHttpRequest just accept DOMString as response. That means, bytes higher then 127 are falsified, whenever the transmitted byte array is not UTF8 compatible.
I'm pretty sure you can just use xhr.responseType="arraybuffer"; developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/…
@Codesmith Yep, if you only need to target IE10 or newer then that option is also valid
1

Blob can be converted to binary string by getting Blob as dataURI and then applying atob. This, however, again requires FileReader. In my case, it's best to skip the blob alltogether:

// Canvas to binary
var data = atob(
  _this.editor.selection.getSelectedImage()  // Canvas
  .toDataURL("image/png")                    // Base64 URI
  .split(',')[1]                             // Base64 code
);

1 Comment

Omg there are so many duplicates of this question and tons of answers and none mentions the toDataURL that is exactly what's needed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.