概要

PukiWiki記法およびはてな記法で書かれたのテキストをXHTML(XML)へ整形するコマンドラインツールです。

当サイトのページはpakinaを拡張したものを使って生成しています。

ダウンロード

ソース中に含まれる各ライブラリはMIT X11ライセンスでのリリースとなります。

  • バイナリ
    • ファイル名
      pakina-0.4-bin.tar.gz
      サイズ
      82.9kB
      sha1sum
      4b2ec12d20968ce93dd71ebac668270720b076ed
    • ファイル名
      pakina-0.3-bin.tar.gz
      サイズ
      39.2kB
      sha1sum
      4a16321de3588a6d5013eeb1f2d410c766f1215b
  • ソース
    • ファイル名
      pakina-0.4.tar.bz2
      サイズ
      75.6kB
      sha1sum
      05a9a20cb0a1f657336dda00637c587463e5ce84
    • ファイル名
      Smdn.Formats.Notations-0.3-1.tar.bz2
      サイズ
      27.5kB
      sha1sum
      846af087c80630216ffb4c015446d6e05f475dff

必須環境

  • .NET Framework 2.0以上またはMono 2.0以上

動作確認済み環境

  • Ubuntu 9.10 + Mono 2.7 (r153937)
  • 確認はしていませんが、Windowsでも恐らく動作すると思います

変更履歴

version 0.4

  • 機能追加
    • 見出しで区切られたセクションをdiv要素でセクション化して整形するオプション(--parser-enable-sectioning)を追加
  • 修正・改善
    • ネストしたdl要素、dt,dd要素が不正な形式で整形される不具合を修正
    • パーサの文字列処理を改善

version 0.3

  • 公開

簡単な使い方

PukiWiki記法のファイルFrontPage.txtを変換し、FrontPage.xhtmlにUTF-8で出力するには、

pakina.exe -i FrontPage.txt -if pukiwiki -o FrontPage.xhtml -oe utf-8

とします。

また、標準入出力を使った変換もできるので、nkfを使ってEUC-JP版PukiWikiのFrontPageを変換するには、

nkf -Ew wiki/46726F6E7450616765.txt | mono pakina.exe -if pukiwiki > FrontPage.xhtml

とできます。 ただ、pakinaで入力のエンコーディング指定もできるので、単に、

mono pakina.exe -i wiki/46726F6E7450616765.txt -ie euc-jp -if pukiwiki > FrontPage.xhtml

とすることもできます。

オプションの一覧と説明は

pakina.exe --help

で表示できます。

出力サンプル

入力ファイル Sandbox.txt
#nofollow
#norelated
* SandBox: お砂場(練習ページ)です [#v299c566]
|SEO SPAMボットの攻撃対象になるため、デフォルトでこのページは凍結されています。|

#contents

* ''見出し'' [#n151e840]

* 大見出し 1 [#a6dc7f79]

** 中見出し 1-1 [#qa1c1e8c]

*** 小見出し 1-1-1 [#k28ac390]
- 項目1
- 項目2
- 項目3

段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
段落1段落1段落1段落1段落1段落''強調''1段落1段落1段落1段落1段落1
段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1

段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2

** 中見出し 1-2 [#zcbf4116]
:用語1|いろいろ書いた解説文1と''強調単語'' ((注釈1: WikiName))
段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
:用語2|いろいろ書いた解説文2 ((注釈2: [[SandBox]]へのリンク))
:用語3|いろいろ書いた解説文3 ((注釈3: 注釈の中にも((注釈の注釈を))作ることができます))

-----------------------------------------
コマンドライン
mono pakina.exe -i Sandbox.txt -if pukiwiki -o Sandbox.xhtml -oe utf-8
出力ファイル Sandbox.xhtml
<?xml version="1.0" encoding="utf-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Sandbox.txt</title>
  </head>
  <body xmlns:pukiwiki="http://smdn.invisiblefulmoon.net/works/tools/pakina/notations#pukiwiki">
    <pukiwiki:plugin pukiwiki:type="block" pukiwiki:name="nofollow" pukiwiki:arguments="">#nofollow</pukiwiki:plugin>
    <pukiwiki:plugin pukiwiki:type="block" pukiwiki:name="norelated" pukiwiki:arguments="">#norelated</pukiwiki:plugin>
    <h2> SandBox: お砂場(練習ページ)です <a id="v299c566" href="#v299c566" title="v299c566" class="anchor_super">&dagger;</a></h2>
    <table>
      <tbody>
        <tr>
          <td>SEO SPAMボットの攻撃対象になるため、デフォルトでこのページは凍結されています。</td>
        </tr>
      </tbody>
    </table>
    <pukiwiki:plugin pukiwiki:type="block" pukiwiki:name="contents" pukiwiki:arguments="">#contents</pukiwiki:plugin>
    <h2> <strong>見出し</strong> <a id="n151e840" href="#n151e840" title="n151e840" class="anchor_super">&dagger;</a></h2>
    <h2> 大見出し 1 <a id="a6dc7f79" href="#a6dc7f79" title="a6dc7f79" class="anchor_super">&dagger;</a></h2>
    <h3> 中見出し 1-1 <a id="qa1c1e8c" href="#qa1c1e8c" title="qa1c1e8c" class="anchor_super">&dagger;</a></h3>
    <h4> 小見出し 1-1-1 <a id="k28ac390" href="#k28ac390" title="k28ac390" class="anchor_super">&dagger;</a></h4>
    <ul>
      <li> 項目1</li>
      <li> 項目2</li>
      <li> 項目3</li>
    </ul>
    <p>段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落<strong>強調</strong>1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1</p>
    <p>段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2</p>
    <h3> 中見出し 1-2 <a id="zcbf4116" href="#zcbf4116" title="zcbf4116" class="anchor_super">&dagger;</a></h3>
    <dl>
      <dt>用語1</dt>
      <dd>いろいろ書いた解説文1<strong>強調単語</strong> <em>注釈1: WikiName</em>段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1</dd>
      <dt>用語2</dt>
      <dd>いろいろ書いた解説文2 <em>注釈2: <pukiwiki:alias pukiwiki:name="SandBox" pukiwiki:aliasto="">[[SandBox]]</pukiwiki:alias>へのリンク</em></dd>
      <dt>用語3</dt>
      <dd>いろいろ書いた解説文3 <em>注釈3: 注釈の中にも<em>注釈の注釈を</em>作ることができます</em></dd>
    </dl>
    <hr />
  </body>
</html>

テスト兼デモ用のページPukiWikiおよびはてなもご参照ください。※一部で本ツールに含まれない拡張を使っている箇所があります

注意点・補足

このツールはPukiWiki記法のテキストをXHTMLにフォーマットするだけの機能しかありません。 PukiWikiで作成されたものから完全なXHTMLページを生成するためには、他のXHTMLエディタと組み合わせるかフォーマッタを拡張して使用する必要があります。

  • ほとんどのプラグインはXHTMLには展開しません
    • 代わりに、他のXMLエディタで編集・置換できるようpukiwiki:plugin要素に変換します
    • &br;や&smile;などの文字参照に類するもの、&sub{};や#refなどはXHTMLに変換します
  • 装飾等は行いません
    • LEFT: SIZE: &color(){};といった記法で定義されているものは装飾済みのXHTMLに変換します

またページ間のリンク等や添付ファイルの処理も行いません。

  • エイリアス・ページ名などはアンカーには変換しません
    • エイリアス・ページ名はプラグイン同様、pukiwiki:alias要素に変換します
    • WikiNameは通常のテキスト、InterWikiは通常のアンカーとして変換します
  • 添付ファイルを扱う機能はありません

そのほかの注意点。

実装予定

  • XHTML以外のフォーマットでの出力機能(HTML5、プレーンテキストなど)
  • 記法間の相互変換機能
  • PukiWikiの$line_breakの設定をオプションで指定できるようにする
  • はてな記法のパーサ・フォーマッタはおまけ程度に作ったもので、不完全です
    • 将来的にははてな記法のサポートを止めて、adiaryのさつき記法に置き換えるかもしれません

なお、GUIは必要ないと思うので今のところ実装する予定はありません。

使い方の詳細

入出力のオプション

-i, -o
入力ファイル・出力ファイルを指定します。 省略した場合、もしくは'-'を指定した場合はファイルの代わりに標準入力・標準出力を使います。
-if, -of
入力ファイル・出力ファイルの記法を指定します。 デフォルトは'pukiwiki'です。 最新のバージョンでは'pukiwiki'および'hatena'のみ使用可能です。
--list-notationsで使用できる記法の一覧を表示します。
-ie, -oe
入力ファイル・出力ファイルのエンコーディングを指定します。 入力のデフォルトはシステムで使われているエンコーディング、出力のデフォルトはUTF-8です。
出力ファイルにBOMを出力するかどうかも-oeオプションで指定できます。 詳しくは--list-charsetsで確認してください。

インデントと改行のオプション

--no-indent
インデントおよび改行をしないようにします。
--indent-spaces[=<number>]
空白を使用してインデントするようにします。 オプションで空白の個数を指定できます。
--indent-tabs[=<number>]
タブを使用してインデントするようにします。 オプションでタブの個数を指定できます。
--newline[=(CR/LF/CRLF)]
改行するようにします。 オプションで改行文字を指定できます。

XHTMLのオプション

--title=<title>
<title>タグのテキストを指定します。 デフォルトでは入力ファイル名を使用します。
--fragment
XML宣言と<html>タグおよび<head>タグを省略して出力します。 <body>タグのみが出力されます。
--doc-element=<name>
--fragmentを指定した場合のみ有効です。 出力するXMLのルート要素の名前を指定します。 デフォルトはbodyです。
--doc-namespace-uri=<uri>
--fragmentを指定した場合のみ有効です。 出力するXMLのルート要素の名前空間のURIを指定します。 デフォルトはXHTML(http://www.w3.org/1999/xhtml)です。
--doc-prefix=<prefix>
--fragmentを指定した場合のみ有効です。 出力するXMLのルート要素の接頭辞を指定します。 デフォルトは指定なしです。
--preserve-pre-whitespaces
<pre>タグに属性'xml:space="preserve"'を追加します。 XHTMLのDTDを参照しないXMLパーサが、<pre>タグ内の空白や改行を無意味なものと判断して勝手に削除してしまわないようにしたい場合などに指定します。

その他のオプション

-h, --help
すべてのオプションとデフォルト値を表示します。

複数ページの一括変換(改ページ)

入力に改ページ(Form Feed, \u000c, Ctrl+L)の制御文字のみの行を含む場合、それを区切りとして複数のページを変換することが出来ます。 例えばスクリプトからいくつものページを変換したい場合などで改ページを用いると、ページ毎にpakinaを起動する場合よりもオーバーヘッドが減るのでより早く処理できます。

改ページを用いた場合、入力に改ページが見つかった時点までのテキストを変換した結果が出力され、最後に改ページで終わる行が出力されます。 次のコードは、rubyスクリプトで複数のページを変換・表示する例です。

pages = [
<<PAGE1 ,
*page1
+test
++test
PAGE1

<<PAGE2 ,
*page2
 pre
 pre
 pre
PAGE2

<<PAGE3 ,
*page3
page3
PAGE3
]

io = IO.popen("mono pakina.exe -if pukiwiki --fragment", "r+")

pages.length.times do |i|
  io.write(pages[i])

  if i == pages.length - 1
    io.close_write
  else
    io.puts "\x0c\n" # CTRL+L
  end

  print "\n=========[page #{i}]=========\n"

  while true
    line = io.gets

    break unless line

    ff = line.index("\x0c")

    if ff
      print line[0, ff]
      break
    else
      print line
    end
  end
end

io.close_read

出力結果はこのようになります。


=========[page 0]=========
<body xmlns="http://www.w3.org/1999/xhtml">
  <h2>page1</h2>
  <ol>
    <li>test<ol><li>test</li></ol></li>
  </ol>
</body>
=========[page 1]=========
<body xmlns="http://www.w3.org/1999/xhtml">
  <h2>page2</h2>
  <pre>pre
pre
pre</pre>
</body>
=========[page 2]=========
<body xmlns="http://www.w3.org/1999/xhtml">
  <h2>page3</h2>
  <p>page3</p>
</body>

なお、改ページを使う場合はページ毎にオプションを変えることはできません。

パーサ・フォーマッタの拡張方法

pakinaは実行ファイルのあるディレクトリとカレントディレクトリにあるアセンブリを読み込みます。 アセンブリにSmdn.Formats.Notations.Notationクラスの派生クラスが含まれている場合、そこからパーサ・フォーマッタを作成してpakinaで使用できるようになっています。

PukiWiki記法フォーマッタの拡張方法

ここでは例として, &date;, &time;, &now;の各プラグインを現在の日付、時刻、日時を表すテキストに変換するように拡張する方法を紹介します。

まずSmdn.Formats.Notations.PukiWiki.Notationクラスの派生クラスを作成し、拡張したフォーマッタExtendedPukiWikiXhtmlFormatterのインスタンスを返すようにします。

public class ExtendedPukiWikiNotation : Smdn.Formats.Notations.PukiWiki.Notation {
  public override Smdn.Formats.Notations.XhtmlFormatter CreateXhtmlFormatter()
  {
    return new ExtendedPukiWikiXhtmlFormatter();
  }
}

次に、ExtendedPukiWikiXhtmlFormatterの実装を記述します。 プラグインの場合、FormatPluginNodeメソッドをオーバーライドすることで任意の形式にフォーマットできます。

using System;
using System.Collections.Generic;
using System.Xml;

using Smdn.Formats.Notations.PukiWiki;
using Smdn.Formats.Notations.PukiWiki.Dom;

public class ExtendedPukiWikiXhtmlFormatter : Smdn.Formats.Notations.PukiWiki.XhtmlFormatter {
  protected override IEnumerable<XmlNode> FormatPluginNode(XmlDocument xml, Plugin plugin)
  {
    switch (plugin.Name) {
      case "date":
        return new XmlNode[] {xml.CreateTextNode(string.Format("built date: {0}", DateTime.Now.ToLongDateString()))};
      case "time":
        return new XmlNode[] {xml.CreateTextNode(string.Format("built time: {0}", DateTime.Now.ToLongTimeString()))};
      case "now":
        return new XmlNode[] {xml.CreateTextNode(string.Format("built: {0}", DateTime.Now.ToString()))};
      default:
        return base.FormatPluginNode(xml, plugin);
    }
  }
}

最後に、上記二つのコードを一つのアセンブリとしてコンパイルし、pakina.exeと同じディレクトリに配置します。 うまくいけば、--list-notationsで

available notations:
  pukiwiki        ExtendedPukiWikiNotation (MyExtension, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
  hatena          Smdn.Formats.Notations.Hatena.Notation (Smdn.Formats.Notations.Hatena, Version=0.3.3445.24815, Culture=neutral, PublicKeyToken=null)
total 2 notations

のように表示されるはずです。 変換結果も、

$ echo "&time;&br;&now;" | mono ./pakina.exe --fragment
<body xmlns="http://www.w3.org/1999/xhtml">
  <p>built time: 17:44:33<br />built: 2009/06/07 17:44:33</p>
</body>

となります。

独自のパーサ・フォーマッタの作成方法

準備中。 Smdn.Formats.Notations.PukiWikiのソースを参照してください。