今までこのブログは HTML を直接書いていました。しかし、最近は Markdown でテキストを書くことが増え、HTML を直書きすることが億劫に感じます。そこで、Markdown から HTML に変換する仕組みに変えてみました。
あわせて、本ブログの文体も「ですます調」に切り替えてみます。
Pandoc の導入
Markdown から HTML ファイルを生成する仕組みとして Pandoc を使います。
Pandoc は各種文書フォーマットを相互変換するツールです。開発言語は Haskell。2006 年から開発が始まっており、2026-02-16 現在の最新バージョンは 3.9 です。
Pandoc のインストール
Pandoc は brew コマンドでインストールできます。
brew install pandoc
Pandoc の基本的な使い方
Pandoc の基本オプションは --from と --to
です。--from オプションで入力フォーマット指定します。今回は
markdown を選択します。--to
オプションは出力フォーマット指定です。出力フォーマットは
html5 を選びます。
出力先は --output オプションで指定できます。
利用例は次の通りです:
pandoc --from=markdown --to=html5 --output=entry.html entry.md
上記コマンドは、Markdown ファイル entry.md を HTML
に変換し、entry.html ファイルを作成します。
例えば entry.md が次のような中身だった場合
# Pandoc の導入
Markdown から HTML ファイルを生成する仕組みとして [Pandoc](https://pandoc.org/) を使います。
Pandoc は各種文書フォーマットを相互変換するツールです。...
## Pandoc のインストール
Pandoc は `brew` コマンドでインストールできます。
出力の entry.html は下記のようになります。
<h1 id="pandoc-の導入">Pandoc の導入</h1>
<p>Markdown から HTML ファイルを生成する仕組みとして <a
href="https://pandoc.org/">Pandoc</a> を使います。</p>
<p>Pandoc は各種文書フォーマットを相互変換するツールです。…</p>
<h2 id="pandoc-のインストール">Pandoc のインストール</h2>
<p>Pandoc は <code>brew</code> コマンドでインストールできます。</p>
markdown フォーマット
--from オプションに設定する markdown
について補足です。 Pandoc には Markdown を表すフォーマットが 8
つあります:
commonmark: CommonMarkcommanmark_x: 拡張付き CommonMarkgfm: GitHub-Flavored Markdownmarkdown_github: deprecated になったgfmmarkdown: Pandoc’s Markdownmarkdown_mmd: MultiMarkdownmarkdown_phpextra: PHP Markdown Extramarkdown_strict: オリジナルの (拡張なしの) Markdown
これらは Markdown の様々な方言に対応したものです。8 つのフォーマットのうち、どのフォーマットを使うのが適切でしょうか?
最もオリジナルに近い Markdown を使いたい場合は
markdown_strict を使います。
GitHub-Flavored Markdown で導入された Fenced Code Block
を使いたい場合は gfm
です。と言いたいところですが、gfm には罠があります。実は
gfm を指定すると、厳密に GitHub-Flavored Markdown
な記法に限定されてしまうのです。言い換えると、gfm は Pandoc
用の拡張を受け付けません。
私は markdown
フォーマットを選んでいます。markdown フォーマットは Pandoc
用の拡張をサポートした Markdown 用フォーマットです。HTML
に変換する時に、例えば class や id
を付与することが可能になります。先の Fenced Code Block も
markdown フォーマットはデフォルトで対応しています。
ブログ出力用のカスタマイズ
自動 ID 付与の抑制
Pandoc はヘッダーに自動で ID を振ってくれます。とても便利な機能ですが、ブログでは少し問題があります。というのは、ブログは複数の記事を 1 つのページに表示することがあるからです。
例えば、ブログの記事毎に「あとがき」なるヘッダーを入れたとします。この時
Pandoc は id="あとがき" という ID
を自動生成します。読者がその記事だけを読むのであれば良いでしょう。しかし、「月別アーカイブ」を開くとどうでしょう。「月別アーカイブ」の中にいくつもの
id="あとがき" が現れてしまいます。ID
が複数あると、<a href="#あとがき">あとがきへのリンク</a>
はどこへジャンプすれば良いのか分からなくなってしまいます。
Pandoc の自動 ID 付与機能をオフにするには、--format
オプションに追加オプション -auto_identifiers
を付けます。
pandoc --from=markdown-auto_identifiers --to=html5 entry.md > entry.html
以下、実行結果です:
<h1>Pandoc の導入</h1>
<p>Markdown から HTML ファイルを生成する仕組みとして <a
href="https://pandoc.org/">Pandoc</a> を使います。</p>
<p>Pandoc は各種文書フォーマットを相互変換するツールです。…</p>
<h2>Pandoc のインストール</h2>
<p>Pandoc は <code>brew</code> コマンドでインストールできます。</p>
ID の指定
ブログ記事を書いていると、ID を付けたいケースもあります。その場合、Pandoc 拡張を利用します。
# Pandoc の導入{#introduce-pandoc}
上記のように、# ヘッダー文字列{#id-text} という形で ID
を記入します。すると、次のように ID が振られます。
<h1 id="introduce-pandoc">Pandoc の導入</h1>
ヘディング・レベルの変更
Blogger では HTML のヘディング・レベルは次のようになっています。
h1: ブログのタイトルh2: 日付ヘッダーh3: 記事タイトル
従って、ブログ記事内の最初のヘッダーは h4
から始まります。ですので、Markdown でブログ記事を書く時も
h4 が始まりとなります。
ですが、いちいち書き手がそんな気を回さなければいけないのは面倒です。幸い
Pandoc には Shift Heading Level
という機能があり、ヘディング・レベルを変更できます。オプションは
--shift-heading-level-by を使います。今回は #
(h1 相当) を #### (h4 相当)
にシフトさせたいので、ヘディング・レベルを 3 つ上げます。
pandoc --from=markdown-auto_identifiers --to=html5 --shift-heading-level-by=3 entry.md > entry.html
これで次のように # が h4
に変換されます。
<h4>Pandoc の導入</h4>
<p>Markdown から HTML ファイルを生成する仕組みとして <a
href="https://pandoc.org/">Pandoc</a> を使います。</p>
<p>Pandoc は各種文書フォーマットを相互変換するツールです。…</p>
<h5>Pandoc のインストール</h5>
<p>Pandoc は <code>brew</code> コマンドでインストールできます。</p>
コードの表示
Pandoc のシンタックス・ハイライト
Pandoc はソースコードのシンタックス・ハイライトをデフォルトでサポートしています。
例えば、次のソースコードを含む Markdown ファイルを HTML に変換してみましょう。
Lua のサンプルコードです。
``` lua
local languages = {
['emacs-lisp'] = true,
elisp = true,
html = true,
lua = true,
markdown = true,
ruby = true,
swift = true,
}
```
上記の Markdown ファイルに対して、次のコマンドを実行してみました。
pandoc --from=markdown-auto_identifiers --to=html5 --shift-heading-level-by=3 --output=source1.html source1.md
結果は次のようになります。
<p>Lua のサンプルコードです。</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode lua"><code class="sourceCode lua"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">local</span> <span class="va">languages</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="op">[</span><span class="st">'emacs-lisp'</span><span class="op">]</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="va">elisp</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="va">html</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="va">lua</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="va">markdown</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="va">ruby</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> <span class="va">swift</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
どうでしょう。ソースコードの構文が解析され、変数やオペレーターに span 要素が追加されています。ただ、CSS が当たっていないので色が付いていません。
ここで --standalone オプションを追加すると、HTML
ファイルに CSS を追加してくれます。
pandoc --from=markdown-auto_identifiers --to=html5 --shift-heading-level-by=3 --standalone --output=source1.html source1.md
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>source1</title>
<style>
/* Default styles provided by pandoc.
** See https://pandoc.org/MANUAL.html#variables-for-html for config info.
*/
html {
color: #1a1a1a;
background-color: #fdfdfd;
}
body {
margin: 0 auto;
max-width: 36em;
padding-left: 50px;
padding-right: 50px;
padding-top: 50px;
padding-bottom: 50px;
hyphens: auto;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
font-kerning: normal;
}
@media (max-width: 600px) {
body {
font-size: 0.9em;
padding: 12px;
}
h1 {
font-size: 1.8em;
}
}
@media print {
html {
background-color: white;
}
body {
background-color: transparent;
color: black;
font-size: 12pt;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3, h4 {
page-break-after: avoid;
}
}
p {
margin: 1em 0;
}
a {
color: #1a1a1a;
}
a:visited {
color: #1a1a1a;
}
img {
max-width: 100%;
}
svg {
height: auto;
max-width: 100%;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.4em;
}
h5, h6 {
font-size: 1em;
font-style: italic;
}
h6 {
font-weight: normal;
}
ol, ul {
padding-left: 1.7em;
margin-top: 1em;
}
li > ol, li > ul {
margin-top: 0;
}
blockquote {
margin: 1em 0 1em 1.7em;
padding-left: 1em;
border-left: 2px solid #e6e6e6;
color: #606060;
}
code {
font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
font-size: 85%;
margin: 0;
hyphens: manual;
}
pre {
margin: 1em 0;
overflow: auto;
}
pre code {
padding: 0;
overflow: visible;
overflow-wrap: normal;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
hr {
border: none;
border-top: 1px solid #1a1a1a;
height: 1px;
margin: 1em 0;
}
table {
margin: 1em 0;
border-collapse: collapse;
width: 100%;
overflow-x: auto;
display: block;
font-variant-numeric: lining-nums tabular-nums;
}
table caption {
margin-bottom: 0.75em;
}
tbody {
margin-top: 0.5em;
border-top: 1px solid #1a1a1a;
border-bottom: 1px solid #1a1a1a;
}
th {
border-top: 1px solid #1a1a1a;
padding: 0.25em 0.5em 0.25em 0.5em;
}
td {
padding: 0.125em 0.5em 0.25em 0.5em;
}
header {
margin-bottom: 4em;
text-align: center;
}
#TOC li {
list-style: none;
}
#TOC ul {
padding-left: 1.3em;
}
#TOC > ul {
padding-left: 0;
}
#TOC a:not(:hover) {
text-decoration: none;
}
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
/* The extra [class] is a hack that increases specificity enough to
override a similar rule in reveal.js */
ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
/* CSS for syntax highlighting */
html { -webkit-text-size-adjust: 100%; }
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
</head>
<body>
<p>Lua のサンプルコードです。</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode lua"><code class="sourceCode lua"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">local</span> <span class="va">languages</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="op">[</span><span class="st">'emacs-lisp'</span><span class="op">]</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="va">elisp</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="va">html</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="va">lua</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="va">markdown</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="va">ruby</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> <span class="va">swift</span> <span class="op">=</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
</body>
</html>
ウェブブラウザーで HTML ファイルを開くと、色が付いているのが分かります。
Prism.js を使ったシンタックス・ハイライト
Pandoc のシンタックス・ハイライトを使っても良いですが、せっかく前記事で Prism.js を導入したので、シンタックス・ハイライトはそちらに任せようと思います。
Prism.js を使う場合、Pandoc
のデフォルトで付いてくるシンタック・ハイライトの出力が邪魔です。まず
--standalone オプションを 付けない
ことで、CSS の挿入を妨げ、続いて --syntax-highlighting=none
オプションを付けてシンタックス・ハイライト用の出力も抑制します。
pandoc --from=markdown-auto_identifiers --to=html5 --shift-heading-level-by=3 --syntax-highlighting=none --output=source1.html source1.md
すると、シンプルな HTML 出力を得ました。
<p>Lua のサンプルコードです。</p>
<pre class="lua"><code>local languages = {
['emacs-lisp'] = true,
elisp = true,
html = true,
lua = true,
markdown = true,
ruby = true,
swift = true,
}</code></pre>
さて、Prism.js にシンタックス・ハイライトをさせるためには、次の書式で HTML を書く必要がありました。 これは HTML5 で推奨されている書き方だそうです。
<pre><code class="language-XXX">
プログラミング言語 XXX のソースコード
</code></pre>
HTML5 の推奨は code 要素の class 属性に
language-lua と書くのですが、Pandoc の出力は
pre 要素の class 属性に lua
と書なっています。
Pandoc には Lua 言語でフィルターを作成することができて、Pandoc の HTML 出力を Prism.js に合わせることが可能です。
- Pandocで作成するHTMLのコードブロックをprism.jsのシンタックスハイライトに対応させる冴えたやり方 - かえるのひみつきち
- GitHub - averms/pandoc-filters: A small, useful collection of pandoc filters
上記サイトを参考にして、Lua フィルタースクリプトを書きました。これを
standard_code.lua という名前で保存します。
なお、保存する時に、シンタックスハイライトしたい言語を
languages に記述してください。
--- standard-code: ouput code blocks with class="language-*" attributes
-- © 2020 Aman Verma. Distributed under the MIT license.
-- Check keywords at https://prismjs.com/#supported-languages
local languages = {
['emacs-lisp'] = true,
elisp = true,
html = true,
lua = true,
makefile = true,
markdown = true,
ruby = true,
shellsession = true,
swift = true,
}
-- local languages = {meta = true,markup = true,css = true,clike = true,javascript = true,abap = true,abnf = true,actionscript = true,ada = true,agda = true,al = true,antlr4 = true,apacheconf = true,apl = true,applescript = true,aql = true,arduino = true,arff = true,asciidoc = true,aspnet = true,asm6502 = true,autohotkey = true,autoit = true,bash = true,basic = true,batch = true,bbcode = true,bison = true,bnf = true,brainfuck = true,brightscript = true,bro = true,bsl = true,c = true,csharp = true,cpp = true,cil = true,clojure = true,cmake = true,coffeescript = true,concurnas = true,csp = true,crystal = true,['css-extras'] = true,cypher = true,d = true,dart = true,dax = true,dhall = true,diff = true,django = true,['dns-zone-file'] = true,docker = true,ebnf = true,editorconfig = true,eiffel = true,ejs = true,['emacs-lisp'] = true,elisp = true,elixir = true,elm = true,etlua = true,erb = true,erlang = true,['excel-formula'] = true,fsharp = true,factor = true,['firestore-security-rules'] = true,flow = true,fortran = true,ftl = true,gml = true,gcode = true,gdscript = true,gedcom = true,gherkin = true,git = true,glsl = true,go = true,graphql = true,groovy = true,haml = true,html = true,handlebars = true,haskell = true,haxe = true,hcl = true,hlsl = true,http = true,hpkp = true,hsts = true,ichigojam = true,icon = true,ignore = true,inform7 = true,ini = true,io = true,j = true,java = true,javadoc = true,javadoclike = true,javastacktrace = true,jolie = true,jq = true,jsdoc = true,['js-extras'] = true,json = true,json5 = true,jsonp = true,jsstacktrace = true,['js-templates'] = true,julia = true,keyman = true,kotlin = true,latex = true,latte = true,less = true,lilypond = true,liquid = true,lisp = true,livescript = true,llvm = true,lolcode = true,lua = true,makefile = true,markdown = true,['markup-templating'] = true,matlab = true,mel = true,mizar = true,mongodb = true,monkey = true,moonscript = true,n1ql = true,n4js = true,['nand2tetris-hdl'] = true,naniscript = true,nasm = true,neon = true,nginx = true,nim = true,nix = true,nsis = true,objc = true,objectivec = true,ocaml = true,opencl = true,oz = true,parigp = true,parser = true,pascal = true,pascaligo = true,pcaxis = true,peoplecode = true,perl = true,php = true,phpdoc = true,['php-extras'] = true,plsql = true,powerquery = true,powershell = true,processing = true,prolog = true,properties = true,protobuf = true,pug = true,puppet = true,pure = true,purebasic = true,purescript = true,python = true,q = true,qml = true,qore = true,r = true,racket = true,jsx = true,tsx = true,reason = true,regex = true,renpy = true,rest = true,rip = true,roboconf = true,robotframework = true,ruby = true,rust = true,sas = true,sass = true,scss = true,scala = true,scheme = true,['shell-session'] = true,smali = true,smalltalk = true,smarty = true,solidity = true,['solution-file'] = true,soy = true,sparql = true,['splunk-spl'] = true,sqf = true,sql = true,stan = true,iecst = true,stylus = true,swift = true,['t4-templating'] = true,['t4-cs'] = true,['t4-vb'] = true,tap = true,tcl = true,tt2 = true,textile = true,toml = true,turtle = true,twig = true,typescript = true,typoscript = true,unrealscript = true,vala = true,vbnet = true,velocity = true,verilog = true,vhdl = true,vim = true,['visual-basic'] = true,warpscript = true,wasm = true,wiki = true,xeora = true,['xml-doc'] = true,xojo = true,xquery = true,yaml = true,yang = true,zig = true}
local function escape(s)
-- Escape according to HTML 5 rules
return s:gsub(
[=[[<>&"']]=],
function(x)
if x == '<' then
return '<'
elseif x == '>' then
return '>'
elseif x == '&' then
return '&'
elseif x == '"' then
return '"'
elseif x == "'" then
return '''
else
return x
end
end
)
end
local function getCodeClass(classes)
-- Check if the first element of classes (pandoc.CodeBlock.classes) matches a
-- programming language name. If it does, it gets removed from classes and a valid
-- HTML class attribute string (with space at beginning) is returned.
if languages[classes[1]] then
return ' class="language-' .. table.remove(classes, 1) .. '"'
else
return ''
end
end
local function makeIdentifier(ident)
-- Returns a valid HTML id attribute (with space at beginning) OR empty string.
if #ident ~= 0 then
return ' id="'.. ident .. '"'
else
return ''
end
end
local function makeClasses(classes)
-- Returns a valid HTML class attribute with classes separated by spaces (with a space
-- at the beginning) OR empty string.
if #classes ~= 0 then
return ' class="' .. table.concat(classes, ' ') .. '"'
else
return ''
end
end
local function makeAttributes(attrs)
-- Returns a string of HTML attributes from key-value pairs
-- (with space at beginning for each attribute) OR empty string.
if #attrs ~= 0 then
local result = ''
for key, value in pairs(attrs) do
result = result .. ' ' .. key .. '="' .. escape(value) .. '"'
end
return result
else
return ''
end
end
return {
{
CodeBlock = function(elem)
if FORMAT ~= 'html5' then
return nil
end
local id = makeIdentifier(elem.identifier)
local classLang = getCodeClass(elem.classes)
local classReg = makeClasses(elem.classes)
local extraAttrs = makeAttributes(elem.attributes)
local preCode = string.format(
'<pre%s%s%s><code%s>%s</code></pre>', id, classReg, extraAttrs, classLang, escape(elem.text)
)
return pandoc.RawBlock('html', preCode, 'RawBlock')
end,
}
}
--lua-filter=standard_code.lua オプションで Lua
スプリプトを指定します。
pandoc --from=markdown-auto_identifiers --to=html5 --shift-heading-level-by=3 --syntax-highlighting=none --lua-filter=standard_code.lua --output=source1.html source1.md
出力を見てみましょう。Prism.js 向けの HTML になりました。
<p>Lua のサンプルコードです。</p>
<pre><code class="language-lua">local languages = {
['emacs-lisp'] = true,
elisp = true,
html = true,
lua = true,
markdown = true,
ruby = true,
swift = true,
}</code></pre>
行番号を表示する
Prism.js
にはプラグインを導入することで、行番号を表示することができます。
そのためには、class="line-numbers"
を指定する必要があります。 Pandoc では {.line-numbers}
と書くことでクラスを追加できます。
実際に試してみます。 Markdown ファイルに {.line-numbers}
を追加した後、
Lua のサンプルコードです。
``` lua {.line-numbers}
local languages = {
['emacs-lisp'] = true,
elisp = true,
html = true,
lua = true,
markdown = true,
ruby = true,
swift = true,
}
```
pandoc コマンドを実行します。
pandoc --from=markdown-auto_identifiers --to=html5 --shift-heading-level-by=3 --syntax-highlighting=none --lua-filter=standard_code.lua --output=source2.html source2.md
出力は次の通り。 行番号対応した HTML が得られました。
<p>Lua のサンプルコードです。</p>
<pre class="line-numbers"><code class="language-lua">local languages = {
['emacs-lisp'] = true,
elisp = true,
html = true,
lua = true,
markdown = true,
ruby = true,
swift = true,
}</code></pre>
行をハイライトする
行をハイライトする方法も見ておきましょう。 ハイライトしたい行を
data-line="行番号" という形で指定するのでした。 Pandoc では
{data-line="5"} と指定すれば OK です。
こちらも試してみましょう。 Markdown ファイルに
data-line="5" を追加してみます。
Lua のサンプルコードです。
``` lua {.line-numbers data-line="5"}
local languages = {
['emacs-lisp'] = true,
elisp = true,
html = true,
lua = true,
markdown = true,
ruby = true,
swift = true,
}
```
これを pandoc で処理します。
pandoc --from=markdown-auto_identifiers --to=html5 --shift-heading-level-by=3 --syntax-highlighting=none --lua-filter=standard_code.lua --output=source3.html source3.md
行ハイライトに対応した HTML が出力されていますね。
<p>Lua のサンプルコードです。</p>
<pre class="line-numbers" data-line="5"><code class="language-lua">local languages = {
['emacs-lisp'] = true,
elisp = true,
html = true,
lua = true,
markdown = true,
ruby = true,
swift = true,
}</code></pre>
Makefile の作成
以上の準備を整えれば、Markdown ファイルを pandoc コマンドで HTML ファイルに変換ができます。 しかし、必要な pandoc コマンドはオプションを含めると長くなってしまいました。 これだけのオプションを毎回手打ちするのは避けたいところです。
そこで、Makefile を作って自動化することにしました。
Markdown ファイルと同じディレクトリーに、次のコードを
Makefile という名前で保存します。
all: html
PANDOC = pandoc
PANDOC_OPTS = --from=markdown-auto_identifiers --to=html5 --shift-heading-level-by=3 --syntax-highlighting=none --lua-filter=standard_code.lua
MD_FILES := $(wildcard *.md)
HTML_FILES := $(MD_FILES:.md=.html)
.PHONY: html
html: $(HTML_FILES)
%.html: %.md
$(PANDOC) $(PANDOC_OPTS) --output=$@ $<
この Makefile は .md
という拡張子のファイルがあったら、pandoc コマンドを
PANDOC_OPTS オプションを使って処理し、同名の
.html ファイルを作成する、というものです。
Makefile を作っておけば、make
と実行するだけで、HTML ファイルが作成されます。
make
あとがき
以上で設定は終わりです。 最近はめっきりブログを書くことが少なくなりました。 Markdown で気軽に書けるシステムを構築できたので、月一くらいで記事を書ければと思います。

0 件のコメント:
コメントを投稿