Base64 encoding fails for non-latin characters - javascript

I'm working on a VueJS project where I generate SVG images based on input. After they have been generated, I send data from them to a back end API as a base64 encoded string.
Now, whenever I try to encode the string with UTF8 characters, I get the following error:
Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.
I tried to solve the issue with replacing all characters in the HTML with their Unicode code points. However, whenever I try to get the DOM element, the Unicode encoded characters are reverted back to their plain text format.
How can I obtain the HTML as a base64 encoded string?
The issue can be seen in a fiddle here.
I've tried both using XMLSerializer and just running innerHTML.toString():
let svgEl = this.$el.querySelector('.svg-container svg')
if (!svgEl) {
console.log('no poster element')
return
}
// Using XMLSerializer:
console.log(btoa(new XMLSerializer().serializeToString(posterEl))
// Using innerHTML:
console.log(btoa(posterEl.innerHTML.toString()))
Both the above examples yield the same error.
Thanks.

You can follow the answer steps in for this answered question:
Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.
The key of solution is to encode the string by this way:
btoa(unescape(encodeURIComponent(str)));

Related

Decoding Base64 String in Java

I'm using Java and I have a Base64 encoded string that I wish to decode and then do some operations to transform.
The correct decoded value is obtained in JavaScript through function atob(), but in java, using Base64.decodeBase64() I cannot get an equal value.
Example:
For:
String str = "AAAAAAAAAAAAAAAAAAAAAMaR+ySCU0Yzq+AV9pNCCOI="
With JavaScript atob(str) I get ->
"Æ‘û$‚SF3«àö“Bâ"
With Java new String(Base64.decodeBase64(str)) I get ->
"Æ?û$?SF3«à§ö?â"
Another way I could fixed the issue is to run JavaScript in Java with a Nashorn engine, but I'm getting an error near the "$" symbol.
Current Code:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
String script2 = "function decoMemo(memoStr){ print(atob(memoStr).split('')" +
".map((aChar) => `0${aChar.charCodeAt(0).toString(16)}`" +
".slice(-2)).join('').toUpperCase());}";
try {
engine.eval(script2);
Invocable inv = (Invocable) engine;
String returnValue = (String)inv.invokeFunction("decoMemo", memoTest );
System.out.print("\n result: " + returnValue);
} catch (ScriptException | NoSuchMethodException e1) {
e1.printStackTrace();
Any help would be appreciated. I search a lot of places but can't find the correct answer.
btoa is broken and shouldn't be used.
The problem is, bytes aren't characters. Base64 encoding does only one thing. It converts bytes to a stream of characters that survive just about any text-based transport mechanism. And Base64 decoding does that one thing in reverse, it converts such characters into bytes.
And the confusion is, you're printing those bytes as if they are characters. They are not.
You end up with the exact same bytes, but javascript and java disagree on how you're supposed to turn that into an ersatz string because you're trying to print it to a console. That's a mistake - bytes aren't characters. Thus, some sort of charset encoding is being used, and you don't want any of this, because these characters clearly aren't intended to be printed like that.
Javascript sort of half-equates characters and bytes and will freely convert one to the other, picking some random encoding. Oof. Javascript sucks in this regard, it is what it is. The MDN docs on btoa explains why you shouldn't use it. You're running into that problem.
Not entirely sure how you fix it in javascript - but perhaps you don't need it. Java is decoding the bytes perfectly well, as is javascript, but javascript then turns those bytes into characters into some silly fashion and that's causing the problem.
What you have there is not a text string at all. The giveaway is the AA's at the beginning. Those map to a number of zero bytes. That doesn't translate to meaningful text in any standard character set.
So what you have there is most likely binary data. Converting it to a string is not going to give you meaningful text.
Now to explain the difference you are seeing between Java and Javascript. It looks to me as if both Java and Javascript are making a "best effort" attempt to convert the binary data as if is was encoded in ISO-8859-1 (aka ISO LATIN-1).
The problem is some of the bytes codes are mapping to unassigned codes.
In the Java case those unassigned codes are being mapped to ?, either when the string is created or when it is being output.
In the Javascript case, either the unassigned codes are not included in the string, or them are being removed when you attempt to display them.
For the record, this is how an online base64 decoder the above for me:
����������������Æû$SF3«àöBâ
The unassigned codes are 0x91 0x82 and 0x93. 0x15 and 0x0B are non-printing control codes.
But the bottom line is that you should not be converting this data into a string in either Java or in Javascript. It should be treated as binary; i.e. an array of byte values.
byte[] data = Base64.getDecoder().decode(str);

Javascript encodeURI returns unexpected value

I have a problem URL-encoding a text with javascript.
I am in Germany, where we have these "Umlaute" (ÄÖÜ), and these letters make some problems.
An online encoder/decoder returned the following results for the word "Äpfel" (apples).
Äpfel >>> url-encode >>> %C3%84pfel
%C3%84pfel >>> url-decode >>> Äpfel
For testing, I created the following php.file (poc.php) with no php-content, just the javascript:
<script type="text/javascript">
var t = "Äpfel";
t = encodeURI(t);
alert(t);
t = decodeURI(t);
alert(t);
</script>
The first alert returns "%EF%BF%BDpfel", which differs from the result of the online encoder.
The second alert returns "�pfel" (yes, the diamond with the "?").
It seems that javascript cannot decode the text it just encoded.
I guess the cause of this behaviour is somewhere in the PHP settings. When I just rename the file from "poc.php" to "poc.html" the encoding is correct and the alerts return the same results as the online encoder/decoder.
When I check the current encoding, javascript and php return "utf-8".
In my "real" project I have a ".js" file included in my php-file (with the same problem).
<script type="text/javascript" src="scripts/functions.js"></script>
Has anybody an idea what causes this behaviour?
The weird byte stream %EF%BF%BD you're receiving is utf-8 version of the Unicode replacement character, that is, literally the � symbol.
The Javascript portion can url-decode the text it just url-encoded, it was just asked to encode the symbol for a missing symbol.
So: some part of your system is not using utf-8, but some other character set instead, and there's an unnecessary conversion done. My guess is that the file is encoded in latin-1, aka. ISO 8859-1, and PHP tries to read it as if it was UTF-8, converting the unrecognized character 0xc4 ('Ä' in latin-1) to the replacament character symbol.

Encoding in C# and Decoding in Javascript

I have encoded some text in C# like below:
var encodedCredential = Convert.ToBase64String(Encoding.Unicode.GetBytes(JsonConvert.SerializeObject("Sample text")));
The encoded String is :IgBTAGEAbQBwAGwAZQAgAHQAZQB4AHQAIgA=
I want to decode the encoded String in java script.
I have tried the below
decodeURIComponent(atob("IgBTAGEAbQBwAGwAZQAgAHQAZQB4AHQAIgA="))
decodeURIComponent(atob("IgBTAGEAbQBwAGwAZQAgAHQAZQB4AHQAIgA=").replace(' ',''))
The result is something different, There are some spaces in each letter. I cant even replace the spaces.
You need to use UTF-8 encoding in C#. Export base64 by this command
Convert.ToBase64String(Encoding.UTF8.GetBytes("Sample text"))
#King_Fisher, you shouldn't be getting additional spaces, also the replace method will replace a single occurrence.
Here's what I did with your code (see attached screenshot)

How to convert this text to correct HTML characters using Javascript

How to convert this text to correct HTML characters using Javascript:
'PingAsyncTask - Token v\ufffdlido'
Put in your console:
console.log('PingAsyncTask - Token v\ufffdlido');
I already try all common functions:
https://gist.github.com/chrisveness/bcb00eb717e6382c5608
http://monsur.hossa.in/2012/07/20/utf-8-in-javascript.html
http://jsfromhell.com/geral/utf-8
Can anyone help me?
If your document is already UTF-8 you don't need to do anything special. The string is already encoded correctly in JavaScript, so when you write it into the document it'll show up correctly. You can see it in this fiddle: https://jsfiddle.net/baar4ew8/
P.S. The character in your code (\ufffd) is U+FFFD, the Unicode replacement character. Most fonts render it as a black diamond with a question mark inside, or just an empty box. Here's how Stack Overflow renders it:
�
If you're seeing that in your output, your string is being rendered correctly.
If you think you should be seeing some other character, then your problem isn't in the HTML or JavaScript—it's with the source of your data, whatever that might be. When a program converts text from a non-Unicode encoding to a Unicode encoding like UTF-8, characters that don't exist in Unicode are replaced with U+FFFD (�)—hence "replacement character." If you're expecting some character that does exist in Unicode but you're getting U+FFFD then it might be the case that the program converting the text to UTF-8 doesn't know what encoding it was originally in and so converted it incorrectly. For example, if you stored text with encoding X in a database table with encoding Y without first converting it to encoding Y.

How to avoid the exception "Invalid hex characters in a String"

I'm using Javascript and Rhino (to do some Java codes). I want to execute the following code.
service.push(deviceToken,payload);
As the first string contains 'x' it gives the following exception.
com.sun.phobos.script.util.ExtendedScriptException: org.mozilla.javascript.WrappedException: Wrapped java.lang.RuntimeException: Invalid hex character: x
That string has to be there as it is. Therefore no replaces are possible.
I'm using Java-apns and I use this method to push notifications with the device token and the payload.
Your device token should contain only hexadecimal characters 0–9 , A, B, C, D, E, F (or alternatively a–f)

Categories

Resources