Home Reference Source

src/encode/EUCJPMS.js

/**
 * The script is part of MojiJS.
 * 
 * AUTHOR:
 *  natade (http://twitter.com/natadea)
 * 
 * LICENSE:
 *  The MIT license https://opensource.org/licenses/MIT
 */

import SJIS from "./SJIS.js";
import CP932 from "./CP932.js";

/**
 * eucJP-ms の変換マップ作成用クラス
 * @ignore
 */
class EUCJPMSMAP {

	/**
	 * 変換マップを初期化
	 */
	static init() {
		if(EUCJPMSMAP.is_initmap) {
			return;
		}
		EUCJPMSMAP.is_initmap = true;

		/**
		 * 変換マップ
		 * CP932のIBM拡張文字の一部は、eucJP-msのG3の83区から84区に配列されている。
		 * @type {Object<number, number>}
		 */
		const eucjpms_to_cp932_map = {
			0xf3f3: 0xfa40, 0xf3f4: 0xfa41, 0xf3f5: 0xfa42, 0xf3f6: 0xfa43, 0xf3f7: 0xfa44,
			0xf3f8: 0xfa45, 0xf3f9: 0xfa46, 0xf3fa: 0xfa47, 0xf3fb: 0xfa48, 0xf3fc: 0xfa49, 0xf3fd: 0x8754, 0xf3fe: 0x8755,
			0xf4a1: 0x8756, 0xf4a2: 0x8757, 0xf4a3: 0x8758, 0xf4a4: 0x8759, 0xf4a5: 0x875a, 0xf4a6: 0x875b, 0xf4a7: 0x875c,
			0xf4a8: 0x875d, 0xf4a9: 0xfa56, 0xf4aa: 0xfa57, 0xf4ab: 0x878a, 0xf4ac: 0x8782, 0xf4ad: 0x8784, 0xf4ae: 0xfa62, 0xf4af: 0xfa6a,
			0xf4b0: 0xfa7c, 0xf4b1: 0xfa83, 0xf4b2: 0xfa8a, 0xf4b3: 0xfa8b, 0xf4b4: 0xfa90, 0xf4b5: 0xfa92, 0xf4b6: 0xfa96, 0xf4b7: 0xfa9b,
			0xf4b8: 0xfa9c, 0xf4b9: 0xfa9d, 0xf4ba: 0xfaaa, 0xf4bb: 0xfaae, 0xf4bc: 0xfab0, 0xf4bd: 0xfab1, 0xf4be: 0xfaba, 0xf4bf: 0xfabd,
			0xf4c0: 0xfac1, 0xf4c1: 0xfacd, 0xf4c2: 0xfad0, 0xf4c3: 0xfad5, 0xf4c4: 0xfad8, 0xf4c5: 0xfae0, 0xf4c6: 0xfae5, 0xf4c7: 0xfae8,
			0xf4c8: 0xfaea, 0xf4c9: 0xfaee, 0xf4ca: 0xfaf2, 0xf4cb: 0xfb43, 0xf4cc: 0xfb44, 0xf4cd: 0xfb50, 0xf4ce: 0xfb58, 0xf4cf: 0xfb5e,
			0xf4d0: 0xfb6e, 0xf4d1: 0xfb70, 0xf4d2: 0xfb72, 0xf4d3: 0xfb75, 0xf4d4: 0xfb7c, 0xf4d5: 0xfb7d, 0xf4d6: 0xfb7e, 0xf4d7: 0xfb80,
			0xf4d8: 0xfb82, 0xf4d9: 0xfb85, 0xf4da: 0xfb86, 0xf4db: 0xfb89, 0xf4dc: 0xfb8d, 0xf4dd: 0xfb8e, 0xf4de: 0xfb92, 0xf4df: 0xfb94,
			0xf4e0: 0xfb9d, 0xf4e1: 0xfb9e, 0xf4e2: 0xfb9f, 0xf4e3: 0xfba0, 0xf4e4: 0xfba1, 0xf4e5: 0xfba9, 0xf4e6: 0xfbac, 0xf4e7: 0xfbae,
			0xf4e8: 0xfbb0, 0xf4e9: 0xfbb1, 0xf4ea: 0xfbb3, 0xf4eb: 0xfbb4, 0xf4ec: 0xfbb6, 0xf4ed: 0xfbb7, 0xf4ee: 0xfbb8, 0xf4ef: 0xfbd3,
			0xf4f0: 0xfbda, 0xf4f1: 0xfbe8, 0xf4f2: 0xfbe9, 0xf4f3: 0xfbea, 0xf4f4: 0xfbee, 0xf4f5: 0xfbf0, 0xf4f6: 0xfbf2, 0xf4f7: 0xfbf6,
			0xf4f8: 0xfbf7, 0xf4f9: 0xfbf9, 0xf4fa: 0xfbfa, 0xf4fb: 0xfbfc, 0xf4fc: 0xfc42, 0xf4fd: 0xfc49, 0xf4fe: 0xfc4b
		};

		/**
		 * @type {Object<number, number>}
		 */
		const cp932_to_eucjpms_map = {};
		
		for(const key in eucjpms_to_cp932_map) {
			const x = eucjpms_to_cp932_map[key];
			cp932_to_eucjpms_map[x] = parseInt(key, 10);
		}

		EUCJPMSMAP.cp932_to_eucjpms_map = cp932_to_eucjpms_map;
		EUCJPMSMAP.eucjpms_to_cp932_map = eucjpms_to_cp932_map;
	}
	
	/**
	 * @returns {Object<number, number>}
	 */
	static CP932_TO_EUCJPMS() {
		EUCJPMSMAP.init();
		return EUCJPMSMAP.cp932_to_eucjpms_map;
	}
	
	/**
	 * @returns {Object<number, number>}
	 */
	static EUCJPMS_TO_CP932() {
		EUCJPMSMAP.init();
		return EUCJPMSMAP.eucjpms_to_cp932_map;
	}

}

/**
 * 変換マップを初期化したかどうか
 * @type {boolean}
 */
EUCJPMSMAP.is_initmap = false;

/**
 * 変換用マップ
 * @type {Object<number, number>}
 */
EUCJPMSMAP.cp932_to_eucjpms_map = null;

/**
 * 変換用マップ
 * @type {Object<number, number>}
 */
EUCJPMSMAP.eucjpms_to_cp932_map = null;

/**
 * eucJP-ms を扱うクラス
 * @ignore
 */
export default class EUCJPMS {

	/**
	 * 文字列を eucJP-ms のバイナリ配列に変換
	 * - 日本語文字は2バイトとして、配列も2つ分、使用します。
	 * @param {String} text - 変換したいテキスト
	 * @returns {Array<number>} eucJP-ms のデータが入ったバイナリ配列
	 */
	static toEUCJPMSBinary(text) {
		const sjis_array = CP932.toCP932Array(text);
		const bin = [];
		const map = EUCJPMSMAP.CP932_TO_EUCJPMS();
		const SS2 = 0x8E; // C1制御文字 シングルシフト2
		const SS3 = 0x8F; // C1制御文字 シングルシフト3
		for(let i = 0; i < sjis_array.length; i++) {
			const code = sjis_array[i];
			const kuten = SJIS.toKuTenFromSJISCode(code);
			if(code < 0x80) {
				// G0 ASCII
				bin.push(code);
			}
			else if(code < 0xE0) {
				// G2 半角カタカナ
				bin.push(SS2);
				bin.push(code);
			}
			else {
				const eucjpms_code = map[code];
				if(!eucjpms_code) {
					// G1 
					bin.push(kuten.ku + 0xA0);
					bin.push(kuten.ten + 0xA0);
				}
				else {
					// シングルシフト SS3 で G3 を呼び出す。
					// G3 は、eucJP-ms の場合 IBM拡張文字 を表す。
					bin.push(SS3);
					bin.push(eucjpms_code >> 8);
					bin.push(eucjpms_code & 0xff);
				}
			}
		}
		return bin;
	}

	/**
	 * eucJP-ms の配列から文字列に変換
	 * @param {Array<number>} eucjp - 変換したいテキスト
	 * @returns {String} 変換後のテキスト
	 */
	static fromEUCJPMSBinary(eucjp) {
		const sjis_array = [];
		const ng = "?".charCodeAt(0);
		const map = EUCJPMSMAP.EUCJPMS_TO_CP932();
		const SS2 = 0x8E; // C1制御文字 シングルシフト2
		const SS3 = 0x8F; // C1制御文字 シングルシフト3
		for(let i = 0; i < eucjp.length; i++) {
			let x1, x2;
			x1 = eucjp[i];
			// ASCII
			if(x1 < 0x80) {
				sjis_array.push(x1);
				continue;
			}
			if(i >= eucjp.length - 1) {
				// 文字が足りない
				break;
			}
			{
				// 3バイト読み込み(G3)
				if(x1 === SS3) {
					// 文字が足りない
					if(i >= eucjp.length - 2) {
						break;
					}
					x1 = eucjp[i + 1];
					x2 = eucjp[i + 2];
					// シングルシフト SS3 で G3 を呼び出す。
					// G3 は、eucJP-ms の場合 IBM拡張文字 を表す。
					const nec_code = map[(x1 << 8 | x2)];
					if(nec_code) {
						sjis_array.push(nec_code);
					}
					else {
						sjis_array.push(ng);
					}
					i += 2;
					continue;
				}
				// 2バイト読み込み
				else {
					x2 = eucjp[i + 1];
					i += 1;
				}
			}
			// 半角カタカナ
			if(x1 === SS2) {
				sjis_array.push(x2);
				continue;
			}

			// 日本語
			if((0xA1 <= x1) && (x1 <= 0xFE) && (0xA1 <= x2) && (x2 <= 0xFE)) {
				const kuten = {
					ku : x1 - 0xA0,
					ten : x2 - 0xA0
				};
				sjis_array.push(SJIS.toSJISCodeFromKuTen(kuten));
			}
			else {
				sjis_array.push(ng);
			}
		}
		return CP932.fromCP932Array(sjis_array);
	}


}