Python library for working with XMP (Extensible Metadata Platform) metadata in files.
—
The XMPFiles class provides convenient access to XMP metadata in files with smart handlers for various file formats. It manages file opening, XMP extraction, modification, and writing back to files.
API for accessing main metadata in files with smart file handlers.
class XMPFiles:
def __init__(self, **kwargs):
"""
Create XMPFiles instance.
Parameters:
- file_path (optional): str, path to file to open
"""def open_file(self, file_path, **kwargs):
"""
Open file and read XMP metadata.
Parameters:
- file_path: str, path to file
- **kwargs: optional keyword arguments from XMP_OPEN_OPTIONS
Raises:
XMPError: if file already open or operation fails
"""
def close_file(self, close_flags=0):
"""
Close file and write XMP changes.
Parameters:
- close_flags: int, optional close flags from XMP_CLOSE_OPTIONS
(default: XMP_CLOSE_NOOPTION)
"""def get_xmp(self):
"""
Get XMP metadata from file.
Returns:
XMPMeta: New XMPMeta instance with file's XMP data, or None if no XMP
Raises:
XMPError: on failure
"""
def put_xmp(self, xmp_obj):
"""
Write XMP metadata to file.
Parameters:
- xmp_obj: XMPMeta instance with XMP data to write
Raises:
XMPError: on failure
"""
def can_put_xmp(self, xmp_obj):
"""
Check if XMP can be written to file.
Parameters:
- xmp_obj: XMPMeta instance
Returns:
bool: True if XMP can be written to file
"""The XMPFiles class includes smart handlers for various file formats:
Available options for open_file() method from libxmp.consts:
XMP_OPEN_NOOPTION = 0x00000000 # No special options
XMP_OPEN_READ = 0x00000001 # Open for read-only access
XMP_OPEN_FORUPDATE = 0x00000002 # Open for read-write access
XMP_OPEN_ONLYXMP = 0x00000004 # Only process XMP, ignore other metadata
XMP_OPEN_CACHETNAIL = 0x00000008 # Cache thumbnail if present
XMP_OPEN_STRICTLY = 0x00000010 # Strict conformance checking
XMP_OPEN_USESMARTHANDLER = 0x00000020 # Use smart handlers
XMP_OPEN_USEPACKETSCANNING = 0x00000040 # Use packet scanning fallback
XMP_OPEN_LIMITSCANNING = 0x00000080 # Limit packet scanning
XMP_OPEN_INBACKGROUND = 0x10000000 # Set if calling from background threadAvailable options for close_file() method:
XMP_CLOSE_NOOPTION = 0x0000 # No special options
XMP_CLOSE_SAFEUPDATE = 0x0001 # Write XMP into safe temp file firstfrom libxmp import XMPFiles, XMPMeta, XMPError
# Open file and read XMP
try:
xmpfile = XMPFiles(file_path="photo.jpg")
xmp_data = xmpfile.get_xmp()
if xmp_data:
# Read existing metadata
creator = xmp_data.get_property("http://purl.org/dc/elements/1.1/", "creator")
print(f"Original creator: {creator}")
# Modify metadata
xmp_data.set_property("http://purl.org/dc/elements/1.1/", "creator", "Updated Creator")
xmp_data.set_property("http://ns.adobe.com/xap/1.0/", "CreatorTool", "Python XMP Toolkit")
# Write back to file
xmpfile.put_xmp(xmp_data)
else:
print("No XMP metadata found in file")
# Close file (writes changes)
xmpfile.close_file()
except XMPError as e:
print(f"XMP error: {e}")from libxmp import XMPFiles, XMPMeta
from libxmp.consts import XMP_OPEN_FORUPDATE, XMP_CLOSE_SAFEUPDATE
# Process multiple file types
file_paths = ["document.pdf", "image.tiff", "photo.jpg"]
for file_path in file_paths:
try:
# Open with specific options
xmpfile = XMPFiles()
xmpfile.open_file(file_path, open_forupdate=True)
xmp_data = xmpfile.get_xmp()
if not xmp_data:
# Create new XMP if none exists
xmp_data = XMPMeta()
# Add processing timestamp
from datetime import datetime
xmp_data.set_property_datetime(
"http://ns.adobe.com/xap/1.0/",
"ModifyDate",
datetime.now()
)
# Check if we can write XMP to this file type
if xmpfile.can_put_xmp(xmp_data):
xmpfile.put_xmp(xmp_data)
print(f"Updated XMP in {file_path}")
else:
print(f"Cannot write XMP to {file_path}")
# Close with safe update
xmpfile.close_file(close_flags=XMP_CLOSE_SAFEUPDATE)
except Exception as e:
print(f"Error processing {file_path}: {e}")from libxmp import XMPFiles, XMPMeta
from libxmp.consts import *
def process_file_metadata(file_path, metadata_updates):
"""
Process file metadata with comprehensive error handling.
Args:
file_path: Path to file
metadata_updates: Dict of namespace -> {property: value} mappings
"""
xmpfile = None
try:
# Open file with comprehensive options
xmpfile = XMPFiles()
xmpfile.open_file(
file_path,
open_forupdate=True,
open_usesmarthandler=True,
open_usepacketscanning=True
)
# Get existing XMP or create new
xmp_data = xmpfile.get_xmp()
if not xmp_data:
xmp_data = XMPMeta()
# Apply metadata updates
for namespace, properties in metadata_updates.items():
for prop_name, prop_value in properties.items():
if isinstance(prop_value, list):
# Handle arrays
for item in prop_value:
xmp_data.append_array_item(namespace, prop_name, item)
else:
# Handle simple properties
xmp_data.set_property(namespace, prop_name, prop_value)
# Verify we can write before attempting
if xmpfile.can_put_xmp(xmp_data):
xmpfile.put_xmp(xmp_data)
return True
else:
print(f"Cannot write XMP to {file_path}")
return False
except Exception as e:
print(f"Error processing {file_path}: {e}")
return False
finally:
if xmpfile:
try:
xmpfile.close_file(close_flags=XMP_CLOSE_SAFEUPDATE)
except:
pass # Ignore close errors after main operation failure
# Usage
updates = {
"http://purl.org/dc/elements/1.1/": {
"creator": "John Doe",
"subject": ["photography", "landscape", "nature"]
},
"http://ns.adobe.com/xap/1.0/": {
"CreatorTool": "Python XMP Toolkit"
}
}
success = process_file_metadata("photo.jpg", updates)
if success:
print("Metadata updated successfully")Common XMP file operation errors and handling:
from libxmp import XMPFiles, XMPError, ExempiLoadError
try:
xmpfile = XMPFiles(file_path="example.jpg")
xmp_data = xmpfile.get_xmp()
# ... work with XMP data
except ExempiLoadError:
print("Exempi library could not be loaded - check installation")
except XMPError as e:
if "Permission denied" in str(e):
print("File access denied - check file permissions")
elif "No such file" in str(e):
print("File not found - check file path")
else:
print(f"XMP operation failed: {e}")
except IOError as e:
print(f"File I/O error: {e}")
finally:
# Always close file if opened
if 'xmpfile' in locals():
try:
xmpfile.close_file()
except:
pass # Ignore close errorsInstall with Tessl CLI
npx tessl i tessl/pypi-python-xmp-toolkit