完全に動作する例は GitHub で入手可能です:
manage-xmp-in-psd-and-ai-files-python

はじめに

マーケティングチームが 400 個の PSD ファイルを資産プラットフォームに投入します。アップロードは成功しますが、検索が機能しません。なぜなら、ファイルにキーワードが付いていないものが多く、半分は著作権表示が欠落しており、デザイナー名はどこかのスプレッドシートにしか存在しないからです。解決策はスプレッドシートを増やすことではありません。XMP 管理は、.NET 経由で Python 用に提供される GroupDocs.Metadata の機能で、Photoshop PSD と Illustrator AI ファイルに埋め込まれたメタデータパケットを読み書きします。これにより、所有権情報や検索データをファイル自体に保持できるようになります。

XMP はバイナリコンテナ内の XML パケットで、スキーマに分かれています。すべてのシステムが理解できるフィールドは Dublin Core、編集コンテキストは Photoshop スキーマ、ツール情報は XmpBasic です。PSD を手作業で解析してそのパケットにたどり着くのは実際に非常に困難です。Metadata クラスを使えば属性参照が 3 回で済み、同じコードが AI ファイルにも適用できます。

このチュートリアルでは、全パケットのスナップショット取得、重要なスキーマの読み取り、著作権と作成者の書き込み、検索用キーワードのタグ付けという 4 つのステップでフルラウンドトリップを実演します。すべてのコードスニペットは、書き込んだ値が永続化されていることを検証する実行可能リポジトリから取得しています。

前提条件

開始する前に、以下が揃っていることを確認してください。

  • Python 3 と pip
  • .NET 経由で提供される GroupDocs.Metadata for Python(リポジトリはバージョン 26.5 を固定しています)
  • 実験用の PSD または AI ファイル

インストール

pip install groupdocs-metadata-net==26.5

ステップ 1 - XMP パケット全体のスナップショット

まずファイルが保持しているすべての情報を確認します。スナップショットはルートパケット、各登録スキーマ、最後に標準外プロパティを走査し、すべてを 1 つのフラットな dict にまとめます。

result = {}

def put(props, prop):
    value = (str(prop.interpreted_value) if prop.interpreted_value is not None
             else (str(prop.value) if prop.value is not None else ""))
    props[prop.name] = value

with Metadata("campaign-hero.psd") as metadata:
    root = metadata.get_root_package()
    xmp = getattr(root, "xmp_package", None)
    if xmp is not None:
        for p in xmp:                                  # root packet properties
            put(result, p)
        schemes = xmp.schemes
        for scheme in (schemes.dublin_core, schemes.xmp_basic, schemes.photoshop,
                       schemes.camera_raw, schemes.paged_text,
                       schemes.xmp_dynamic_media, schemes.xmp_media_management):
            if scheme is None:
                continue
            for p in scheme:
                put(result, p)
    for p in metadata.find_properties(lambda p: p.name is not None):
        if p.name not in result:                       # catch custom packets
            put(result, p)

重要ポイント:

  • interpreted_value を優先: 日付や列挙型は生データではなく人が読める形で取得できます。
  • 7 つのスキーマと最終走査: 後方の find_properties が名前付きスキーマで捕捉できないベンダーパケットを取得します。
  • ファイルは 1 回だけ開く: スナップショット全体は単一の Metadata コンテキストで完了するため、バルク取り込み時に重要です。

ヒント: 取り込み時にこの dict をインデックス化すれば、後続のメタデータ問い合わせはファイル読み取りではなく辞書参照で済むようになります。

どの XMP スキーマを最初に読み取るべきか?

まず Dublin Core から始めます。9 つの dc: フィールドはタイトル、作成者、権利、テーマを保持しており、ほとんどの DAM システム、検索インデックス、ライセンスチェックが共通で利用します。PSD と AI の両方が同一の形でこのスキーマを公開しています。次に Photoshop スキーマを読み取り、City、Credit、DateCreated といった編集コンテキストを取得します。全パケットの走査は、すべてを捕捉する必要がある取り込みジョブ向けに残しておきます。

ステップ 2 - 実際の質問に答えるスキーマを読む

リクエスト時のコードでは、1 つのスキーマに絞って読み取ります。所有権と検索に関する質問は Dublin Core が答えてくれます。

dc_fields = {}
with Metadata("campaign-hero.psd") as metadata:
    xmp = getattr(metadata.get_root_package(), "xmp_package", None)
    dc = xmp.schemes.dublin_core if xmp is not None else None
    if dc is not None:
        for p in dc:
            dc_fields[p.name] = (str(p.interpreted_value)
                                 if p.interpreted_value is not None else
                                 str(p.value) if p.value is not None else "")

print(dc_fields.get("dc:rights", "<no rights recorded>"))

Photoshop スキーマも同様に型付きプロパティで取得できます: ps.color_mode, ps.icc_profile, ps.city, ps.country, ps.date_created, ps.caption_writer, ps.credit, ps.source。これらは Bridge、Lightroom、DAM の検索フィルタが Adobe ファイルで参照する項目です。

XMP が存在しないファイルでは、ガードが空の dict を返し例外は発生しません。新規にエクスポートされたアセットはこのケースが頻繁に起こるため、統合時にこの挙動を考慮してください。

同じ 3 つのルックアップは Illustrator ファイルでも機能します。campaign-hero.psd を brand-mark.ai に置き換えるだけでコードは変わりません。実際、AI のエクスポートは Photoshop 保存に比べてスキーマが少ないことが多く、空 dict のパスがより頻繁に走ります。

ステップ 3 - 著作権と作成者を書き込む

次は書き込みパスです。所有権スタンプは 3 つのフィールドに触れるので、すべてのリーダーが同じ情報を取得できます: 法的通知用の dc:rights、順序付きリストの dc:creator、そして XmpBasic スキーマで読むツール情報用の xmp:CreatorTool。かつて、デザイナーがタグ付けしたはずのアセットに「Unknown author」と表示されるバグに数時間を費やしたことがあります。原因は dc:creator に値が入っていたものの、ツールが xmp:CreatorTool しか読んでいなかったためです。両方を書き込むことでこの種のバグは解消しました。

with Metadata("campaign-hero.psd") as metadata:
    root = metadata.get_root_package()
    xmp = getattr(root, "xmp_package", None)
    if xmp is None:                          # file has no XMP at all
        root.xmp_package = XmpPacketWrapper()
        xmp = root.xmp_package
    if xmp.schemes.dublin_core is None:
        xmp.schemes.dublin_core = XmpDublinCorePackage()

    dc = xmp.schemes.dublin_core
    dc.set_rights("(C) 2026 GroupDocs Sample")
    dc.set("dc:creator", XmpArray.from_(["Digital Asset Team"],
                                        XmpArrayType.ORDERED))

    if xmp.schemes.xmp_basic is None:
        xmp.schemes.xmp_basic = XmpBasicPackage()
    xmp.schemes.xmp_basic.creator_tool = "Digital Asset Team"

    metadata.save("campaign-hero-stamped.psd")

重要ポイント:

  • ガードで欠落レイヤーを作成: XmpPacketWrapper と XmpDublinCorePackage は必要に応じて生成されるため、XMP が全く無いファイルでも書き込みが可能です。
  • ORDERED 配列で作成者を保持: 作成者の順序は意味を持つため、XmpArray の順序付き配列を使用します。
  • 新しいパスに保存: 元ファイルはそのまま残り、エクスポート工程のデフォルトとして適切です。

ステップ 4 - 検索用キーワードをタグ付けする

dc:subject は DAM 検索インデックスが参照するキーワードバッグです。書き込みは 1 回の呼び出しでバッグ全体を置き換えます。

with Metadata("campaign-hero.psd") as metadata:
    root = metadata.get_root_package()
    xmp = getattr(root, "xmp_package", None)
    if xmp is None:
        root.xmp_package = XmpPacketWrapper()
        xmp = root.xmp_package
    if xmp.schemes.dublin_core is None:
        xmp.schemes.dublin_core = XmpDublinCorePackage()

    xmp.schemes.dublin_core.set(
        "dc:subject",
        XmpArray.from_(["landscape", "sunset", "commercial"],
                       XmpArrayType.UNORDERED))
    metadata.save("campaign-hero-tagged.psd")

キーワードは UNORDERED 配列で保持します。インデクサにとって順序は意味がないからです。また set は既存のバッグを置き換えるため、追加タグ付けが必要な場合は事前に現在のキーワードを読み取り、Python 側でマージしてから書き込んでください。

書き込みが正しく行われたか確認するには、ステップ 2 のリーダーを出力ファイルに対して再実行します。リポジトリはこの検証を自動化しており、出力バイトに著作権文字列と最初のキーワードが残っていることをアサートします。

実際の活用例

DAM 取り込み

すべての入稿ファイルに対してステップ 1 のスナップショットを取得し、dict を資産レコードと共に保存します。検索、重複除去、権利チェックはバイナリファイルを再度開く代わりにデータベース上で実行できます。リポジトリの小さなサンプル PSD でも 1 回の呼び出しで多数のプロパティが取得でき、同じ呼び出しは数千件のフォルダにも同様に機能します。

ライセンス遵守

資産をクライアントポータルに配信する前に、dc:rights が空でないことを必須条件にします。条件を満たさないファイルは自動的にステップ 3 のスタンプ処理が適用されるため、通知なしで出荷されることはありません。

バッチ再タグ付け

タクソノミーが変更されたときは、各ファイルの dc:subject を読み取り、Python で古い用語を新しい用語にマッピングし、ステップ 4 でマージしたバッグを書き戻します。PSD と AI の両方のアーカイブで同一ループが使えるため、Photoshop のライセンスは不要です。

ベストプラクティスとヒント

  • 空は正常とみなす: XMP が無いファイルは例外ではなく日常的に発生するケースです。早期リターンパターンでパイプラインを止めないようにしましょう。
  • キーワードはマージしてから書く: set は dc:subject を上書きするため、追加タグ付けは「読み取り → 拡張 → 書き込み」の順序で行います。
  • 両スキーマに同じ情報を書き込む: dc:creator と xmp:CreatorTool の両方を設定すれば、Dublin Core リーダーと XmpBasic リーダーが一致した情報を取得できます。
  • 書き込み後は再読で検証: 保存直後に再読すると、コンテナ固有のサプライズをすぐに検出できます。
  • 本番環境ではライセンスを取得: 本チュートリアルは評価モードで動作しますが、実際のクライアント資産にスタンプを付ける際は正規ライセンスをご利用ください。

結論

Adobe ファイルの XMP 読み書きは、get_root_package() でパケットを取得し、必要なスキーマをガードし、型付き値を読み書きする 3 手順に集約できます。この手順で本チュートリアルは、パケットのスナップショット取得からスキーマ読み取り、著作権スタンプ、キーワードタグ付けまでのフルラウンドトリップを構築しました。コードは PSD と AI の両方で同一に機能します。

プロジェクトで実装する準備はできましたか?次のステップをご確認ください:

追加リソース

XMP ワークフローに関する質問がありますか?support forum でお尋ねください。