今年お世話になったプリントデバッグ手法2選

## PDL によるプリントデバッグ

時として印刷に用いるデータと印刷物とが一致しない場合があります。例えばプリンタドライバによる画像や中間色の扱いの差によって、画像化されたバーコードに混ざる中間色がノイズとなり、バーコードの読み取り精度が落ちる、ということがありました。

印刷に用いるデータが PDF だろうと PNG だろうとプリンタがどう解釈するのか分かりません。こんなときに使えるのがページ記述言語（PDL）のデータを使ったプリントデバッグです。PDL データを画像としてレンダリングすることで、紙に印刷することなくプリンタドライバ等による挙動の変化を確認できます。

PDL データは Windows であればプリンタドライバの設定を変更することでファイル化、もしくは表示できます。ただプリンタの機種により手順が異なるので調査しました。

### やり方

今回は日本国内で入手し易そうな A4 モノクロレーザプリンタを対象としました。どれもプリンタの実機は必要ありませんが、以下の準備をする必要があります。

1.  プリンタドライバを入手
2.  プリンタポートは FILE: を指定してインストール
3.  ファイル化した PDL データをゲット！

印刷に用いるデータは「aiueo」と書いたテキストファイルです。メモ帳から印刷して PDL データを作成します。
ではメーカ別で現行機種と思われる製品を1つずつ見ていきます。

#### OKI

-   機種: [B432dnw](https://www.oki.com/jp/printing/products/mono/b432dnw/)
-   PDL: PCL6
-   ドライバ名: OKI B412(PCL6)
-   バージョン: 1.1.4

デフォルトで生成できる PDL データは [GhostPCL](https://www.ghostscript.com/doc/current/WhatIsGS.htm#GhostPCL) だとレンダリングできません。
プリンタドライバの詳細設定 > 印刷モードで「イメージ」から「PCL」に変更することで正しくレンダリングできます。

<img width="200" alt="oki.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/ae7fff8c-b828-ee5a-424f-eea517f555e4.png">

#### Canon

-   機種: [LBP321](https://cweb.canon.jp/satera/lbp/lineup/a4-mono/322i-321/)
-   PDL: LIPS LX
-   ドライバ名: Canon Generic Plus LIPSLX
-   バージョン: 2.1.0

この PDL については [【レーザービームプリンター】LIPS4、LIPS LX、CAPT、CARPS2の違い](https://faq.canon.jp/app/answers/detail/a_id/46076/~/%E3%80%90%E3%83%AC%E3%83%BC%E3%82%B6%E3%83%BC%E3%83%93%E3%83%BC%E3%83%A0%E3%83%97%E3%83%AA%E3%83%B3%E3%82%BF%E3%83%BC%E3%80%91lips4%E3%80%81lips-lx%E3%80%81capt%E3%80%81carps2%E3%81%AE%E9%81%95%E3%81%84) を参照。
基本設定 > 出力方法で「編集+プレビュー」を選択して印刷することで Canon PageComposer というアプリが起動してレンダリング結果を確認できます。

<img width="300" alt="canon.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/353f60c3-3582-418b-8fda-4b887d17d6eb.png">


#### 富士ゼロックス

-   機種: [P360 dw](https://www.fujixerox.co.jp/product/printer_m/docuprint_p360dw)
-   PDL: ?
-   ドライバ名: FX DocuPrint P360 dw
-   バージョン: 1.2.0

印刷設定から「印刷プレビュー」のチェックボックスがあります。これを有効にして印刷することでレンダリング結果を確認できます。

<img width="256" alt="xerox.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/3ad86027-8e25-24eb-52ae-cfc79952944b.png">


使用してる PDL について製品仕様ページに具体的な名前が書いてありません。ファイル的には PCL っぽいですが [GhostPCL](https://www.ghostscript.com/doc/current/WhatIsGS.htm#GhostPCL) ではレンダリングできません。

```
Y:\ghostpcl-9.50-win64>find "PCL" fx-docuprint-p360dw.prn

---------- FX-DOCUPRINT-P360DW.PRN
@PJL ENTER LANGUAGE=PCL

Y:\ghostpcl-9.50-win64>gpcl6win64.exe fx-docuprint-p360dw.prn
+ c:\users\cjl\artifex\gs-release\9.50\ghostpdl-9.50\pcl\pcl\rtgmode.c:564: set_compression_method(): unsupported mode 1030

End of page 1, press <enter> to continue.


Y:\ghostpcl-9.50-win64>
```

#### RICOH

-   機種: [SP 4510](https://www.ricoh.co.jp/printer/sp/4510_4500/)
-   PDL: RPCS
-   ドライバ名: RICOH SP 4510 JPN RPCS
-   バージョン: 3.1.0.0

もしかして印刷プレビュー機能や RPCS レンダラとかがない？


#### RISO

-   機種: [LP3045DN](https://www.riso.co.jp/product/prioa/lp3045dn/)
-   PDL: PCL5e?, PCL XL?
-   ドライバ名: RISO Prioa LP3045DN, RISO Prioa LP3045DN KX
-   バージョン: 6.3.22.6

[GhostPCL](https://www.ghostscript.com/doc/current/WhatIsGS.htm#GhostPCL) でレンダリングできてない…？

(PCL5e? / RISO Prioa LP3045DN)
<img width="250" alt="riso-mini.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/10c460cb-69f9-1831-6647-eb04a40ca590.png">

(PCL XL? / RISO Prioa LP3045DN KX)
<img width="250" alt="riso-xl.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/c2741306-6396-5c60-35ec-c5fb6ce702f8.png">


データ的には PCL みたいなんだけど…

```
Y:\ghostpcl-9.50-win64>find "PCL" lp3045*.prn

---------- LP3045DN-PCL5E.PRN
@PJL ENTER LANGUAGE = PCL

---------- LP3045DN-PCLXL.PRN
@PJL ENTER LANGUAGE=PCLXL
!R!SPSZ0,0;SIR2;EXIT;) HP-PCL XL;3;0;Comment Copyright(C) 2000 Kyocera Corporation

Y:\ghostpcl-9.50-win64>
```

### まとめ

意外とレンダリングできないものですね。各社様々でおもしろい。

## 漢字調査

PDL デバッグの記事が締まらなかったのでもう1つ小ネタをやります。

漢字を扱ってるいると「この漢字ってもっと簡単な文字ないの？」「読み方が分からない」などと困るときがあります。文字についてググったり、人に聞いたり、という方法もありますが、できれば機械的に処理したいです。そんな時の強い味方といえば Unihan データベースです。
データの定義については [UAX #38: Unicode Han Database (Unihan)](http://www.unicode.org/reports/tr38/tr38-27.html) から調べることができます。

つまり、この問題は以下の項目を使って解決してみましょう。

-   「この漢字ってもっと簡単な文字ないの？」
    -   → 異体字 = kZVariant
    -   → （その文字が簡体字であれば）繁体字 = kTraditionalVariant
-   「読み方が分からない」
    -   → 読み = kMandarin

でも Unihan データベースの ZIP ファイルを解凍して grep するのは面倒です。これが例えば Perl を使った開発であれば [Unicode::Unihan - The Unihan Data Base 5.1.0 - metacpan.org](https://metacpan.org/pod/Unicode::Unihan) を使うところですが、今回はもう少し汎用的な手段を考えました。

### Unihan データベース by Elasticsearch
先日 [unihan-etl · PyPI](https://pypi.org/project/unihan-etl/) を見つけました。これを使うと unicode.org から Unihan データベースをダウンロードしてきて JSON などのフォーマットに変換、1つのファイルに結合できます。項目全部を1つに収めた場合、ファイルサイズは数 MB 程度ありますが大分扱い易いです。
当初はこれを jq で検索していたんですが、やっぱり検索といったら Elasticsearch ですね。これらを組み合わせて環境を構築してみましょう。

```
# カレントディレクトリに unihan-etl をインストール
$ env PYTHONUSERBASE=. pip3 install --no-warn-script-location --quiet --user unihan-etl
# unihan-etl を使って Unihan データベースをダウンロード & JSON に変換
$ env PYTHONUSERBASE=. ./bin/unihan-etl -F json --destination ./unihan.json
# Elasticsearch の Bulk API 用のリクエストボディを組み立てる
$ jq -acr '.[] | { "index" : { "_index" : "unihan", "_id" : .ucn } }, .' < unihan.json > requests
$ docker run -d --rm -P --name elasticsearch-unihan -e discovery.type=single-node docker.elastic.co/elasticsearch/elasticsearch:7.5.0
$ docker port elasticsearch-unihan
9200/tcp -> 0.0.0.0:32773
9300/tcp -> 0.0.0.0:32772
# Elasticsearch の起動が落ち着いたら unihan インデックスにデータを投入
$ time curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:32773/_bulk --data-binary "@requests" | jq '.errors'
false
curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:32773/_bulk   0.03s user 0.16s system 0% cpu 25.620 total
jq '.errors'  0.71s user 0.09s system 3% cpu 25.810 total
# データが入ったかザックリ確認
$ curl -s localhost:32773/_stats/indexing | jq '.indices.unihan' | head
{
  "uuid": "8tNxpJu7TzOOTmXWNaBUiA",
  "primaries": {
    "indexing": {
      "index_total": 88889,
      "index_time_in_millis": 16084,
      "index_current": 0,
      "index_failed": 0,
      "delete_total": 0,
      "delete_time_in_millis": 0,
$
```

では使っていきましょう。Elasticsearch のアクセス方法を多少知っておく必要はありますが、数十ミリ秒で結果が返ってきます。

```
# 寜（U+5BDC）の異体字は？
$ curl -s localhost:32773/unihan/_doc/U+5BDC | jq '._source.kZVariant'
[
  "U+5BE7"
]
$ curl -s localhost:32773/unihan/_doc/U+5BE7 | jq '._source.char'
"寧"
# 赵（U+8D75）の繁体字は？
$ curl -s localhost:32773/unihan/_doc/U+8D75 | jq '._source.kTraditionalVariant'
[
  "U+8D99"
]
$ curl -s localhost:32773/unihan/_doc/U+8D99 | jq '._source.char'
"趙"
# 妵（U+59B5）の読み方は？
$ curl -s localhost:32773/unihan/_doc/U+59B5 | jq '._source.kMandarin'
{
  "zh-Hans": "tǒu",
  "zh-Hant": "tǒu"
}
```

ちなみに jq だとこんな感じです。unihan.json はデータ量的に1回検索して結果が返るのに数秒かかってしまいます。この場合は特定の項目だけに絞ったファイルを用意すれば Elasticsearch に迫る速度で検索できます。

```
# 「赵」の繁体字は？
$ time jq '.[] | select(.char=="\u8D75") | .kTraditionalVariant' < ./unihan.json
[
  "U+8D99"
]
jq '.[] | select(.char=="\u8D75") | .kTraditionalVariant' < ./unihan.json  5.66s user 0.56s system 98% cpu 6.329 total
# kTraditionalVariant だけに絞ったファイルを用意する
$ env PYTHONUSERBASE=. ./bin/unihan-etl -F json -f kTraditionalVariant --destination ./unihan-kTraditionalVariant.json
$ time jq '.[] | select(.char=="\u8D75") | .kTraditionalVariant' < unihan-kTraditionalVariant.json
[
  "U+8D99"
]
jq '.[] | select(.char=="\u8D75") | .kTraditionalVariant' <   0.05s user 0.00s system 91% cpu 0.058 total
```

### 後日談 cihai-cli

unihan-etl は同じ作者の別ツール [cihai-cli · PyPI](https://pypi.org/project/cihai-cli/) で使われてたんですね…Elasticsearch も jq も持ち出す必要がなかった…

```
$ env PYTHONUSERBASE=. pip3 install --no-warn-script-location --quiet --user cihai-cli
$ env PYTHONUSERBASE=. ./bin/cihai --version
cihai-cli 0.5.0, cihai 0.9.0post1, unihan-etl 0.10.3
$ time env PYTHONUSERBASE=. ./bin/cihai info -a 赵
char: 赵
kCangjie: GOK
kCantonese: ziu6
kDefinition: surname; ancient state
kFourCornerCode: '4480'
kFrequency: '3'
kMandarin: zhào
kRSKangXi: '156.2'
kRSUnicode: '156.2'
kTotalStrokes: '9'
kTraditionalVariant: U+8D99
kXHC1983: 1460.050:zhào
ucn: U+8D75
env PYTHONUSERBASE=. ./bin/cihai info -a 赵  0.27s user 0.03s system 97% cpu 0.311 total
$ echo -e '\u8D99'
趙
```

更に後日 https://hub.docker.com/r/karronoli/cihai-cli を作りました。`docker run --rm karronoli/cihai-cli:latest cihai info -a 赵` 等ができます。

### まとめ

デバッグに Elasticsearch を使うというのは富豪的ですね。検索というより高速な jq 程度な使い方ですが、意外と便利に使えてます。

## 総括
デバッグとか調査とかの多い1年だった気がします。でも今回取り上げたような問題に対しての基礎力が身に付けられて楽しかったです。