PDFWiki - Wiki 記法で PDF を生成する

これは,C# Advent Calendar 2011 参加記事です.現在,開催されている Advent Calendar の一覧は Advent Calendar 2011 (jp) 開催予定リスト - Life like a clown を参照下さい.

iTextSharp の勉強がてら,ちょっとしたアプリケーションを作ってみようと思い立ちました.今回選んだ題材は「Wiki 記法で記述されたテキストファイルを PDF に変換する」と言うものです.もうちょっと頑張る予定だったのですが,結局 2 日くらいしか費やせなかったので実用にはまだまだと言う所です(w

PDF と言うフォーマット自体は,ある (x, y) 座標に文字列を描画するなど絶対値を指定して描画する形なのですが,iText (iTextSharp) はこれをある程度抽象化して論理的に描画していけるようなクラス群を用意しています.iTextSharp のリファレンスが見当たらなかったので iText のリファレンスで代替しますが,API リファレンス によると,Section, Paragraph, List, Image, 等ひと通りのものが揃っているようです.PDFWiki では,これらのクラスを利用して変換する事を考えます.

今回は,Pukiwiki の記法の一部を実装します.具体的には,以下の通りです(括弧内は,変換する際に使用した iTextSharp のクラス).

  • パラグラフ (Paragraph) ..... 改行のみの行までを一つのパラグラフとして扱います.Pukiwiki では末尾に「~」を記載すると強制的に改行できますが,今回はこの実装は行っていません.
  • セクション (Chapter, および Section) ..... 行頭に「*」記号を付与するとセクションになります.Pukiwiki では,*** (サブサブセクション)までを許していますが,PDFWiki では特に制限はありません.
  • リスト (List) ..... シンボル型(行頭に「-」を付与)と数字型(行頭に「+」を付与)を実装しています.ただし,リストに関しては 1 段階までしか対応していません(++ のように記述されても無視される).
  • 画像 (Image) ..... PDFWiki では,#ref(...) の形で指定された場合,画像ファイルと見なして PDF に埋め込もうとします.

サンプル

細かい話は置いておいて,取りあえずサンプルを載せます.example.txt と言うテキストファイルを PDFWiki を用いて変換した結果が以下になります(変換する際には,example.jpg を同じフォルダに配置して下さい).引数で指定されたテキストファイルを PDF に変換すると言う簡単なプログラムなので,コマンドライン上で実行するか,変換したいファイルをドラッグ&ドロップして下さい.

example.pdf

IElement の各種クラスの使い方

この辺りからは,実装に絡むお話です.以下に,IElement の各種クラスを使った簡単なサンプルを記述します.

// using iTextSharp.text;
// using iTextSharp.text.pdf;

var basefont = BaseFont.CreateFont(@"c:\windows\fonts\msmincho.ttc,1", BaseFont.IDENTITY_H, true);

var title = new Paragraph("チャプターのタイトル", new Font(basefont, 16, Font.BOLD));
var chapter = new Chapter(title, 1);
var someText = new Paragraph("チャプターの本文です。"", new Font(basefont, 10));
chapter.Add(someText);

var title1 = new Paragraph("This is Section 1 in Chapter 1", new Font(basefont, 12, Font.BOLD));
var section = chapter.AddSection(title1);
var someSectionText = new Paragraph("This is some silly paragraph in a chapter and/or section.", new Font(basefont, 10));
section.Add(someSectionText);

基本的には使用したい要素に当たるクラスのインスタンスを生成して,親に当たるクラスに Add していくだけなのですが,Section クラスのみ少し特殊になっています.Section クラスは,最も親の要素(セクション,サブセクション,サブサブセクション,...,のセクションに相当するもの)のみ Chapter クラスで定義するようになっています.それ以降は,親の Section (Chapter 含む) クラスの AddSection メソッドを利用して階層構造を表していきます.尚,AddSection メソッドを用いてインスタンス化された Section クラスは,改めて Add する必要はないようです.

実装方針

最後に,メモ代わりに簡単な実装方針を記述しておきます.PDFWiki は,各種 Wiki 記法をパースする Parser,パースされたデータを実際に PDF に変換する Converter,変換された PDF の各種見た目を定義する Style と大きく 3 つのクラスに分かれます(これらのクラスは pdfwiki-engine 下に存在します).

Parser

Parser クラスは,各種 Wiki 記法をパースするための抽象クラスです.

// using System;
// using System.Text;
// using System.IO;
// using Container = System.Collections.Generic.List<PDFWiki.Element>;

namespace PDFWiki {
    public class Element {
        public ElementType Type { get; set; }
        public int Depth { get; set; }
        public string Value { get; set; }
    }

    public abstract class Parser {
        public abstract void Run(TextReader src);
        public Container Elements { get; set; }
    }
}

実際に何らかの Wiki 記法をパースするためのクラスを作成する際には,この Parser クラスを継承して Run メソッドを実装します.例えば,今回作成した Pukiwiki の記法をパースするクラスは PukiwikiParser となります.

Converter

Converter クラスは,Parser クラスによってパースされたデータを元に実際に PDF へ変換するためのクラスです.

namespace PDFWiki {
    public class Converter {
        public Converter(Parser parser);
        public Converter(Parser parser, Style policy);
        public void Run(string path);
        public void Clear();
        public Style Policy { get; set; }
    }
}

コンストラクタに何らかの Wiki 記法をパースした Parse クラスのインスタンスを指定し,Run メソッドに出力先パスを指定すると言うものです.Style クラスに関しては後述します.

Style

Style クラスは,PDF に変換する際に,各種要素の見た目を定義するためのクラスです.

namespace PDFWiki {
    public class Style {
        public SectionStyle Chapter { get; set; }
        public SectionStyle Section { get; set; }
        public ParagraphStyle Paragraph { get; set; }
        public ListStyle List { get; set; }
        public NumericListStyle NumericList { get; set; }
        public ImageStyle Image { get; set; }
    }
}

何が設定可能か等の詳細は Style を参照下さい.現在は決め打ちなのですが,いずれ設定ファイル等でカスタマイズ可能なように修正していく予定です.

今後の予定

  • Style クラスの整備.具体的には,設定可能な項目とカスタマイズ可能にするための検討.
  • 表,整形済みテキスト(pre タグ)辺りの実装.
  • リストの多段指定を可能なように修正.
  • インライン系の要素への対応方法を検討.