1
0

More than 1 year has passed since last update.

ImageMagick で PSD のレイヤー情報を JSON 形式で取り出す & 関連情報

Last updated at Posted at 2021-02-22

PSD を JavaScript で扱いたかったのですが、PSD.js がなぜか自分の手持ちの PSD ファイルのパース時にエラーが発生して使えなかっため、ImageMagick で JavaScript から利用しやすい形式に変換することにしました。

※2022/03/31 追記: おそらく PSD.js のバージョンアップで不具合が解決されたため、他の不具合がなければ PSD.js を利用した方が便利かと思います。

参考「PSD.js by meltingice
参考「meltingice/psd.js: A Photoshop PSD file parser for NodeJS and browsers - GitHub


動作確認は WSL の Ubuntu になります。

ImageMagick のバージョンは 6.8.9 です。

1. コマンドの例

input.psd のレイヤー情報を output.json に出力。

コマンド
convert input.psd json: | \
    nkf -Sw | \
    sed -r -z \
        -e 's/^/[\n/' -e 's/$/]\n/' \
        -e 's/(\n[^\n]*)"(scene|origingeometry)": ([^\n"]*)\n/\1"\2": "\3",\n/g' \
        -e 's/\}\n\{/\},\n\{/g' | \
    jq '[.[].image] | map({ x: .pageGeometry.x, y: .pageGeometry.y, label: .properties.label, opacity: .artifacts["psd:layer.opacity"], compose: .compose })' \
        >output.json

nkf は状況に応じて使用。

出力の例
[
  {
    "x": "0",
    "y": "0",
    "label": null,
    "opacity": null,
    "compose": "Over"
  },
  {
    "x": "0",
    "y": "0",
    "label": "レイヤー3",
    "opacity": "0",
    "compose": "Over"
  },
  {
    "x": "0",
    "y": "0",
    "label": "レイヤー2",
    "opacity": "0",
    "compose": "Over"
  },
  {
    "x": "0",
    "y": "0",
    "label": "レイヤー1",
    "opacity": "0",
    "compose": "Over"
  }
]

※レイヤーの階層情報はおそらく取得不可。

2. 説明

2.1. convert で PSD の情報を JSON 出力する

convert input.psd json: で、PSD のレイヤーごとの情報を JSON 出力できます。ただし、バグと仕様で一部不正な JSON が出力されるので、一部の文字列置換が必要になります。

※ ImageMagick のバージョンによって変更される可能性あり。

自分が確認したところ、origingeometryscene にダブルクォートが付いていませんでした。

不正な JSON の例
    "pageGeometry": {
      "width": "517",
      "height": "246",
      "x": "0",
      "y": "1354"
    },
    "origingeometry": +0+1354
    "dispose": "Undefined",
    "iterations": "0",
    "scene": 9 of 86
    "compression": "RLE",
    "orientation": "Undefined",

また、レイヤーごとのオブジェクトの配列ではなく、単にレイヤーごとの JSON 文字列を連結しただけの出力になるため、まるごと JSON として扱うには文字列の書き換えが必要です。

扱いやすい出力: [{}, {}, {}, {} ...]
実際の出力: {}{}{}{} ...

ここでは、sed を用いて文字列置換します。

sed -r -z \
    -e 's/^/[\n/' -e 's/$/]\n/' \
    -e 's/(\n[^\n]*)"(scene|origingeometry)": ([^\n"]*)\n/\1"\2": "\3",\n/g' \
    -e 's/\}\n\{/\},\n\{/g'
オプション 説明
-r 拡張正規表現を使用する
-z 行の区切りを NULL 文字にする (改行を含めて検索できるようにする)
-e 正規表現を実行する

2.2. PSD のレイヤー名の文字コードの問題

PSD ファイル内の一部のデータは Unicode で記述されますが、レイヤー名の文字コードは仕様書で定義されていません。

参考「Layer records - Adobe Photoshop File Formats Specification

そのため、PSD を扱うソフトウェアごとに文字コードの実装が異なります。
例えば、Windows 版の FireAlpaca ではレイヤー名が Shift-JIS になります。

JavaScript から扱うには何かと UTF-8 が都合が良いので、ここでは nkf を用いて Shift-JIS から UTF-8 に変換します。

nkf -Sw
文字コード 入力 出力
ISO-2022-JP J j
Shift-JIS S s
EUC-JP E e
UTF W w

※ UTF はさらにオプションで細かく指定可能ですが、デフォルトは UTF-8 です。

2.3. jq で JSON から必要な情報だけ取り出す

jq コマンドを使用することで、コマンドラインから JSON を便利に操作することができます。

参考「jq Manual (development version)
参考「jq コマンドを使う日常のご紹介 - Qiita

各レイヤー情報のオブジェクトのルートに .image があり、その下に具体的な情報が格納されています。

パス 説明 詳細
.pageGeometry.x X 座標 レイヤー画像が配置される座標
.pageGeometry.y Y 座標 レイヤー画像が配置される座標
.properties.label レイヤー名 文字コードを調べて対応する必要がある
.artifacts["psd:layer.opacity"] 透明度 0: 不透明
65535: 透明

※一般的に扱われる「不透明度」と逆になっていることに注意
.compose ブレンドモード
レイヤー表示有無
レイヤー非表示の場合は None になり、本来のブレンドモードを取得できない
・ブレンドモード NormalOver 表記
・その他のブレンドモードの表記にしては後述の表参照

※クリッピングの有無は取得できないようです。
※レイヤーの階層情報はおそらく取得不可。

2.4. ブレンドモードの対応関係

2.4.1. 早見表

別記事に分離しました。

参考「CSS または JavaScript におけるブレンドモード対応 - Qiita

2.4.2. Compose における表記の注意点

  • None の場合、レイヤーが非表示であることが分かるが、表示したときのブレンドモードを取得できない
  • PSD の仕様的には「減算」合成も「除算」合成も対応しているが、ImageMagick では「通常」扱い (バージョンによって変更される可能性あり) 。
  • 「通貨」フォルダは「通常」フォルダ扱い。
  • LinearDodge は本来、単なる「加算」合成をさすが、ソフトウェアによっては「加算 (発光)」合成として使用される。

2.5. レイヤーの階層情報は取得できない?

convert で得られる JSON にはフォルダのレイヤーレコードも含まれますが、JSON のキーや値を参照してもレイヤーかどうかを区別することができないため、少なくとも本記事の手法では階層情報は得られなそうです…。残念…。

PSD レイヤーの階層情報が欲しい場合は他の手段を使う必要がありそうです。

3. 関連情報

3.1. レイヤーごとに情報を取得する

input.psd[5] のように添え字をつけることで、n 番目のレイヤーの情報のみを取得することができます。
ただし、[0] はレイヤーでなく全ての表示レイヤーを結合した画像になる ため、1 始まりで指定する。

※ PSD の仕様的に、結合した画像データを含めないこともできるため、全ての PSD について ImageMagick が [0] で結合画像の情報を返すかどうかは要確認。

参考「Input Filename - ImageMagick - Command-line Processing」(※ "Selecting Frames" の項目参照)

3.2. レイヤー画像を出力する

レイヤー画像を 3 桁の連番で png 出力
convert input.psd output%03d.png

オプションなしだと、各レイヤーごとに異なるサイズで出力されます (同じサイズの可能性もありますが) 。

オプションでサイズを統一することもできますが、プログラムから利用することを考えると、レイヤーごとに適切なサイズになっていた方が容量を押さえられて動作も快適になり、望ましいと思います。

3.3. 直接 webp ファイルにレイヤー分割できない?

convert コマンドで webp 形式でレイヤーを出力しようとしたところ、単一の webp ファイルに格納されてしまい、分割することができませんでした…。
+adjoin オプションを使用しても不可でした。

※ ImageMagick 6 系列で webp を扱うには別途パッケージ webp のインストールが必要。
※ ImageMagick 7 系列では webp 変換に必要なパッケージが異なるため注意。

現状、webp にしたい場合は、一旦 png 等で出力してから以下のようなシェルスクリプトで一括変換することになるかと思います。

png を一括で webp に変換
#!/bin/sh

images=`find . -type f -name '*.png'`

for image in $images;
do
    cwebp -lossless $image -o ${image%.*}'.webp' >/dev/null 2>&1
done
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0