convertの結果にタイムスタンプを埋め込まないようにする

ImageMagickの`convert`コマンドでaiをpngに変換すると、冪等になりません。変換結果をGitなどで保存している場合、毎回変わるので面倒です。aiファイルに変化があった時だけ変換結果のpngも変わるようにしたいです。

## 環境

libpngのバージョンによって結果が異なったので、OS XとDebianの2種類の環境で検証しました。

### OS X Yosemite

* ImageMagick 6.8.9-8
* libpng 1.6.20

```sh
$ convert --version
Version: ImageMagick 6.8.9-8 Q16 x86_64 2014-10-23 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2014 ImageMagick Studio LLC
Features: DPC Modules
Delegates: bzlib freetype jng jpeg ltdl lzma png zlib

$ convert -list format | grep PNG
      JNG* PNG       rw-   JPEG Network Graphics
      MNG* PNG       rw+   Multiple-image Network Graphics (libpng 1.6.13,1.6.20)
      PNG* PNG       rw-   Portable Network Graphics (libpng 1.6.13,1.6.20)
           See http://www.libpng.org/ for details about the PNG format.
    PNG00* PNG       rw-   PNG inheriting bit-depth and color-type from original
    PNG24* PNG       rw-   opaque or binary transparent 24-bit RGB (zlib 1.2.5)
    PNG32* PNG       rw-   opaque or transparent 32-bit RGBA
    PNG48* PNG       rw-   opaque or binary transparent 48-bit RGB
    PNG64* PNG       rw-   opaque or transparent 64-bit RGBA
     PNG8* PNG       rw-   8-bit indexed with optional binary transparency
```

`(libpng 1.6.13,1.6.20)`という表記は、1.6.13でコンパイルされ、1.6.20の共有ライブラリを使っているという意味[だそうです](http://stackoverflow.com/questions/33634402/how-to-check-libpng-version)。

### Debian Jessie

* ImageMagick 6.8.9-9
* libpng 1.2.50

```sh
$ convert --version
Version: ImageMagick 6.8.9-9 Q16 x86_64 2015-01-05 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2014 ImageMagick Studio LLC
Features: DPC Modules OpenMP
Delegates: bzlib djvu fftw fontconfig freetype jbig jng jpeg lcms lqr ltdl lzma openexr pangocairo png tiff wmf x xml zlib

$ convert -list format | grep PNG
      JNG* PNG       rw-   JPEG Network Graphics
      MNG* PNG       rw+   Multiple-image Network Graphics (libpng 1.2.50)
      PNG* PNG       rw-   Portable Network Graphics (libpng 1.2.50)
           See http://www.libpng.org/ for details about the PNG format.
    PNG00* PNG       rw-   PNG inheriting bit-depth and color-type from original
    PNG24* PNG       rw-   opaque or binary transparent 24-bit RGB (zlib 1.2.8)
    PNG32* PNG       rw-   opaque or transparent 32-bit RGBA
    PNG48* PNG       rw-   opaque or binary transparent 48-bit RGB
    PNG64* PNG       rw-   opaque or transparent 64-bit RGBA
     PNG8* PNG       rw-   8-bit indexed with optional binary transparency
```

## 結論

とりあえず結論から。

OS X (libpng 1.6.20) の環境では、`-strip`オプションを使うとPNGに埋め込まれるメタ情報を削除できます。

```sh
$ convert -density 300 -strip original.ai converted.png
```

他のメタ情報は残し、タイムスタンプだけを取り除きたい場合は以下のように`+set`オプションを使うこともできます。

```sh
$ convert -density 300 +set date:create +set date:modify original.ai converted.png
```

Debian (libpng 1.2.50) の環境では、これらの方法だと`tIME`チャンクが残ってしまったので、以下のようにすると取り除くことができました。

```sh
$ convert -density 300 -define png:exclude-chunk=tEXt,zTXt,tIME original.ai converted.png
```

## 事象の確認

OS X (libpng 1.6.20) の環境で、時間を置いて2つのファイルを生成します。

```sh
$ convert -density 300 original.ai old.png
$ # 時間を置いてから
$ convert -density 300 original.ai new.png
```

ファイルサイズは同じですが、`diff`を取ると異なっていることがわかります。

```sh
$ ll *.png
-rw-r--r--  1 orange  staff  25197  1 24 15:38 new.png
-rw-r--r--  1 orange  staff  25197  1 24 15:38 old.png
$ diff old.png new.png
Binary files old.png and new.png differ
```

`diff`コマンドではバイナリの詳しい比較ができないので、`xxd`でHex形式に変換して差分を取ります。

```sh
$ xxd old.png old.hex
$ xxd new.png new.hex
$ diff new.hex old.hex
1566,1567c1566,1567
< 00061d0: 3454 3135 3a33 383a 3235 2b30 393a 3030  4T15:38:25+09:00
< 00061e0: b867 4a97 0000 0025 7445 5874 6461 7465  .gJ....%tEXtdate
---
> 00061d0: 3454 3135 3a33 383a 3230 2b30 393a 3030  4T15:38:20+09:00
> 00061e0: ea5f 6530 0000 0025 7445 5874 6461 7465  ._e0...%tEXtdate
1569,1570c1569,1570
< 0006200: 3234 5431 353a 3338 3a32 352b 3039 3a30  24T15:38:25+09:0
< 0006210: 30c9 3af2 2b00 0000 2074 4558 7470 6466  0.:.+... tEXtpdf
---
> 0006200: 3234 5431 353a 3338 3a32 302b 3039 3a30  24T15:38:20+09:0
> 0006210: 309b 02dd 8c00 0000 2074 4558 7470 6466  0....... tEXtpdf
```

タイムスタンプらしきものが埋め込まれており、これが異なっていることがわかります。


hexファイルを開いてみると、ファイルの末尾に`tEXt`チャンクとして作成日時 (`date:create`) と更新日時 (`date:modify`) が埋め込まれています。

```new.hex
...
00061a0: 02bf 6bfa 0000 0040 1ffd ffe7 d465 a21a  ..k....@.....e..
00061b0: 5fe2 4c00 0000 2574 4558 7464 6174 653a  _.L...%tEXtdate:
00061c0: 6372 6561 7465 0032 3031 362d 3031 2d32  create.2016-01-2
00061d0: 3454 3135 3a33 383a 3235 2b30 393a 3030  4T15:38:25+09:00
00061e0: b867 4a97 0000 0025 7445 5874 6461 7465  .gJ....%tEXtdate
00061f0: 3a6d 6f64 6966 7900 3230 3136 2d30 312d  :modify.2016-01-
0006200: 3234 5431 353a 3338 3a32 352b 3039 3a30  24T15:38:25+09:0
0006210: 30c9 3af2 2b00 0000 2074 4558 7470 6466  0.:.+... tEXtpdf
0006220: 3a48 6952 6573 426f 756e 6469 6e67 426f  :HiResBoundingBo
0006230: 7800 3531 3578 3233 342b 302b 301e fd8f  x.515x234+0+0...
0006240: fe00 0000 1474 4558 7470 6466 3a56 6572  .....tEXtpdf:Ver
0006250: 7369 6f6e 0050 4446 2d31 2e33 2053 06ac  sion.PDF-1.3 S..
0006260: bf00 0000 0049 454e 44ae 4260 82         .....IEND.B`.                                   `.
```

Debian (libpng 1.2.50) の環境で同様に比較すると、これらに加えて`tIME`チャンクが異なっていることがわかります。

```sh
$ diff <(xxd debian_old.png) <(xxd debian_new.png)
155,156c155,156
< 00009a0: 0000 0007 7449 4d45 07e0 0118 062d 0bc1  ....tIME.....-..
< 00009b0: 2818 3700 0063 bc49 4441 5478 daed dd3b  (.7..c.IDATx...;
---
> 00009a0: 0000 0007 7449 4d45 07e0 0118 062d 0fc6  ....tIME.....-..
> 00009b0: 45dc 2e00 0063 bc49 4441 5478 daed dd3b  E....c.IDATx...;
1755c1755
< 0006da0: 3130 2b30 303a 3030 e5d8 3ed2 0000 0025  10+00:00..>....%
---
> 0006da0: 3134 2b30 303a 3030 1197 1ac1 0000 0025  14+00:00.......%
1758c1758
< 0006dd0: 3a31 302b 3030 3a30 3094 8586 6e00 0000  :10+00:00...n...
---
> 0006dd0: 3a31 342b 3030 3a30 3060 caa2 7d00 0000  :14+00:00`..}...
```

なお、`pngcheck`コマンドを使うと以下のようにチャンクの値を確認できます。

```sh
$ pngcheck -vtc debian_old.png
File: debian_old.png (28213 bytes)
  chunk IHDR at offset 0x0000c, length 13
    2146 x 975 image, 8-bit grayscale, non-interlaced
  chunk iCCP at offset 0x00025, length 2340
    profile name = icc, compression method = 0 (deflate)
    compressed profile = 2335 bytes
  chunk cHRM at offset 0x00955, length 32
    White x = 0.3127 y = 0.329,  Red x = 0.64 y = 0.33
    Green x = 0.3 y = 0.6,  Blue x = 0.15 y = 0.06
  chunk bKGD at offset 0x00981, length 2
    gray = 0x00ff
  chunk pHYs at offset 0x0098f, length 9: 300x300 pixels/unit (1:1)
  chunk tIME at offset 0x009a4, length 7: 24 Jan 2016 06:45:11 UTC
  chunk IDAT at offset 0x009b7, length 25532
    zlib: deflated, 32K window, maximum compression
  chunk tEXt at offset 0x06d7f, length 37, keyword: date:create
    2016-01-24T06:45:10+00:00
  chunk tEXt at offset 0x06db0, length 37, keyword: date:modify
    2016-01-24T06:45:10+00:00
  chunk tEXt at offset 0x06de1, length 32, keyword: pdf:HiResBoundingBox
    515x234+0+0
  chunk tEXt at offset 0x06e0d, length 20, keyword: pdf:Version
    PDF-1.3
  chunk IEND at offset 0x06e2d, length 0
No errors detected in debian_old.png (12 chunks, 98.7% compression).
```

## 調査

適当にググると以下のページがヒットします。

Getting ImageMagick convert to not write out extra info - Stack Overflow
http://stackoverflow.com/questions/13577280/getting-imagemagick-convert-to-not-write-out-extra-info

### 解決策1：-stripオプションを使う

```sh
$ convert -density 300 -strip original.ai old_without_date.png
$ # 時間を置いて
$ convert -density 300 -strip original.ai new_without_date.png
$ diff old_without_date.png new_without_date.png
```

`diff`の結果から差分がないことがわかります。

hexファイルに変換してファイル末尾を見てみると、`tEXt`チャンクが存在しません。

```
0006400: 9fcb 6417 1d27 ba78 7ebd 4ea3 f145 5760  ..d..'.x~.N..EW`
0006410: 3c24 0600 4cd6 5f3a bd56 d9e7 75f6 2dc6  <$..L._:.V..u.-.
0006420: 5720 1412 0300 0078 4062 0000 000f fe1f  W .....x@b......
0006430: dfcb 7ebb a432 7e04 0000 0000 4945 4e44  ..~..2~.....IEND
0006440: ae42 6082                                .B`.
```

なお、`-strip`は画像のメタ情報を取り除くオプションです。

> strip the image of any profiles or comments.

http://www.imagemagick.org/www/command-line-options.html#strip


### 解決策2：+setオプションを使う

```sh
$ convert -density 300 +set date:create +set date:modify original.ai old_without_date2.png
$ # 時間を置いて
$ convert -density 300 +set date:create +set date:modify original.ai new_without_date2.png
$ diff old_without_date2.png new_without_date2.png
```

同じように`diff`の結果から差分がないことがわかります。

hexファイルに変換してファイル末尾を見てみると、`pdf:HiResBoundingBox`という`tEXt`チャンクは残っていますが、`date:create`と`date:modify`は消えていることがわかります。

```
0006400: a274 cae2 7cc3 eb37 6501 bf48 0c00 9894  .t..|..7e..H....
0006410: 9fcb 6417 1d27 ba78 7ebd 4ea3 f145 5760  ..d..'.x~.N..EW`
0006420: 3c24 0600 4cd6 5f3a bd56 d9e7 75f6 2dc6  <$..L._:.V..u.-.
0006430: 5720 1412 0300 0078 4062 0000 000f fe1f  W .....x@b......
0006440: dfcb 7ebb a432 7e04 0000 0020 7445 5874  ..~..2~.... tEXt
0006450: 7064 663a 4869 5265 7342 6f75 6e64 696e  pdf:HiResBoundin
0006460: 6742 6f78 0035 3135 7832 3334 2b30 2b30  gBox.515x234+0+0
0006470: 1efd 8ffe 0000 0014 7445 5874 7064 663a  ........tEXtpdf:
0006480: 5665 7273 696f 6e00 5044 462d 312e 3320  Version.PDF-1.3 
0006490: 5306 acbf 0000 0000 4945 4e44 ae42 6082  S.......IEND.B`.
```

なお、`+set`は画像の属性やプロパティを削除するためのオプションです。

> Using the +set form of the option will either remove, or reset that setting to a default state, as appropriate.

http://www.imagemagick.org/www/command-line-options.html#set

### 解決策3: -defineオプションを使う

Debian (libpng 1.2.50) の環境では、解決策1, 2では`tIME`チャンクを取り除けませんでした。以下のように`-define`オプションを使うとチャンクの種類を指定して取り除くことができます。

```
$ convert -density 300 -define png:exclude-chunk=tEXt,zTXt,tIME original.ai converted.png
```

なお、`-strip`オプションは以下のオプションと同等とのことです。

```
-define png:exclude-chunk=EXIF,iCCP,iTXt,sRGB,tEXt,zCCP,zTXt,date
```

http://www.imagemagick.org/www/command-line-options.html#define


## 参考

* [diff - How do I compare binary files in Linux? - Super User](http://superuser.com/questions/125376/how-do-i-compare-binary-files-in-linux)
* [Portable Network Graphics - Wikipedia, the free encyclopedia](https://en.wikipedia.org/wiki/Portable_Network_Graphics)
* [Getting ImageMagick convert to not write out extra info - Stack Overflow](http://stackoverflow.com/questions/13577280/getting-imagemagick-convert-to-not-write-out-extra-info)
* [ImageMagick: Command-line Options](http://www.imagemagick.org/www/command-line-options.html)
* [imagemagick - How to check libpng version - Stack Overflow](http://stackoverflow.com/questions/33634402/how-to-check-libpng-version)
* [What software can I use to read png metadata? - Super User](http://superuser.com/questions/219642/what-software-can-i-use-to-read-png-metadata)