Markdownの変換結果にgoogle-code-prettifyを組込む

はじめに

google-code-prettifyはhtmlの中に記述したソースコードのシンタックスハイライトモジュールである.予約語やコメントの色を変えて表示してくれるので,ソースコードが見やすくなる.

また,このサイトはMarkdownを使って記述し,それをxhtmlに変換している.Markdownからxhtmlへの変換時に,ソースコードがあったら自動的にgoogle-code-prettifyを組込んでくれると便利だ.そんなことができるxsltを考えてみた.

環境は以下の通り.

なお,今回はオリジナルのMarkdownを使っているが,PHP MarkdownでもPHP Markdown Extraでも同じやり方で組込み可能である.

Markdownでの記述とxmlへの変換

Markdownでソースコードを記述するには,4文字以上の空白文字またはタブを行頭に記述する.ブロック要素ではない,インライン要素の場合はバッククオート(`)で文字列を囲む.今回はサンプルとして以下のようなファイルを用意した.

sam.md:

perl:

    #!/usr/bin/perl -w
    use strict;

    #comment
    print "hello\n"

インラインで書いてあるコード.例えば`print "hello"`.

コマンドライン(Cygwin版bash)から以下のように実行してxhtmlに変換する.なお,Markdownをインストールしたディレクトリを$markdown.homeと表記する.

${markdown.home}/Markdown.pl sam.md>sam.xml

こんな結果になる.

sam.xml:

<p>perl:</p>

<pre><code>#!/usr/bin/perl -w
use strict;

#comment
print "hello\n"
</code></pre>

<p>インラインで書いてあるコード.例えば<code>print "hello"</code>.</p>

ブロック要素のソースコードは<pre><code>,インライン要素のソースコードは<code>に変換される.

このままでは,整形式(well-formed)のxmlではないため,xsltでの変換ができない.そこで,ルート要素<root>を追加する.

sam.xml:

<root>
<p>perl:</p>

<pre><code>#!/usr/bin/perl -w
use strict;

#comment
print "hello\n"
</code></pre>

<p>インラインで書いてあるコード.例えば<code>print "hello"</code>.</p>
</root>

google-code-prettify組込み用xslt

google-code-prettifyを組込むには以下の事を行う必要がある.

  • prettify.jsとprettify.cssを読込む
  • body要素のonload属性にprettyPrint();を追記
  • ブロック要素<pre><code>の場合はpre要素のclass属性にprettyprintを指定
  • インライン要素<code>の場合はcode要素のclass属性にprettyprintを指定

prettify.js/prettify.cssの読込みとbody要素のonload属性の追記は対象のxmlの中にcode要素がある時だけ行えば良い.また,class属性にprettyprintを指定する場所は,ブロック要素ならpre要素,インライン要素ならcode要素の中である.このあたりに注意して作成したxsltは以下の通り.

sam.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:output
    method="xml"
    encoding="UTF-8"
    indent="no"/>

  <xsl:template match="root">
    <html>
      <head>
        <xsl:if test="//code"> <!-- code要素があったら -->
          <script type="text/javascript" src="prettify/prettify.js"></script>
          <link rel="stylesheet" type="text/css" href="prettify/prettify.css"/>
        </xsl:if>
      </head>
      <body>
        <xsl:if test="//code">
          <xsl:attribute name="onload">
            <xsl:text>prettyPrint();</xsl:text>
          </xsl:attribute>
        </xsl:if>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="pre[child::code]"> <!-- pre/codeだったらpreのclass属性にprettyprintを指定 -->
    <pre class="prettyprint">
      <xsl:apply-templates/>
    </pre>
  </xsl:template>

  <xsl:template match="code[not(parent::pre)]"> <!-- codeだったらcodeのclass属性にprettyprintを指定 -->
    <code class="prettyprint">
      <xsl:apply-templates/>
    </code>
  </xsl:template>

  <xsl:template match="*|@*|text()">
    <xsl:copy>
      <xsl:apply-templates select="*|@*|text()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

sam.xmlを以下のコマンドで変換する.

java -jar "${saxon_home}/saxon9.jar" -versionmsg:off -s:sam.xml -xsl:sam.xsl

実行結果:

<?xml version="1.0" encoding="UTF-8"?>
<html>
        <head>
            <script type="text/javascript" src="prettify/prettify.js"/>
            <link rel="stylesheet" type="text/css" href="prettify/prettify.css"/>
        </head>
        <body onload="prettyPrint();">
            <p>perl:</p>
<pre class="prettyprint"><code>#!/usr/bin/perl -w
use strict;

#comment
print "hello\n"
</code></pre>

<p>インラインで書いてあるコード.例えば<code class="prettyprint">print "hello"</code>.</p>
  </body>
    </html>

念のためソースコードのないxml(<pre>はあるが<code>はない)も変換してみよう.

sam2.xml

<root>
<p>perl:</p>

<pre>
code要素のないpre
</pre>

文字列.
</root>

sam2.xmlを以下のコマンドで変換する.

java -jar "${saxon_home}/saxon9.jar" -versionmsg:off -s:sam2.xml -xsl:sam.xsl

実行結果:

<?xml version="1.0" encoding="UTF-8"?>
    <html>
        <head/>
        <body>

  <p>perl:</p>

  <pre>
code要素のないpre
  </pre>
  文字列.
  </body>
    </html>

<code>がないのでgoogle-code-prettify関連の出力はない.