import UnsupportedOperationException from '../UnsupportedOperationException';
|
import CharacterSetECI from '../common/CharacterSetECI';
|
/**
|
* Responsible for en/decoding strings.
|
*/
|
export default class StringEncoding {
|
/**
|
* Decodes some Uint8Array to a string format.
|
*/
|
static decode(bytes, encoding) {
|
const encodingName = this.encodingName(encoding);
|
if (this.customDecoder) {
|
return this.customDecoder(bytes, encodingName);
|
}
|
// Increases browser support.
|
if (typeof TextDecoder === 'undefined' || this.shouldDecodeOnFallback(encodingName)) {
|
return this.decodeFallback(bytes, encodingName);
|
}
|
return new TextDecoder(encodingName).decode(bytes);
|
}
|
/**
|
* Checks if the decoding method should use the fallback for decoding
|
* once Node TextDecoder doesn't support all encoding formats.
|
*
|
* @param encodingName
|
*/
|
static shouldDecodeOnFallback(encodingName) {
|
return !StringEncoding.isBrowser() && encodingName === 'ISO-8859-1';
|
}
|
/**
|
* Encodes some string into a Uint8Array.
|
*/
|
static encode(s, encoding) {
|
const encodingName = this.encodingName(encoding);
|
if (this.customEncoder) {
|
return this.customEncoder(s, encodingName);
|
}
|
// Increases browser support.
|
if (typeof TextEncoder === 'undefined') {
|
return this.encodeFallback(s);
|
}
|
// TextEncoder only encodes to UTF8 by default as specified by encoding.spec.whatwg.org
|
return new TextEncoder().encode(s);
|
}
|
static isBrowser() {
|
return (typeof window !== 'undefined' && {}.toString.call(window) === '[object Window]');
|
}
|
/**
|
* Returns the string value from some encoding character set.
|
*/
|
static encodingName(encoding) {
|
return typeof encoding === 'string'
|
? encoding
|
: encoding.getName();
|
}
|
/**
|
* Returns character set from some encoding character set.
|
*/
|
static encodingCharacterSet(encoding) {
|
if (encoding instanceof CharacterSetECI) {
|
return encoding;
|
}
|
return CharacterSetECI.getCharacterSetECIByName(encoding);
|
}
|
/**
|
* Runs a fallback for the native decoding funcion.
|
*/
|
static decodeFallback(bytes, encoding) {
|
const characterSet = this.encodingCharacterSet(encoding);
|
if (StringEncoding.isDecodeFallbackSupported(characterSet)) {
|
let s = '';
|
for (let i = 0, length = bytes.length; i < length; i++) {
|
let h = bytes[i].toString(16);
|
if (h.length < 2) {
|
h = '0' + h;
|
}
|
s += '%' + h;
|
}
|
return decodeURIComponent(s);
|
}
|
if (characterSet.equals(CharacterSetECI.UnicodeBigUnmarked)) {
|
return String.fromCharCode.apply(null, new Uint16Array(bytes.buffer));
|
}
|
throw new UnsupportedOperationException(`Encoding ${this.encodingName(encoding)} not supported by fallback.`);
|
}
|
static isDecodeFallbackSupported(characterSet) {
|
return characterSet.equals(CharacterSetECI.UTF8) ||
|
characterSet.equals(CharacterSetECI.ISO8859_1) ||
|
characterSet.equals(CharacterSetECI.ASCII);
|
}
|
/**
|
* Runs a fallback for the native encoding funcion.
|
*
|
* @see https://stackoverflow.com/a/17192845/4367683
|
*/
|
static encodeFallback(s) {
|
const encodedURIstring = btoa(unescape(encodeURIComponent(s)));
|
const charList = encodedURIstring.split('');
|
const uintArray = [];
|
for (let i = 0; i < charList.length; i++) {
|
uintArray.push(charList[i].charCodeAt(0));
|
}
|
return new Uint8Array(uintArray);
|
}
|
}
|