Please bookmark this page to avoid losing your text tool!

Text To ISO 8859 Encoding Converter

(Free & No Login Required)
Output will appear here...
You can edit the below JavaScript code to customize the text tool.
// Store the promise for loading the text-encoding polyfill to ensure it's loaded only once.
let textEncodingPolyfillLoaderPromise = null;

async function processText(originalText, encodingName = "iso-8859-1", onUnrepresentable = "replace") {
    // onUnrepresentable: "replace" (use substitution char) or "error" (throw an error)

    // Helper function for manual encoding of ISO-8859-1 and ISO-8859-15
    // This is used as a fallback if the polyfill fails or isn't used.
    function _manualEncodeHelper(text, encName, onUnrepresentableBehavior) {
        let outputBytes = [];
        const lcEncName = encName.toLowerCase();
        
        // Standard replacement byte for unrepresentable characters is 0x3F ('?')
        // This character is representable in all ISO-8859 variants.
        const replacementByte = 0x3F; 

        if (lcEncName === "iso-8859-1") {
            for (let i = 0; i < text.length; i++) {
                const charCode = text.charCodeAt(i); // Gets UTF-16 code unit
                if (charCode <= 0xFF) { // ISO-8859-1 maps U+0000 to U+00FF directly to bytes 0x00-0xFF
                    outputBytes.push(charCode);
                } else {
                    if (onUnrepresentableBehavior === "error") {
                        throw new Error(`Unrepresentable character U+${charCode.toString(16).padStart(4, '0')} ('${text[i]}') at index ${i} for ${lcEncName}.`);
                    }
                    outputBytes.push(replacementByte);
                }
            }
        } else if (lcEncName === "iso-8859-15") {
            // ISO-8859-15 is similar to ISO-8859-1 but replaces 8 characters to include Euro and others.
            const mapUnicodeToByte15 = {
                0x20AC: 0xA4, // Euro sign €
                0x0160: 0xA6, // Š (Latin S with caron)
                0x0161: 0xA8, // š (Latin s with caron)
                0x017D: 0xB4, // Ž (Latin Z with caron)
                0x017E: 0xB8, // ž (Latin z with caron)
                0x0152: 0xBC, // Œ (Latin OE ligature)
                0x0153: 0xBD, // œ (Latin oe ligature)
                0x0178: 0xBE  // Ÿ (Latin Y with diaeresis)
            };
            // Code points in ISO-8859-1 that are DIFFERENT in ISO-8859-15 (their original symbols are lost)
            const replacedOriginalCharsIn15 = [0xA4, 0xA6, 0xA8, 0xB4, 0xB8, 0xBC, 0xBD, 0xBE];

            for (let i = 0; i < text.length; i++) {
                const charCode = text.charCodeAt(i);
                if (mapUnicodeToByte15.hasOwnProperty(charCode)) {
                    outputBytes.push(mapUnicodeToByte15[charCode]);
                } else if (charCode <= 0xFF) {
                    // If this charCode was one of the positions reassigned in ISO-8859-15
                    if (replacedOriginalCharsIn15.includes(charCode)) {
                        if (onUnrepresentableBehavior === "error") {
                            throw new Error(`Unrepresentable character U+${charCode.toString(16).padStart(4, '0')} ('${text[i]}') at index ${i}. Its position in ISO-8859-1 is occupied by a different character in ${lcEncName}.`);
                        }
                        outputBytes.push(replacementByte); 
                    } else {
                        outputBytes.push(charCode); // Standard ISO-8859-1 mapping for other chars
                    }
                } else { // Character > U+00FF and not in the special map
                    if (onUnrepresentableBehavior === "error") {
                        throw new Error(`Unrepresentable character U+${charCode.toString(16).padStart(4, '0')} ('${text[i]}') at index ${i} for ${lcEncName}.`);
                    }
                    outputBytes.push(replacementByte);
                }
            }
        } else {
            return null; // Manual encoding not supported for this encoding type
        }

        let hexString = "";
        for (const byte of outputBytes) {
            hexString += byte.toString(16).padStart(2, '0');
        }
        return `Hex (${lcEncName}, manual fallback): ${hexString.toUpperCase()}`;
    }
    // --- End of _manualEncodeHelper ---

    const lcEncodingName = encodingName.toLowerCase();
    const fatalMode = (onUnrepresentable === "error");

    // Handle UTF-8 separately as native TextEncoder always supports it and is efficient.
    if (lcEncodingName === "utf-8" || lcEncodingName === "utf8") {
        try {
            const encoder = new TextEncoder(); // Native encoder is always UTF-8
            const byteArray = encoder.encode(originalText);
            let hexString = "";
            for (const byte of byteArray) hexString += byte.toString(16).padStart(2, '0');
            return `Hex (UTF-8): ${hexString.toUpperCase()}`;
        } catch (e) {
            // Should not happen for UTF-8, but as a safeguard.
            return `Error encoding to UTF-8: ${e.message}`;
        }
    }

    // Attempt 1: Use global TextEncoder. This works if a polyfill is already loaded and active.
    // Native TextEncoder only supports UTF-8 and will throw RangeError for other labels.
    try {
        const encoder = new window.TextEncoder(lcEncodingName, { fatal: fatalMode });
        const byteArray = encoder.encode(originalText);
        let hexString = "";
        for (const byte of byteArray) hexString += byte.toString(16).padStart(2, '0');
        // Succeeded with an existing TextEncoder (likely polyfilled)
        return `Hex (${encoder.encoding}): ${hexString.toUpperCase()}`;
    } catch (e) {
        // If fatalMode=true, this error could be a TypeError from the polyfill for an unmappable character.
        // The 'text-encoding' polyfill throws TypeError messages like "Invalid character" or "character U+XXXX can't be encoded"
        if (fatalMode && e.name === 'TypeError' && e.message.toLowerCase().includes('can\'t be encoded') || e.message.toLowerCase().includes('invalid character')) {
             return `Error: Text contains characters not representable in ${lcEncodingName}. (Polyfill Error: ${e.message})`;
        }
        // If not a fatal error type, or if it's a RangeError (from native TextEncoder), proceed to load polyfill.
        // Other errors might also occur if window.TextEncoder is undefined or not a constructor.
    }

    // Attempt 2: Dynamically load the 'text-encoding' polyfill if not already attempted.
    // This polyfill extends TextEncoder and TextDecoder to support many encodings.
    if (!textEncodingPolyfillLoaderPromise) {
        textEncodingPolyfillLoaderPromise = new Promise((resolve, reject) => {
            // Check if polyfill was somehow loaded between the check above and here (unlikely but possible in complex apps)
             try {
                new window.TextEncoder(lcEncodingName, { fatal: false }); // Test instantiation
                // If this doesn't throw, polyfill is present and supports encoding (or lcEncodingName is UTF-8 - handled)
                // This path means it's likely already loaded and working.
                resolve(); 
                return;
            } catch (e) {
                // Expected if polyfill not loaded or doesn't support encoding: proceed to load script
            }

            const script = document.createElement('script');
            // Use a reliable CDN for the polyfill.
            script.src = 'https://cdn.jsdelivr.net/npm/text-encoding@0.7.0/lib/encoding.js';
            script.onload = () => {
                // After script load, verify polyfill is active and TextEncoder supports non-UTF-8.
                try {
                    // Test with a common non-UTF-8 encoding. This helps confirm polyfill is working.
                    new window.TextEncoder('iso-8859-1', { fatal: false }); 
                    resolve();
                } catch (err) {
                    reject(new Error('Polyfill loaded, but TextEncoder still seems to be native UTF-8 only or faulty.'));
                }
            };
            script.onerror = () => {
                textEncodingPolyfillLoaderPromise = null; // Allow retry if script fails to load
                reject(new Error('Failed to load text-encoding polyfill from CDN. Check network connection and Content Security Policy.'));
            };
            document.head.appendChild(script);
        });
    }

    try {
        await textEncodingPolyfillLoaderPromise;
        // Polyfill should be loaded. Attempt encoding again.
        const encoder = new window.TextEncoder(lcEncodingName, { fatal: fatalMode });
        const byteArray = encoder.encode(originalText);
        let hexString = "";
        for (const byte of byteArray) hexString += byte.toString(16).padStart(2, '0');
        return `Hex (${encoder.encoding}, via polyfill): ${hexString.toUpperCase()}`;
    } catch (e) {
        // This catch handles errors after polyfill loading attempt:
        // 1. Polyfill failed to load (promise rejected, error already includes this).
        // 2. Polyfill loaded, but lcEncodingName is not supported by it (e.g., `encoder.encoding` might be different).
        // 3. Polyfill loaded, fatal=true, and unrepresentable char encountered (TypeError).
        if (fatalMode && e.name === 'TypeError' && (e.message.toLowerCase().includes('can\'t be encoded') || e.message.toLowerCase().includes('invalid character'))) {
             return `Error: Text contains characters not representable in ${lcEncodingName}. (Polyfill Error After Load: ${e.message})`;
        }
        // If error is not a fatal encoding error, try manual fallback or return a generic error.
        // console.warn(`Encoding with polyfill for '${lcEncodingName}' failed: ${e.message}`); // For debugging
    }
    
    // Attempt 3: Manual encoding for ISO-8859-1 and ISO-8859-15 as a final resort.
    try {
        const manualResult = _manualEncodeHelper(originalText, lcEncodingName, onUnrepresentable);
        if (manualResult !== null) {
            return manualResult;
        }
    } catch (e) { // This catches errors specifically from _manualEncodeHelper (if onUnrepresentable="error")
        return `Error: ${e.message}`;
    }
    
    // If all strategies fail (e.g. polyfill load error, or encoding not supported by polyfill and not manual):
    let finalErrorMessage = `Error: Could not encode text to '${encodingName}'.`;
    if (textEncodingPolyfillLoaderPromise) {
        try {
            // Check if polyfill promise had resolved or rejected:
            await textEncodingPolyfillLoaderPromise;
            // If promise resolved, but we're here, it means polyfill loaded but didn't support the encoding or errored.
            finalErrorMessage += ` The text-encoding polyfill was loaded, but '${lcEncodingName}' might not be supported by it or another error occurred.`;
        } catch (polyfillLoadError) {
            // If promise rejected (e.g. script failed to load).
            finalErrorMessage += ` ${polyfillLoadError.message}`;
        }
    } else {
        // Should not be reached if lcEncodingName is not UTF-8, as polyfillPromise would be initialized.
        finalErrorMessage += ` Native TextEncoder failed and polyfill was not loaded.`;
    }
    return finalErrorMessage;
}

Free Text Tool Creator

Can't find the text tool you're looking for?
Create a text tool based on your own idea now!

Description

The Text To ISO 8859 Encoding Converter is a tool designed to convert text into ISO 8859-1 or ISO 8859-15 encoding formats. It handles text transformation and encodes characters into a hexadecimal representation while allowing users to specify how to handle unrepresentable characters, with options for replacement or error notification. This tool can be particularly useful for developers and content creators who need to ensure that their text is encoded in specific legacy formats for compatibility with older systems or software that rely on these encodings. Typical use cases include preparing text for email, web pages, or applications that do not support UTF-8 encoding.

Leave a Reply

Your email address will not be published. Required fields are marked *