Ghostscript を C# から利用する

先日、7-Zip ライブラリとしての CubeICE と言う記事を公開しましたが、ライブラリ化の試みは CubePDF シリーズでも行っています。CubePDF シリーズは Cube.Pdf と言うリポジトリで管理しており、ライブラリ部分は Libraries に、最終的なアプリケーション部分は Applications に配置しています。この記事では、CubePDF ライブラリの内 Ghostscript を C# から利用できるものを紹介します。

事前準備

Cube.Pdf および Cube.Pdf.Ghostscript は NuGet 経由で取得する事ができますが、この中には gsdll32.dll が含まれていないので別途ダウンロードして実行ディレクトリに配置して下さい。gsdll32.dll は www.ghostscript.com からインストーラ経由で取得できる他、Zip 形式で圧縮したものを GitHub Releases にも公開しています。

尚、C# 等から Unmanaged な dll を利用する場合 x86/x64 の設定が問題になるので、特に Any CPU でビルドする場合、「32 ビットを優先 (Prefer 32-bit)」の項目が有効かどうかを含めて、どちらの dll を必要とするか注意して下さい。また、オリジナルの x64 版は gsdll64.dll と言うファイル名となっているので、gsdll32.dll と言うファイル名に変更して利用して下さい(GitHub Releases で公開しているものは変更済みです)。

簡単なサンプルコード

まずは簡単なサンプルとして、PostScript ファイルを PDF に変換するコードを記載します。PDF に変換する場合は PdfConverter クラスを利用します。ページサイズや解像度、埋め込み画像の圧縮方式などを設定するプロパティが用意されているので、必要な場合は値を設定し、Invoke メソッドを実行する事で変換が完了します。

// using Cube.Pdf.Ghostscript;
var converter = new PdfConverter
{
    Paper        = Paper.Auto,
    Orientation  = Orientation.Auto,
    ColorMode    = ColorMode.Rgb,
    Resolution   = 600,
    Compression  = Encoding.Jpeg,
    Downsampling = Downsampling.None,
    Version      = new Version(1, 7),
};
converter.Invoke(@"path\to\src.ps", @"path\to\dest.pdf");

Cube.Pdf.Ghostscript の詳細

Ghostscript API では gsapi_init_with_args() と言う関数で変換処理を実行しますが、この関数はコマンドライン版とまったく同じ引数を指定する事ができます*1。Ghostscript のコマンドラインは、下記のように 3 種類に大別されます。

gs <通常オプション> -c <PostScript コード> -f <入力ファイル>

Cube.Pdf.Ghostscript の基底クラスである Converter は、上記のコマンドラインを生成して変換を実行するための非常に薄いラッパーとなっています。

public class Converter
{
    public Converter(Format format);
    public ICollection<Argument> Options;
    public ICollection<Code> Codes;
    public void Invoke(string src, string dest);
}

使い方としては、まずコンストラクタに変換対象となる Format を指定します。次に、Options プロパティには通常オプション、Codes プロパティには PostScript コードをそれぞれ必要な数だけ指定します。そして、最後に Invoke メソッドに変換元ファイル (PS, EPS, PDF) および保存先のパスを指定して変換を実行します。

通常時は基底クラスである Converter をそのまま使用するよりも、各種継承クラスを必要に応じて使用する方が便利です。Cube.Pdf.Ghostscript では現在、以下の変換クラスを提供しています。これらのクラスは、ページサイズや画像の圧縮形式などを指定するためのプロパティを用意しており、設定値に応じた Ghostscript 引数を自動的に追加します。

対応するプロパティが存在せず、自ら Ghostscript の引数を追加する必要がある場合、Options 引数に Argument オブジェクトを追加します。Argument クラスのコンストラクタは下記のようになっています。

public class Argument
{
    public Argument(string name, bool value);
    public Argument(string name, int value);
    public Argument(string name, string value);
    public Argument(char type);
    public Argument(char type, int value);
    public Argument(char type, string name);
    public Argument(char type, string name, bool value);
    public Argument(char type, string name, int value);
    public Argument(char type, string name, string value);
    public Argument(char type, string name, string value, bool literal);
}

Ghostscript の引数一覧は How to Use Ghostscript および、そのリンク先で確認する事ができます。

-dAutoRotatePages=/PageByPage

例えば、上記のような Ghostscript 引数を生成する場合、以下のようにコンストラクタの各引数を指定して Argument オブジェクトを生成します。

new Argument('d', "AutoRotatePages", "PageByPage", true);

最後の引数は、値に "/" (スラッシュ)が必要な場合は true、それ以外は false を指定します。尚、ほとんどの場合、上記以外のコンストラクタを利用する事で、自ら "/" の有無を判定しなくても良いように設計しています。

その他の注意事項

Ghostscript の引数は基本的に日本語などのマルチバイト文字を指定する事はできないようです。そのため、Invoke メソッドに指定する引数には ASCII のみで指定するようにして下さい。

また、マルチバイト文字におけるもう一つの問題は一時フォルダです。Ghostscript は作業フォルダとして TEMP 環境変数の値を利用しますが、Windows のユーザ名に日本語が混在していると問題になる事があります。この問題を回避するために、Converter クラスには WorkDirectory と言うプロパティを設定しています。Converter クラスは、このプロパティに値が設定されている場合、Invoke メソッドの実行中のみ TEMP 環境変数を設定された値に変更します。

*1:コマンドラインの引数をそのまま指定するせいか、最初の引数は無視されます