Python library for working with XMP (Extensible Metadata Platform) metadata in files.
—
Utility functions for extracting XMP data to Python dictionaries, comprehensive XMP constants for namespaces and options, and library management functions.
def object_to_dict(xmp):
"""
Extract all XMP data from XMPMeta object into standard Python dictionary.
Parameters:
- xmp: XMPMeta instance
Returns:
dict: Standard Python dictionary with XMP data organized by namespace
"""
def file_to_dict(file_path):
"""
Extract XMP data from file into dictionary.
Parameters:
- file_path: str, path to file
Returns:
dict: Dictionary with XMP data (empty if no valid XMP found)
Raises:
IOError: if file doesn't exist
"""def terminate():
"""
Terminate library usage for memory cleanup.
Warning: Calling this crashes Python if libxmp methods are used afterward.
Only call when completely done with XMP operations.
"""# Core XMP namespaces
XMP_NS_XMP = "http://ns.adobe.com/xap/1.0/"
XMP_NS_XMP_Rights = "http://ns.adobe.com/xap/1.0/rights/"
XMP_NS_XMP_MM = "http://ns.adobe.com/xap/1.0/mm/"
XMP_NS_XMP_BJ = "http://ns.adobe.com/xap/1.0/bj/"
# Dublin Core
XMP_NS_DC = "http://purl.org/dc/elements/1.1/"
# IPTC Core
XMP_NS_IPTCCore = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
# Adobe application namespaces
XMP_NS_Photoshop = "http://ns.adobe.com/photoshop/1.0/"
XMP_NS_CameraRaw = "http://ns.adobe.com/camera-raw-settings/1.0/"
XMP_NS_CameraRawSavedSettings = "http://ns.adobe.com/camera-raw-saved-settings/1.0/"
# Document formats
XMP_NS_PDF = "http://ns.adobe.com/pdf/1.3/"
XMP_NS_PDFX = "http://ns.adobe.com/pdfx/1.3/"
# Technical namespaces
XMP_NS_TIFF = "http://ns.adobe.com/tiff/1.0/"
XMP_NS_EXIF = "http://ns.adobe.com/exif/1.0/"
XMP_NS_EXIF_Aux = "http://ns.adobe.com/exif/1.0/aux/"
# Image formats
XMP_NS_PNG = "http://ns.adobe.com/png/1.0/"
XMP_NS_JPEG = "http://ns.adobe.com/jpeg/1.0/"
XMP_NS_JP2K = "http://ns.adobe.com/jp2k/1.0/"
XMP_NS_SWF = "http://ns.adobe.com/swf/1.0/"
# Media formats
XMP_NS_DM = "http://ns.adobe.com/xmp/1.0/DynamicMedia/"
XMP_NS_ASF = "http://ns.adobe.com/asf/1.0/"
XMP_NS_WAV = "http://ns.adobe.com/xmp/wav/1.0/"
# Adobe applications
XMP_NS_PSAlbum = "http://ns.adobe.com/album/1.0/"
XMP_NS_Lightroom = "http://ns.adobe.com/lightroom/1.0/"
XMP_NS_AdobeStockPhoto = "http://ns.adobe.com/StockPhoto/1.0/"
XMP_NS_CreatorAtom = "http://ns.adobe.com/creatorAtom/1.0/"
# XMP structure types
XMP_NS_XMP_Note = "http://ns.adobe.com/xmp/note/"
XMP_NS_XMP_IdentifierQual = "http://ns.adobe.com/xmp/Identifier/qual/1.0/"
XMP_NS_XMP_Dimensions = "http://ns.adobe.com/xap/1.0/sType/Dimensions#"
XMP_NS_XMP_Text = "http://ns.adobe.com/xap/1.0/t/"
XMP_NS_XMP_PagedFile = "http://ns.adobe.com/xap/1.0/t/pg/"
XMP_NS_XMP_Graphics = "http://ns.adobe.com/xap/1.0/g/"
XMP_NS_XMP_Image = "http://ns.adobe.com/xap/1.0/g/img/"
XMP_NS_XMP_Font = "http://ns.adobe.com/xap/1.0/sType/Font#"
XMP_NS_XMP_ResourceEvent = "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
XMP_NS_XMP_ResourceRef = "http://ns.adobe.com/xap/1.0/sType/ResourceRef#"
XMP_NS_XMP_ST_Version = "http://ns.adobe.com/xap/1.0/sType/Version#"
XMP_NS_XMP_ST_Job = "http://ns.adobe.com/xap/1.0/sType/Job#"
XMP_NS_XMP_ManifestItem = "http://ns.adobe.com/xap/1.0/sType/ManifestItem#"
# Standards and schemas
XMP_NS_DICOM = "http://ns.adobe.com/DICOM/"
XMP_NS_PDFA_Schema = "http://www.aiim.org/pdfa/ns/schema#"
XMP_NS_PDFA_Property = "http://www.aiim.org/pdfa/ns/property#"
XMP_NS_PDFA_Type = "http://www.aiim.org/pdfa/ns/type#"
XMP_NS_PDFA_Field = "http://www.aiim.org/pdfa/ns/field#"
XMP_NS_PDFA_ID = "http://www.aiim.org/pdfa/ns/id/"
XMP_NS_PDFA_Extension = "http://www.aiim.org/pdfa/ns/extension/"
XMP_NS_PDFX_ID = "http://www.npes.org/pdfx/ns/id/"
XMP_NS_CC = "http://creativecommons.org/ns#"
XMP_NS_AVM = "http://www.communicatingastronomy.org/avm/1.0/"
# XML and RDF
XMP_NS_XML = "http://www.w3.org/XML/1998/namespace"
XMP_NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
XMP_NS_XMPMeta = "adobe:ns:meta/"# Document formats
XMP_FT_PDF = 0x50444620 # 'PDF '
XMP_FT_PS = 0x50532020 # 'PS ' PostScript
XMP_FT_EPS = 0x45505320 # 'EPS ' Encapsulated PostScript
# Image formats
XMP_FT_JPEG = 0x4A504547 # 'JPEG'
XMP_FT_JPEG2K = 0x4A505820 # 'JPX ' ISO 15444-1
XMP_FT_TIFF = 0x54494646 # 'TIFF'
XMP_FT_GIF = 0x47494620 # 'GIF '
XMP_FT_PNG = 0x504E4720 # 'PNG '
# Media formats
XMP_FT_SWF = 0x53574620 # 'SWF '
XMP_FT_FLA = 0x464C4120 # 'FLA '
XMP_FT_FLV = 0x464C5620 # 'FLV '
XMP_FT_MOV = 0x4D4F5620 # 'MOV ' Quicktime
XMP_FT_AVI = 0x41564920 # 'AVI '
XMP_FT_MPEG = 0x4D504547 # 'MPEG'
XMP_FT_MPEG2 = 0x4D503220 # 'MP2 '
XMP_FT_MPEG4 = 0x4D503420 # 'MP4 ' ISO 14494-12 and -14
# Audio formats
XMP_FT_WAV = 0x57415620 # 'WAV '
XMP_FT_MP3 = 0x4D503320 # 'MP3 '
XMP_FT_AIFF = 0x41494646 # 'AIFF'
XMP_FT_WMAV = 0x574D4156 # 'WMAV' Windows Media Audio and Video
# Adobe applications
XMP_FT_PHOTOSHOP = 0x50534420 # 'PSD '
XMP_FT_ILLUSTRATOR = 0x41492020 # 'AI '
XMP_FT_INDESIGN = 0x494E4444 # 'INDD'
XMP_FT_AEPROJECT = 0x41455020 # 'AEP ' After Effects Project
XMP_FT_AEPROJTEMPLATE = 0x41455420 # 'AET ' After Effects Project Template
XMP_FT_AEFILTERPRESET = 0x46465820 # 'FFX ' After Effects Filter Preset
XMP_FT_ENCOREPROJECT = 0x4E434F52 # 'NCOR'
XMP_FT_PREMIEREPROJECT = 0x5052504A # 'PRPJ'
XMP_FT_PREMIERETITLE = 0x5052544C # 'PRTL'
# Other formats
XMP_FT_CIN = 0x43494E20 # 'CIN ' Cineon
XMP_FT_SES = 0x53455320 # 'SES ' Audition session
XMP_FT_CEL = 0x43454C20 # 'CEL ' Audition loop
XMP_FT_HTML = 0x48544D4C # 'HTML'
XMP_FT_XML = 0x584D4C20 # 'XML '
XMP_FT_TEXT = 0x74657874 # 'text'
XMP_FT_UNKNOWN = 0x20202020 # Default/unknown# Property type flags
XMP_PROP_VALUE_IS_URI = 0x00000002
XMP_PROP_HAS_QUALIFIERS = 0x00000010
XMP_PROP_IS_QUALIFIER = 0x00000020
XMP_PROP_HAS_LANG = 0x00000040
XMP_PROP_HAS_TYPE = 0x00000080
XMP_PROP_VALUE_IS_STRUCT = 0x00000100
XMP_PROP_VALUE_IS_ARRAY = 0x00000200
XMP_PROP_ARRAY_IS_UNORDERED = 0x00000000
XMP_PROP_ARRAY_IS_ORDERED = 0x00000400
XMP_PROP_ARRAY_IS_ALT = 0x00000800
XMP_PROP_ARRAY_IS_ALTTEXT = 0x00001000XMP_SERIAL_OMITPACKETWRAPPER = 0x0010 # Omit XMP packet wrapper
XMP_SERIAL_READONLYPACKET = 0x0020 # Mark packet as read-only
XMP_SERIAL_USECOMPACTFORMAT = 0x0040 # Use compact RDF formatting
XMP_SERIAL_INCLUDETHUMBNAILPAD = 0x0100 # Include padding for thumbnail
XMP_SERIAL_EXACTPACKETLENGTH = 0x0200 # Exact packet length
XMP_SERIAL_WRITEALIASCOMMENTS = 0x0400 # Write alias comments
XMP_SERIAL_OMITALLFORMATTING = 0x0800 # Omit all formattingXMP_ITER_PROPERTIES = 0x0000 # Iterate all properties
XMP_ITER_JUSTCHILDREN = 0x0100 # Just immediate children
XMP_ITER_JUSTLEAFNODES = 0x0200 # Just leaf nodes
XMP_ITER_JUSTLEAFNAME = 0x0400 # Just leaf names
XMP_ITER_INCLUDEALIASES = 0x0800 # Include alias properties
XMP_ITER_OMITQUALIFIERS = 0x1000 # Omit qualifier properties# File open options
XMP_OPEN_NOOPTION = 0x00000000 # No open option
XMP_OPEN_READ = 0x00000001 # Open for read-only access
XMP_OPEN_FORUPDATE = 0x00000002 # Open for reading and writing
XMP_OPEN_ONLYXMP = 0x00000004 # Only XMP wanted, allows optimizations
XMP_OPEN_CACHETNAIL = 0x00000008 # Cache thumbnail if possible
XMP_OPEN_STRICTLY = 0x00000010 # Be strict about locating XMP
XMP_OPEN_USESMARTHANDLER = 0x00000020 # Require smart handler use
XMP_OPEN_USEPACKETSCANNING = 0x00000040 # Force packet scanning
XMP_OPEN_LIMITSCANNING = 0x00000080 # Only scan files known to need it
XMP_OPEN_INBACKGROUND = 0x10000000 # Set if calling from background thread
# File close options
XMP_CLOSE_NOOPTION = 0x0000 # No close option
XMP_CLOSE_SAFEUPDATE = 0x0001 # Write to temp file and swap for safetydef has_option(xmp_option, bitmask):
"""
Check if option bit is set in bitmask.
Parameters:
- xmp_option: int, option value to check
- bitmask: int, bitmask to check against
Returns:
bool: True if option bit is set
"""
def options_mask(xmp_options, **kwargs):
"""
Convert option dictionary and keyword arguments to bitmask.
Parameters:
- xmp_options: dict, option name to constant mappings
- **kwargs: keyword arguments with option names
Returns:
int: Combined bitmask for all specified options
"""# Serialization options mapping
XMP_SERIAL_OPTIONS = {
'omit_packet_wrapper': XMP_SERIAL_OMITPACKETWRAPPER,
'read_only_packet': XMP_SERIAL_READONLYPACKET,
'use_compact_format': XMP_SERIAL_USECOMPACTFORMAT,
'include_thumbnail_pad': XMP_SERIAL_INCLUDETHUMBNAILPAD,
'exact_packet_length': XMP_SERIAL_EXACTPACKETLENGTH,
'write_alias_comments': XMP_SERIAL_WRITEALIASCOMMENTS,
'omit_all_formatting': XMP_SERIAL_OMITALLFORMATTING
}
# Property options mapping
XMP_PROP_OPTIONS = {
'value_is_uri': XMP_PROP_VALUE_IS_URI,
'has_qualifiers': XMP_PROP_HAS_QUALIFIERS,
'is_qualifier': XMP_PROP_IS_QUALIFIER,
'has_lang': XMP_PROP_HAS_LANG,
'has_type': XMP_PROP_HAS_TYPE,
'value_is_struct': XMP_PROP_VALUE_IS_STRUCT,
'value_is_array': XMP_PROP_VALUE_IS_ARRAY,
'array_is_ordered': XMP_PROP_ARRAY_IS_ORDERED,
'array_is_alt': XMP_PROP_ARRAY_IS_ALT,
'array_is_alttext': XMP_PROP_ARRAY_IS_ALTTEXT
}
# Open file options mapping
XMP_OPEN_OPTIONS = {
'open_nooption': XMP_OPEN_NOOPTION,
'open_read': XMP_OPEN_READ,
'open_forupdate': XMP_OPEN_FORUPDATE,
'open_onlyxmp': XMP_OPEN_ONLYXMP,
'open_cachetnail': XMP_OPEN_CACHETNAIL,
'open_strictly': XMP_OPEN_STRICTLY,
'open_usesmarthandler': XMP_OPEN_USESMARTHANDLER,
'open_usepacketscanning': XMP_OPEN_USEPACKETSCANNING,
'open_limitscanning': XMP_OPEN_LIMITSCANNING,
'open_inbackground': XMP_OPEN_INBACKGROUND
}
# Close file options mapping
XMP_CLOSE_OPTIONS = {
'close_nooption': XMP_CLOSE_NOOPTION,
'close_safeupdate': XMP_CLOSE_SAFEUPDATE
}from libxmp import XMPMeta
from libxmp.utils import object_to_dict, file_to_dict
# Extract from XMPMeta object
xmp = XMPMeta()
xmp.set_property("http://ns.adobe.com/xap/1.0/", "CreatorTool", "Python")
xmp.set_property("http://purl.org/dc/elements/1.1/", "creator", "John Doe")
# Convert to dictionary
xmp_dict = object_to_dict(xmp)
print(xmp_dict)
# Output: {
# 'http://ns.adobe.com/xap/1.0/': {'CreatorTool': 'Python'},
# 'http://purl.org/dc/elements/1.1/': {'creator': 'John Doe'}
# }
# Extract directly from file
file_dict = file_to_dict("photo.jpg")
if file_dict:
print("File contains XMP metadata:")
for namespace, properties in file_dict.items():
print(f" {namespace}:")
for prop, value in properties.items():
print(f" {prop}: {value}")
else:
print("No XMP metadata found in file")from libxmp import XMPMeta
from libxmp.consts import *
# Using namespace constants
xmp = XMPMeta()
xmp.set_property(XMP_NS_DC, "creator", "John Doe")
xmp.set_property(XMP_NS_XMP, "CreatorTool", "Python XMP Toolkit")
xmp.set_property(XMP_NS_Photoshop, "ColorMode", "RGB")
# Using serialization options with keyword arguments
xmp_str = xmp.serialize_to_str(
use_compact_format=True,
omit_packet_wrapper=True
)
# Using property options
xmp.set_property(
XMP_NS_DC,
"rights",
"Copyright 2024",
value_is_uri=False
)
# Create ordered array
xmp.append_array_item(
XMP_NS_DC,
"creator",
"First Author",
array_is_ordered=True
)from libxmp.consts import has_option, options_mask, XMP_PROP_OPTIONS
# Check if specific option is set
prop_options = 0x00000210 # Some combined options
if has_option(prop_options, XMP_PROP_HAS_QUALIFIERS):
print("Property has qualifiers")
if has_option(prop_options, XMP_PROP_VALUE_IS_STRUCT):
print("Property is a structure")
# Create option mask from keywords
mask = options_mask(XMP_PROP_OPTIONS,
value_is_struct=True,
has_qualifiers=True)
print(f"Combined option mask: {mask}")from libxmp import XMPMeta
from libxmp.consts import *
def set_image_metadata(file_path, metadata):
"""Set comprehensive image metadata using standard namespaces."""
from libxmp import XMPFiles
xmpfile = XMPFiles(file_path=file_path)
xmp = xmpfile.get_xmp() or XMPMeta()
# Dublin Core metadata
if 'title' in metadata:
xmp.set_localized_text(XMP_NS_DC, "title", "x-default", "en-US", metadata['title'])
if 'creator' in metadata:
xmp.set_property(XMP_NS_DC, "creator", metadata['creator'])
if 'description' in metadata:
xmp.set_localized_text(XMP_NS_DC, "description", "x-default", "en-US", metadata['description'])
if 'keywords' in metadata:
for keyword in metadata['keywords']:
xmp.append_array_item(XMP_NS_DC, "subject", keyword)
# XMP Basic metadata
if 'creator_tool' in metadata:
xmp.set_property(XMP_NS_XMP, "CreatorTool", metadata['creator_tool'])
if 'create_date' in metadata:
xmp.set_property_datetime(XMP_NS_XMP, "CreateDate", metadata['create_date'])
# Rights management
if 'rights' in metadata:
xmp.set_localized_text(XMP_NS_XMP_Rights, "UsageTerms", "x-default", "en-US", metadata['rights'])
# Photoshop-specific metadata
if 'color_mode' in metadata:
xmp.set_property(XMP_NS_Photoshop, "ColorMode", str(metadata['color_mode']))
# IPTC Core metadata
if 'location' in metadata:
xmp.set_property(XMP_NS_IPTCCore, "Location", metadata['location'])
xmpfile.put_xmp(xmp)
xmpfile.close_file()
# Usage
metadata = {
'title': 'Sunset Over Mountains',
'creator': 'Jane Photographer',
'description': 'Beautiful sunset photograph taken in the mountains',
'keywords': ['sunset', 'mountains', 'landscape', 'nature'],
'creator_tool': 'Python XMP Toolkit',
'rights': 'Copyright 2024 Jane Photographer. All rights reserved.',
'color_mode': 3, # RGB
'location': 'Rocky Mountains, Colorado'
}
set_image_metadata("sunset.jpg", metadata)from libxmp.utils import terminate
from libxmp import XMPMeta, XMPFiles
# Process many files
files = ["img1.jpg", "img2.jpg", "img3.jpg"] # ... many files
for file_path in files:
try:
xmpfile = XMPFiles(file_path=file_path)
xmp = xmpfile.get_xmp()
# ... process XMP data
xmpfile.close_file()
except Exception as e:
print(f"Error processing {file_path}: {e}")
# When completely done with all XMP operations
# (only call this at program end)
try:
terminate()
print("XMP library terminated successfully")
except:
pass # Ignore termination errorsInstall with Tessl CLI
npx tessl i tessl/pypi-python-xmp-toolkit