Full working example available on GitHub:
edit-xmp-in-psd-and-ai-files-using-groupdocs-metadata-dotnet
製作上の課題:誰も見ない場所にあるメタデータ
ブランドスタジオがキャンペーンを提供する:レイヤー化された PSD マスター、AI ソースファイル、数百のアセットがクライアントの DAM にプッシュされる。3 週間後、ライセンス部門がヒーロー画像の所有者を尋ねる。答えは存在するが、メールスレッドにしかなく、ファイル自体が空の dc:rights フィールドを持っているためである。XMP 編集は .NET 用の GroupDocs.Metadata の機能で、パイプラインレベルでこの種の問題を解決し、Adobe ソフトウェアを介さずに PSD と AI ファイル内の XMP メタデータパケットを読み書きする。
規模の問題は静かに現れる。1 人のデザイナーはしばらく手作業でメタデータパネルを丁寧に埋めることができる。四半期ごとに数千のアセットを扱う制作チームはできず、エージェンシー、スタジオ、クライアント間のすべてのハンドオフで、psd メタデータが誰も検証していないファイルが増えていく。検索は存在するアセットを見つけられなくなる。権利に関する質問は考古学的作業となり、考古学にはサービスレベル合意がない。
これらのチームが実際に必要としているものは API 仕様のように読める:取り込み時にファイルが持つすべてをスナップショットし、ライセンスゲートで特定のフィールドをチェックし、エクスポート時に所有権とキーワードを書き込む。Adobe の両フォーマットで同一のコードを使用する。
現実チェック:空の dc:rights フィールドを持つアセットが無許可というわけではないが、下流でそれを証明できる人を見つけなければ誰も証明できない。
なぜ従来の対策が不十分なのか
チームは通常、適切に自動化する前に 3 つのアプローチを試す:
- Photoshop または Bridge での手動パネル編集:ファイル単位で機能するが監査できず、根本的にデータ入力作業であるため Adobe のライセンスが必要になる。
- サイドカースプレッドシート:メタデータは存在するが、ファイルがコピー、リネーム、再配布される瞬間にアセットから切り離される。
- 社内パーシング:PSD のリソースブロックや AI のコンテナは簡単なフォーマットではなく、Adobe が何かを改訂した最初の瞬間に自前のパーサーは保守負担になる。
GroupDocs.Metadata は 1 つの API でギャップを埋める:ルートパッケージを IXmp にキャストすれば、パケットは両フォーマットで読み書き可能になる。さらに、ドキュメンテーション に記載されている 170 以上のスキーマもサポートする。
ソリューション:パイプライン内での XMP 操作
.NET 用の GroupDocs.Metadata はアセットパイプラインの 3 つのポイントに組み込まれる。取り込み時に完全なパケットをデータベースがインデックスできる辞書にスナップショットする。ライセンスゲートでは Dublin Core(dc:rights と dc:creator が存在するスキーマ)を読み取る。エクスポート時には所有権と dc:subject キーワードを書き込み、XMP が全く無いファイルに対しては欠落しているスキーマを作成する。権利データなしで出荷されたアセットが根本原因となるリリース後の振り返りに何度も参加したが、振り返りは読み取りよりもコストが高いため、ゲートが必要になる。
実装を追うには、以下が必要です:
- .NET SDK 8.0 以降
- GroupDocs.Metadata 26.6.0(一時ライセンスを取得)
- 実験用の PSD または AI ファイル
dotnet add package GroupDocs.Metadata --version 26.6.0
コンパニオンリポジトリ は各フォーマットのサンプルを 1 つ用意し、以下のすべてのステップを検証している。
ワークフローの実装ステップバイステップ
ステップ 1 – 取り込み時にすべてをスナップショット
1 回のパスでパケット、名前付きスキーマ、ベンダーツールが隠しているものすべてを取得する。辞書をアセットレコードの横に保存すれば、後の質問はデータベース検索になる。
// Full XMP snapshot: packet, schemes, then a deep sweep
var result = new Dictionary<string, string>();
using (var metadata = new Metadata(adobeFilePath))
{
var root = metadata.GetRootPackage() as IXmp;
if (root?.XmpPackage == null) return result;
foreach (var property in root.XmpPackage)
{
result[property.Name] = property.InterpretedValue?.ToString()
?? property.Value?.ToString() ?? string.Empty;
}
// CollectScheme(...) repeats this loop for DublinCore, XmpBasic,
// Photoshop, CameraRaw, PagedText, XmpDynamicMedia, XmpMediaManagement
foreach (var p in metadata.FindProperties(p => p.Name != null))
{
if (!result.ContainsKey(p.Name))
{
result[p.Name] = p.InterpretedValue?.ToString()
?? p.Value?.ToString() ?? string.Empty;
}
}
}
return result;
InterpretedValue が常に最初に来るため、日付や列挙型は人が読める形になる。最後の FindProperties スイープは、名前付きスキーマが認識しないプラグインが書き込んだ Adobe Illustrator メタデータの完全性を保証する。
取り込み規模で実行した際の 2 つの運用上の注意点。スナップショットはアセット ID をキーにし、取得日でスタンプを付けて保存する。ファイルは変わる可能性があり、スナップショットが「以前」の状態を示すからだ。また、空の辞書はエラーではなくシグナルとして扱う。これにより、アセットはインテーク失敗せずに直接スタンプステップへ流れる。
ステップ 2 – ライセンスゲートで Dublin Core をチェック
9 つの dc:* フィールドが法務・ライセンスが実際に尋ねる質問に答える。そのスキーマだけを読むことでゲートは高速になる。
// dc:* fields only - Title, Creator, Rights, Subject and friends
var result = new Dictionary<string, string>();
using (var metadata = new Metadata(adobeFilePath))
{
var root = metadata.GetRootPackage() as IXmp;
var dc = root?.XmpPackage?.Schemes?.DublinCore;
if (dc == null) return result;
foreach (var property in dc)
{
result[property.Name] = property.InterpretedValue?.ToString()
?? property.Value?.ToString() ?? string.Empty;
}
}
return result;
これらの設定が制作チームにとって重要な理由:
- Null 条件チェーン:新規エクスポートでは XMP が無いファイルが日常的で、空の辞書は「スタンプしてください」を意味し、エラーではない。
- スキーマの範囲:ゲートはすべてのアセット移動で実行されるため、全ツリーを読むより 9 フィールドだけ読む方がコストが低い。
ステップ 3 – エクスポート時に所有権をスタンプ
書き込みは 3 層に及び、すべてのリーダー(XMP 対応か否か)に同一の識別子が見える。ガードが欠落しているパケットとスキーマオブジェクトを先に作成する。
// Guard-create the packet and scheme, then write rights and creator
using (var metadata = new Metadata(inputPath))
{
var root = metadata.GetRootPackage() as IXmp;
if (root == null) return;
if (root.XmpPackage == null)
root.XmpPackage = new XmpPacketWrapper();
if (root.XmpPackage.Schemes.DublinCore == null)
root.XmpPackage.Schemes.DublinCore = new XmpDublinCorePackage();
var dc = root.XmpPackage.Schemes.DublinCore;
dc.SetRights(copyright);
dc.Set("dc:creator", XmpArray.From(new[] { creator }, XmpArrayType.Ordered));
// Mirror the identity for XmpBasic readers and tag-classified fields
if (root.XmpPackage.Schemes.XmpBasic == null)
root.XmpPackage.Schemes.XmpBasic = new XmpBasicPackage();
root.XmpPackage.Schemes.XmpBasic.CreatorTool = creator;
metadata.SetProperties(p => p.Tags.Contains(Tags.Person.Creator),
new PropertyValue(creator));
metadata.Save(outputPath);
}
Tags.Person.Creator を使用した SetProperties 呼び出しは注目すべきポイントで、ライブラリがクリエイターフィールドと分類したすべてのプロパティを更新する。フォーマットがどこに保存していても、XMP を読まないツールでも正しい名前が表示される。
ステップ 4 – 検索用キーワードを書き込む
dc:subject は DAM 検索がインデックスする語彙である。これが無ければ、アセットは存在してもクエリに一致しない。
// Replace the dc:subject bag with the pipeline's keyword list
root.XmpPackage.Schemes.DublinCore.Set(
"dc:subject",
XmpArray.From(keywords, XmpArrayType.Unordered));
metadata.Save(outputPath);
書き込みは既存のバッグを置き換えるため、追加タグ付けは「読み取り → C# でマージ → 書き込み」になる。リポジトリは 3 つのサンプルキーワードを書き込み、最初のキーワードが保存バイトに残ることを検証している。タクソノミーをバージョン管理するチームは通常、キャンペーンごとに正規キーワードセットを保存し、パイプラインがエクスポート時にファイルと照合させる。これによりキーワードのドリフトが議論ではなく差分になる。
メタデータ修正だけに Photoshop ライセンスは必要ですか?
いいえ、これが自動化の目的でもある。GroupDocs.Metadata は .NET でパケットを直接読み書きできるため、サーバー側のジョブでアーカイブ全体の権利スタンプやキーワード修正が可能で、Adobe アプリケーションを一つも開く必要がない。デザイナーはデザイン作業のためにツールを保持し、パイプラインがスケールでメタデータの衛生管理を担う。