مثال کامل قابل اجرا در گیت‌هاب موجود است:
manage-xmp-in-psd-and-ai-files-python

مقدمه

یک تیم بازاریابی ۴۰۰ فایل PSD را به پلتفرم دارایی‌های شما می‌اندازد. بارگذاری انجام می‌شود. جستجو کار نمی‌کند، چون هیچ‌یک از فایل‌ها کلیدواژه ندارند، نیمی از آن‌ها اعلان حق‌کپی ندارند و نام طراحان فقط در یک جدول‌محور در جایی ذخیره شده است. راه‌حل یک جدول‌محور بزرگ‌تر نیست. مدیریت XMP یک قابلیت GroupDocs.Metadata برای پایتون از طریق .NET است که بستهٔ متادیتای تعبیه‌شده در فایل‌های Photoshop PSD و Illustrator AI را می‌خواند و می‌نویسد؛ به این معنی که مالکیت و داده‌های جستجو می‌توانند در خود فایل‌ها زندگی کنند.

XMP یک بستهٔ XML داخل یک محفظهٔ باینری است که به طرح‌ها (schemes) سازماندهی می‌شود: Dublin Core برای فیلدهایی که هر سیستمی می‌فهمد، طرح Photoshop برای زمینهٔ ویرایشی، XmpBasic برای هویت ابزار. تجزیهٔ دستی یک PSD برای رسیدن به آن بسته واقعاً دشوار است. با کلاس Metadata فقط سه جستجوی ویژگی کافی است و کد یکسان برای فایل‌های AI نیز کار می‌کند.

این آموزش تمام مسیر کامل را در چهار گام مرور می‌کند: گرفتن تصویر کلی از بستهٔ کامل، خواندن طرح‌های مهم، نوشتن حق‌کپی و سازنده، و برچسب‌گذاری کلیدواژه‌ها برای جستجو. هر قطعه کد از یک مخزن قابل اجرا می‌آید که صحت مقادیر نوشته‌شده را تأیید می‌کند.

پیش‌نیازها

قبل از شروع، مطمئن شوید که موارد زیر را دارید:

  • پایتون ۳ به همراه pip
  • GroupDocs.Metadata برای پایتون از طریق .NET (مخزن نسخهٔ ۲۶.۵ را قفل کرده است)
  • یک فایل PSD یا AI برای آزمایش

نصب

pip install groupdocs-metadata-net==26.5

گام ۱ - گرفتن تصویر کلی از بستهٔ کامل XMP

با دیدن تمام آنچه فایل حمل می‌کند شروع کنید. این تصویر کلی ریشهٔ بسته، هر طرح ثبت‌شده و در نهایت درخت ویژگی‌ها را برای هر چیز غیراستاندارد می‌پیماید و همهٔ آن‌ها را در یک دیکشنری صاف جمع‌آوری می‌کند.

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: تاریخ‌ها و مقادیر شمارشی به صورت قابل خواندن برای انسان برگردانده می‌شوند نه به شکل خام.
  • هفت طرح به‌همراه یک اسکن کلی: عبور نهایی find_properties بسته‌های فروشنده‌ای را که طرح‌های نام‌دار از دست می‌دهند، می‌گیرد.
  • یک بار باز کردن فایل: تمام تصویر کلی با یک زمینهٔ Metadata انجام می‌شود که در پردازش انبوه اهمیت دارد.

نکته: این دیکشنری را در زمان ورود (ingestion) ایندکس کنید تا بیشتر پرسش‌های متادیتا بعداً به جستجوهای دیکشنری تبدیل شوند نه خواندن مجدد فایل.

کدام طرح XMP باید ابتدا توسط یکپارچه‌سازی من خوانده شود؟

با Dublin Core شروع کنید. نه‌فیلد dc: آن عنوان، سازنده، حقوق و موضوع را حمل می‌کنند که اکثر سیستم‌های DAM، ایندکس‌های جستجو و بررسی‌های مجوز بر روی آن‌ها توافق دارند و هر دو فایل PSD و AI به‌طور یکسان آن را ارائه می‌دهند. سپس طرح Photoshop را برای زمینهٔ ویرایشی مانند City، Credit و DateCreated بخوانید. اسکن کامل بسته را برای کارهای ورودی (ingestion) که باید همه چیز را بگیرند، نگه دارید.

گام ۲ - خواندن طرح‌هایی که به سوالات واقعی پاسخ می‌دهند

برای کدهای زمان درخواست، خواندن را به یک طرح محدود کنید. 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، که هر کدام با یک محافظ None خوانده می‌شوند. این‌ها فیلدهایی هستند که Bridge، Lightroom و فیلترهای جستجوی DAM برای فایل‌های Adobe بر پایهٔ آن‌ها عمل می‌کنند.

به آنچه در فایل‌های بدون XMP اتفاق می‌افتد توجه کنید: محافظ‌ها یک دیکشنری خالی برمی‌گردانند، نه استثنا. دارایی‌های تازه صادرشده این حالت را به‌طور معمول نشان می‌دهند، بنابراین این رفتار را در یکپارچه‌سازی خود حفظ کنید.

سه جستجوی بالا در فایل‌های Illustrator نیز کار می‌کند. campaign-hero.psd را با brand-mark.ai عوض کنید و هیچ چیز دیگری تغییر نمی‌کند؛ این همان چیزی است که مسیر کد واحد را برای آرشیوهای ترکیبی Adobe واقعی می‌کند. در عمل یک خروجی تازهٔ AI معمولاً طرح‌های کمتری پر می‌کند نسبت به ذخیرهٔ Photoshop، بنابراین مسیر دیکشنری خالی بیشتر در آنجا به کار می‌رود.

گام ۳ - نوشتن حق‌کپی و سازنده

حالا مسیر نوشتن. مهر مالکیت سه فیلد را تحت‌تأثیر قرار می‌دهد تا هر خواننده همان هویت را ببیند: dc:rights برای اعلان قانونی، dc:creator به‌عنوان یک لیست مرتب، و xmp:CreatorTool برای ابزارهایی که طرح XmpBasic را می‌خوانند به‌جای Dublin Core. یک بار من بعدازظهرم را به‌دلیل بنر مجوزی که «نویسنده ناشناخته» را روی دارایی‌ها نشان می‌داد، هدر دادم؛ مقادیر در 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 مرتب استفاده می‌کند.
  • ذخیره به مسیر جدید: فایل منبع دست‌نخورده می‌ماند که برای گام‌های خروجی پیش‌فرض صحیح است.

گام ۴ - برچسب‌گذاری کلیدواژه‌ها برای جستجو

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 کیسهٔ موجود را جایگزین می‌کند، برای برچسب‌گذاری افزایشی ابتدا کلیدواژه‌های فعلی را بخوانید و سپس در پایتون ترکیب کنید تا به‌جای جایگزینی، افزودن انجام شود.

برای تأیید هر نوشتن، گام ۲ خواننده را روی فایل خروجی دوباره اجرا کنید. مخزن دقیقاً این کار را خودکار می‌کند: خروجی‌های خود را دوباره می‌خواند و صحت رشتهٔ حق‌کپی و اولین کلیدواژه را در بایت‌های ذخیره‌شده تأیید می‌کند.

کاربردهای دنیای واقعی

ورود به DAM

تصویر کلی گام ۱ را روی هر فایل ورودی اجرا کنید و دیکشنری را همراه با رکورد دارایی ذخیره کنید. سپس جستجو، حذف تکراری و بررسی حقوق بر خلاف دیتابیس شما انجام می‌شود نه باز کردن مجدد فایل‌های باینری. تصویر کلی نمونهٔ کوچک PSD مخزن، در یک عبور یک مجموعهٔ سالم از ویژگی‌ها را برمی‌گرداند و همان فراخوانی شکل خود را وقتی ورودی به یک پوشهٔ هزاران فایل تبدیل می‌شود، حفظ می‌کند.

اجرای مجوز

قبل از اینکه دارایی به پورتال مشتری تحویل داده شود، dc:rights غیرخالی باشد. فایل‌هایی که این شرط را ندارند به‌صورت خودکار با گام ۳ مهر می‌خورند، بنابراین هیچ‌چیزی بدون اعلان خروج نمی‌کند.

برچسب‌گذاری دسته‌ای

وقتی طبقه‌بندی تغییر می‌کند، dc:subject هر فایل را بخوانید، عبارات قدیمی را به عبارات جدید در پایتون نگاشت کنید و کیسهٔ ترکیبی را با گام ۴ بنویسید. هر دو آرشیو PSD و AI همان حلقه را می‌پذیرند. نیازی به داشتن Photoshop نیست.

بهترین روش‌ها و نکات

  • خالی را به‌عنوان عادی در نظر بگیرید: فایل‌های بدون XMP روتین هستند، نه خطا؛ الگوی بازگشت زودهنگام خطوط لوله را روان نگه می‌دارد.
  • قبل از نوشتن کلیدواژه‌ها ترکیب کنید: set dc:subject را جایگزین می‌کند، بنابراین برچسب‌گذاری افزایشی یعنی خواندن، گسترش، نوشتن.
  • هویت را در هر دو طرح بنویسید: جفت کردن dc:creator با xmp:CreatorTool خوانندگان Dublin Core و XmpBasic را در توافق نگه می‌دارد.
  • نوشتارها را با خواندن مجدد تأیید کنید: یک خواندن پس از ذخیره ارزان است و بلافاصله شگفتی‌های محفظه را می‌گیرد.
  • مجوز برای تولید: حالت ارزیابی همه چیزهای نشان‌داده‌شده را اجرا می‌کند؛ قبل از مهر زدن دارایی‌های واقعی مشتری، از یک مجوز استفاده کنید.

نتیجه‌گیری

خواندن و نوشتن XMP در فایل‌های Adobe به سه حرکت خلاصه می‌شود: حل بسته از طریق get_root_package()، محافظت از طرح مورد نیاز، و خواندن یا نوشتن مقادیر تایپ‌شده. با این حرکات، شما یک مسیر کامل در این آموزش ساختید؛ از تصویر کلی بسته تا خواندن طرح‌ها، مهر حق‌کپی و برچسب‌گذاری کلیدواژه‌ها، با همان کد برای فایل‌های PSD و AI.

آماده‌اید این را در پروژه‌تان پیاده‌سازی کنید؟ گام‌های بعدی عبارتند از:

منابع اضافی

سوال دربارهٔ جریان کاری XMP خود دارید؟ در forum پشتیبانی بپرسید.