Prism-JS Plug-in

This is a syntax highlighting DITA-OT Plug-in which integrates the flexible PrismJS highlighting library into the DITA Open Toolkit engine. This enables the generation of documents including code snippets which are automatically colorized according to language syntax. The plug-in extends both static HTML and PDF transtypes.

Background

Prism is a lightweight, extensible syntax highlighter, built with modern web standards in mind. It’s a spin-off project from Dabblet.

  • Highlights embedded languages (e.g. CSS inside HTML, JavaScript inside HTML)
  • Highlights inline code (<codeph>) as well, not just code blocks (<codeblock>)
  • Highlights nested languages (CSS in HTML, JavaScript in HTML)
  • It doesn’t force you to use any Prism-specific markup

You can learn more on http://prismjs.com/.

Why another syntax highlighter?.

Install

This plug-in requires the presence of the fox.jason.extend.css to function correctly. It also needs Node.js running on user's machine to avoid deprecation errors.

Run the plug-in installation commands:

dita install https://github.com/jason-fox/fox.jason.extend.css/archive/master.zip
dita install https://github.com/jason-fox/fox.jason.prismjs/archive/master.zip

The dita command line tool requires no additional configuration.

Installing Node.js

Node.js is an open-source, cross-platform, back-end JavaScript runtime environment that runs on the V8 engine and executes JavaScript code outside a web browser

Due to the deprecation and removal of the Nashorn Engine in JDK11-14 JEP 335 any plug-in using JavaScript within <script> or <scriptdef> ANT tasks will start throwing warnings with Java 11 onwards and above. From Java 15 onwards, these plugins will no longer work.

The DITA-OT Prism-JS syntax highlighter relies heavily the Prism-JS JavaScript library, and therefore has been updated to run using Node.js where present on a user’s machine. Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine.

To download a copy follow the instructions on the Install Page.

Usage

To highlight the syntax within codeblocks, add an @outputclass attribute to any <codeph> or <codeblock> elements in your *.dita files. Alternatively add an @outputclass attribute to the <body> element, and all <codeph> or <codeblock> elements will inherit from it.

With the default Prism-JS library the following languages can be highlighted

  • @outputclass="language-markup" - HTML, XML etc.
  • @outputclass="language-css" - Cascading Style Sheet highlighting
  • @outputclass="language-clike" - C-language family highlighting
  • @outputclass="language-javascript" - JavaScript highlighting …etc.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE topic PUBLIC "-//OASIS//DTD DITA Topic//EN" "topic.dtd">
<topic id="examples">
  <title>Examples</title>
  <body  outputclass="language-markup">

  <p>The Prism source, highlighted with Prism:</p>
  <codeblock outputclass="language-javascript">
    <coderef href="../src/prism.js"/>
  </codeblock>

  <p>This page’s CSS code, highlighted with Prism:</p>
  <codeblock outputclass="language-css">
    <coderef href="../src/style.css"/>
  </codeblock>

  <p>This page’s HTML, highlighted with Prism:</p>
  <codeblock outputclass="language-html">
    <coderef href="../src/index.html"/>
  </codeblock>

  <p>This page’s logo (SVG), highlighted with Prism:</p>
  <codeblock outputclass="language-markup">
    <coderef href="../src/logo.svg"/>
  </codeblock>
  </body>
</topic>
Figure 1. Usage

A test document including HTML, CSS and JavaScript code snippets can be found within the plug-in at: PATH_TO_DITA_OT/plugins/fox.jason.prismjs/sample.

Invocation from the command line

The Plug-in extends the existing PDF and HTML transforms.

  • to create a PDF with highlighted code snippets run:
dita --format pdf \
    --input document.ditamap \
    --output out
Figure 2. PDF Output
  • to create static HTML with highlighted code snippets run:
dita --format html5 \
    --input document.ditamap \
    --output out
Figure 3. HTML Output

Line numbering

To display line numbers in codeblocks in HTML and PDF, add the @ outputclass="show-line-numbers" to the <codeblock>

<codeblock outputclass="language-javascript show-line-numbers">
  <coderef href="../src/prism.js"/>
</codeblock>
Figure 4. Line numbering
function jsonFetch(json, filename) {
  return new Promise(function (resolve, reject) {
    if (json) {
      return resolve(json);
    }

    fetch(JSON_PATH + filename)
      .then((response) => {
        if (!response.ok) {
          return reject(new Error(`HTTP error, status = ${response.status}`));
        }
        return response.json();
      })
      .then((data) => {
        return resolve(data);
      });
  });
}

Parameter Reference

  • prism.default - Specifies the default Prism language
  • prism.use.theme - Specifies which of three included themes to use.
    • default - default Prism.js theme
    • solarized - theme based on solarized colors
    • bootstrap - theme based on Bootstrap 5 colors
  • prism.css.theme - Specifies the location of a custom color theme file relative to the output directory.

Customizing the output

Prism-JS is easily extended to other languages since it purely relies on regular expressions. Additional languages are loaded dynamically during processing. A large number of additional languages are supported - just look at the list on https://github.com/PrismJS/prism/tree/master/components.

Extend with an additional plug-in which overrides the default prismjs.css.file property and amend a copy of the resource/style.css file to alter the look-and-feel of the rendered HTML.

<plugin id="com.example.prismjs-theme">
  <feature extension="ant.import" file="theme.xml"/>
  <require plugin="fox.jason.extend.css"/>
  <require plugin="fox.jason.prismjs"/>
  <feature extension="extend.css.process.pre" value="prismjs.override.css"/>
</plugin>
Figure 5. Plugin.xml
<project name="com.example.prismjs-theme">
  <target name="prismjs.override.css">
    <property name="prismjs.css.file" value="${dita.plugin.com.example.prismjs-theme.dir}/resource/style.css"/>
  </target>
</project>
Figure 6. ANT Target

A working example can be found in the Dark Theme CSS DITA-OT plug-in.

The cfg/fo/attrs/prismjs-attr.xsl provides the colors for the PDF output. The names of the attributes match the CSS file, copy and amend the prismjs-attr.xsl file in your own plug-in.

<plugin id="com.example.prismjs-theme">
  <require plugin="fox.jason.prismjs"/>
  <feature extension="dita.xsl.xslfo" value="xsl/xslfo.xsl" type="file"/>
</plugin>
Figure 7. Plugin.xml

Override the <xsl:template match="*[contains(@class,' topic/ph ') and contains(@outputclass, 'token')]"> template as shown:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:fo="http://www.w3.org/1999/XSL/Format"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:param name="PRISM-THEME" select="'com.example.prismjs-theme'"/>

    <xsl:include href="../cfg/fo/attrs/prismjs-attr.xsl"/>

    <xsl:template match="*[contains(@class,' topic/ph ') and contains(@outputclass, 'token')]">
      <fo:inline xsl:use-attribute-sets="__codeph__language__">
        <xsl:call-template name="commonattributes"/>
        <xsl:call-template name="processPrismAttrSetReflection">
          <xsl:with-param name="attrSet"
            select="replace(@outputclass,'token ','__token__')"/>
          <xsl:with-param name="path" select="concat('../../', concat($PRISM-THEME, '/cfg/fo/attrs/prismjs-attr.xsl'))"/>
        </xsl:call-template>
        <xsl:apply-templates/>
      </fo:inline>
  </xsl:template>
</xsl:stylesheet>
Figure 8. xslfo.xsl

A working example can be found in the Dark Theme CSS DITA-OT plug-in.