Forum How do I...?

Errors when trying Prism and the latest version of Highlight.js

elrond25
I found several issues in the Prince JavaScript engine (dom.js) when attempting to run standard syntax highlighting libraries, and to share the extensive workaround required to get it functioning.

Older versions of Highlight.js used to work, but since Version 10 they have moved exclusively to ES6 and dropped support for IE11 and older versions of browsers.

Environment:

  • Tested Versions: Prince 16.2 and the latest Alpha 20260225 (macOS Apple Silicon)
  • Pipeline: HTML-> XHTML -> PrinceXML
  • Libraries Tested: Prism.js, Highlight.js (v11)

There are three distinct failure points in how Prince handles modern JavaScript and DOM manipulation:

1. Lack of ES6 Support (Prince 16.2)

Attempting to run a modern bundle of Highlight.js (v11) in Prince 16.2 immediately fails with a SyntaxError: unexpected token keyword("class"). To get the library to load, it must be manually transpiled down to ES5 using Babel. (Note: The Alpha version handles the class keyword successfully, which is a welcome improvement).

2. Incomplete DOM API Polyfills (undefined value is not an object)

Even when transpiled to ES5 (or when running native ES6 in the Alpha), standard API calls like hljs.highlightElement(el) or Prism's auto-loader crash the layout engine.

Libraries like Highlight.js rely on standard HTML5 DOM properties (specifically element.parentNode, element.classList, and element.dataset) to inspect the tree before highlighting. Because the Prince dom.js simulator leaves many of these properties undefined, the libraries throw fatal TypeError or undefined value is not an object exceptions during the node inspection phase.

3. Character Duplication via innerHTML ("Ghost Nodes")

If you attempt to manipulate the DOM manually to bypass the library's internal DOM methods, Prince struggles to serialize the updates cleanly. Using el.innerHTML = newTokens frequently results in duplicated characters in the final PDF (for example, 404 becomes 4044). This suggests the layout engine is rendering both the original text node and the newly injected HTML simultaneously.

The Workaround

To get syntax highlighting working without crashing the engine or duplicating text, we had to abandon standard DOM manipulation entirely and build a custom string-to-HTML bypass:

  • Transpile: Run Highlight.js through Babel to strip ES6 features for v16.2 compatibility.
  • Bypass the DOM: Extract el.textContent, run it through the raw string parser hljs.highlight(text).value, and avoid passing HTML elements to the library entirely.
  • Manual Node Purge: Forcefully empty the target element before setting the innerHTML to prevent Prince from rendering the ghost characters.

// Workaround snippet
var cleanText = el.textContent.replace(/\u00A0/g, ' ');
var result = hljs.highlight(cleanText, { language: lang }).value;

// Forcefully clear the DOM to prevent character duplication
while (el.firstChild) {
  el.removeChild(el.firstChild);
}
el.innerHTML = result;


Questions for the Development Team

Is there a timeline for bringing the stable release's JavaScript engine up to modern ES2015+ standards so build-step transpilation is no longer required? Many of  the libraries that used to work with Prince out of the box (like version 9 of Highlight.js) have no moved to an ESM-only packaging system so they will no longer work.

Are there plans to fully implement missing DOM tree properties (like classList and dataset) in dom.js so standard libraries do not crash when inspecting elements?