Hyphenopoly

repository·master·Indexed 20 days ago

https://github.com/mnater/hyphenopoly

A JavaScript polyfill for HTML hyphenation and a Node.js module for text processing. It provides hyphenation for browsers lacking native CSS hyphenation support or specific language support using WebAssembly modules. Features include a loader for early execution, configurable language requirements, exception handling, and support for custom hyphenation characters.

Tokens
17.7K
Snippets
56
Records
68
Agent score
72%

What's inside hyphenopoly

  1. Configure hyphenopoly in synchronous mode

    master

    If your codebase cannot handle asynchronous code, you can enable synchronous mode by setting "sync": true in the configuration object.

    When sync is true:

    1. hyphenopoly.config() returns a Map of hyphenator functions directly (instead of a Map of Promises).
    2. You must provide a loaderSync function instead of a loader function.
    3. Note that loaderSync cannot use inherently asynchronous methods like fetch or https.
    import hyphenopoly from "hyphenopoly";
    import {readFileSync} from "node:fs";
    
    function loaderSync(file, patDir) {
        return readFileSync(new URL(file, patDir));
    }
    
    const hyphenator = hyphenopoly.config({
        "exceptions": {
            "en-us": "en-han-ces"
        },
        "hyphen": "•",
        loaderSync,
        "require": ["de", "en-us"],
        "sync": true
    });
    
    // hyphenator is a Map of functions
    const hy1 = hyphenator.get("en-us")("hyphenation enhances justification.");
    const hy2 = hyphenator.get("de")("Silbentrennung verbessert den Blocksatz.");
    
    console.log(hy1);
    console.log(hy2);
  2. Use the tearDown event for native hyphenation fallbacks

    master
    The tearDown event is fired by Hyphenopoly_Loader.js when it decides NOT to load Hyphenopoly.js (usually because native CSS hyphenation is already available). This event occurs before the global Hyphenopoly object is deleted. You can use this event to trigger alternative scripts or logic that should only run if Hyphenopoly is not being used.
  3. Traversing the Succinct Trie using rank1 and select0

    master

    To navigate the Succinct Trie without expanding it, the implementation relies on two fundamental bitwise operations:

    1. rank1(bitmap, position): Returns the number of set bits (1) up to a specific position in the bitmap.
    2. select0(bitmap, count): Returns the smallest position such that the number of unset bits (0) up to that position equals count. It also returns the number of set bits following that position.

    Pattern Lookup Algorithm

    To find a pattern (e.g., ba) within the Trie:

    1. Start with the first character (b).
    2. Find the first 0 in the bitmap to identify the current node's scope.
    3. Count the following 1s to determine the number of child nodes.
    4. Iterate through the child nodes by checking if the character in sTrie chars matches the target character. If no match is found, the pattern does not exist.
    5. If sTrie hasVal is 1 for the current node, extract the hyphenation values using rank1 and select0 on the values bitmask.
    6. Use the number of the child node and the position of the current 0 to find the next 0 in the bitmap.
    7. Repeat for the next character in the pattern.
    // Example of rank1 and select0 behavior
    // bitmap: 101101011101100100000
    
    rank1(101101011101100100000, 3) -> 3
    select0(101101011101100100000, 3) -> 6, 3
  4. How the Succinct Trie data structure works in Hyphenopoly

    master

    Hyphenopoly uses a Succinct Trie to store hyphenation patterns. Unlike traditional approaches that require expanding patterns into a large in-memory Trie (which can consume significant RAM), a Succinct Trie allows the algorithm to run directly on the compressed data. This reduces memory usage to approximately 10% of traditional methods.

    Core Components

    The structure is composed of several bitstreams and arrays:

    • sTrie bitmap: A bitmask representing the tree structure. For each node, it stores the number of children by setting a sequence of 1s followed by a 0.
    • sTrie chars: An array of characters representing the labels of the nodes in the Trie.
    • sTrie hasVal: A bitmask indicating whether a specific node contains hyphenation values (split points).
    • Compressed Hyphenation Values: A separate structure for the actual split points (digits from the TeX patterns). These are compressed by:
      1. Calculating the offset of leading zeros.
      2. Storing values in a compact format where two values (each < 16) fit into a single byte.
      3. Storing the pattern length in a bitmask.

    Advantages and Trade-offs

    • Pros: Extremely low memory footprint; no expensive expansion phase required; near information-theoretic minimum storage.
    • Cons: Slightly larger .wasm file size due to the more complex logic required to traverse the bitstreams; slightly more complex code for pattern lookup.
  5. Access Hyphenators via Hyphenopoly.hyphenators

    master

    Hyphenopoly exposes a hyphenators object containing Promises for different hyphenation tasks. Hyphenopoly_Loader.js populates this object based on your configuration.

    There are two types of hyphenators available:

    1. Language-specific hyphenators: Accessible via Hyphenopoly.hyphenators[<lang>]. These resolve to functions that hyphenate strings for a specific language.
    2. The polyglot HTML-hyphenator: Accessible via Hyphenopoly.hyphenators.HTML. This resolves to a function that hyphenates DOM elements (HTMLElement) using all loaded languages.

    Note that these are Promises. You must use .then() or await to access the actual hyphenation functions.

    // Accessing the hyphenators object
    console.log(Hyphenopoly.hyphenators); 
    // Output example: { en-us: Promise, HTML: Promise }
  6. Debug hyphenation opportunities using a visible character

    master

    To identify where Hyphenopoly is attempting to hyphenate words, you can configure it to use a visible character (like a bullet point) instead of a soft hyphen. This is useful for proofreading and seeing all hyphenation opportunities in your text.

    Use the hyphen property within a selector's configuration in Hyphenopoly.config() to specify the character.

    Hyphenopoly.config({
        require: [...],
        setup: {
            selectors: {
                ".hyphenate": {
                    hyphen: "•"
                }
            }
        }
    });
  7. Handle Hyphenopoly events via configuration

    master

    You can intercept and respond to Hyphenopoly's lifecycle events by providing a handleEvent object within the Hyphenopoly.config() call. Each key in the handleEvent object corresponds to an event name, and its value should be a function that executes when that event is fired.

    Note that many of these events are cancellable; calling e.preventDefault() within your handler can stop the default behavior associated with that event.

    Hyphenopoly.config({
        require: {
            //[...]
        },
        handleEvent: {
            "hyphenopolyEnd": function () {
                console.log("Hyphenopoly ended");
            }
        }
    });
  8. Fix hyphenation errors in the text using soft hyphens

    master

    If you want to manually override automatic hyphenation for a specific instance of a word, you can insert a soft hyphen (&shy;) directly into your HTML text. Hyphenopoly.js will respect these manual markers and will not attempt to apply its own automatic hyphenation to those words.

    Pros: Easy to implement. Cons: Must be repeated for every occurrence; does not benefit other users.

  9. Embed Hyphenopoly on a website

    master

    To use Hyphenopoly, first load Hyphenopoly_Loader.js. This script registers the global window.Hyphenopoly object. Then, call Hyphenopoly.config() to define your requirements and settings. Calling config() triggers the hyphenation process for the page.

    Note: The API changed in version 5. Ensure you are using the config() method.

    <script src="../Hyphenopoly_Loader.js"></script>
    <script>
        Hyphenopoly.config({
            require: {
                "en-us": "supercalifragilisticexpialidocious"
            }
        });
    </script>