{ "id": "154895", "key": "TIMOB-20395", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [], "resolution": { "id": "3", "description": "The problem is a duplicate of an existing issue.", "name": "Duplicate" }, "resolutiondate": "2017-08-08T22:25:39.000+0000", "created": "2016-02-12T19:41:21.000+0000", "priority": { "name": "Low", "id": "4" }, "labels": [ "base64" ], "versions": [ { "id": "17072", "name": "Release 5.1.2", "archived": false, "released": true, "releaseDate": "2016-01-12" }, { "id": "17532", "name": "Release 5.1.1", "archived": false, "released": true, "releaseDate": "2015-11-24" } ], "issuelinks": [], "assignee": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "updated": "2018-08-06T17:41:05.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10224", "name": "TiAPI", "description": "This component is used for cross-platform API work. Specifications are most likely to use this component." } ], "description": "h6.Issue Description\r\n\r\nTrying to encode the string using Ti.Utils.base64enocde method as below, \r\n{code}\r\nTi.Utils.base64encode('Test実験室じっけんしつ').\r\n{code}\r\nIt returns the encoded string : \r\nVGVzdOWun+mok+WupA== \r\n\r\nBut trying to encode the same string in https://www.base64encode.org/ \r\nreturns the encoded string : VGVzdOWun+mok+WupOOBmOOBo+OBkeOCk+OBl+OBpA== \r\n\r\nTi.Utils.base64encode method encodes the partial string only.\r\n\r\n\r\n\r\nh6.Steps to reproduce/Testcase\r\n\r\n# Create a new classic default application\r\n# open the app.js file\r\n# replace the code \r\n{code}\r\nvar win = Ti.UI.createWindow({backgroundColor:\"white\"});\r\nvar testStr = \"Test実験室じっけんしつ\";\r\nvar encoded = Ti.Utils.base64encode(testStr);\r\nTi.API.info(encoded);\r\nwin.open();\r\n{code}\r\n#Run\r\n\r\nThe return string don't match with the https://www.base64encode.org/ string", "attachment": [], "flagged": false, "summary": "iOS: Ti.Utils.base64encode is not encoding unicode correctly", "creator": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "environment": "Xcode 7.2 \r\niOS Simulator 9.1\r\niOS iPhone 6 plus Device 9.2\r\nSDK 5.2.1GA 5.1.1GA ", "closedSprints": [ { "id": 933, "state": "closed", "name": "2017 Sprint 16 SDK", "startDate": "2017-07-30T16:17:10.306Z", "endDate": "2017-08-13T16:17:00.000Z", "completeDate": "2017-08-13T23:06:34.398Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "376763", "author": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Workaround: \r\n\r\n{code}\r\nvar win = Ti.UI.createWindow({backgroundColor:\"white\"});\r\n\r\nvar Base64 = {\r\n\r\n\r\n _keyStr: \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\",\r\n\r\n\r\n encode: function(input) {\r\n var output = \"\";\r\n var chr1, chr2, chr3, enc1, enc2, enc3, enc4;\r\n var i = 0;\r\n\r\n input = Base64._utf8_encode(input);\r\n\r\n while (i < input.length) {\r\n\r\n chr1 = input.charCodeAt(i++);\r\n chr2 = input.charCodeAt(i++);\r\n chr3 = input.charCodeAt(i++);\r\n\r\n enc1 = chr1 >> 2;\r\n enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);\r\n enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);\r\n enc4 = chr3 & 63;\r\n\r\n if (isNaN(chr2)) {\r\n enc3 = enc4 = 64;\r\n } else if (isNaN(chr3)) {\r\n enc4 = 64;\r\n }\r\n\r\n output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);\r\n\r\n }\r\n\r\n return output;\r\n },\r\n\r\n\r\n decode: function(input) {\r\n var output = \"\";\r\n var chr1, chr2, chr3;\r\n var enc1, enc2, enc3, enc4;\r\n var i = 0;\r\n\r\n input = input.replace(/[^A-Za-z0-9\\+\\/\\=]/g, \"\");\r\n\r\n while (i < input.length) {\r\n\r\n enc1 = this._keyStr.indexOf(input.charAt(i++));\r\n enc2 = this._keyStr.indexOf(input.charAt(i++));\r\n enc3 = this._keyStr.indexOf(input.charAt(i++));\r\n enc4 = this._keyStr.indexOf(input.charAt(i++));\r\n\r\n chr1 = (enc1 << 2) | (enc2 >> 4);\r\n chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);\r\n chr3 = ((enc3 & 3) << 6) | enc4;\r\n\r\n output = output + String.fromCharCode(chr1);\r\n\r\n if (enc3 != 64) {\r\n output = output + String.fromCharCode(chr2);\r\n }\r\n if (enc4 != 64) {\r\n output = output + String.fromCharCode(chr3);\r\n }\r\n\r\n }\r\n\r\n output = Base64._utf8_decode(output);\r\n\r\n return output;\r\n\r\n },\r\n\r\n _utf8_encode: function(string) {\r\n string = string.replace(/\\r\\n/g, \"\\n\");\r\n var utftext = \"\";\r\n\r\n for (var n = 0; n < string.length; n++) {\r\n\r\n var c = string.charCodeAt(n);\r\n\r\n if (c < 128) {\r\n utftext += String.fromCharCode(c);\r\n }\r\n else if ((c > 127) && (c < 2048)) {\r\n utftext += String.fromCharCode((c >> 6) | 192);\r\n utftext += String.fromCharCode((c & 63) | 128);\r\n }\r\n else {\r\n utftext += String.fromCharCode((c >> 12) | 224);\r\n utftext += String.fromCharCode(((c >> 6) & 63) | 128);\r\n utftext += String.fromCharCode((c & 63) | 128);\r\n }\r\n\r\n }\r\n\r\n return utftext;\r\n },\r\n\r\n _utf8_decode: function(utftext) {\r\n var string = \"\";\r\n var i = 0;\r\n var c = c1 = c2 = 0;\r\n\r\n while (i < utftext.length) {\r\n\r\n c = utftext.charCodeAt(i);\r\n\r\n if (c < 128) {\r\n string += String.fromCharCode(c);\r\n i++;\r\n }\r\n else if ((c > 191) && (c < 224)) {\r\n c2 = utftext.charCodeAt(i + 1);\r\n string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));\r\n i += 2;\r\n }\r\n else {\r\n c2 = utftext.charCodeAt(i + 1);\r\n c3 = utftext.charCodeAt(i + 2);\r\n string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));\r\n i += 3;\r\n }\r\n\r\n }\r\n\r\n return string;\r\n }\r\n\r\n};\r\n\r\nvar testStr = \"Test実験室じっけんしつ\";\r\n//var encoded = Ti.Utils.base64encode('testStr');\r\nvar encoded = Base64.encode(testStr);\r\nTi.API.info(encoded);\r\n\r\nwin.open();\r\n{code}", "updateAuthor": { "name": "rramirez", "key": "rramirez", "displayName": " Ricardo Ramirez", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-02-12T19:41:56.000+0000", "updated": "2016-02-12T19:41:56.000+0000" }, { "id": "426278", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "[~emerriman] We should do this together with the related issues (TIMOB-16606, TIMOB-15590) and TIMOB-9111 which would be a breaking change by removing the \"\\n\" characters from the string. I would do it for 7.0.0 then, but if this urges, we can do the encoding fix before and the new-line changes in 7.0.0.\r\n\r\n*EDIT*: TIMOB-9111 is ready for review and will be part of 7.0.0. In the mean-time, here is a tiny Hyperloop solution that can be used to use the correct encoding today - without an SDK upgrade or native module:\r\n{code:js}\r\nvar NSUTF8StringEncoding = require('Foundation').NSUTF8StringEncoding;\r\n\r\nvar myString = 'Test実験室じっけんしつ';\r\nvar base64EncodedData = myString.dataUsingEncoding(NSUTF8StringEncoding);\r\nvar base64EncodedString = base64EncodedData.base64EncodedStringWithOptions(0);\r\n\r\nTi.API.info('Base 64 Encoded: ' + base64EncodedString);\r\n{code}", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2017-08-08T21:15:48.000+0000", "updated": "2017-08-08T22:25:16.000+0000" }, { "id": "439972", "author": { "name": "emerriman", "key": "emerriman", "displayName": "Eric Merriman ", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Closing as a duplicate. If this is in error, please reopen.", "updateAuthor": { "name": "emerriman", "key": "emerriman", "displayName": "Eric Merriman ", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2018-08-06T17:41:05.000+0000", "updated": "2018-08-06T17:41:05.000+0000" } ], "maxResults": 8, "total": 8, "startAt": 0 } } }