Read and base64 encode a binary file - javascript

I'm trying to read a binary file from the filesystem and then base64 encode it in JavaScript. I'm using the FileReader API to read the data and the base64 encoder found here.
The code I have seems close to working, the problem is that the generated base64 data is wrong. Here's what I've got so far:
function saveResource() {
var file = $(".resourceFile")[0].files[0];
var reader = new FileReader();
reader.onload = function(evt) {
var fileData = evt.target.result;
var bytes = new Uint8Array(fileData);
var binaryText = '';
for (var index = 0; index < bytes.byteLength; index++) {
binaryText += String.fromCharCode( bytes[index] );
}
console.log(Base64.encode(binaryText));
};
reader.readAsArrayBuffer(file);
};
Here's the file I'm testing with (it's a 100x100 blue square):
According to an online base64 decoder/encoder, this file should encode to:
/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABkAGQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDxyiiiv3E8wKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//Z
...but instead what I'm getting out of the JavaScript is:
w7/DmMO/w6AAEEpGSUYAAQIAAAEAAQAAw7/DmwBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDLDv8ObAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMsO/w4AAEQgAZABkAwEiAAIRAQMRAcO/w4QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoLw7/DhADCtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDLCgcKRwqEII0LCscOBFVLDkcOwJDNicsKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5esKDwoTChcKGwofCiMKJworCksKTwpTClcKWwpfCmMKZwprCosKjwqTCpcKmwqfCqMKpwqrCssKzwrTCtcK2wrfCuMK5wrrDgsODw4TDhcOGw4fDiMOJw4rDksOTw5TDlcOWw5fDmMOZw5rDocOiw6PDpMOlw6bDp8Oow6nDqsOxw7LDs8O0w7XDtsO3w7jDucO6w7/DhAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgvDv8OEAMK1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIywoEIFELCkcKhwrHDgQkjM1LDsBVicsORChYkNMOhJcOxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXrCgsKDwoTChcKGwofCiMKJworCksKTwpTClcKWwpfCmMKZwprCosKjwqTCpcKmwqfCqMKpwqrCssKzwrTCtcK2wrfCuMK5wrrDgsODw4TDhcOGw4fDiMOJw4rDksOTw5TDlcOWw5fDmMOZw5rDosOjw6TDpcOmw6fDqMOpw6rDssOzw7TDtcO2w7fDuMO5w7rDv8OaAAwDAQACEQMRAD8Aw7HDiijCosK/cTzDgMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooAMKiwoooA8O/w5k=
If i had to hazard a guess I'd say that the issue has something to do with nonprintable characters in the binary data (if I encode a plaintext document, that works fine). But what's the best approach to work around the issue?
Edit
It looks like this may be a problem with the base64 library itself (or if not that, then with how the Uint8Array is unpackaged into a string for the library call). If I use the browser's btoa() function instead, and pass it the Uint8Array binaryText directly, that works. Too bad that function doesn't exist in all browsers.

And Google to the rescue. I found the following code, which takes the input data as a plain array of "bytes" (numbers between 0 and 255, inclusive; also works fine if the Uint8Array is passed to it directly), and added it to the library I was using:
//note: it is assumed that the Base64 object has already been defined
//License: Apache 2.0
Base64.byteToCharMap_ = null;
Base64.charToByteMap_ = null;
Base64.byteToCharMapWebSafe_ = null;
Base64.charToByteMapWebSafe_ = null;
Base64.ENCODED_VALS_BASE =
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz' +
'0123456789';
/**
* Our default alphabet. Value 64 (=) is special; it means "nothing."
* #type {string}
*/
Base64.ENCODED_VALS = Base64.ENCODED_VALS_BASE + '+/=';
Base64.ENCODED_VALS_WEBSAFE = Base64.ENCODED_VALS_BASE + '-_.';
/**
* Base64-encode an array of bytes.
*
* #param {Array.<number>|Uint8Array} input An array of bytes (numbers with
* value in [0, 255]) to encode.
* #param {boolean=} opt_webSafe Boolean indicating we should use the
* alternative alphabet.
* #return {string} The base64 encoded string.
*/
Base64.encodeByteArray = function(input, opt_webSafe) {
Base64.init_();
var byteToCharMap = opt_webSafe ?
Base64.byteToCharMapWebSafe_ :
Base64.byteToCharMap_;
var output = [];
for (var i = 0; i < input.length; i += 3) {
var byte1 = input[i];
var haveByte2 = i + 1 < input.length;
var byte2 = haveByte2 ? input[i + 1] : 0;
var haveByte3 = i + 2 < input.length;
var byte3 = haveByte3 ? input[i + 2] : 0;
var outByte1 = byte1 >> 2;
var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
var outByte4 = byte3 & 0x3F;
if (!haveByte3) {
outByte4 = 64;
if (!haveByte2) {
outByte3 = 64;
}
}
output.push(byteToCharMap[outByte1],
byteToCharMap[outByte2],
byteToCharMap[outByte3],
byteToCharMap[outByte4]);
}
return output.join('');
};
/**
* Lazy static initialization function. Called before
* accessing any of the static map variables.
* #private
*/
Base64.init_ = function() {
if (!Base64.byteToCharMap_) {
Base64.byteToCharMap_ = {};
Base64.charToByteMap_ = {};
Base64.byteToCharMapWebSafe_ = {};
Base64.charToByteMapWebSafe_ = {};
// We want quick mappings back and forth, so we precompute two maps.
for (var i = 0; i < Base64.ENCODED_VALS.length; i++) {
Base64.byteToCharMap_[i] =
Base64.ENCODED_VALS.charAt(i);
Base64.charToByteMap_[Base64.byteToCharMap_[i]] = i;
Base64.byteToCharMapWebSafe_[i] =
Base64.ENCODED_VALS_WEBSAFE.charAt(i);
Base64.charToByteMapWebSafe_[
Base64.byteToCharMapWebSafe_[i]] = i;
}
}
};
The full code for the library containing the above functions is available here, but in its non-modified form it appears to depend upon a number of other libraries. The slightly hacked-up version above should work for anyone who just needs a quick fix for this issue.

Treat the binary as arraybuffer, this is independent from any character encoding. Your blue-square (.jpg) has 361 native bytes, means octets from 0..255 (decimal) and they are no characters!
It means: Use ArrayBuffer for encoding this to Base64 with well known base64-algorithm.
With Perl back to origin, shows the blue-square as above:
my $fh = IO::File->new;
$fh->open("d:/tmp/x.jpg", O_BINARY|O_CREAT|O_RDWR|O_TRUNC) or die $!;
$fh->print(decode_base64("/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBD
AQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABkAGQDASIAAhEBAxEB/8QAFQABAQAA
AAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAUH/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMR
AD8AjgDcUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//2Q==
"));
$fh->close;

Related

How to duplicate symfony passwordEncode in javascript

I need to create a javascript hashing algorithm the same as Symfony 3 encodePassword.
This was a similar problem to that in but in symfony3:
Symfony2 password encoder function in Javascript
this is to create a message digest to test a rest endpoint with wsse headers in Symfony with fosbundle in postman.
I've managed to simplify and duplicate the Symfony hashing function in PHP
$pass = "hello";
$salt = "";
$iterations=5000;
echo $this->encoder->encodePassword($pass,$salt);
//contains: U5xyFq7KQU1CWeX3UcLB0mwWZZQUq0PL8U+GLWomfGW/WQWxxGLi+0ifhmnlw/gQ5pPjNNZV1/q8kMVpAXsFZw==
//simplyfying and replicating the hashing algo in php with same pass/salt:
$salted = $pass.$salt;
$digest = hash("sha512", $salted, true);
for($i=1; $i<$iterations; $i++) {
$digest = hash("sha512", $digest.$salted, true);
}
echo base64_encode($digest);
//contains: U5xyFq7KQU1CWeX3UcLB0mwWZZQUq0PL8U+GLWomfGW/WQWxxGLi+0ifhmnlw/gQ5pPjNNZV1/q8kMVpAXsFZw==
but trying to replicate it in javascript using CryptoJS is proving troublesome. I suspect its to do with the character encoding too.
according to https://code.google.com/archive/p/crypto-js/#The_Hasher_Input
The hash algorithms accept either strings or instances of
CryptoJS.lib.WordArray [...] an array of 32-bit words. When you pass a
string, it's automatically converted to a WordArray encoded as UTF-8.
password = 'hello';
//attempt 1 use hex converted pass
hexpass = CryptoJS.enc.Utf8.parse(password);
digest = CryptoJS.SHA512(hexpass);
for (i = 1; i < 5000; ++i) {
hexvar = CryptoJS.SHA512(digest + hexpass);
}
digest = digest.toString(CryptoJS.enc.Base64);
console.log(digest);
// need hash to contain: U5xyFq7KQU1CWeX3UcLB0mwWZZQUq0PL8U+GLWomfGW/WQWxxGLi+0ifhmnlw/gQ5pPjNNZV1/q8kMVpAXsFZw==
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
I've tried many different ways to with converting to word array first etc. but none seem to come up with the same hash
https://jsfiddle.net/munkiepus/awdoq4kL/34/
EDIT: i think the problem is that the php uses some form of raw binary
outputting the result of $digest = hash("sha512", $salted, true); to the terminal shows:
▒q▒$▒b▒x]▒▒j▒▒=s1▒▒�
▒▒▒▒▒%g<▒##▒ٛ▒▒|z▒n▒▒▒
FcG.:▒▒os▒▒▒C
so maybe it's not possible in JS after all. if the digest was encoded to a readable string during each iteration then it may be possible, as in the linked example.
Ok so it was the binary data causing the problem, if we convert the word array into a binary string it works.
needed some other functions to do the conversions see the runnable example for the functions. example
hashWordArray = CryptoJS.SHA512(password);
uint8array = convertWordArrayToUint8Array(hashWordArray);
binaryString = convertUint8ArrayToBinaryString(uint8array);
for (var i=1; i<5000; i++) {
wordArrayFromString = CryptoJS.enc.Latin1.parse(binaryString+password);
hashWordArray = CryptoJS.SHA512(wordArrayFromString);
uint8array = convertWordArrayToUint8Array(hashWordArray);
binaryString = convertUint8ArrayToBinaryString(uint8array);
}
b64_encoded = btoa(binaryString);
const password = "hello";
// set up the container to display output
var div = document.getElementById('message');
div.innerHTML += 'string to hash:<br>';
div.innerHTML += password+'<br><br>';
div.innerHTML += 'php generated hash:<br>';
correct_hash = 'U5xyFq7KQU1CWeX3UcLB0mwWZZQUq0PL8U+GLWomfGW/WQWxxGLi+0ifhmnlw/gQ5pPjNNZV1/q8kMVpAXsFZw=='
div.innerHTML += correct_hash+'<br><br>';
//actually do the hashing
hashWordArray = CryptoJS.SHA512(password);
uint8array = convertWordArrayToUint8Array(hashWordArray);
binaryString = convertUint8ArrayToBinaryString(uint8array);
for (var i=1; i<5000; i++) {
wordArrayFromString = CryptoJS.enc.Latin1.parse(binaryString+password);
hashWordArray = CryptoJS.SHA512(wordArrayFromString);
uint8array = convertWordArrayToUint8Array(hashWordArray);
binaryString = convertUint8ArrayToBinaryString(uint8array);
}
b64_encoded = btoa(binaryString);
// add the outputr to the display container
div.innerHTML += 'javascript generated hash:<br>';
div.innerHTML += b64_encoded +"<br><br>"; //b64_encode()
// functions from
// https://gist.github.com/getify/7325764
function convertWordArrayToUint8Array(wordArray) {
var len = wordArray.words.length,
u8_array = new Uint8Array(len << 2),
offset = 0, word, i
;
for (i=0; i<len; i++) {
word = wordArray.words[i];
u8_array[offset++] = word >> 24;
u8_array[offset++] = (word >> 16) & 0xff;
u8_array[offset++] = (word >> 8) & 0xff;
u8_array[offset++] = word & 0xff;
}
return u8_array;
}
function convertUint8ArrayToBinaryString(u8Array) {
var i, len = u8Array.length, b_str = "";
for (i=0; i<len; i++) {
b_str += String.fromCharCode(u8Array[i]);
}
return b_str;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js"></script>
<div id="message"></div>
Why do you need that? The best thing is that you only encrypt it on one side, either from JS or Symfony and only compare the hashes.
The other option is to not use encodePassword() and use md5() for example.
In this link shows you how it works encodePassword().
Regards!

Equivalent of Java's getBytes in JavaScript for different encodings

I have a function in Java that I need to convert to JavaScript and that contains this line:
byte[] bytes = ttText.getBytes(Charset.forName("Cp1250"));
ttText is String. I need to do the same. I need to get the bytes of a string encoded in Cp1250 (windows-1250), modify those bytes and then convert it back to string. Is there a way how to do it in JavaScript?
I discovered for example TextEncoder and TextDecoder but the support for different encodings than UTF-8 was dropped some time ago.
var cp1250 = '€ ‚ „…†‡ ‰Š‹ŚŤŽŹ ‘’“”•–— ™š›śťžź ˇ˘Ł¤Ą¦§¨©Ş«¬­®Ż°±˛ł´µ¶·¸ąş»Ľ˝ľżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙';
function encodeCP1250(text) {
var buf = [];
for (var i = 0; i < text.length; i++) {
var code = cp1250.indexOf(text[i]);
if (code >= 0) {
code += 128;
} else {
code = text.charCodeAt(i);
}
buf.push(code > 255 ? 32 : code);
}
return buf;
}
function decodeCP1250(buf) {
var text = '';
for (var i = 0; i < buf.length; i++) {
var code = buf[i];
text += code > 127 ? cp1250[code - 128] : String.fromCharCode(code);
}
return text;
}
var buf = encodeCP1250('AÁÂĂÄ'); // [65, 193, 194, 195, 196]
var text = decodeCP1250(buf); // 'AÁÂĂÄ'
Upd: Chrome and Firefox have TextDecoder as experimental feature, but TextEncoder works only with UTF-8.
Try this.
https://mths.be/windows-1250
This looks promising. It provides support for both encoding and decoding.
All you need to do is add the library and use the methods.
var encodedData = windows1250.encode(text);

How to get image color mode (CMYK, RGB ...) in Javascript

can the JavaScript check the color mode of a image?
I did a lot of search about this but the only thing I saw was color mode convert (but the convert wants you to set the original color mode)
I add this: --allow-file-access-from-files to have a full control of img in canvas beacause I am using GoogleChrome
Html
<canvas id="canvas" width=6000 height=7919></canvas>
Js
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.crossOrigin = "anonymous";
img.onload = start;
img.src = "file:///D:/vincent-van-gogh-the-starry-night-picture-157544-2.png";
Yes - basically JavaScript is able to determine the color mode of a png, but therefore it would be required to
1. convert png to base64
2. convert base64 to byte array
3. reading / parsing the array regarding png specification
A possible approach could look like this:
var PNG = {
parse: function(imgTag) {
var base64 = PNG.asBase64(imgTag);
var byteData = PNG.utils.base64StringToByteArray(base64);
var parsedPngData = PNG.utils.parseBytes(byteData);
return PNG.utils.enrichParsedData(parsedPngData);
},
asBase64: function(imgTag) {
var canvas = document.createElement("canvas");
canvas.width = imgTag.width;
canvas.height = imgTag.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(imgTag, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.split('base64,')[1];
},
utils: {
base64StringToByteArray: function(base64String) {
//http://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
var byteCharacters = atob(base64String);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
return new Uint8Array(byteNumbers);
},
parseBytes: function(bytes) {
var pngData = {};
//see https://en.wikipedia.org/wiki/Portable_Network_Graphics
//verify file header
pngData['headerIsValid'] = bytes[0] == 0x89
&& bytes[1] == 0x50
&& bytes[2] == 0x4E
&& bytes[3] == 0x47
&& bytes[4] == 0x0D
&& bytes[5] == 0x0A
&& bytes[6] == 0x1A
&& bytes[7] == 0x0A
if (!pngData.headerIsValid) {
console.warn('Provided data does not belong to a png');
return pngData;
}
//parsing chunks
var chunks = [];
var chunk = PNG.utils.parseChunk(bytes, 8);
chunks.push(chunk);
while (chunk.name !== 'IEND') {
chunk = PNG.utils.parseChunk(bytes, chunk.end);
chunks.push(chunk);
}
pngData['chunks'] = chunks;
return pngData;
},
parseChunk: function(bytes, start) {
var chunkLength = PNG.utils.bytes2Int(bytes.slice(start, start + 4));
var chunkName = '';
chunkName += String.fromCharCode(bytes[start + 4]);
chunkName += String.fromCharCode(bytes[start + 5]);
chunkName += String.fromCharCode(bytes[start + 6]);
chunkName += String.fromCharCode(bytes[start + 7]);
var chunkData = [];
for (var idx = start + 8; idx<chunkLength + start + 8; idx++) {
chunkData.push(bytes[idx]);
}
//TODO validate crc as required!
return {
start: start,
end: Number(start) + Number(chunkLength) + 12, //12 = 4 (length) + 4 (name) + 4 (crc)
length: chunkLength,
name: chunkName,
data: chunkData,
crc: [
bytes[chunkLength + start + 8],
bytes[chunkLength + start + 9],
bytes[chunkLength + start + 10],
bytes[chunkLength + start + 11]
],
crcChecked: false
};
},
enrichParsedData: function(pngData) {
var idhrChunk = PNG.utils.getChunk(pngData, 'IDHR');
//see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
pngData.width = PNG.utils.bytes2Int(idhrChunk.data.slice(0, 4));
pngData.height = PNG.utils.bytes2Int(idhrChunk.data.slice(4, 8));
pngData.bitDepth = PNG.utils.bytes2Int(idhrChunk.data.slice(8, 9));
pngData.colorType = PNG.utils.bytes2Int(idhrChunk.data.slice(9, 10));
pngData.compressionMethod = PNG.utils.bytes2Int(idhrChunk.data.slice(10, 11));
pngData.filterMethod = PNG.utils.bytes2Int(idhrChunk.data.slice(11, 12));
pngData.interlaceMethod = PNG.utils.bytes2Int(idhrChunk.data.slice(12, 13));
pngData.isGreyScale = pngData.colorType == 0 || pngData.colorType == 4;
pngData.isRgb = pngData.colorType == 2 || pngData.colorType == 6;
pngData.hasAlpha = pngData.colorType == 4 || pngData.colorType == 6;
pngData.hasPaletteMode = pngData.colorType == 3 && PNG.utils.getChunk(pngData, 'PLTE') != null;
return pngData;
},
getChunks: function(pngData, chunkName) {
var chunksForName = [];
for (var idx = 0; idx<pngData.chunks.length; idx++) {
if (pngData.chunks[idx].name = chunkName) {
chunksForName.push(pngData.chunks[idx]);
}
}
return chunksForName;
},
getChunk: function(pngData, chunkName) {
for (var idx = 0; idx<pngData.chunks.length; idx++) {
if (pngData.chunks[idx].name = chunkName) {
return pngData.chunks[idx];
}
}
return null;
},
bytes2Int: function(bytes) {
var ret = 0;
for (var idx = 0; idx<bytes.length; idx++) {
ret += bytes[idx];
if (idx < bytes.length - 1) {
ret = ret << 8;
}
}
return ret;
}
}
}
Which could be used as follows:
var pngData = PNG.parse(document.getElementById('yourImageId'));
console.log(pngData);
It contains some information, like color mode, number of chunks, the chunks itself, bit depth, etc.
Hope it helps.
Can the JavaScript check the color mode of a image?
Yes and no.
No if you use ordinary image loading. All images that are supported by the browser are converted to RGB(A) before they are handed to us through the Image object (HTMLImageElement).
At this point no information about the original format and color model is presented except from its size and origin.
Canvas only deals with RGBA as all other elements that are displayed and there exist no native method to address color models such as CMYK.
You could always read the RGBA values and convert to CMYK naively, however, without a ICC profile for the target printer you would run into all sort of problems due to the wider gamut of RGB, and printer as well as print medium characteristics which must be adjusted for through ICC profiles. Meaning the result will not produce correct colors in most cases.
You would have to have a server-side solution setup for this to work. Send image to server, convert to CMYK/apply ICC, then send to printer. ImageMagick could be one way to go about it. The browser is simply not cut (nor intended) for print oriented processing.
Yes if you're willing to write image parser manually as well as ICC parser. You would need to support a variety of formats and format combinations, and also be able to apply ICC/gamma on import as well as export to CMYK through the target ICC (meaning you need to support XYZ/LAB color spaces).
The export would require you to write a file writer supporting the CMYK color space and ICC embedding.
The maturing process, bugs, instabilities etc. would be a natural part of the process and likely not something you would want to use in production right away.
If you're just after the color mode of the file and can load the image like ordinary you can of course load the file first through XHR, then scan the file using typed array to locate the information describing the color format depending on format. Then pass the typed array as Blob -> object-URL to load as an image, go through canvas and convert each pixel to CMYK values.
But from this point you will have the same problems as described above and would not escape writing the file writer etc.
You can use exifreader package to read image metadata.
import ExifReader from 'exifreader'
const reader = new FileReader()
reader.onload = (event) => {
const metadata = ExifReader.load(event.target.result, { expanded: true })
const { file: { 'Color Components': { value } } } = metadata
switch (value) {
case 3: console.log('RGB'); break
case 4: console.log('CMYK'); break
default: console.log('Unknown')
}
}
reader.readAsArrayBuffer(instaceOfFileObject)
It doesn't have to be File object, but the ExifReader.load() expects array buffer as its first arg.
Reference: https://www.get-metadata.com/file-info/color-components

How to build a binary-array from hex strings

I'm new to the concept of working with data on the binary level and am hoping someone can give me a hand here...
I'd like to build a binary buffer out of a series of hex numbers that are represented as strings.
For example,
suppose I have "xFCx40xFF" and I want to turn this into an array that looks like: 111111000100000011111111.
What's the best way to do this?
My best attempt seems to not be working:
var raw = "xFCx40xFF"
var end = raw.length-2;
var i = 1;
var j = 0;
var myArray = new Uint8Array(raw.len);
while (i < end) {
var s = raw.substr(i,2);
var num = parseInt(s,16);
i += 3;
myArray[j] = num;
j += 8;
}
Each 3 characters in string will represent 1 number in Uint8Array. Each number in Uint8Array will represent 8 bits. Your code was creating Uint8Array larger than needed and then placing values are wrong locations.
I have simplified the code to use a singe index i which represents the location in the Uint8Array. Corresponding location in string can be easily computed from i.
var raw = "xFCx40xFF"
var myArray = new Uint8Array(raw.length / 3);
for (var i = 0; i < raw.length / 3; i++) {
var str = raw.substr(3 * i + 1, 2);
var num = parseInt(str, 16);
myArray[i] = num;
}
Remove the 'x' character of your hex string and call parseInt() with the radix 16 and toString() with the radix 2 to get the binary string.
var raw = "xFCx40xFF";
var bin = parseInt(raw.split('x').join(''), 16).toString(2);
document.body.textContent = bin;
and if you need an array, just add .split('') at the end.
parseInt(raw.split('x').join(''), 16).toString(2).split('');
or iterate through each character.

Encrypting files with SJCL client-side

I have problem encrypting files with SJCL and javascript.
I have managed to encrypt text-files with using FileReader API and readAsBinaryString.
When it comes to encrypting pdf/png/.. then problem arrises probably due to encoding.
I found that I can use readAsArrayBuffer which suits this task perfectly, so I basically read file and create new typed array with new Uint8Array() but i dont know how exactly I am supposed to encrypt such a data.
Here's my code:
/** Convert from an array of bytes to a bitArray. */
function toBitArrayCodec(bytes) {
var out = [], i, tmp=0;
for (i=0; i<bytes.length; i++) {
tmp = tmp << 8 | bytes[i];
if ((i&3) === 3) {
out.push(tmp);
tmp = 0;
}
}
if (i&3) {
out.push(sjcl.bitArray.partial(8*(i&3), tmp));
}
return out;
}
/** Convert from a bitArray to an array of bytes. */
function fromBitArrayCodec(arr) {
var out = [], bl = sjcl.bitArray.bitLength(arr), i, tmp;
for (i=0; i<bl/8; i++) {
if ((i&3) === 0) {
tmp = arr[i/4];
}
out.push(tmp >>> 24);
tmp <<= 8;
}
return out;
}
var reader = new FileReader();
reader.readAsArrayBuffer(fileData); //filedata comes from function
reader.onload = function() {
var bytes = new Uint8Array(reader.result);
var bits = toBitArrayCodec(bytes);
var crypt = sjcl.encrypt("aaaaa", bits);
var decrypt = sjcl.decrypt("aaaaa", crypt);
var byteNumbers = fromBitArrayCodec(decrypt);
var byteArray = new Uint8Array(byteNumbers);
saveData(byteArray, 'png.png');
I am getting error on
Uncaught URIError: URI malformed sjcl.js:12sjcl.codec.utf8String.fromBits sjcl.js:12sjcl.json.decrypt sjcl.js:44reader.onload
I need to know how to encrypt uint8array or another alternative how to encrypt(pdf/png/..) files.
The plaintext in sjcl is expected to be utf8 encoded. Encrypting a manually built bitArray works, because the encryption is done on the bitArray and it doesn't have to be decoded. But at the end of the decryption is an encoding step which converts the recovered plaintext bitArray into a utf8string. This doesn't work, because it contains unprintable characters, because the source was probably binary.
The solution would be to encode it as Base64 before encrypting and convert it back after decrypting.
var bytes = new Uint8Array(reader.result);
var bits = toBitArrayCodec(bytes);
var base64bits = sjcl.codec.base64.fromBits(bits); // added
var crypt = sjcl.encrypt("aaaaa", base64bits);
var base64decrypt = sjcl.decrypt("aaaaa", crypt);
var decrypt = sjcl.codec.base64.toBits(base64decrypt); // added
var byteNumbers = fromBitArrayCodec(decrypt);
var byteArray = new Uint8Array(byteNumbers);

Categories

Resources