2018 年の振り返り

2018 年も残り僅かとなりました。この記事では、私の 1 年間の活動を振り返ります。

GitHub Activity

GitHub

まず、GitHub Activity に目を向けると、2018 年は 2,321 コミット (contributions) と言う結果になりました。GitHub の Activity を意識し始め ておよそ 2 年が経過しましたが、特別な理由がない限り毎日コミットし続けると言う目標を今年も概ね達成できた事は何よりでした。

レコーディング・ダイエットのような形で自らのコミットを記録し続けて気付いた点として、平均的に見た場合、1 日 10 コミットは想像以上に大変 と言うものが挙げられます。もちろん、1 コミット当たりの修正量によって総作業量は変わってきますが、この事実は自分がそれまでに漠然と想像していた量よりも随分と少ないと言うのが率直な感想です。何らかのソフトウェアを開発する際にも、この事実を念頭に置きながら大雑把に見積もる事で、当初想定する期間と現実とのギャップが埋まってきたのは良い副産物であったように感じます。

リポジトリおよびコードの整理

2018 年は、2017 年の中頃から始まった これまで CubeSoft として公開してきたソフトウェアに対する開発サイクルの再構築 を推し進めた年となりましたが、その中の一つに、リポジトリおよびコードの整理があります。CubeSoft としては現在、下記の 8 個をアクティブな公開リポジトリとして管理しています。非公開リポジトリや fork したリポジトリを含めるともう少し存在しますが、ここ 2 年は多くの時間をこれらのリポジトリに注いでいます。

コードの整理と言う観点で見ると、2018 年は コメントの一部を英語で書くように方針転換した 事が大きな特徴の一つです。私自身は「下手な英語よりは、まともな日本語の方がマシ」と言う価値観であるため、これまではコードのコメントも日本語で記述していました。しかし、Visual Studio を始めとした最近の開発環境には、JavadocXML Documentation などの構造化されたコメントを解析してポップアップ表示する機能が標準搭載される事も珍しくなくなってきました。

XML Documentation

このため、上記のようなコメントを日本語で記述した場合、該当コードを利用するユーザの開発環境では、そのユーザの言語設定に関わらず日本語が表示される事となります。前述したリポジトリもその多くは利用可能なライブラリとして NuGet に登録しており、その結果、少しずつですが海外のユーザにも試して頂けているようです。このような状況を鑑みると日本語のコメントが表示されてしまうのは好ましくないと思い、方針の転換を決定しました。

方針転換に際しては当初、英語と日本語、両方のコメントを管理する事も選択肢として検討しました。しかし、XML Documentation では複数言語のコメントを管理するための良い仕組みが見当たらなかった事、管理するコメント量が増えるにつれて嘘のコメントの危険性が増加する事、そして何より、私自身、まったく同じコメントが複数言語で記述されたソースコードなど見たくなかった事などの理由で、日本語のコメントを捨てる事にしました。2018 年現在、既存のコードには大量の日本語コメントが残ってはいますが、この辺りは少しずつ修正していこうと思います。

CI の徹底およびテストカバレッジの可視化

Codecov

ユニットテストで振り返るプログラマとしての自分史 でも触れましたが、開発サイクルを再構築して大きく変わった点は AppVeyor を利用した継続的インテグレーション (CI: Continuous Integration) の徹底、および Codecov によるテストカバレッジの可視化です。

テストカバレッジの可視化に関しては、長年、自分の中にも「テストカバレッジの数値を気にするようになると、数値を上げるためだけのユニットテストを記述するようになるのではないか?」という不安が常にありました。そして実際、数値を上げる以外の意義を感じられないユニットテストを数多く記述し、これに何の意味があるのか……と言う感情を抱く事も何度もありました。

しかし、面白い事に 2 年ほどリファクタリングや修正を続けていると、数値を上げる以外に意義を感じられないユニットテストに何度も救われる事となりました。例えば、View に表示するメニューやメッセージの文字列 などは、記述時は「定義ファイルに決め打ちで記述するのだし、テストするまでもなく明白」のような気がしていました。しかし、ある時、何らかの修正時にうっかり定義ファイルの記述位置がずれてしまい、さらに不幸な事に、ビルド時にも実行時にもエラーが発生しなかった事がありました。このケースに関しては、ユニットテストがなければ表示内容が異なっている事に、しばらく気付く事ができなかったのではないかと言う気がします。

テストカバレッジの数値を上げるためだけの近視眼的なテストコードを記述する事によって、それよりも重要なテストコードを見逃すのではないかと言う不安は依然としてあります。ただ最近は、ある程度は仕方がないと割り切り、以下のような指針で実装およびテストコードの記述を行っています。

  1. 初期リリースまでは、取り合えずテストカバレッジの数値(80%~95% 程度)を指標としてテストコードを記述する。この結果、初期リリース時点では、正常ケースおよび容易に予想可能な異常ケースのテストに留まる事が多い。
  2. リリース後に何らかの不都合が発覚した場合、必ず最初に該当の不都合が再現するテストコードを記述する。そして、テストを実行してレッド・シグナルを確認する。
  3. 該当部分の実装コードを修正し、テストを実行してグリーン・シグナルを確認する。

このサイクルによって、完全ではないにしても「少しずつだが、良くなってはいる」事を実感できるのは、テストコードを含めた保守を続けていく、あるいは習慣化する上でプラスに働いていると思います。

CubePDF シリーズの大改修

ソフトウェア別で見ると、2018 年は CubeSoft において最も多くの人達に利用されている CubePDF を始め、CubePDF シリーズの開発を継続的に続けていけるよう、様々な面において保留となっていた事柄に取り組めた 1 年であり、その意味でも充実度は高かったように思います。

具体的に挙げるとすると、GUI の英語化は大きな特徴の一つでしょうか。特に CubePDF に関しては、全て日本語表示であった頃から海外ユーザが存在していたようで、そう言った事も含めて複数言語の View を開発するためのパターンが自分の中で確立できたのは良かったと思います。

また、CubePDF には ユーザーズマニュアル と言う PDF を同梱していますが、この基となるドキュメントを markdown 形式で書き直す事でバージョン管理に含められるようになったのも、個人的には満足した出来事です。

CubePDF に含まれる Ghostscript を手動で更新する方法

先日、Ghostscript が 9.26 にバージョンアップしました。リリースノートによると Ghostscript 9.26 では再び、セキュリティ問題に関する修正が実施されているようです。CubePDF も、近日中に Ghostscript のバージョンアップを含めた最新版をリリースする予定ですが、この記事では CubePDF に含まれる Ghostscript を手動で更新する方法について記載します。

まず、CubePDF のバージョンを確認して下さい。CubePDF のバージョンは、適当なものを CubePDF プリンタで印刷し、表示されるメイン画面の「その他」タブで確認する事ができます。

CubePDF バージョン情報

該当項目が存在しない場合は、CubePDF のバージョンが古いため最新版にアップデートして下さい。CubePDF は 1.0.0RC12 にてフォルダ構成等を変更しているので、バージョンが 1.0.0RC12 未満(1.0.0RC11 や 0.9.9β など)の場合は、必ず最新版に更新するようお願いします。バージョンが問題なければ、その右隣に記述されている文字列(x86 または x64)を覚えておいて下さい。

次に、Releases - Cube.Pdf - GitHub へ移動します。ここで、先ほどのバージョン表示画面で確認した x86/x64 に対応する Ghostscript の最新版 Zip ファイル(例えば、gs-9.26-x86.zip など)をダウンロードして下さい。

Ghostscript の差し替え方法

ダウンロード終了後、解凍・展開したフォルダにある gsdll32.dll および各種フォルダを全て CubePDF のインストールフォルダ(初期設定では C:\Program Files\CubePDF)にコピーして差し換えると、手動による Ghostscript の更新作業は完了です。

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:コマンドラインの引数をそのまま指定するせいか、最初の引数は無視されます