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 のバージョンによって変更される可能性あり。
自分が確認したところ、origingeometry
と scene
にダブルクォートが付いていませんでした。
"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 になり、本来のブレンドモードを取得できない・ブレンドモード Normal は Over 表記・その他のブレンドモードの表記にしては後述の表参照 |
※クリッピングの有無は取得できないようです。
※レイヤーの階層情報はおそらく取得不可。
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. レイヤー画像を出力する
convert input.psd output%03d.png
オプションなしだと、各レイヤーごとに異なるサイズで出力されます (同じサイズの可能性もありますが) 。
オプションでサイズを統一することもできますが、プログラムから利用することを考えると、レイヤーごとに適切なサイズになっていた方が容量を押さえられて動作も快適になり、望ましいと思います。
3.3. 直接 webp ファイルにレイヤー分割できない?
convert
コマンドで webp 形式でレイヤーを出力しようとしたところ、単一の webp ファイルに格納されてしまい、分割することができませんでした…。
+adjoin
オプションを使用しても不可でした。
※ ImageMagick 6 系列で webp を扱うには別途パッケージ webp
のインストールが必要。
※ ImageMagick 7 系列では webp 変換に必要なパッケージが異なるため注意。
現状、webp にしたい場合は、一旦 png 等で出力してから以下のようなシェルスクリプトで一括変換することになるかと思います。
#!/bin/sh
images=`find . -type f -name '*.png'`
for image in $images;
do
cwebp -lossless $image -o ${image%.*}'.webp' >/dev/null 2>&1
done