/** * Vietnamese text encoding utilities */ /** * Remove Vietnamese tones to ASCII */ export function removeVietnameseTones(text: string): string { const accentsMap: Record = { // Chữ thường à: 'a', á: 'a', ả: 'a', ã: 'a', ạ: 'a', ă: 'a', ằ: 'a', ắ: 'a', ẳ: 'a', ẵ: 'a', ặ: 'a', â: 'a', ầ: 'a', ấ: 'a', ẩ: 'a', ẫ: 'a', ậ: 'a', đ: 'd', è: 'e', é: 'e', ẻ: 'e', ẽ: 'e', ẹ: 'e', ê: 'e', ề: 'e', ế: 'e', ể: 'e', ễ: 'e', ệ: 'e', ì: 'i', í: 'i', ỉ: 'i', ĩ: 'i', ị: 'i', ò: 'o', ó: 'o', ỏ: 'o', õ: 'o', ọ: 'o', ô: 'o', ồ: 'o', ố: 'o', ổ: 'o', ỗ: 'o', ộ: 'o', ơ: 'o', ờ: 'o', ớ: 'o', ở: 'o', ỡ: 'o', ợ: 'o', ù: 'u', ú: 'u', ủ: 'u', ũ: 'u', ụ: 'u', ư: 'u', ừ: 'u', ứ: 'u', ử: 'u', ữ: 'u', ự: 'u', ỳ: 'y', ý: 'y', ỷ: 'y', ỹ: 'y', ỵ: 'y', // Chữ hoa À: 'A', Á: 'A', Ả: 'A', Ã: 'A', Ạ: 'A', Ă: 'A', Ằ: 'A', Ắ: 'A', Ẳ: 'A', Ẵ: 'A', Ặ: 'A', Â: 'A', Ầ: 'A', Ấ: 'A', Ẩ: 'A', Ẫ: 'A', Ậ: 'A', Đ: 'D', È: 'E', É: 'E', Ẻ: 'E', Ẽ: 'E', Ẹ: 'E', Ê: 'E', Ề: 'E', Ế: 'E', Ể: 'E', Ễ: 'E', Ệ: 'E', Ì: 'I', Í: 'I', Ỉ: 'I', Ĩ: 'I', Ị: 'I', Ò: 'O', Ó: 'O', Ỏ: 'O', Õ: 'O', Ọ: 'O', Ô: 'O', Ồ: 'O', Ố: 'O', Ổ: 'O', Ỗ: 'O', Ộ: 'O', Ơ: 'O', Ờ: 'O', Ớ: 'O', Ở: 'O', Ỡ: 'O', Ợ: 'O', Ù: 'U', Ú: 'U', Ủ: 'U', Ũ: 'U', Ụ: 'U', Ư: 'U', Ừ: 'U', Ứ: 'U', Ử: 'U', Ữ: 'U', Ự: 'U', Ỳ: 'Y', Ý: 'Y', Ỷ: 'Y', Ỹ: 'Y', Ỵ: 'Y', } return text.replace(/[^\u0000-\u007F]/g, (char) => accentsMap[char] || char) } /** * Count non-ASCII characters */ function countNonAsciiChars(s: string): number { let count = 0 for (let i = 0; i < s.length; i++) { const code = s.charCodeAt(i) if (code > 0x7f) count++ } return count } /** * Check if encoding lost data (converted to '?') */ export function isLossy(original: string, encoded: Uint8Array): boolean { const nonAscii = countNonAsciiChars(original) if (nonAscii === 0) return false let qm = 0 for (let i = 0; i < encoded.length; i++) if (encoded[i] === 0x3f) qm++ return qm >= Math.ceil(nonAscii * 0.5) } /** * Log encoding attempt for debugging */ export function logAttempt( label: string, encName: string, cp: number, data: Uint8Array, lossy: boolean, ): void { const preview = Array.from(data.slice(0, 16)) .map((b) => b.toString(16).padStart(2, '0').toUpperCase()) .join(' ') console.log( `[JS] ESCPOSUtils.encodeVietnamese - DEBUG: Attempt=${label} encoding=${encName} cp=${cp} len=${data.length} lossy=${lossy} bytes[0..15]=${preview}`, ) }