I'm getting an image that has been pasted into the content editable div and during paste the types
in listed in the Clipboard
include "image/png
" and "image/tiff
".
When I get that image using a FileReader
and readAsDataURL
the base64
image string it returns is a TIFF
, "data:image/tiff,
".
Is there a way to specify to the FileReader
to return a PNG
, "data:image/png
"?
Related question here.
You don't.
FileReader.readAsDataURL()
only converts the binary data held in the passed Blob to a base64 string, it doesn't do any other conversion.
It will only prepend the corresponding MIME header to whatever it thinks this data is (this check is first done against the Blob's type
, and then probably against magic-numbers), but you can't force it to convert the data to something else.
const fr = new FileReader();
const fakeGif = new Blob(['foo'], {type:'image/gif'});
fr.onload = onload;
fr.readAsDataURL(fakeGif);
function onload(){
console.log(fr.result); // with image/gif MIME
console.log(atob(fr.result.split(',')[1])) // still just "foo"...
}
Now to your case, since Safari still doesn't support the DataTransferItem interface, you are out of luck.
Even though Grab utility does save a png File in the clipboard, Safari chose to display the tiff raw data.
You can check this assertion in your dev's Network panel, where you will see that the image is fetched as image/tiff.
Browsers that do support DataTransferItem interface can retrieve the png file attached in the clipboard though, so let's hope Safari implement this interface soon enough.
So one way to convert this TIFF image to a png one, is to go through a canvas element:
// an Array to hold pasted Files
var files = [];
// We start by checking if the browser supports the
// DataTransferItem interface. If not, we need to create a
// contenteditable element that catches all pasted data
if (!window.DataTransferItem) {
var pasteCatcher = document.createElement("div");
pasteCatcher.setAttribute("contenteditable", "");
// We can hide the element and append it to the body,
pasteCatcher.style.opacity = 0.5;
document.body.appendChild(pasteCatcher);
// as long as we make sure it is always in focus
pasteCatcher.focus();
document.addEventListener("click", function() {
pasteCatcher.focus();
});
}
// Add the paste event listener
window.addEventListener("paste", pasteHandler);
/* Handle paste events */
function pasteHandler(e) {
// We need to check if event.clipboardData is supported (Chrome)
if (e.clipboardData) {
// Get the items from the clipboard
var items = e.clipboardData.items || e.clipboardData.files;
itemcount = items.length;
if (itemcount) {
// Loop through all items, looking for any kind of image
for (var i = 0; i < items.length; i++) {
getItem(items[i]);
}
} else {
// This is a cheap trick to make sure we read the data
// AFTER it has been inserted.
setTimeout(checkInput, 1);
}
// If we can't handle clipboard data directly (Firefox),
// we need to read what was pasted from the contenteditable element
} else {
console.log("checking input");
// This is a cheap trick to make sure we read the data
// AFTER it has been inserted.
setTimeout(checkInput, 1);
}
}
/* For browsers that support DataTransferItem interface */
function getItem(item) {
if (item.type.indexOf("image") !== -1) {
// We need to represent the image as a file,
var blob = item instanceof Blob ? item : item.getAsFile();
// save the File for later use if needed
files.push(blob);
// and use a URL or webkitURL (whichever is available to the browser)
// to create a temporary URL to the object
var URLObj = window.URL || window.webkitURL;
var source = URLObj.createObjectURL(blob);
// The URL can then be used as the source of an image
createImage(source);
}
}
/* For older browsers */
/* Parse the input in the paste catcher element */
function checkInput() {
// Store the pasted content in a variable
var child = pasteCatcher.childNodes[0];
if (child) {
// If the user pastes an image, the src attribute
// will represent the image as a base64 encoded string.
if (child.tagName === "IMG") {
getPngFromIMG(child, function(blob) {
// Clear the inner html to make sure we're always
// getting the latest inserted content
pasteCatcher.innerHTML = "";
// save the png blob in our list
files.push(blob);
createImage(URL.createObjectURL(blob));
});
}
}
}
function getPngFromIMG(img, callback) {
if (img.naturalWidth) // if already loaded
draw();
else
img.onload = draw;
function draw() {
var canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
canvas.getContext('2d').drawImage(img, 0, 0);
canvas.toBlob(callback);
}
}
/* Creates a new image from a given source */
function createImage(source) {
var pastedImage = new Image();
pastedImage.onload = function(e) {
loadImage.src = e.target.src;
}
pastedImage.src = source;
}
btn.onclick = function() {
console.log(files);
}
<textarea id="pasteArea" placeholder="Paste Image Here"></textarea>
<img id="loadImage" />
<button id="btn">do something with pasted files</button>