完整的工作範例可在 GitHub 上取得: manage-xmp-in-psd-and-ai-files-python

介紹

行銷團隊將 400 個 PSD 檔案放入您的資產平台。上傳可以成功,搜尋卻失敗,因為檔案中沒有關鍵字,一半缺少版權聲明,設計師名稱只存在於某個試算表中。解決方案不是更大的試算表。XMP 管理是 GroupDocs.Metadata 在 Python 透過 .NET 的功能,能讀寫嵌入於 Photoshop PSD 與 Illustrator AI 檔案中的 metadata 封包,這意味著所有權與搜尋資料可以直接存放在檔案本身。

XMP 是位於二進位容器內的 XML 封包,依照不同的 schema 組織:Dublin Core 為所有系統皆能理解的欄位,Photoshop schema 提供編輯上下文,XmpBasic 為工具身分。手動解析 PSD 以取得該封包相當困難。使用 Metadata 類別只需要三個屬性查詢,且相同程式碼同樣適用於 AI 檔案。

本教學以四個步驟完整說明往返流程:快照整個封包、讀取重要的 schema、寫入版權與創作者、以及標記關鍵字以供搜尋。每段程式碼皆取自可執行的儲存庫,並驗證寫入的值會持續存在。

前置條件

開始之前,請確保您已具備:

  • Python 3 與 pip
  • 透過 .NET 的 GroupDocs.Metadata for Python(儲存庫固定使用 26.5 版)
  • 一個可供實驗的 PSD 或 AI 檔案

安裝

pip install groupdocs-metadata-net==26.5

步驟 1 - 快照整個 XMP 封包

先查看檔案攜帶的所有資訊。此快照會遍歷根封包、每個已註冊的 schema,最後掃描屬性樹中任何非標準項目,將所有內容收集到一個平面字典中。

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:日期與列舉會以可讀形式返回,而非原始值。
  • 七個 schema 加上掃描:最後的 find_properties 會捕捉命名 schema 未涵蓋的供應商封包。
  • 只開啟一次檔案:整個快照只需一次 Metadata 內容上下文,對大量匯入非常重要。

рџ’Ў 提示:在匯入時將此字典建立索引,之後的大多數 metadata 查詢都會變成字典查找,而不必再次讀取檔案。

我的整合應該先讀哪個 XMP schema?

先從 Dublin Core 開始。它的九個 dc: 欄位包含標題、創作者、權利與主題等資訊,這些是大多數 DAM 系統、搜尋索引與授權檢查所共通認可的,且 PSD 與 AI 檔案皆以相同方式呈現。接著讀取 Photoshop schema,以取得城市、版權說明、建立日期等編輯上下文。最後的完整封包掃描則留給必須捕捉全部資訊的匯入工作。

步驟 2 - 讀取解答實際問題的 Schemas

在請求時的程式碼只需聚焦於單一 schema。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 schema 以相同方式透過型別化屬性存取:ps.color_mode、ps.icc_profile、ps.city、ps.country、ps.date_created、ps.caption_writer、ps.credit、ps.source,每個屬性皆以 None 防護。這些欄位正是 Bridge、Lightroom 與 DAM 搜尋過濾器在 Adobe 檔案上所依賴的。

注意在沒有 XMP 的檔案上會發生什麼:防護會產生空字典,而不會拋出例外。新匯出的資產常會出現此情況,請在整合中保留此行為。

相同的三個查詢同樣適用於 Illustrator 檔案。只要把 campaign-hero.psd 換成 brand-mark.ai,其他皆不變,這正是混合 Adobe 檔案庫能使用單一路徑程式碼的原因。實務上,剛匯出的 AI 檔案往往比 Photoshop 儲存的檔案填充的 schema 更少,因此空字典路徑會更常被觸發。

步驟 3 - 寫入版權與創作者

現在來看寫入流程。所有權標記會觸及三個欄位,讓每個讀取者看到相同的身分資訊:dc:rights 為法律聲明,dc:creator 為有序列表,xmp:CreatorTool 為讀取 XmpBasic schema 時使用的工具名稱。我曾因資產的版權橫幅顯示「未知作者」而浪費整個下午,原因是值只寫在 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 搜尋索引使用的關鍵字集合。寫入時一次性取代整個集合:

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 的快照,並將字典與資產記錄一起存放。之後的搜尋、去重與權利檢查皆可直接對資料庫查詢,而不必重新開啟二進位檔案。儲存庫中小樣本 PSD 的快照已在一次遍歷中返回大量屬性,當輸入規模擴大至數千檔案時,同樣的呼叫仍能保持效能。

授權執行

在資產上傳至客戶入口前,必須確保 dc:rights 不為空。未通過檢查的檔案會自動套用步驟 3 的標記流程,確保所有檔案皆帶有版權聲明。

批次重新標記

當分類法變更時,讀取每個檔案的 dc:subject,在 Python 中將舊詞彙映射為新詞彙,然後使用步驟 4 寫回合併後的集合。PSD 與 AI 檔案皆可使用相同迴圈,無需 Photoshop 授權。

最佳實踐與提示

  • 將空視為正常:沒有 XMP 的檔案是常態,而非錯誤;提前返回的模式可讓管線持續運作。
  • 寫入關鍵字前先合併:set 會取代 dc:subject,因此累加標記必須先讀取、擴充再寫入。
  • 在兩個 schema 中寫入身分:同時寫入 dc:creator 與 xmp:CreatorTool 可讓 Dublin Core 與 XmpBasic 讀取者保持一致。
  • 以讀回驗證寫入:儲存後立即重新讀取成本低,能即時捕捉容器層面的異常。
  • 生產環境需授權:評估模式可執行本文示範的所有操作;在正式為客戶資產加蓋標記前請先取得授權。

結論

在 Adobe 檔案中讀寫 XMP 只需三個步驟:透過 get_root_package() 取得封包、對所需的 schema 加上防護,然後讀取或寫入型別化值。掌握這三個步驟後,您即可在本教學中完成完整的往返流程:從封包快照到 schema 讀取,再到版權標記與關鍵字標記,且相同程式碼同時支援 PSD 與 AI 檔案。

準備好在您的專案中實作了嗎?以下是後續建議步驟:

其他資源

有關 XMP 工作流程的問題嗎?請在 support forum 提問。