Pandoc Markdownで任意の要素にクラス/IDを指定する

2017年初頭に追加されたがPandoc ユーザーズガイド (日本語版) には反映されていない機能。

従来もヘッダー及びフェンスコードブロックにはクラスやIDを書くことができたが、汎用のdiv/spanで書けるようになっている。

任意のブロック (div)

次のように書くことでdivで囲んで someclass クラスを付与することができる。

また、次のようにして someId IDを付与することができる。

クラスとIDの両方をつけることもできる。

任意のインライン要素 (span)

同様にインラインでもつけることができる。

インラインコード

インラインコードでもつけられるようになった。

PureBuilder Simplyと新しいサイトのお話

PureBuilder Simply

デザイン

PureBuilder Simplyは従来とは大幅に考え方を変えた。

従来は基本的に「いっぺんの処理ですべて行う」というものであった。 このため、特に難しかったのが、ACCSにおける「前後の記事へのリンク」である。

PureBuilder Simplyは処理が分離されている。 一番基準となるスクリプトはデフォルトで全記事を処理する。 そして、全記事のメタデータをデータベースに書き出すのである。

従来はPureDocメタセクションに直接書くという方法をとってきた。 そのため、ドキュメントが処理時に変更されるのだが、これは扱いにくい側面が結構あった。

従来のPureBuilderはドキュメントごとに処理するようになっており、 メタデータはドキュメントにある。 そのため、処理時には「そのドキュメントのメタデータしか知らない」状態であった。

今回のPureBuilderもすべてのドキュメントのメタデータを読み込むことはしないが、 すべてのドキュメントのメタデータの書き出しは行う。

メインスクリプト以外については、このスクリプトが書き出したデータベースに基づいて処理を行うため、 すべてのドキュメントのメタデータを知った状態で処理することができる。

Frontmatter

もともとPureBuilderはPureDocを前提としていて、Markdown supportはPureBuilder2で採用された。

Markdown Supportを採用したのはWindowser向けのフレンドリーシュガーであり、 Mimir Yokohamaでサービスを展開するにあたって、記述の難しいPureDocではなく、 記述しやすいMarkdownで書けるようにすることで「ユーザーによる更新の容易さ」を向上させる目的があった。

この時点でMarkdown YAML Frontmatterのことを知らなかった。 そのため、従来のPureBuilderではMarkdownドキュメント中にHTMLコメントを入れ、その中にPureDoc meta headerを入れるという仕様になっていた。

だが、そもそもMarkdownにYAMLでメタデータを書ける仕様である。 同じYAMLのメタデータなのだから、これに従うべきだ。

そのため、PureBuilder SimplyではMarkdownドキュメントのFrontmatterを必須としており、 frontmatterが存在する前提で処理する。 (titleメタデータが必須であるため、どのみちfrontmatterは必須となる)

さらにReStructured Textに対応したが、これも先頭にコメントを入れてYAMLでヘッダを書く、という仕様とした。 次のような具合である。

ReSTなら

Pandoc

PureBuilder Simplyはドキュメント処理をPandocにゆだねている。

これは別に手抜きというわけではない。従来にしてもMarkdownの処理はKramdownによって行っているのであり、 PureDocの実装は別なので特にPandoc採用でアウトソーシングしたという感はない。

Pandocの利便性と「標準性」から、「Pandocを主としてPureBuilderは補助であるほうがユーザービリティが高い」と判断したのだ。

PureBuilder Refinedでは新しいPureDoc Zをサポートする予定だが、 PureBuilder Refined自体がPureBuilder Simplyでは不足の場合に限られるので微妙かもしれない。

「事前生成」戦略

PureBuilderは言葉の意味としてはCMSで合っているのだが、どちらかというとCCS(Content construction system)に近い。 基本的に生成機能だけで、管理に関してはシステムの標準的機能でなるべく簡単てにできるようにしている。

一般的なCMSとの最大の違いは、「Webアプリケーションとして動作しない」ということだろう。 原則としてPureBuilderは(ホームページビルダーなどで書いた場合と同様に)ローカル環境でウェブページを組み上げ、そのままアップロードすることで動作する。

従来のPureBuilderにあった、Webアプリケーションとして動的に生成するためのプラガブルな仕様は現時点ではPureBuilder Simplyにはない。 予定もされていない。単一ページのサポートは限定的で、原則としてディレクトリ単位でビルドを行う。 全体ビルド機能も排除された。

より「サイトを構築するためのスクリプト」という点に特化した。

私のウェブコンテンツ生成は可能な限り「事前生成戦略」をとっている。 通常、ページは書く回数よりも読まれる回数が多いので、コストのかかる処理は1度だけ行いたい、という方式だ。

これは構造的に最速の方式でもある。

ウェブサイトの仕組みを考えればわかるが、たとえばWordPressでは

  1. ウェブサーバーが接続を受け付ける
  2. ウェブサーバーがPHPプロセスにWordPressを渡す
  3. WordPressがデータベースにアクセスする
  4. データベースから必要なデータを受け取ったWordPressがページを生成して返す
  5. ウェブサーバーが応答する

という手順になる。 WordPressのキャッシュ機能ではWordPressはデータベースから受け取った結果を処理せずにそのまま応答するため、高速化するのだが、 それでもこの接続にかかる時間が決して小さくない。

対して、静的ファイルならば

  1. ウェブサーバーが接続を受け付ける
  2. ウェブサーバーがファイルを読み込んで応答する

とプロセスが少なくなり、より高速だ。 WordPressの高速化手法も、PureBuilderが同様の手法を適用すればさらに高速する。

さらに、ウェブサーバーにとって静的ファイルの応答は基本的機能であるため、 より高速なウェブサーバーが選択できる、というメリットがある1

また、静的ファイルのみでウェブサイトの構築が可能なことで、ウェブサーバーの選択肢が増える。 PHPが動作しないGeocitiesやGitHub Pagesでも構築可能だ。

Mimir Yokohama新サイト

新サイト開設

Mimir Yokohamaの新ウェブサイト開設は、主に「異様にサイトが重くなっている」という事情からきているのだが、 実はそもそもPureBuilder Simplyはこの新サイト構築のために書き直した。

Aki SIE新サイト開設時もそのためにPureBuilder2を作ったのだが、 PureBuilder2が6ヶ月も要したのに対して、作業時間で4日、経過時間でも9日と非常に速く書き上がった。 やはりデザインは大事だ。

そして当然ながら、Mimir YokohamaウェブサイトがPureBuilder Simplyのデビュー戦となった。

「楽」という言葉の意味

Mimir Yokohamaのウェブサイトの出来には非常に満足している。

だが、この開発において“PureBuilder Simplyの開発”は全行程における30%程度でしかなかった。

PureBuilder Simplyを使ったサイトは非常に「楽」であり、「簡単」なのだが、誤解してはいけない部分がある。

今回はWordPressからの移行であるため、WordPressで利用している機能に近い状態を狙った。 デザイン的にも近く、バックエンドの変更や、それによって品質が低下した印象を与えないようにしている。

まず言っておくと、「WordPressのサイトを欲している」のであれば、PureBuilderを採用するよりも、 WordPressを採用したほうが確実に楽にできる。 なぜならば、そもそも欲しているのはWordPressなのだから、WordPressよりも楽にはならない。

そもそもPureBuilder Simplyによる構築と、WordPressによる構築はちょっと話が違うのだ。

WordPressの場合、テーマ機能やプラグインなどは既に揃っている段階から話をはじめる。 PureBuilder SimplyはそもそもWordPressではできない完全なカスタマイズを実現するという前提があり、 WordPressで言えばテーマやプラグイン、CSSなどを「すべて0から記述するところからはじめる」というのと同じ位置にある。

もしもWordPressをその位置ではじめるという形で条件を揃えたならば、PureBuilder Simplyのほうが「楽だ」と断言できる。

実際はそのような条件では比べないと思うのだが、逆にWordPressに合わせて構造やビルドの仕組み、テンプレートやCSSなどがすべて揃った状態からはじめるならば、 これもPureBuilderとしてはそこまで分の悪い勝負ではない。

なぜならばその状態で必要なことは

  • 文書を配置したい場所に新しくファイルを作り、MarkdownまたはReSTで書く
  • PureBuilderのバッチスクリプトを実行する
  • Gitでcommit&pushする

というだけのことだからだ。

「設定ファイルを書く」「コマンドを実行する」というあたりに、「Unixer的な楽さ」の感覚があるが、 これに関しても別にお客様に納品するときにはちょちょいと調整すれば良い話だ。 若干手順が多いようにも感じられるが、実際はWordPressの場合は

  • ログインページにアクセスしてログインする
  • ダッシュボードから新規記事の投稿を選択する
  • ページ上で文章を書き、タイトルやカテゴリなどを選択する
  • 投稿ボタンを押す

という手順で、慣れてしまえば麻痺するが、かなり手順は多い。 私が使っているWordPressサーバーは遅く、1クリックあたり1分とか待たされるので、 「WordPressは手順が多くて面倒」という印象が強い。 あちこち飛んではキーボードとマウスを持ち帰るUIも嫌いだ。

もし、PureBuilder Simplyでできることと相当のものとして、 「WordPressもデザインや挙動をカスタマイズする」 という条件にしたならば、これはPureBuilder Simplyが有利だろう。

PureBuilder Simplyを採用するのは「入り口」

だが、実際はPureBuilder Simplyの採用というのは全行程においてそこまで大きなものではない。 おそらく、PureBuilder Simplyのデフォルトで作られたサイトには誰もが失望するだろう。

PureBuilder Simplyの場合、「テンプレート」と「CSS」という要素がある。 これを書くのは、PureBuilder Simplyで「簡単にしている」部分ではないところであり、技術者の腕のみせどころだ。

最近はwebといってもCMSありきのエンジニアが増えていて、HTMLやCSSを直接記述することができない人が増えている。 だが、それはCMSの枠の中でしか仕事ができていない。 PureBuilder Simplyはそれをイチから作るのであり、ウェブデザイナーとエンジニアの腕の見せ所だ。

PureBuilder SimplyのテンプレートはPandocのHTMLテンプレートである。 ただし、「Pandocの出力に対してeRubyで処理できる」ようになっている。 結果、テンプレートの展開は2度行われる。

Pandocのテンプレート機能は意外なほど強力だが、 「値が○○だったら」という式はかけない。

eRubyは完全なプログラムであり、「なんでもあり」である。

比較的単純な(PHPやSSLで行うような)ものに関してはPandocテンプレートで処理できる。 実際、現時点でMimir YokohamaのウェブサイトはeRubyでの処理を無効にしている。

PureBuilder Simplyにおけるテンプレートの作成は「若干の、もしくは本格的なロジックを含むHTMLテンプレートを書く」という作業である。 もちろん、それはデザインが必要であり、CSSと連動した「ページ設計」が必要となる。 ページ設計はかなりエンジニアリングレベルの高い作業だし、 最も単純にはデフォルトテンプレートを使っても良いのだが

CSSと連動した高度なロジックを使いたいといった場合には難易度は上がる。

プログラミングではないけれど、ロジカルに考える

もちろん、CSSを書くのはプログラミングに近い次元になってくる場合もある。

今回のMimir YokohamaのウェブサイトはハンバーガーメニューとドロワーについてはJavaScriptを一切使用していない。 これはAki SIEウェブサイトで使用されていたものの改善版である。

だがそのために、配置や仕組みに関してはかなりロジックが含まれている。 CSSにはロジックはほとんど含まれないが、「ロジックをほとんど含むことのできないものによって動的なフローを実現する方法」を考えるところにロジックが行使されているのだ。

プロモーションモードのページでは下からフェードインするようなページデザインになっているが、 これもJavaScriptは使用していない。

これは単純にHTMLとCSSで実現するウェブサイトを構築するときに、 「単に書くのではなく、そこにロジックを含んだ構造をもたせる」ときに必要となるものだ。

PureBuilder Simplyはウェブサイト構築ツールであって、 デザインに関しては感知しない。 だから、そこに関しては「自分で作る」必要があるのだ。

今回も難易度や作業量としては、テンプレートとCSSを書くことのほうが大きかった。

PureBuilderはサイトの内容については感知しないのだから、別にJavaScriptを含んでもいい。 特にそれを禁止はしていないし、支援もしていない。 別にWordPressのクローンを作ることも、できなくはないのだ。

「サイトを構築する」手間で考えると…

単純に「見栄えするサイトを作りたい」という条件で考えた場合、 WordPressのほうがずっと楽だ。 なぜならば、デザインやロジックが既に存在していて、それを選ぶだけだからだ。

PureBuilderが担っている部分に関してはWordPressよりも楽かもしれないが、 WordPressにはあるがPureBuilderにはない部分がある。 それについてはどうしてもWordPressでは発生しない労力を払わざるをえない。

だから、サイトを構築する手間がWordPressよりも軽いということはない。 WordPressのインストールの労力を含めてもだ。

だが、そのような「敷居の低さ」を求める人がPureBuilder Simplyを自らインストールしてサイトを構築することは想定していない。 PureBuilder Simplyで自ら構築する人は、そんなものがなかったとしてもテンプレートを書き、CSSを書き、JavaScriptを書く人だ。 私がお客様にPureBuilderの簡便性を説くときは、そのような想定で話しているわけではない。 構築はこちらで行って、更新をお客様がされる際には難しくない、ということを言っているのだ。

テンプレートやCSSやJavaScriptは自分で書く、という前提のもとでは、PureBuilder Simplyの採用はかなり労力を削減することができる。 PureBuilder2と比べ管理の手間もかなり減り、非常に楽になった。

新サイトのデザイン、ポリシー

PureBuilderの強みは事前生成による高速性である。

さらにMimir Yokohamaでもポリシーとしている「軽量さ」と「アクセシビリティ」に重きをおいたデザインとなっている。

ただし、Aki SIEのウェブサイトは発想力をアピールする意図もあったのだが、デザインがちょっと古くさい2。 今回Mimir Yokohamaのサイトではよりモダンなデザインを採用することにした。

といっても、そのデザインはほとんどがWordPressで使用していたテーマのビジュアルを踏襲している。 ただし、全く同じというわけではなく、もとがシンプルなデザインであったために「ブロックごとに端までボーダーをひっぱる」「白に対してグレーのラインを引くモノトーン調」「右側サイドバーの2カラム」といったことを踏襲しているだけで、 メニューデザインなどは異なるし、実はカラーテーマも違う。 このあたりは「変えることが目的だった」わけではなく、そもそももとのブログでもカスタマイズしたかった部分なのだ。

ちなみに、ブログではそんなシンプルなデザインであるにもかかわらず、ちょっと古い環境(IEなど)では表示が崩れる。 なんでその程度のもので崩れてしまうのか、私には疑問でならない。

前述の通り、ハンバーガーメニューやフェードインテキストなどはCSSで記述されている。 これは、主に次のような意図だ。

  • JavaScript禁止の環境や、JavaScriptを搭載しないブラウザでもエクスペリエンスを損ねない
  • JavaScriptを使用していない分容量的に軽量
  • HTMLを「意味」を欠落させない

ただし、古いブラウザでのエクスペリエンスは若干犠牲になっている。 だが、それは「アニメーションが表示されない」程度の話で、表示自体はChrome 1.0, Firefox 1.7, Opera 9.0, Safari 3.1で対応している。 あまり言いたくはないが、IEは9.0からの対応だ。

実は今回のサイトではHTML中に同じメニューが2回登場する。これは、上記環境よりも古い環境においてはドロワーが表示できない代わりにサイドバーにメニューを表示させるようにするためだ。 これはアクセシビリティのための処置だが、場合によっては1kB程度はサイズが増加するため、ちょっと残念なところだ。 このメニューはごく初期のCSSしかサポートしない環境でも正常に表示される。 CSSを全くサポートしない環境の場合、メニューは2つ表示される。 この場合上のメニューは記事タイトルよりも上になり、高さもあるため見栄えしないが、そのような環境では仕方ないし、十分許容範囲だろう。 むしろタイトルと記事の間に入るよりはずっと良いと考えられる。

軽量性重視だが、従来と違い飾りとしての画像がところどころに入っている。 従来があまりにも殺風景すぎたという反省であり、このあたりはブログの時点で投入しているものを継承している。

プロモーションページの実現

全く印象の異なるプロモーションページだが、書き方はほとんど同じである。 別にテンプレートをかき分けることもしていないし、普通にMarkdownで書かれたページだ。

プロモーションページの印象の違いは、テンプレートにおける若干の工夫と、CSSによって実現されている。 frontmatterでプロモーションページであることを指定すると、Pandocテンプレートによってそれがクラス設定される。 あとはそのクラス設定に基づいてCSSで見せたくない要素を消去し、一部レイアウトを変更する。

こうした発想力は、やっぱりロジカルシンキングなのだ。

プロモーションページもかなりシンプルだが、 見出しには画像を使いたかった、という気持ちがある。

そうしていないのは、「適切な画像」の選定が非常に困難だったことと、 ヘッダーごとに画像を選択するためにはそれなりにあまりスマートでない余計なロジックを追加する必要があったからだ。

もしもこれぞという画像があれば採用することだろう。

ちなみに、プロモーションページはスマートフォンの商品プロモーションページをイメージしている。 なるべく説明的にならないようにしたのだが、後に書いたものほど説明的になってしまった。 どこまでイメージで語り、どこまで具体的に語るかは難しいところだ。

速度

実際のところ、Mimir Yokohamaにおける制作ポリシーと同一ポリシーで制作したのであり、「最速を目指す」という方式ではない。3

だがそれでも、旧WordPressサイトの10倍程度の速度になっている。 高速化のために使用した手段は以下のようなものだ

高速なサーバーを選択
ConoHaはSSDを採用しており、ネットワーク帯域も太く高速だ。 Nginxを採用
静的ファイルの応答に関してはNginxは最速に近い コンパクトなHTML
できるだけ冗長な表現を避けてコンパクトなHTMLが生成されるようにした。ただし、アクセシビリティを優先してメニューは重複している 可能な限りCSS
JavaScriptなしで動作するようにしたのは「JavaScript無効というオプションを与えるため」だが、ロジックの最適化によってある程度の容量削減に繋がっている WebP
WebPをサポートするのはChrome/Chromium系列だけだが、WebPは「アルファチャンネルつきロッシー圧縮」が可能なため、PNGよりもだいぶ軽くなる。一部PNG画像についてはobjectタグによりWebPを優先して使用するようにしている。アクセシビリティを重視し、重要な画像ではこの処理は行われずimg画像を使用している。 画像よりもCSS
「デザインはセンスでがんばる」方式で、画像などのアイテムの使用数は非常に少ない。寂しさはグラデーションで埋めているが、華やかさに欠ける感は否めない。

高速性ということでいえば、「CSSが重い」ということは感じるが、改善は若干難しい。 もちろん、minifyなどのテクニックである程度は小さくなるが、保守性を重視しているためだ。

また、CSSを3つから1つに減らすというアイディアもあったのだが、比較的サイズの大きいファイルであり、並列ダウンロードしてもらったほうが速いことから分割に戻した。

ミーミルのロゴなどは埋め込んだほうが高速化するだろうが、軽量化には寄与しないし、アクセシビリティが下がるので実施していない。 トップページで51.5KB、低速なうちの環境でTTFBは14.08ms、DNS lookupとfaviconを除いた時間は101.36msとなった。 従来が500kB/5s程度だったことを考えると、この計測では1/10の容量、50倍の高速化となっている。

MarkdownとReST

今回、Pandocの採用によってMarkdown以外のソースフォーマットに対応することが可能になった。 そして、最初に採用したのがReStructured Text(ReST)である。

Pandocがサポートする中で最もMarkdownに近い汎用性があるのはReSTである。 他にそのようなフォーマットはないが、あるいは専門分野での記述用にDocBookなどをサポートする可能性はある。

なお、入力ソースとしてのHTMLは、判別しがたさの観点からサポートは行わない。

ReSTはMarkdownと比べて表現力に優れている。 記述の簡便性も「好みの問題」と言えるような差異だ。 ただし、ReSTが優れている点について必ずしもそのまま適用できるわけではない。 なぜならばPandocがReSTをフルサポートしていないからだ。

実際のところ他フォーマットへの変換を想定しないのであれば、HTMLを直接記述できるMarkdownの優位性は揺らがない。


  1. 今回はNginxを使ったが、基本的にNginxも静的HTMLファイル向けのサーバーで、アプリケーションは他のサーバーに接続するリバースプロキシとして動作する方式だ。Thinなどを使う手もあるかもしれないが、Nginxよりも高速化するかは疑問。

  2. 800pxのブロックが中央にフロートしているデザインは2005年ころから流行したブログデザインと類似のものだ。今でもamebloなんかはこのデザインなのだが、新規にサイト立ち上げると画面めいっぱいまで使うデザインがデフォルトになっている。

  3. 最速を目指すのであればコードゴルフやminified、画像の排除や機能の統一化なども考えられる。実際のところ「速度は上がるがアクセシビリティが下がる」方式は取ってないし、コスト過剰にもならないようにしている。

理系でも文系でも2時間で書けるようになるMarkdown講座

「Markdown書きたいけど難しい」と言われる方がちらほらおられるので優しくお教えしよう。

Markdownとは

普通のテキストなんだけれど、強調したり箇条書きしたりできる。

さらに、ウェブ表示用とか印刷用とかに変換したりもできる。 主には変換するために使うのだけれど、ウェブサービスではそのままMarkdownで書いたら勝手に変換していい感じにしてくれたりもするものもある。

Markdownは「見た目」を決めるものではない。 だから、華やかなチラシを書いたりするためのものではない。 意味を持った文章を書くためのものだ。 論文やレポートや記事や本を書くのに適している。

必要なもの

とりあえずコンピュータ。

あとは特別なものはいらない。 テキストエディタで書く。 Windowsならばメモ帳でいいし、LinuxならGeditでもPlumaでもKwriteでもMousepadでもVimでも良い。

ただ、できればWindowsならば良いテキストエディタを入手することをおすすめしたい。 高機能なのはAtom、複雑なのは苦手ならMarkdownPadあたりでどうだろう。

基本的な機能

Markdownには様々な事情や拡張や機能があるが、 それを覚えなければ書けないわけではない。

基本的には

  • 段落
  • 強調
  • 箇条書き
  • 章立て

があれば十分機能的に記述できるだろう。

段落

段落というのは、文のまとまりだ。

ウェブページ上では行間が広くとられて空行があるように見える(一般的には)。

日本語の文章では、改行して、書き出しを一文字下げるアレだ。

Markdownは「見た目」ではなく、意味を定義する。 だから、段落によって「ひとまとまりの文が終わって次の文のまとまりになる」ことを示す。

Markdownで改行することはできないことはないが、基本的には使用しない。 「行を変える」という見た目ではなく、「ひとまとまり」を示す段落を使おう。

段落を示すには「空行を開ける」。次のようにだ。

段落1

段落2

改行は単なるスペースだとみなされる。 だから、次の例

段落1
段落1

段落2

は次と同じ意味になる。

段落1 段落1

段落2

だから、次のように書けばいい。

吾輩は猫である。
名前はまだない。

ところで君。
月が綺麗ですね。

強調

書いていると、特に重要な部分があったりする。

声を大にして言いたいようなことだ。

強調をするには強調したい部分を*で囲む。

あなたのことが*好き*です。

もっと強調したい場合は**で囲む。

あなたのことが*好き*です。**愛して**います。

なお、気にしなくても良いが、*で囲む場合は文を囲ってもよいのだが、 **は単語を囲むことになっている。 日本語の場合は単語を囲むのは難しいので、文節を囲むことになると思うが。

箇条書き

箇条書きもよく使うだろう。 箇条書きをするには行頭に*を書いてスペースを開ける。 前後の段落はあけなくてはならない。

今日のご飯は

* カツ丼
* 秋刀魚丼
* ご飯ドーン

番号つきで箇条書きしたい場合は、数字の後に.をつけてスペースをあける。 数字は1, 2…と書かなくて良い。 変換するときに勝手に番号が振られる。

あなたがするべきこと

0. 私のTwitterページにアクセスします
0. 「フォロー」と書かれたボタンをクリックします
0. 愛の告白をします

他にも書き方はあるが、一種類覚えれば書けるだろう。

章立て

長い文章は1章, 2章, 1節, 2節と分けたほうが見やすいだろう。 この記事もそのように書かれている。

章立てを行うには、先頭に#を並べた上でスペースをあけて、見出しを書く。 #の数が多いほど細かな分け方になる。

# 第一章

## 第一章第一節

## 第一章第二節

# 第二章

## 第二章第一節

### 第二章第一節第一項

### 第二章第一節第二項

先頭にまとめて書くわけではなく、章や節が切り替わるところで書く。

キャンセル

例えば*のような文字は特別な意味に取られてしまう。 そのようなことを避けて、「単に*という文字を書きたい」という場合には、 特別な意味にとられてしまう文字の前に\と書いて\*のようにする。

もうちょっと書きたい

コード類の記述はプログラムを書く人しか使わないし、 そのような人はMarkdownのリファレンスを読むなどたやすいだろうから、 それ以外のものを少し。

リンク

クリックして他のページに飛ぶことができるリンクを表現するには

[タイトル](アドレス)

とする。 つまり

[Chienomiブログ](http://chienomi.reasonset.net/)

のようにだ。

画像

Markdownで画像を表現するには

![タイトル](アドレス)

と書く。アドレスなので、基本的にインターネット上にあるものを書くことになるだろう。 印刷用に変換する場合はローカルで必要となり、画像を扱う場合は出力先のことを意識して書くことになる

次のように書く

![誰かのアイコン](http://example.com/img/icon.png)

変換する

PandocとかKramdownとかとても便利なのだが、 だいたいエディタにはエクスポート機能があるので、とりあえずそれを使えば良い。

AtomであればCtrl+Shift+Mでプレビューを開き、プレビューウィンドウ上で右クリックしてSave As HTMLを選択すれば良い。

おまけ: この記事のMarkdownは

この記事はMarkdownで書かれている。

---
title: 理系でも文系でも2時間で書けるようになるMarkdown講座
cats: [ digi.it ]
tags: [ markdown, howto ]
---

「Markdown書きたいけど難しい」と言われる方がちらほらおられるので優しくお教えしよう。

## Markdownとは

普通のテキストなんだけれど、強調したり箇条書きしたりできる。

さらに、ウェブ表示用とか印刷用とかに変換したりもできる。
主には変換するために使うのだけれど、ウェブサービスではそのままMarkdownで書いたら勝手に変換していい感じにしてくれたりもするものもある。

Markdownは「見た目」を決めるものではない。
だから、華やかなチラシを書いたりするためのものではない。
意味を持った文章を書くためのものだ。
論文やレポートや記事や本を書くのに適している。

## 必要なもの

とりあえずコンピュータ。

あとは特別なものはいらない。
テキストエディタで書く。
Windowsならばメモ帳でいいし、LinuxならGeditでもPlumaでもKwriteでもMousepadでもVimでも良い。

ただ、できればWindowsならば良いテキストエディタを入手することをおすすめしたい。
高機能なのはAtom、複雑なのは苦手ならMarkdownPadあたりでどうだろう。

## 基本的な機能

Markdownには様々な事情や拡張や機能があるが、
それを覚えなければ書けないわけではない。

基本的には

* 段落
* 強調
* 箇条書き
* 章立て

があれば十分機能的に記述できるだろう。

### 段落

段落というのは、文のまとまりだ。

ウェブページ上では行間が広くとられて空行があるように見える(一般的には)。

日本語の文章では、改行して、書き出しを一文字下げるアレだ。

Markdownは「見た目」ではなく、意味を定義する。
だから、段落によって「ひとまとまりの文が終わって次の文のまとまりになる」ことを示す。

Markdownで改行することはできないことはないが、基本的には使用しない。
「行を変える」という見た目ではなく、「ひとまとまり」を示す段落を使おう。

段落を示すには「空行を開ける」。次のようにだ。

```markdown
段落1

段落2
```

改行は単なるスペースだとみなされる。
だから、次の例

```markdown
段落1
段落1

段落2
```

は次と同じ意味になる。

```markdown
段落1 段落1

段落2
```

だから、次のように書けばいい。

```
吾輩は猫である。
名前はまだない。

ところで君。
月が綺麗ですね。
```

### 強調

書いていると、*特に*重要な部分があったりする。

**声を大にして**言いたいようなことだ。

*強調*をするには強調したい部分を`*`で囲む。

```markdown
あなたのことが*好き*です。
```

もっと強調したい場合は`**`で囲む。

```markdown
あなたのことが*好き*です。**愛して**います。
```

なお、気にしなくても良いが、`*`で囲む場合は文を囲ってもよいのだが、
`**`は単語を囲むことになっている。
日本語の場合は単語を囲むのは難しいので、文節を囲むことになると思うが。

### 箇条書き

箇条書きもよく使うだろう。
箇条書きをするには行頭に`*`を書いてスペースを開ける。
前後の段落はあけなくてはならない。

```markdown
今日のご飯は

* カツ丼
* 秋刀魚丼
* ご飯ドーン
```

番号つきで箇条書きしたい場合は、数字の後に`.`をつけてスペースをあける。
数字は`1`, `2`...と書かなくて良い。
変換するときに勝手に番号が振られる。

```markdown
あなたがするべきこと

0. 私のTwitterページにアクセスします
0. 「フォロー」と書かれたボタンをクリックします
0. 愛の告白をします
```

他にも書き方はあるが、一種類覚えれば書けるだろう。

### 章立て

長い文章は1章, 2章, 1節, 2節と分けたほうが見やすいだろう。
この記事もそのように書かれている。

章立てを行うには、先頭に`#`を並べた上でスペースをあけて、見出しを書く。
`#`の数が多いほど細かな分け方になる。

```markdown
# 第一章

## 第一章第一節

## 第一章第二節

# 第二章

## 第二章第一節

### 第二章第一節第一項

### 第二章第一節第二項
```

先頭にまとめて書くわけではなく、章や節が切り替わるところで書く。

### キャンセル

例えば`*`のような文字は特別な意味に取られてしまう。
そのようなことを避けて、「単に*という文字を書きたい」という場合には、
特別な意味にとられてしまう文字の前に`\`と書いて`\*`のようにする。

## もうちょっと書きたい

コード類の記述はプログラムを書く人しか使わないし、
そのような人はMarkdownのリファレンスを読むなどたやすいだろうから、
それ以外のものを少し。

### リンク

クリックして他のページに飛ぶことができるリンクを表現するには

```markdown
[タイトル](アドレス)
```

とする。
つまり

```markdown
[Chienomiブログ](http://chienomi.reasonset.net/)
```

のようにだ。

### 画像

Markdownで画像を表現するには

```markdown
![タイトル](アドレス)
```

と書く。アドレスなので、基本的にインターネット上にあるものを書くことになるだろう。
印刷用に変換する場合はローカルで必要となり、*画像を扱う場合は出力先のことを意識して書くことになる*。

次のように書く

```markdown
![誰かのアイコン](http://example.com/img/icon.png)
```

## 変換する

PandocとかKramdownとかとても便利なのだが、
だいたいエディタにはエクスポート機能があるので、とりあえずそれを使えば良い。

Atomであれば`Ctrl+Shift+M`でプレビューを開き、プレビューウィンドウ上で右クリックして`Save As HTML`を選択すれば良い。

Markdownにまつわるお話

ことの起こり

なんだか急にMarkdown関連のことが盛り上がってきた。

Commonmark絡みなのかもしれないけれど、ちょっと私にも言いたいこともある。

Takeshi KOMIYA‏さんによるツイートその1

Markdown は記法に限りがあるというシンプルさが強みであり、弱みだよね。表がないのが非常に不満だけど、それ以外は大体これで十分だよね。それ以降、いろんな人がいくつも派生記法を作っているけれど、どれも方言の域を出ていない。脚注や参照はなくても生きていけるんだ。

その2

もちろん、それで満足が行かない人たちがいるのは知っているし、ある用途 (執筆やら巨大なリファレンスの管理とか) では不十分なのは分かるけど、それは特別なユースケースであって、Markdown のそれから外れているだけなんだよね。そういう人たちは別のものを使う方が幸せになれる

へぇ、テーブルってMarkdownの標準じゃなかったんだぁ…というのはおいといて、私が言いたいのはこんな感じ。

少なくとも私はPandocのMarkdownに9割は満足している。 Kramdownでも良い

そして、Markdownの優れているところは単にシンプルたからではない。DocbookやPODやroffやtrfやXMLのようなドキュメントメタフォーマットと比べても多くの人に取って受け入れやすく、だからこそメタフォーマットとして非常によく機能することだ。

私には自分で作ったPureDocがある。けれど、Markdownのほうがよく使う。そのほうか変換の余地が大きく汎用性が高い。つまりはドキュメントメタフォーマットとして優れているのだ。 そして、Markdownは処理系依存でもそれほど困らないが、PureDocは他に処理系はない。

Markdownの拡張が気に食わない、そんな人はMarkdownでないものを使え、というなら、拡張されたMarkdownはMarkdownではない別のフォーマットだと思えばいいではないか。Markdownを名乗らなければいいのか?それとも、似た記法を使うなんてけしからんということか?

MarkdownはHTMLを含めることすら許しているんだ。足りなければ自分で拡張したっていいんだよ。好きなものを使えばいいさ。強制することなんかない。

そんなくだらないことで言い争わないで、空を見てご覧よ。今日は満月。天使が降りてきそうな素敵な星空だよ。こんなに月が明るいのに2等星までよくみえる。

ドキュメントメタフォーマット

とりあえず紹介

ブログに書くのだからもう少し補足しよう。

ドキュメントメタフォーマットというのは、なにか別の文書形式に変換することを前提としたドキュメント形式のことである。

例えばPODはそれ自体が読みやすいテキストではあるが、「意味付け」ということはできても、それがplain textの状態で「フォーマットすることで意味付けを感じられる」というようなものではない。 PODはトランスレータと呼ばれるソフトウェアに通すことを前提としており、これにより様々な形式に変換する。 対応形式はplain, man, html, latexが標準で付属し、そのほかにも様々な形式に変換可能だ。 Perl cookbookはPODで書かれているという。

RDocも似たようなもので、こっちはもっと「Wiki」っぽい。 こちらはriと呼ばれるtexinfo形式を標準としているが、HTMLにも変換できる。

Wiki記法、はてな記法はマークアップ形式だが、あくまでもHTMLに変換することを前提としている。 HTMLよりも楽に書けるとことがその価値であり、「HTMLの簡易記法」であるといえる。

plain2, reStructuredText, Re:view, AsciiDocはMarkdownに近いものだ。 plain2はLaTeXへの変換に限られているが、ReStructuredTextはSphinxを介して

  • HTML
  • LaTeX
  • ePub
  • Texinfo
  • man
  • plain

に変換することができ、Re:viewは

  • EPUB
  • PDF (LaTeX)
  • InDesign (IDGXML)
  • Markdown
  • plain text (TOPBuilder Text Markup Language)

asciidocは、(公式プロセッサではなく)asciidoctorが

  • HTML (HTML5)
  • XHTML (XHTML5)
  • DocBook (DocBook5, DocBook 4.5)
  • manpage
  • PDF
  • ePub 3
  • LaTeX
  • Mallard

に変換する。

これらのフォーマットは「そのフォーマットをplain textとして書いて、他の形式に変換する」ことを前提としているのだ。

ちなみに、これから執拗にDocBookの話をするが、DocBookもまた他の形式に変換するためのドキュメントメタフォーマットなのだが、「XMLまたはSGMLで書かれている」という特徴がある。 はっきり言えば、前述の形式と比べて圧倒的に読みにくく、書きにくい。 前述のフォーマットがHTMLよりも簡易な記述法を使っているのに対して、むしろODFやHTML, EPUBのようなドキュメント形式として作られたものだと考えたほうが良い。

だが、DocBookに執拗に言及するのは、DocBookが技術文書の形式として普及したからであり、かつトランスレータが多様であるからだ。標準処理系で

  • HTML
  • PostScript
  • PDF
  • RTF
  • DVI
  • plain

に対応している。

ちなみに、PureDocというのは私が作ったもので、RubyをベースとしたDSLである。 これは、Markdownのようなフォーマットが目指しているものとはちょっと方向性が違う。HTMLよりも簡易な記述とHTMLよりも強力な表現を求めており、実際変換したものはHTMLを拡張したものになる(XMLネームスペースを使って独自のタグを追加する)。

RubyのDSLであるため、「ロジックを含めた記述ができる」というのがポイントになっている。

読みやすいか??

まず言っておくが、どのフォーマットが優れているかということに関しては、もはや宗教であり、好みによる。

個人的にはReStructuredTextはかなり好きだ。 plain textの状態でも読みやすいし、表現力も高い。 Python(私は好きでない)のくせにやるじゃないか、と思う。

Re:View形式と、asciidoc形式はあまり好きではない。 PureDocがそんな感じじゃないかというかもしれないが、 plain textとしての自然さがないのだ。

例えば強調は、MarkdownやReStructuredTextでは

*強調されたテキスト*

となるが、Re:Viewは

@<em>{強調されたテキスト}

であり、asciidocには強調という概念がない。 (asciidocは「見た目」での定義であり、どちらかというとTeXなどに近い)

PureDocでは

e "強調されたテキスト"

となる。DocBook/XMLは完全に技術マニュアル用であり、そもそも「エレメントを定義しろ」とあるので、こちらも強調はない。 HTMLでは

<em>強調されたテキスト</em>

となる。RDocも見た目定義だが、一応「イタリックにするとiではなくemを使う」仕様なので

_強調されたテキスト_

となる。PODにも強調はなく、一応はイタリックを強調としているので

I<強調されたテキスト>

となる。

好みはともかくとして、ドキュメントメタフォーマットとしては「読みやすさ」は「表現力」に並ぶ重要な価値だ。 なぜならば、ドキュメントメタフォーマットはそもそもMicrosoft Word形式などのプロプライエタリフォーマットに対するアンチテーゼとしての意味合いがあるからだ。

そして、同時に「書きやすい」ことは、HTMLが簡易なマークアップ言語とはいえ、「普通の人が書くにはハードルが高い」という側面があったためで、より誰にでもかけて、HTMLのように面倒な(タグ表現が妙に長い上に、「なぜ終端にまでタグを書かなければいけないのか」という冗長さを避けたかったということから来ているように思う。

そもそも「書きやすいフォーマット」というのはWiki記法から流行り始めたように思うし、 徐々にブログなどでもHTMLではなくより簡易な記法でマークアップを可能にした。

ドキュメント形式なのにロジックがあるPureDoc

ちなみに、PureDocはRuby DSLであるため、

3.times do |i|
  p { "Hello!" }
end

なんてことができ、この結果

<p>Hello!</p>
<p>Hello!</p>
<p>Hello!</p>

という出力が得られる。 全く一般向けではない仕様だ。

さらに言えばZshで書かれたPureDoc Zのほうは

repeat 3
do
  p Hello
done

とすれば同じような結果が得られる。 これを「読みやすい」と思う人はどうかしていると思う。 PureDocは「書きやすい」ようにはしているが、「読みやすい」ようにはしていない。

PureDocはほぼすべてのテキスト系HTMLタグを使用することができ、かつオプションも指定できる。 さらにnoteなども備えていて、「HTML以上」であることが前提だ。 実のところ「ロジックで書ける」のも「HTML以上」の一部であり、「仕様」だったりする。

PureDocはinstance_evalを使ってRubyコードとして評価しているし、PureDoc Zに至ってはドキュメント中でライブラリをincludeすることでDSL的に使うための語彙がshell functionとして追加されるだけのものだ。

「メタフォーマット」としての価値

WikiなどはHTMLを簡単に書くためのものなのだから、特に変換先としての意味はないのだが、 メタフォーマットとしての価値は、「一度書けば表示向けにも印刷向けにもできる」ということにある。 基本的にはHTMLとPDFにできれば良いだろうという考え方が成り立つのだが、例えば「自分はコマンドドリブンなマークアップで書けば良いけれど、みんなはWordだからDocxで吐きたい」というケースはあるものであり、多様なフォーマットでの出力というのは割と重要な価値である。

実は、私はMarkdown/Pandocと出会うまでずっとPODで書いていた。 PODの多彩な変換形式に助けられてきたからだ。

だが、PODは純粋に汎用というわけではなかったし、 「DOCよりも多くのフォーマットに変換でき、より汎用性と表現力がある形式」を求めていた。

Pandocは極めて強力な変換ツールであり、入力形式はMarkdownに限ったわけではないのだが、 一応、Markdownを軸にしている…のだと思う。

そのため、PandocをMarkdownツールだと考えれば、Markdownは圧倒的なアドバンテージがある。 (もちろん、PandocがReStructuredTextをMarkdownと同等に変換できるのであれば、特にMarkdownにアドバンテージがあるわけではなくなる)

実はPandocは多彩なフォーマットが仇となり、適切に変換されないケースがそこそこある。 さすがに完璧に…というのは相当難しいのだろう。

Markdownにはもうひとつ、様々な処理系があるということがある。 KramdownはRubyで書かれており、Rubyプログラムにおいて使いやすい処理系だ。 KramdownもLaTeXへの出力に対応しており、割と使いやすい。

方言の話

Kramdownがinput formatとして

  • Kramdown
  • Markdown
  • GitHub Flavored Markdown

の3つを挙げていることに既に闇を感じるが、 実は処理系それぞれがMarkdownを拡張していて、一口にMarkdownといっても書き方に互換性がない、という問題が存在する。 冒頭で言及されていたのはそういうことだろう。

だが、それほど問題があるとは思わない。

まず、「多彩な処理系があるマークアップ言語」自体珍しく、Re:Viewなんてほとんどないし、ReStructuredTextだってそう多くはない。 なので、「特定の処理系向けに書かれたMarkdown」がそんなに問題なのか、という疑問がまずある。

さらに、MarkdownはGFMとPHP Markdown Extraが普及していて、これらの記法はだいたい通用する。 だから、純粋なMarkdownでは表現力はかなり限られるが、これらの記法を取り込めばかなりの表現力を持つし、これらの記法を取り込んでなお複数の処理系で取り扱うことができるのだ。

拡張記法のせいで無駄に「意味」をとられるのが気に入らないのであれば、 KramdownのようにMarkdown Standardに従った処理をできる処理系を使えば良い。

Markdownが良いという話

Markdownの記法が完璧だとは思わないが、少なくともPandoc Markdownは表現力にほとんど不満はないし、Pandocの変換フォーマットにも不足はない。

Markdownという記法について「及第点」で、変換という実用面に不満がないのだから、 それで文句を言うべきことなどない。

だから、別に「Markdownじゃ足りない」というのであれば拡張すれば良いし、それが許されてもいる。 拡張されたMarkdownを使ってはいけないということはないだろうし、素のMarkdownと拡張Markdownは別物だと考えれば使い分けだって成立する。

あるいは、「簡素さ優先のMarkdown」と「高機能なGFM」のように考えればよいではないか、ということだ。

無理に「Markdownとはこうだ」ということを押し付け合う必要もないし、Markdownはいままでの苦痛を緩和してくれる、「良いもの」なのだ。

そうでないというのならば、試しに大量のドキュメントをDocBookで書いてみればいい。 DocBookは確かに技術文書を書くのに適しているが、相当に苦痛だろう。 書く量が膨大で、しかもテキストはほとんどエレメントに埋もれてしまう。

別にHTMLで書いても構わない。 Markdownよりも優れていると思うのであれば。

Markdownが好きで、しかし機能が足りないから拡張しようという気持ちはなにも間違っていないし、それはまた別のものだと考えればごく自然なものなのだ。 方言があってはいけないというものではないし、が方言があったところでMarkdownは十分に共通性があると思う。

このブログもMarkdownで書いて、Kramdownで変換している。

ツールや記法でいがみあうことなどなにもない。 誰かに何かを強制する必要などないのだ。 あなたが幸せになるように使えばいい。

Markdownで書かれたスライドをHTMLとポータブルPDFと印刷用PDFにする

Aki SI&EのPR用に書いたものだが、Markdownで書き、Web(HTML)、配布用PDF、印刷用PDFの3種類を生成する考えでいた。

手でやろうかと思っていたのだが、再生成の回数も多そうだったので、自動化できるようにした。

Markdownで記述したドキュメントと、オリジナルサイズの画像ディレクトリ、mogrify -geometry x300でリサイズした画像ディレクトリが用意されている。
Web用のものについてはHTMLで出力し、リサイズされた画像のWebサーバー上のコピーを参照しなければならない。

PDFについては、MarkdownをLaTeXに変換してという方法もあるにはあったが、画像を含めた細かなデザインを施したかったため、HTMLをベースにPDFに変換することにした。

MarkdownコンバータはRubyのKramdownを使用。改ページはないため、wkhtmltopdfを用いてPDFに変換することとした。
参照しているディレクトリの違いで2回ループを回す。

それぞれHTMLは別に出力し、テンポラリファイルとして出力したHTMLを元にPDFのテンポラリファイルを生成、unitepdfを使ってこれを結合する。

専用のコードなので多くをハードコーディングしているが、これで正常に機能する。

#!/usr/bin/ruby
# -*- mode: ruby; coding: UTF-8 -*-

require 'kramdown'
require 'erb'

def treat_md(file, findex)
  $article_body = doc = Kramdown::Document.new(File.read(file)).to_html
  
  $article_title = doc[/<h1[^>]*>(.*?)<\/h1>/, 1]
  
  foot = Array.new
  if findex > 0
    foot << '<a href="' + ( SOURCEFILES[findex - 1].sub(/\.md$/, '')  + ".html" ) + '">前へ</a>'
  end
  
  foot << '<a href="/si/" target="_top">Aki SI&Eトップページへ</a>'
  
  if findex < ( SOURCEFILES.length - 1 )
    foot << '<a href="' + ( SOURCEFILES[findex + 1].sub(/\.md$/, '')  + ".html" ) + '">次へ</a>'
  end
  
  $article_footer = foot.join
end


TEMPLATE = <<'END'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" http://www.w3.org/TR/xhtml1/dtd/xhtml1-transitional.dtd">
<html>
  <head>
    <title><%= $article_title %></title>
    <meta http-equiv="content-language" content="ja" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <style>
body {
% case $mode
% when :Web
  font-size: 130%;
  color: #333;
  font-weight: bold;
  margin: 0px;
  padding: 0px;
% else
  font-size: 18pt;
  color: #000;
  font-weight: bold;
  margin: 2cm auto;
  padding: auto;
% end
  background-color: #fff;
  font-family: "Migu 1C"
}   

h3 {
  font-size: 135%;
}

#Container {
  margin: auto auto 100px;
  max-width: 950px;
  text-align: center;
}

img {
% case $mode
% when :Web
  height: 300px;
% else
  height: 18em;
  max-width: 100%;
% end
}

ul {
  text-align: left;
  margin: auto 1.8em;
}


#ArticleFooter {
% if $mode != :Web
  display:none;
% end
  position: fixed;
  bottom: 0px;
  margin: 0px;
  width: 100%;
  max-height: 100px;
  background-color: #339;
  color: #fff;
  text-align: center;
}

#ArticleFooter a {
  padding: 0 0.8em;
  text-decoration: none;
  color: #fff
}

#ArticleFooter a:hover {
  text-decoration: underline;
}
    </style>
  </head>
  <body>
    <div id="Container">
    <%= $article_body.gsub(
      %r{src="img/},
      %(src="#{$img_type}_img/)
    ) 
    %>
  </div>
    <div id="ArticleFooter">
      <%= $article_footer %>
    </div>
  </body>
</html>
END

$article_body = nil
$article_footer = nil
$article_title = nil

SOURCEFILES = Dir.entries(".").select {|i| i =~ /\.md$/ }.sort

# HTML
$img_type = "/img/si/intro_slideshow/resized"
$mode = :Web

SOURCEFILES.each_with_index do |x, i|
  begin
  treat_md(x, i)
  File.open("out/#{x.sub(".md", ".html")}", "w") do |f|
    f.puts(ERB.new(TEMPLATE, nil, "%<").result)
  end
  ensure
    $article_body = nil
    $article_footer = nil
    $article_title = nil
  end
end

# Web PDF
$img_type = "resized"
$mode = :PDF

SOURCEFILES.each_with_index do |x, i|
  begin
  treat_md(x, i)
  fn = "out/_tmp_#{x.sub(".md", ".html")}"
    File.open(fn, "w") do |f|
      f.puts(ERB.new(TEMPLATE, nil, "%<").result)
    end
  system('wkhtmltopdf "%s" "%s"' % [ fn, fn.gsub(".html", ".pdf") ])
  ensure
    $article_body = nil
    $article_footer = nil
    $article_title = nil
  end
end

system 'pdfunite out/_tmp_*.pdf out/portable.pdf'
system 'rm out/_tmp_*'

# Printable
$img_type = "original"
$mode = :PDF

SOURCEFILES.each_with_index do |x, i|
  begin
  treat_md(x, i)
  fn = "out/_tmp_#{x.sub(".md", ".html")}"
    File.open(fn, "w") do |f|
      f.puts(ERB.new(TEMPLATE, nil, "%<").result)
    end
  system('wkhtmltopdf "%s" "%s"' % [ fn, fn.gsub(".html", ".pdf") ])
  ensure
    $article_body = nil
    $article_footer = nil
    $article_title = nil
  end
end

system 'pdfunite out/_tmp_*.pdf out/printable.pdf'
system 'rm out/_tmp_*'

完成したものについては

Google DOCs Viewerはスライドショー向きではないので、ウェブ版を使うか、PDFをダウンロードするのがお勧め。

PureBuilder2(3) Markdown for Blog

今やブログは主に Markdownで書かれている。

当初、blogtrというPuredoc用のスクリプトを書いたが、ブログ程度であれば圧倒的にMarkdownで事足りることが多いからだ。
実のところ、ACCSコンテンツなどでもMarkdownで十分なケースは多く、そのためにPureBuilder2はMarkdownをサポートする。

Markdownを変換し、ヘッダーを操作するblogmdはPureBuilder1.5で既に実装されたが、これはPandocを使ったスクリプトである。
PureBuilder2は全面的にRubyでいく予定であるため、MarkdownトランスレータにはKramdownを使用している。

これに合わせてKramdownを採用することにした。
機能的にはほとんど変わらないが、唯一の違いとして、手前に

* TOC
{:toc}

を入れるようにした。
これにより、KramdownはTOCを自動生成する。Pandocにはなかった便利な機能で、ちゃんとオフセットしたTOCを生成してくれる。

ちなみに、PureBuilderのMarkdown用ライブラリがKramdownをモンキーパッチングで拡張し、自動的にPureDocオブジェクトのメタ情報としてヘッダーを埋め込むので、XHTPureDocを使ってTOCを作ることもできなくはない。

まだpush予定はないため、コードを掲載する。

#!/usr/bin/ruby
# -*- mode: ruby; coding: UTF-8 -*-

require "purebuilder/purebuilder"
require "kramdown"
require "optparse"

opt = {}
op = OptionParser.new

op.on("-m", "--marshal") {|v| opt[:marshal] = true }
op.on("-M", "--without-meta") {|v| opt[:marshal] = false }
op.parse!(ARGV)


# String for Kramdown's TOC
TOC_PREFIX = "* TOC\n{:toc}\n\n"

# Get article file
sourcefile = ARGV.length == 1 ?  ARGV[0] : nil
sourcestr = ARGF.read

pbp = PureBuilder::Parser.new(sourcestr, sourcefile)
pbp.proc_header

html = Kramdown::Document.new(TOC_PREFIX + sourcestr.gsub(//m, "") ).to_html

if opt[:marshal]
  Marshal.dump({body: html, head: DOC.meta}, STDOUT)
else
  puts html
end

これだけ見るとシンプルだが、PureBuilderとの関係が深く、結構中まで突っ込んでみることになってしまった。
PureBuilderの設計があまりよくないのかもしれないとも思ったが、そもそもPureBuilderのMarkdownサポートは「MarkdownをPureDocに見せかける」ものなので、PureBuilderとPureDocの密結合はやむをえまい。

これで初めて動かすこととなったPureBuilderだが、これによってPureBuilder、さらにPureBuilder登場によって修正されたPureDocのバグが発見され、デバッグにかなりの手間を費やした。

基本的にはシンプルだが、オプションへの対応を加えたため、いくらか複雑化した。
オプションは、将来的にパイプしてAPI経由でのブログアップロードに対応するため、メタを含めたMarshalで渡すためのものだ。

Markdownにまつわるもろもろ

Markdownで書くということ

Markdownはもうだいぶ普及している形式だと思う。

様々なマークアップ言語や記法がこれまで発達してきた。 それは例えばWikiであったり、plain2だったり、textileだったり、 場合によってはRDocだったり。

しかしながら、そのいずれもそれほど普及しなかった。 だが、Markdown記法はもはやスタンダードとも言うべき普及を見せている。

PureDocは当初、Markdownのような形式をとっていた。 実際に正規表現パーサだった時期もあるし、もう少し発展してZshの内部DSLだった時代もある。

この目的はHTMLと印刷用フォーマットの両方を生成するドキュメントメタフォーマットで、 かつHTMLと比べ簡潔に記述できるフォーマットを求めていた。

その要求を満たしてくれるのだ。 現在はPureDocはReasonSetに特化した多彩な機能を持つためMarkdownに乗り換えるということはしないが、 多くの場合Markdownで事足りるのも事実だ。

Markdown Editor

Markdownは普及している分、専用のEditorが多く存在する。 単に強調表示や入力支援があるだけでなく、リアルタイムで表示を確認できる。

主要な候補となるのは

  • Markdown#Editor(Windows)
  • Remarkable(Linux)
  • CuteMarkEd
  • Haroopad

の4つであるようだ。

Markdown#Editorに関しては表示領域がマッチしないことが多く、 いまひとつ使いにくい。この問題はCuteMarkEdでも生じる。

Haroopadはクロスプラットフォームで、最初はフォントに違和感があったが、 CSSによってフォントを含め見栄えを指定することができる。

Haroopadの弱点は、改行を反映してしまうことだろう。 だが、全体にはスタイリッシュで見やすく使いやすい。 ドキュメントの動的なリロード機能がないのと、Donateのバルーンがちょっとしつこいのは残念。 だが、Windowsではこれを使っている。 基本的に表示位置は狭い画面ではエディタ側の入力位置を上のほうにもってくると適切に表示してくれる。

Haroopadの欠点として、Fcitxで入力できなくなることが結構あるというのもある。 この対応として、入力のない、空のHaroopadを立ち上げておくと入力できるようだ。

RemarkableはUbuntu的なUIを持つ。 使いやすいといえば使いやすいが、D&Dによって開くことができないため、ファイル操作がちょっと面倒。

おもしろいのが、Remarkableはビューワがめいっぱい上までスクロールすると下にループする。下はしない。

またエクスポート機能もあり、CSSにも対応する。 今のところ最も安定しているということもあり、LinuxではRemarkableを使用している。

Remarkableで記述する場合は、エディタの記述部分を上のほうになるようにするか、めいいっぱい下にすると適切に表示される。 当然ながら、中途半端で適切に表示されない位置はどうしても生じる。

だが、なるべくならどのエディタも最も下に書いていくのが良いようだ。

Markdownで既存のテキストを引用する

HTMLを含め引用はpreされるべきではないかと思うのだが、そうなっていない。 プレーンなテキストを引用するには、次のようにすると良いようだ。

sed -i "s/\(.*\)/> \1\n> /" file

sedの出力は改行を伴うので最後には改行はいらない。 段落を分けてもらう必要があるため、空行を入れておく。 ちょっと複雑だ。

MarkdownをPureBuilderに取り込み

Markdownのほうが楽に、適切に書けるケースが多いようなので、MarkdownをPureBuilderの一部として取り込んでみた。

とりあえずblog用で、変換にはpandocを使う。 これはいずれPureBuilderの一部となる。

ちなみに、今回コードの埋め込みは次のようにした。

sed "s/\(.*\)/\t\1/" ~/local/devel/reasonset_builder02/scripts/md_processor.rb >| ~/tmp/out

主な動作としては、PureDoc同様のヘッダの取り扱いと、 pandocからbodyだけを切り出すことである。

#!/usr/bin/ruby
# -*- mode: ruby; coding: UTF-8 -*-

require 'yaml'

module YEK
  class ReasonBuild

#=NAME
#
#ReasonBuild MD Processor - PureBuilder script for Markdown file.
#=SYNOPSIS
#
#  md_processor.rb [ _file_ ] [ -- _pandocoptions_ ... ]
#
#=DESCRIPTION
#
#MD Processor reads ARGF and process with pandoc.
#
#If +-s+ any _file_ given, MD Processor understands header with same style as PureDoc,
#modify timestamp, and write out to given _file_.
#
#If pandoc options given, MD Processor invoke pandoc with these options.
#Otherwise, MD Processor invoke pandoc 
#
# pandoc -t -s -p
#
    class MDProceessor
      
      def proc_header(file=nil)
        @file_content ||= ARGF.read
        # Any file given?
        if file && @file_content =~ /^##--.*$/ && $' =~ /^##--.*$/

          begin
            yax = $`.each_line.map {|i| i.sub(/^# /, "") }.join
            header_meta = YAML.load(yax) || Hash.new
    
            # Is Header missing last-update or since?
            if  ( ! header_meta.key?("last-update") ) || ( ! header_meta.key?("since") )
                
              now = Time.now
            
              modsince, modupdate = nil, nil

              # Set to since
              if ! header_meta.key?("since") 
                modsince = true
                header_meta["since"] = now
              end
            
              # Set to last-update.
              if ! header_meta.key?("last-update") 
                modupdate = true
              end
            
              # OK, Header is ready.
              # Open the file!
              File.open(file.first, "r+") do |f|
                content = f.gets(nil)
            
                if content.sub!(/^##--.*?^##--.*?$/m) {
                  el = $&.each_line.to_a # Get header texts.
                
                  el.insert(1, "# since : #{now.strftime '%Y-%m-%d %H:%M:%S %:z'}\n") if modsince # Add since if since was not exist.
                
	          # Update last update time if last-upadte is not set or last-update is older than mtime.
                  el.insert(1, "# last-update : #{File.mtime(file.first).strftime '%Y-%m-%d %H:%M:%S %:z'}\n") if modupdate# Add last update timestamp 
              
                  el.join
                }

                  # Write to file if updated.
                  f.seek(0)
	          f.truncate(0)
	          f.write( content )
                end
              end # Close file.
            end # Missing header
            
          rescue # YAML or IO Rescue.
            STDOUT.puts $!
          end
          
        end # file given.
      end #proc_header
      
      # Invoke pandoc, format, and out.
      def pandoc(options)
        
        outstr = nil
        
        # filter pandoc.
        IO.popen(( ["pandoc"] + options ), "w+") do |io|
          io.write @file_content
          io.close_write
          
          outstr = io.gets(nil)
        end
                                                  
        # subscribe content
        flag = false
        outstr = outstr.each_line.select do |line|
            
          if line =~ /^<\/body>$/
            flag = false
          end

          if line =~ /^<body>$/
            flag = true
            next false
          end
            
          flag
        end.join
        
        return outstr
        
      end
      
      
      def initialize
          
        pandoc_opt = nil
        
        # Get pandoc options from argv.
        if sep = ARGV.index("--")
          pandoc_opt = ARGV[(sep + 1) .. -1]
          ARGV.pop
        else
          pandoc_opt = [ "-t", "html", "-s", "-p" ]
        end
        
        proc_header( ( ARGV.empty? ? nil : ARGV.dup ) )
        
        doc = pandoc(pandoc_opt)
        print doc

      end
   
    end #MDProceessor
    
  end
end

YEK::ReasonBuild::MDProceessor.new

Manjaro LinuxでMarkdown

ManjaroでMarkdownを活用しようと思ったのだが、案外苦労したのでレジュメを兼ねて記載する。

MarkdownはGitHubでおなじみの書式だ。この書式については車輪の再開発となるので省略する。

その変換を行うのがmarkdownプログラムだが、この実装はRuby、Rythonなど他にも色々とある。だが、markdownは単純なプログラムで単純なHTMLを出力する。そのため、組み込んで使いやすいが、今回は完全なHTMLがほしい。

そこでpandocを使うことにした。だが、どうもHaskellまわりがぐちゃぐちゃしている。結局インストールしたパッケージは

  • ghc
  • alex
  • happy
  • pandoc-static

さらにPDF出力のために以下をインストール

  • texlive-most
  • texlive-langcjk

CSSファイルを指定してHTMLを出力。

pandoc \ # Pandoc
-s ~/doc/work/si/twitter.md \ # ソースファイルの選択
-t html5 \ # HTML5で出力
-o ~/tmp/twitter.html \ #出力ファイルパス
-c ~/doc/materials/markdown.css/github.css # CSSファイルの指定。埋め込みはしない。

TeX経由のPDF出力

pandoc \ # Pandoc
-s ~/doc/work/si/twitter.md \ #ソースファイル
-o ~/tmp/twitter.pdf \ #出力ファイル
-V documentclass=ltjarticle \ #日本語出力のためのオプション
--latex-engine=lualatex #LuaLaTeXを使用

フォント指定のためのTeXファイルを作成

\setmainfont{Rounded-X M+ 2c medium}
\setsansfont{07YasashisaGothic}
\setmonofont{Ricty}

フォント指定の上PDF出力

pandoc \ # Pandoc
-s ~/doc/work/si/twitter.md \ #ソースファイル
-o ~/tmp/twitter.pdf \ #出力ファイル
-V documentclass=ltjarticle \ #日本語出力のためのオプション
--latex-engine=lualatex #LuaLaTeXを使用
-H ~/tmp/fontdefine.tex #フォント設定を行ったTeXファイル