I am using JSZip to make a program that generates the image data from a canvas element and puts the image into a zip file.
Right now, it is turning the canvas image into an DataURL. Then, I get rid of the part of the resulting string that says data:image/png;base64,. Now, there is nothing left but the base64 data. I then use atob to change it to ascii.
It seems like putting the remaining string into an image file should work, but the generated ascii text is not correct. Many parts of it are correct, but something is not right.
Here is my code:
//screen is the name of the canvas.
var imgData = screen.toDataURL();
imgData = imgData.substr(22);
imgData = atob(imgData);
console.log(imgData);
Here is an image of the resulting png file (in notepad):
incorrect text http://upurs.us/image/71280.png
And here is what is should look like:
correct text http://upurs.us/image/71281.png
As you can see, there are slight differences, and I have no idea why. I know absolutely nothing about the PNG file type or ASCII, so I don't know where to go from here.
If you want to see all my work, here's the project:
http://s000.tinyupload.com/download.php?file_id=09682586927694868772&t=0968258692769486877226111
EDIT: My end goal is to have a program that exports every single frame of a canvas animation so that I can use them to make a video. If anyone knows a program that does that, please post it!
When you use zip.file("hello.png", imgData) where imgData is a string, you tell JSZip to save an (unicode) string. Since it's not a textual content, you get a corrupted content. To fix that, you can do:
zip.file("hello.png", imgData, {binary: true})
As dandavis suggested, using a blob will be more efficient here. You can convert a canvas to a blob with canvas.toBlob:
screen.toBlob(function (blob) {
zip.file("hello.png", blob);
});
The only caveat is that toBlob is asynchronous: you should disable the download button during that time (or else, if a user is quick enough or the browser slow enough, zip.file won't be executed and you will give an empty zip to your user).
document.getElementById("download_button").disabled = true;
screen.toBlob(function (blob) {
zip.file("hello.png", blob);
document.getElementById("download_button").disabled = false;
});
Related
I keep facing a problem converting XHR request response properly. I've read couple of solutions on Stackoverflow and on some other platforms but couldn't success to convert string to dataURL.
I've tried to convert string to through atob() and btoa() methods mention on MDN and this Stackoverflow post.
+ I've keep dive much to find a solution within lost of different methods but non of them took me aim.
How can I convert this response below to a dataURL properly to be able set the URL as image src?
XHR Request Response:
"�PNG
IHDR�� m"H3PLTE���������������������������������������������������?�U�iIDATx�흋�� Eۈ�Z����R�[���BB�]�_�Y�� x��a�a�a�a�a�a�a�a��-��nFI]]�}_x�}�����$ʆ�h�s�_4M_w�WI��*��.o���"��~۔�C^uڟ�P5W�'�[��(��}�=)���U�����Z�J#U��G�'�8G�ۓH�>��E���G�>݁K�ތ4l��C��
v�xu���?�*R��^������ł�B[�YkkY����=�
l|H�s7Yp"���+6zV�cvSj+�
�}�c�c݄Pކ
~�W�N֨(�3����0[QƄ�Q�)o�_R,�]J����G�b��?��M 9� w*h��!=�%"�4������˔*a��
���6��w'��>��el3�e�l����c�ݍ(U.p�Q2�э����#��BɺB�h�4Sh��I�
�s��B���P735���(L�KU_�����s��v�]~�������6�+
/��iD��� �����uԏf�ﳽ�}nA�1_7��t�
+m�2W���P:�8�N�.Ԉ}�KQ�`�G0�P�Y�}�=A|�$� ��Xǭ�)w���>����m�J�R�֖��~}�n�#�G��7�ͽ���d������58C��i�6|�&�ۄ?pIl:P�l *FE�
q��wj��v��6�.�BQ�߁�����GBm�{�(�
�!f�k�Df*?}�+�N��"u��V,N��.eҚj��r�t��А�r��>)��*a��J����4�U���L��Z�/�ҵ���e=�;Qp����=x�[5=N��:8O}��k�?�Rr"�ma��tڱQ�I���R���=ܣU�MI�3Y�6\�~�v�.yJ�)��q�&���/�_�R�A-����{Ҡ�$��RNx%�}'�D�Tm�d9��}�n��~�kz��Ӝ���K� b��] L|�iqo3�O��p��l�� 'd�$�D�!:e��F=����'�"7g�F=b��7+Qܤ֩n�_"��c�����w$~`�V�"��I�{�&R̰G�O�|��%� ��/�L���>� �wb�S����- �3O����*J��7�͈�
�m�JL�Fdҗ��0��>���0��<����0�I!�33,y
d>3Ò�<y��)�'�pxS�E����;�����)�2�p��Dt��P�*�����Y�.�܈�
��D�5�Y�Ld����l��cb�lQ�|4�DED�͒�n
^3��&F!|��D��?�'��q
G�jN�h�\� 2ODVu�d���'���LLs_�ۏ�>CV� �gQ�{���e�
���2��*�Q�>|y��fg�M9a��E�IEND�B`�"
UPDATE:
I've succeed to render image through this post #
https://stackoverflow.com/a/8022521/7163711
Your XHR data is a png file, as expected. In order to show you how to embed it properly, I've taken the liberty of generating a "better" PNG - a 10x10 yellow square. it is base64-encoded in b64data on the first line of the snippet. The second line sets yourData as what you would have gotten from an XHR.
var b64data = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFklEQVR42mP8/5/hDAMRgHFUIX0VAgBfZRvvtreybgAAAABJRU5ErkJggg==";
// Assuming your data is a raw PNG
var yourData = atob(b64data);
// We convert...
var btoa_data = btoa(yourData);
var elem = document.createElement("img");
elem.src = "data:image/png;base64,"+btoa_data;
document.getElementById("app").append(elem);
console.log(atob(b64data));
<div id="app"></div>
As you can see, btoa() works in order to base64-encode data; the only thing you need to do after this is to indicate in the src part of your img tag that:
It is a PNG (if it isn't, or if you are not sure if it will be, you'll need to check the first few bytes of your reply)
It is base64-encoded
Here is my attempt to convert an svg string to a png buffer using node and the imagemagick convert tool. The png buffer is then used to draw an image in a pdf using pdfkit.
Td;lr I have a large svg string that needs to get to a child process "whole" (i.e not chunked). How do I do so?
This is an example that works for small files.
var child_process = require('child_process');
var pdfDocument = require('pdfkit');
var convert = child_process.spawn("convert", ["svg:", "png:-"]),
svgsrc = '<svg><rect height="100" width="100" style="fill:red;"/></svg>';
convert.stdout.on('data', function(data) {
console.log(data.toString('base64')
doc = new pdfDocument()
doc.image(data)
}
convert.stdin.write(svgsrc);
convert.stdin.end()
This works when the svg string is 'small' (like the on provided in the example) -- I'm not sure where the cut-off from small to large is.
However, when attempting to use a larger svg string (something you might generate using D3) like this [ large string ]. I run into:
Error: Incomplete or corrupt PNG file
So my question is: How do I ensure that the convert child process reads the entire stream before processing it?
A few things are known:
The png buffer is indeed incomplete. I used a diff tool to check
the base64 string generated by the app
versus the base64 of a png-to-svg converter online. The non-corrupted
string is much larger than the corrupted string. (sorry I haven't
been more specific with file size). That is, the convert tool seems
to not be reading the entire source at any given time.
The source svg string is not corrupted (as evidenced by the fact the the
gist rendered it)
When used in the command line the convert tool correctly generate a
png file from a svg "stream" with cat large_svg.svg | convert svg:png:- So this is not an issue with the convert tool.
This lead me to down a rabbit hole of looking a node's buffer size for writeable and readable streams but to no avail. Maybe someone has worked with larger streams in node and can help out with getting the to work.
As #mscdex pointed out I had to wait for the process to finish before
attemping downstream work. All that was need was to wait for the end event on the convert.stdout stream and concatenate buffers on the data events.
// allocate a buffer of size 0
graph = Buffer.alloc(0)
// on data concat the incoming and the `graph`
convert.stdout.on('data', function(data) {
graph = Buffer.concat([graph, data])
}
convert.stdout.on('end', function(signal) {
// ... draw on pdf
}
EDIT:
Here is an more efficient version of the above where we use #mscdex
suggestion to do the concatenation on the end callback and keeping a chunksize argument to help the Buffer allocate size when concatenation the chunks.
// allocate a buffer of size 0
var graph = [];
var totalchunks = 0;
convert.stdout.on('data', function(data) {
graph.push(data);
totalsize +=data.length;
}
convert.stdout.on('end', function(signal) {
var image = Buffer.concat(graph, totalsize);
// ... draw on pdf
}
I am stuck in problem regarding base64.
The situation is - I download image in iPad and getting path of that download image like this :-
{"isFile":true,"isDirectory":false,"name":"testImage.png","fullPath":"/testImage.png","filesystem":"","nativeURL":"file:///var/mobile/Containers/Data/Application/4048F634-D44A-40F8-ACA0-32BC9F401F56/Documents/testImage.png"}
In the above object ,nativeURl from which I am getting image and I am able to append on HTML page, but I want convert it into base64 ,& I am not been able to get till now.
I know somewhere it is possible. I am just making a mistake.
So anybody,expert please help me sort out that issue, if provide example that will be help full in any online editor.
Thanks
Shivam
<div id='mainPostDiv'></div>
//hello, you need to convert into base64
// set your data first in localStorage
var imageData = localStorage.getItem("ImageData");
var imgPost = document.createElement('img');
imgPost.setAttribute('id', "imgPost");
imgPost.src = "data:image/jpeg;base64," + imageData;
$('#mainPostDiv').append(imgPost);
i'm trying to use localStorage to save an image, or multiple images for retrieval at a later date to upload to a server.
The current camera code is as follows:
function capturePhoto() {
navigator.camera.getPicture(onCameraSuccess, onCameraFail, {quality: 70, destinationType : Camera.DestinationType.DATA_URL});
}
function onCameraSuccess(imageData) {
//In our success call we want to first process the image to save in our image box on the screen.
var image = document.getElementById('image');
image.src = "data:image/jpeg;base64," + imageData;
//Create a new canvas for our image holder
var imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d");
// Make sure canvas is as big as the picture
imgCanvas.width = image.width;
imgCanvas.height = image.height;
// Draw image into canvas element
imgContext.drawImage(image, 0, 0, image.width, image.height);
// Get canvas contents as a data URL
var imgAsDataURL = imgCanvas.toDataURL("image/png");
// Save image into localStorage
try {
// localStorage.setItem(“savedImage”, imgAsDataURL);
localStorage.setItem("savedImage", imageData);
alert('Image Saved');
}
catch (e) {
alert("Storage failed: " + e);
}
var imageStorage = localStorage.getItem("savedImage");
// myCardHolder= document.getElementById(“m1-cardStorage-image1″);
// Reuse existing Data URL from localStorage
var imageInfo = document.getElementById('image');
imageInfo.src = "data:image/jpeg;base64," + imageStorage;
}
This triggers the camera, and the image captured is displayed into
<img id="image" src=""></img>
It also draws a canvas to output the image into. What i'm really trying to achieve is to capture the images base64 data to be able to store it into an array so that it may be uploaded/downloaded from a server.
Ideally i'd like to completely avoid having to display the image to the user, and simply store the images data
I may have misunderstood the localStorage/camera api a little, so any pointers would be great.
Does the image HAVE to be output into an element before the data can be stored? If i could just output it into the canvas that may never have to be shown, and extract the data from the canvas element?
Does the image HAVE to be output into an element before the data can be stored?
Not at all, in this case anyways. You are already receiving the image as base64 data so just store that directly.
Problems:
datauris can be chopped by the browser if too long
if not chopped by browser on string level, the data can be chopped by localstorage itself which has a size limit (i think it's currently around 5 mb for most browsers but there is no standard here)
a string uses two bytes per char so the storage is in effect the half
A better local storage is to use indexedDB.
When you read the base64 data, then you have to use an Image to show the data. Just prefix as you do with data:... etc. and remember to use correct file type.
Last year I was trying to solve the same problem, I don't have the code right now but I followed kind of the approach taken on this answer:
How to convert image into base64 string using javascript
Remember that localStorage has a limit of 5 MB, so if you save a lot of images in b64 you can reach that limit easily. (which was my case), so I had to move my storage to somewhere else, like a sqlite or something like that.
I'm trying to produce the same base64 data for an image file in both JavaScript and in Ruby. Unfortunately both are outputting two very different values.
In Ruby I do this:
Base64.encode64(File.binread('test.png'));
And then in JavaScript:
var image = new Image();
image.src = 'http://localhost:8000/test.png';
$(image).load(function() {
var canvas, context, base64ImageData;
canvas = document.createElement('canvas');
context = canvas.getContext('2d');
canvas.width = this.width;
canvas.height = this.height;
context.drawImage(this, 0, 0);
imageData = canvas.toDataURL('image/png').replace(/data:image\/[a-z]+;base64,/, '');
console.log(imageData);
});
Any idea why these outputs are different?
When you load the image in Ruby the binary file without any modifications will be encoded directly to base-64.
When you load an image in the browser it will apply some processing to the image before you will be able to use it with canvas:
ICC profile will be applied (if the image file contains that)
Gamma correction (where supported)
By the time you draw the image to canvas, the bitmap values has already been changed and won't necessarily be identical to the bitmap that was encoded before loading it as image (if you have an alpha channel in the file this may affect the color values when drawn to canvas - canvas is a little peculiar at this..).
As the color values are changed the resulting string from canvas will naturally also be different, before you even get to the stage of re-encoding the bitmap (as PNG is loss-less the encoding/compressing should be fairly identical, but factors may exist depending on the browser implementation that will influence that as well. to test, save out a black unprocessed canvas as PNG and compare with a similar image from your application - all values should be 0 incl. alpha and at the same size of course).
The only way to avoid this is to deal with the binary data directly. This is of course a bit overkill (in general at least) and a relative slow process in a browser.
A possible solution that works in some cases, is to remove any ICC profile from the image file. To save an image from Photoshop without ICC choose "Save for web.." in the file menu.
The browser is re-encoding the image as you save the canvas.
It does not generate an identical encoding to the file you rendered.
So I actually ended up solving this...
Fortunately I am using imgcache.js to cache images in the local filesystem using the FileSystem API. My solution is to use this API (and imgcache.js makes it easy) to get the base64 data from the actual cached copy of the file. The code looks like this:
var imageUrl = 'http://localhost:8000/test.png';
ImgCache.init(function() {
ImgCache.cacheFile(imageUrl, function() {
ImgCache.getCachedFile(imageUrl, function(url, fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
console.log($.md5(this.result.replace(/data:image\/[a-z]+;base64,/, '')));
};
reader.readAsDataURL(file);
});
});
});
});
Also, and very importantly, I had to remove line breaks from the base64 in Ruby:
Base64.encode64(File.binread('test.png')).gsub("\n", '');