canvas toDataURL is not converting base64 string to real image - javascript

I want to create a function which compress the image after selecting and append it to the body.
Here is my code =>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Image Compression</title>
</head>
<body>
<input type="file" accept=".jpg,.jpeg,.png" id="random-img" />
</body>
<script>
const Compress = function (img_obj, quality, output_format) {
var mime_type = output_format;
var cvs = document.createElement("canvas");
img_obj.onload = function () {
cvs.width = img_obj.naturalWidth;
cvs.height = img_obj.naturalHeight;
};
var ctx = cvs.getContext("2d").drawImage(img_obj, 0, 0);
var newImageData = cvs.toDataURL(mime_type, quality / 100);
var result_image_obj = new Image();
result_image_obj.src = newImageData;
return result_image_obj;
};
function getImg() {
const src_obj = document.getElementById("random-img").files[0];
const newImg = new Image();
newImg.src = URL.createObjectURL(src_obj);
const compressed_img = Compress(newImg, 80, src_obj.type);
document.querySelector("body").appendChild(compressed_img);
}
document.querySelector("input").addEventListener("change", getImg);
</script>
</html>
The problem is I am not able to see the image on the screen.Instead I see any empty white box of 300 x 150 size when I open the developer tool and hover over the image element.
Please help! Thankyou!

img_obj.onload() is asynchronous, you can't just immediately return from it.
const Compress = function (img_obj, quality, output_format) {
var mime_type = output_format;
var cvs = document.createElement("canvas");
img_obj.onload = function () {
cvs.width = img_obj.naturalWidth;
cvs.height = img_obj.naturalHeight;
var ctx = cvs.getContext("2d").drawImage(img_obj, 0, 0);
var newImageData = cvs.toDataURL(mime_type, quality / 100);
var result_image_obj = new Image();
result_image_obj.src = newImageData;
document.querySelector("body").appendChild(result_image_obj);
};
};
function getImg() {
const src_obj = document.getElementById("random-img").files[0];
const newImg = new Image();
newImg.src = URL.createObjectURL(src_obj);
const compressed_img = Compress(newImg, 80, src_obj.type);
}
document.querySelector("input").addEventListener("change", getImg);
<input type="file" accept=".jpg,.jpeg,.png" id="random-img" />

Related

How to resize Image on HTML Canvas while loading from client side?

I am trying to control Image size whenever user upload an Image, so when user upload larger image like 1600x1600 it should be fixed in a canvas with w=500 and h=800, how can I do this in canvas, also image quality have to be same only resize in image.
How can I set maxwidth and maxHeight to control the Image size ?
var jcp;
var save = document.getElementById('save');
var imageLoader = document.getElementById('imageLoader');
var img = document.getElementById("target");
imageLoader.onchange = function handleImage(e) { //handling our image picker <input>:
var reader = new FileReader();
reader.onload = function(event) {
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
save.onclick = function() {
if (jcp && jcp.active) {
var i = 0;
for (area of jcp.crops) {
i++;
canvas = document.createElement("canvas");
canvas.setAttribute('width', area.pos.w);
canvas.setAttribute('height', area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(img, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
temp = document.createElement('a');
temp.setAttribute('download', 'area' + i + '.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
Jcrop.load('target').then(img => {
jcp = Jcrop.attach(img, {
multi: true
});
});
<head>
<title>Jcrop Example</title>
<link href="https://unpkg.com/jcrop#3.0.1/dist/jcrop.css" rel="stylesheet" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://unpkg.com/jcrop#3.0.1/dist/jcrop.js"></script>
</head>
<body>
<div><a id="save">Save</a>
<input type="file" id="imageLoader" name="imageLoader" />
<!-- add this for file picker -->
</div>
<div>
<h1 style="font-family:Helvetica,sans-serif;">
Image Crop <span style="color:lightgray;"></span>
</h1>
<img id="target" style="background-size: cover !important;">
</div>
</body>
I'm not sure what is not working for you, but here I made a preview mode so you can see what's your crop result in canvas. If you wonder why the donwload not working, maybe it's SO snippets not allowed it, I'm not sure, but here you can test full working ecxample in jsfiddle
var jcp;
var save = document.getElementById('save');
var imageLoader = document.getElementById('imageLoader');
var img = document.getElementById("target");
var max_width = 500
var max_height = 500
var fileName = 'filename.png'
var changes
imageLoader.onchange = function handleImage(e) { //handling our image picker <input>:
var reader = new FileReader();
reader.onload = function(event) {
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
save.onclick = function() {
if (jcp && jcp.active) {
var i = 0;
for (let area of jcp.crops) {
i++;
let naturalWidth = img.naturalWidth;
let naturalHeight = img.naturalHeight;
let scaleX = naturalWidth / img.width
let scaleY = naturalHeight / img.height
let canvasSave = document.createElement("canvas");
var ctxSave = canvasSave.getContext('2d');
canvasSave.width = area.pos.w * scaleX
canvasSave.height = area.pos.h * scaleY
ctxSave.drawImage(img, -area.pos.x * scaleX, -area.pos.y * scaleY, naturalWidth, naturalHeight)
var link = document.createElement('a');
link.download = fileName;
link.href = canvasSave.toDataURL("image/png")
link.click();
alert('file save')
}
}
};
Jcrop.load('target').then(img => {
jcp = Jcrop.attach(img, {
multi: true,
});
changes = img.nextSibling
observer.observe(changes, config);
});
const callback = function(mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('A child node has been added or removed.');
} else if (mutation.type === 'attributes') {
updatePreview()
}
}
};
const observer = new MutationObserver(callback);
const config = {
attributes: true,
childList: true,
subtree: true
};
function updatePreview() {
if (jcp && jcp.active) {
var i = 0;
for (let area of jcp.crops) {
canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
//for preview
canvas.width = area.pos.w
canvas.height = area.pos.h
ctx.drawImage(img, -area.pos.x, -area.pos.y, img.width, img.height)
}
}
}
<head>
<title>Jcrop Example</title>
<link href="https://unpkg.com/jcrop#3.0.1/dist/jcrop.css" rel="stylesheet" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://unpkg.com/jcrop#3.0.1/dist/jcrop.js"></script>
</head>
<body>
<div><a id="save">Save</a>
<input type="file" id="imageLoader" name="imageLoader" />
<!-- add this for file picker -->
</div>
<div>
<h1 style="font-family:Helvetica,sans-serif;">
Image Crop <span style="color:lightgray;"></span>
</h1>
<img id="target" style="background-size: cover !important;" width='500'>
<div style='width:100%;'>
<canvas id="canvas" style='border: 4px solid blue;' />
</div>
</div>
</body>

JavaScript: Multiple cropping selection in one image?

PS: Is it not a research kind of question! I have been trying to do this from very long time.
I am trying to make web based an image editor where user can select multiple cropping area and after selection save/download all the image area. like below.
As of now I discovered two libraries
1.Cropper.JS where is only single selection feature is available.
2.Jcrop where only single selection area restrictions.
I am currently using cropper.Js but it seems impossible for me to make multiple selection cropping.
Any help is much appreciated.if any other method/library available in JavaScript, Angular or PHP or reactJS for multiple image area selection and crop and download in one go as in the image below.
As per #Keyhan Answer I am Updating my Jcrop library Code
<div style="padding:0 5%;">
<img id="target" src="https://d3o1694hluedf9.cloudfront.net/market-750.jpg">
</div>
<button id="save">Crop it!</button>
<link rel="stylesheet" href="https://unpkg.com/jcrop/dist/jcrop.css">
<script src="https://unpkg.com/jcrop"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
JavaScript
<script>
setImage();
var jcp;
var jcp;
Jcrop.load('target').then(img => {
//You can enable multiple cropping with this line:
jcp = Jcrop.attach(img, { multi: true });
});
// to fix security issue when trying to convert to Data URI
function setImage() {
document.getElementById('target').setAttribute('crossOrigin', 'anonymous');
document.getElementById('target').src = 'https://d3o1694hluedf9.cloudfront.net/market-750.jpg';
}
var link = document.getElementById('save');
link.onclick = function () {
//we check if at least one crop is available
if (jcp.active) {
var i = 0;
var fullImg = document.getElementById("target");
//we are looping cropped areas
for (area of jcp.crops) {
i++;
//creating temp canvas and drawing cropped area on it
canvas = document.createElement("canvas");
canvas.setAttribute('width', area.pos.w);
canvas.setAttribute('height', area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(fullImg, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
//creating temp link for saving/serving new image
temp = document.createElement('a');
temp.setAttribute('download', 'area' + i + '.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
</script>
I tried to explain the code with comments:
var jcp;
Jcrop.load('target').then(img => {
//You can enable multiple cropping with this line:
jcp = Jcrop.attach(img,{multi:true});
});
//assuming you have a button with id="save" for exporting cropped areas
var link=document.getElementById('save');
link.onclick = function(){
//we check if at least one crop is available
if(jcp.active){
var i=0;
var fullImg = document.getElementById("target");
//we are looping cropped areas
for(area of jcp.crops){
i++;
//creating temp canvas and drawing cropped area on it
canvas = document.createElement("canvas");
canvas.setAttribute('width',area.pos.w);
canvas.setAttribute('height',area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(fullImg, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
//creating temp link for saving/serving new image
temp = document.createElement('a');
temp.setAttribute('download', 'area'+i+'.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
EDIT: As you commented it would be nicer if we have local image loader, we can add a file input to our html
<img id="target" />
<br/>
<input type="file" id="imageLoader" name="imageLoader"/><!-- add this for file picker -->
<button id="save">save</button>
and a function to our js to handle it
var jcp;
var save=document.getElementById('save');
var imageLoader = document.getElementById('imageLoader');
var img = document.getElementById("target");
imageLoader.onchange=function handleImage(e){//handling our image picker <input>:
var reader = new FileReader();
reader.onload = function(event){
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
save.onclick = function(){
if(jcp&&jcp.active){
var i=0;
for(area of jcp.crops){
i++;
canvas = document.createElement("canvas");
canvas.setAttribute('width',area.pos.w);
canvas.setAttribute('height',area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(img, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
temp = document.createElement('a');
temp.setAttribute('download', 'area'+i+'.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
Jcrop.load('target').then(img => {
jcp = Jcrop.attach(img,{multi:true});
});
Yes, #keyhan was right <input type="file"> is another question, but still, I am giving you an idea of how to implement Kayhan's code above.
<div>
<input type="file" id="image-input" accept="image/*">
<!-- img id name should be "target" as it is also using by Jcrop -->
<img id="target"></img>
</div>
and Now you can put below JavaScript Code just above setImage()
<script>
let imgInput = document.getElementById('image-input');
imgInput.addEventListener('change', function (e) {
if (e.target.files) {
let imageFile = e.target.files[0];
var reader = new FileReader();
reader.onload = function (e) {
var img = document.createElement("img");
img.onload = function (event) {
var MAX_WIDTH = 1600;
var MAX_HEIGHT = 800;
var width = img.width;
var height = img.height;
// Change the resizing logic
if (width > height) {
if (width > MAX_WIDTH) {
height = height * (MAX_WIDTH / width);
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width = width * (MAX_HEIGHT / height);
height = MAX_HEIGHT;
}
}
// Dynamically create a canvas element
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
// var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// Actual resizing
ctx.drawImage(img, 0, 0, width, height);
// Show resized image in preview element
var dataurl = canvas.toDataURL(imageFile.type);
document.getElementById("target").src = dataurl;
}
img.src = e.target.result;
}
reader.readAsDataURL(imageFile);
}
});
</script>

Use image as watermark with compressor.js

I am using compressor.js for image compression and watermarking. However, only text based watermark seems to be possible to me. I want to add image (logo) into watermark too.
Any idea how to use image as watermark with compressor.js?
You can do this by using the drew function, like so:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/compressorjs/1.0.7/compressor.min.js"></script>
</head>
<body>
<img id="watermark" src="watermark.png" style="display: none" />
<h3>Input</h3>
<div id="input"><img id="image" src="picture.png" style="max-width:250px"></div>
<h3>Output</h3>
<div id="output"></div>
<script>
window.addEventListener('DOMContentLoaded', function () {
var Compressor = window.Compressor;
var URL = window.URL || window.webkitURL;
var image = document.getElementById('image');
var output = document.getElementById('output');
var watermark = document.getElementById('watermark');
var xhr = new XMLHttpRequest();
xhr.onload = function () {
new Compressor(xhr.response, {
strict: false,
drew: function (context, canvas) {
context.drawImage(
watermark,
0,
0,
watermark.width,
watermark.height,
canvas.width - ((watermark.width / 4) + 10),
canvas.height - ((watermark.height / 4) + 10),
(watermark.width / 4),
(watermark.height / 4))
},
success: function (result) {
var newImage = new Image();
newImage.src = URL.createObjectURL(result);
newImage.style = "max-width:250px";
output.appendChild(newImage);
},
error: function (err) {
window.alert(err.message);
},
});
};
xhr.open('GET', image.src);
xhr.responseType = 'blob';
xhr.send();
});
</script>
</body>
</html>
All of the dividing by 4 is because my watermark image is massive. That's not required.
Here's a screenshot of the before/after:
Alternative Method
Here's an alternative way to add a watermark to an image:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<img id="watermark" src="https://upload.wikimedia.org/wikipedia/en/7/71/Corona_Extra.svg" style="display: none" />
<img id="image" src="https://images.unsplash.com/photo-1665606855702-144fd49af552?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=870&q=80%20870w" style="display:none">
<canvas id="output"></canvas>
<script>
var imagesLoaded = 0;
var main = document.getElementById("image");
var watermark = document.getElementById("watermark");
function imageHasLoaded() {
imagesLoaded++;
if (imagesLoaded == 2) {
var c = document.getElementById("output");
var ctx = c.getContext("2d");
c.width = main.width;
c.height = main.height;
ctx.drawImage(main,
0, 0,
main.width, main.height,
0, 0,
main.width, main.height);
ctx.drawImage(watermark,
0, 0,
watermark.width, watermark.height,
(main.width - watermark.width) / 2, (main.height - watermark.height),
watermark.width, watermark.height);
}
}
main.load = imageHasLoaded();
watermark.load = imageHasLoaded();
</script>
</body>
</html>

Remove all white space from Image with Base64 and Cors

My idea is to grab an image from an URL, send it through a cors proxy, convert that to base64, then remove the white space from the image and replace that with a transparent background. I have managed to do that:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ajax Image</title>
<style>
body {
background: darkslategrey;
}
.text {
color: white;
}
</style>
</head>
<body>
<!-- Original -->
<h3 class="text">Original</h3>
<img src="" id="original">
<!-- Modified -->
<h3 class="text">Modified</h3>
<canvas id="modified"></canvas>
<script src="../CommonLinkedFiles/jquery3_2_1.js"></script>
<script>
var stockSymbol = "extr";
var logoUrl = "https://storage.googleapis.com/iex/api/logos/" + stockSymbol.toUpperCase() + ".png";
// Converting URL to Base64 with the use of a proxy
var getDataUri = function (targetUrl, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var reader = new FileReader();
reader.onloadend = function () {
callback(reader.result);
};
reader.readAsDataURL(xhr.response);
};
var proxyUrl = 'https://cors-anywhere.herokuapp.com/';
xhr.open('GET', proxyUrl + targetUrl);
xhr.responseType = 'blob';
xhr.send();
};
// Returns Base 64 of image
getDataUri(logoUrl, function (base64) {
console.log('RESULT:', base64);
//original
$("#original").attr("src", base64);
var canvas = document.getElementById("modified"),
ctx = canvas.getContext("2d"),
image = document.getElementById("original");
canvas.height = canvas.width = 128;
ctx.drawImage(image,0,0);
var imgd = ctx.getImageData(0, 0, 128, 128),
pix = imgd.data,
newColor = {r:0,g:0,b:0, a:0};
for (var i = 0, n = pix.length; i <n; i += 4) {
var r = pix[i],
g = pix[i+1],
b = pix[i+2];
if(r == 255&& g == 255 && b == 255){
// Change the white to the new color.
pix[i] = newColor.r;
pix[i+1] = newColor.g;
pix[i+2] = newColor.b;
pix[i+3] = newColor.a;
}
}
ctx.putImageData(imgd, 0, 0);
});
</script>
</body>
You can see here at: JsFiddle
My question is how do I remove all of the white space given any image.
Edit: Keith Solved the image by adding on onload function.
After some tinkering, I figured out that all of the "white" pixels were not exactly white so I so modifying the if statement it searched for all of near white pixels:if(r >= 250&& g >= 250 && b >= 250){
JSFiddle

Insert image from URL into Google Docs

I am trying to get image from external link and add in a document.
Html service work fine. However, document should fill out after onload function.
How can I fix it ?
code.gs
function doGet(e){
var content = HtmlService.createHtmlOutputFromFile('index').getContent();
return HtmlService.createHtmlOutput(content);
}
function im(baseUrl){
var resp = UrlFetchApp.fetch(baseUrl);
var docID = "0000xxxxx1111"
var doc = DocumentApp.openById(docID);
doc.getChild(0).asParagraph().appendInlineImage(resp.getBlob());
return baseUrl;
}
index.html
<!DOCTYPE html>
<head>
<base target="_top">
<script>
window.onload = function() {
var url = "http://xxx.co/image.png"
var canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 100;
document.getElementById('barcode').appendChild(canvas);
var ctx = canvas.getContext('2d');
var image = new Image();
image.src = url;
ctx.drawImage(image, 0, 0);
google.script.run.withSuccessHandler(function(a) {
document.getElementById("imageid").src=a;
}).im(canvas.toDataURL());
}
</script>
</head>
<body>
<img id="imageid" src="" alt="image">
</body>
</html>
There are a few problems with your HTML which is why it probably isn't working. You also had not tagged any elements with the barcode id.
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body id="barcode">
<img id="imageid" src="" alt="image">
</body>
<script>
window.onload = function() {
var url = "http://lorempixel.com/400/200/"
var canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 100;
document.getElementById('barcode').appendChild(canvas);
var ctx = canvas.getContext('2d');
var image = new Image();
image.src = url;
ctx.drawImage(image, 0, 0);
google.script.run.withSuccessHandler(function(a) {
document.getElementById("imageid").src=a;
}).im(canvas.toDataURL());
}
</script>
</html>

Categories