From 4cef06a71fbed643ab8d3bd49d74d119c03812d5 Mon Sep 17 00:00:00 2001 From: Willem Bult Date: Thu, 7 Jul 2011 15:27:53 -0400 Subject: [PATCH] New base64 string encoder. Old one sometimes gave wrong results. --- mobl/imagefetch.mobl | 119 ++++++++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 46 deletions(-) diff --git a/mobl/imagefetch.mobl b/mobl/imagefetch.mobl index 8727c35..7877663 100644 --- a/mobl/imagefetch.mobl +++ b/mobl/imagefetch.mobl @@ -6,57 +6,84 @@ module mobl::imagefetch external function downloadImageAsBase64(url : String) : String +/* + * getByteAt and encodeBinary inspired by base64 string encoder: + * + * Copyright (c) 2010 Nick Galbreath + * http://code.google.com/p/stringencoders/source/browse/#svn/trunk/javascript + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + mobl.imagefetch.Base64 = { // private property - _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", - + _alphaStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", + + _padChar: '=', + + getByteAt: function(str, idx) { + var x = str.charCodeAt(idx) & 0xff; // throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data + if (x > 255) { + throw new Error(); + } + return x; + }, + encodeBinary: function(input){ - var output = ""; - var bytebuffer; - var encodedCharIndexes = new Array(4); - var inx = 0; - var paddingBytes = 0; + if (arguments.length !== 1) { + throw new SyntaxError("Not enough arguments"); + } + + var i, b10; + var x = []; - while (inx < input.length) { - // Fill byte buffer array - bytebuffer = new Array(3); - for (jnx = 0; jnx < bytebuffer.length; jnx++) - if (inx < input.length) - bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff; // throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data - else - bytebuffer[jnx] = 0; + // convert to string + input = '' + input; - // Get each encoded character, 6 bits at a time - // index 1: first 6 bits - encodedCharIndexes[0] = bytebuffer[0] >> 2; - // index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2) - encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4); - // index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3) - encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6); - // index 3: forth 6 bits (6 least significant bits from input byte 3) - encodedCharIndexes[3] = bytebuffer[2] & 0x3f; + var imax = input.length - input.length % 3; - // Determine whether padding happened, and adjust accordingly - paddingBytes = inx - (input.length - 1); - switch (paddingBytes) { - case 2: - // Set last 2 characters to padding char - encodedCharIndexes[3] = 64; - encodedCharIndexes[2] = 64; - break; - case 1: - // Set last character to padding char - encodedCharIndexes[3] = 64; - break; - default: - break; // No padding - proceed - } - // Now we will grab each appropriate character out of our keystring - // based on our index array and append it to the output string - for (jnx = 0; jnx < encodedCharIndexes.length; jnx++) - output += this._keyStr.charAt(encodedCharIndexes[jnx]); + if (input.length === 0) { + return input; + } + for (i = 0; i < imax; i += 3) { + b10 = (this.getByteAt(input,i) << 16) | (this.getByteAt(input,i+1) << 8) | this.getByteAt(input,i+2); + x.push(this._alphaStr.charAt(b10 >> 18)); + x.push(this._alphaStr.charAt((b10 >> 12) & 0x3F)); + x.push(this._alphaStr.charAt((b10 >> 6) & 0x3f)); + x.push(this._alphaStr.charAt(b10 & 0x3f)); + } + switch (input.length - imax) { + case 1: + b10 = this.getByteAt(input,i) << 16; + x.push(this._alphaStr.charAt(b10 >> 18) + this._alphaStr.charAt((b10 >> 12) & 0x3F) + + this._padChar + this._padChar); + break; + case 2: + b10 = (this.getByteAt(input,i) << 16) | (this.getByteAt(input,i+1) << 8); + x.push(this._alphaStr.charAt(b10 >> 18) + this._alphaStr.charAt((b10 >> 12) & 0x3F) + + this._alphaStr.charAt((b10 >> 6) & 0x3f) + this._padChar); + break; } - return output; + return x.join(''); } }; @@ -71,7 +98,7 @@ mobl.imagefetch.downloadImageAsBase64 = function(url, callback) { xhr.overrideMimeType('text/plain; charset=x-user-defined'); }, success: function(data) { - var b64 = mobl.imagefetch.Base64.encodeBinary(data); + var data = mobl.imagefetch.Base64.encodeBinary(data); // my super image-type detection code var fileType = 'image/png'; var firstPart = data.substring(0, 100); @@ -80,7 +107,7 @@ mobl.imagefetch.downloadImageAsBase64 = function(url, callback) { } else if(firstPart.indexOf('JFIF') !== -1) { fileType = 'image/jpeg'; } - callback("data:" + fileType + ";base64," + b64); + callback("data:" + fileType + ";base64," + data); }, error: function() { callback(null);