Module: XmpToolkitRuby

Defined in:
lib/xmp_toolkit_ruby.rb,
lib/xmp_toolkit_ruby/cli.rb,
lib/xmp_toolkit_ruby/version.rb,
lib/xmp_toolkit_ruby/xmp_file.rb,
lib/xmp_toolkit_ruby/xmp_value.rb,
lib/xmp_toolkit_ruby/namespaces.rb,
lib/xmp_toolkit_ruby/xmp_char_form.rb,
lib/xmp_toolkit_ruby/xmp_file_format.rb,
lib/xmp_toolkit_ruby/xmp_file_open_flags.rb,
lib/xmp_toolkit_ruby/xmp_file_handler_flags.rb,
ext/xmp_toolkit_ruby/xmp_toolkit_ruby.cpp

Overview

The ‘XmpToolkitRuby` module serves as a Ruby interface to Adobe’s XMP Toolkit, a native C++ library. This module allows Ruby applications to read and write XMP (Extensible Metadata Platform) metadata from and to various file formats.

It handles the initialization and termination of the underlying C++ toolkit, manages paths to necessary plugins (like PDF handlers), processes and cleans XMP XML data, and provides a user-friendly API for XMP operations.

Key functionalities include:

  • Reading XMP metadata from files.

  • Writing XMP metadata to files, with options to override or update existing data.

  • Automatic management of the XMP Toolkit’s lifecycle.

  • Platform-aware resolution of plugin paths, with environment variable override.

  • Parsing and cleaning of XMP packet data.

  • Mapping of numerical handler flags to descriptive representations.

rubocop:disable Metrics/ModuleLength

Examples:

Reading XMP from a file

 = XmpToolkitRuby.xmp_from_file("path/to/image.jpg")
puts ["xmp_data"] # Access the cleaned XMP XML string

Writing XMP to a file

new_xmp_data = "<x:xmpmeta xmlns:x='adobe:ns:meta/'><rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'></rdf:RDF></x:xmpmeta>"
XmpToolkitRuby.xmp_to_file("path/to/image.jpg", new_xmp_data, override: true)

Defined Under Namespace

Modules: Namespaces, XmpCharForm, XmpFileFormat, XmpFileHandlerFlags, XmpFileOpenFlags, XmpToolkit Classes: CLI, Error, FileNotFoundError, XmpFile, XmpValue, XmpWrapper

Constant Summary collapse

PLUGINS_PATH =

The ‘PLUGINS_PATH` constant defines the directory where the XMP Toolkit should look for its plugins, particularly the PDF handler. The path is determined based on the current operating system (macOS or Linux) and system architecture.

This path can be overridden by setting the ‘XMP_TOOLKIT_PLUGINS_PATH` environment variable, which takes precedence if set and not empty.

If the platform or architecture is unsupported, a warning will be issued, and the path may be empty, potentially affecting PDF handling capabilities.

Returns:

  • (String)

    The absolute path to the plugins directory.

if ENV["XMP_TOOLKIT_PLUGINS_PATH"] && !ENV["XMP_TOOLKIT_PLUGINS_PATH"].empty?
  ENV["XMP_TOOLKIT_PLUGINS_PATH"]
else
  case RUBY_PLATFORM
  when /darwin/ # macOS
    File.expand_path("./xmp_toolkit_ruby/plugins/PDF_Handler/macintosh/universal/", __dir__)
  when /linux/
    if RbConfig::CONFIG["host_cpu"] =~ /x86_64|amd64/
      File.expand_path("./xmp_toolkit_ruby/plugins/PDF_Handler/i80386linux/i80386linux_x64/", __dir__)
    elsif RbConfig::CONFIG["host_cpu"] =~ /i[3-6]86/ # Matches i386, i486, i586, i686
      File.expand_path("./xmp_toolkit_ruby/plugins/PDF_Handler/i80386linux/i80386linux/", __dir__)
    else
      warn "Unsupported Linux architecture for PLUGINS_PATH: #{RbConfig::CONFIG["host_cpu"]}. PDF Handler might not work."
      "" # Or some other default
    end
  else
    # Fallback or error for unsupported platforms
    warn "Unsupported platform for PLUGINS_PATH: #{RUBY_PLATFORM}. PDF Handler might not work."
    "" # Or some other default that makes sense for your application
  end
end
VERSION =
"0.1.0"

Class Method Summary collapse

Class Method Details

.check_file!(file_path, need_to_read: true, need_to_write: false) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Validates file accessibility before performing read or write operations.

Checks for:

  • Nil file path.

  • File existence.

  • File readability (if ‘need_to_read` is true).

  • File writability (if ‘need_to_write` is true).

Parameters:

  • file_path (String)

    The path to the file to check.

  • need_to_read (Boolean) (defaults to: true)

    (true) Whether the file needs to be readable.

  • need_to_write (Boolean) (defaults to: false)

    (false) Whether the file needs to be writable.

Raises:



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/xmp_toolkit_ruby.rb', line 195

def check_file!(file_path, need_to_read: true, need_to_write: false)
  if file_path.nil?
    raise FileNotFoundError, "File path cannot be nil"
  elsif !File.exist?(file_path)
    raise FileNotFoundError, "File not found: #{file_path}"
  elsif need_to_read && !File.readable?(file_path)
    raise FileNotFoundError, "File exists but is not readable: #{file_path}"
  elsif need_to_write && !File.writable?(file_path)
    raise FileNotFoundError, "File exists but is not writable: #{file_path}"
  end
end

.sdk_initialized?Boolean

Checks if the XMP Toolkit SDK has been initialized. This method is useful for ensuring that the SDK is ready for use

Returns:

  • (Boolean)


177
178
179
# File 'lib/xmp_toolkit_ruby.rb', line 177

def sdk_initialized?
  XmpToolkitRuby::XmpToolkit.initialized?
end

.with_init(path = nil) { ... } ⇒ Object

Ensures the native XMP Toolkit is initialized before executing a block of code and terminated afterward. This is crucial for managing the lifecycle of the underlying C++ library resources.

This method should wrap any calls to the native ‘XmpToolkitRuby::XmpToolkit` methods.

Parameters:

  • path (String, nil) (defaults to: nil)

    (nil) Optional path to the XMP Toolkit plugins directory. If ‘nil` or not provided, it defaults to `PLUGINS_PATH`.

Yields:

  • The block of code to execute while the XMP Toolkit is initialized.

Returns:

  • The result of the yielded block.



166
167
168
169
170
171
172
# File 'lib/xmp_toolkit_ruby.rb', line 166

def with_init(path = nil, &block)
  XmpToolkitRuby::XmpToolkit.initialize_xmp(path || PLUGINS_PATH) unless XmpToolkitRuby.sdk_initialized?

  block.call
ensure
  XmpToolkitRuby::XmpToolkit.terminate
end

.xmp_from_file(file_path) ⇒ Hash

Reads XMP metadata from a specified file.

This method first checks if the file exists and is readable. It then initializes the XMP Toolkit, reads the XMP data using the native extension, cleans up the extracted XML, maps handler flags to a descriptive format, and ensures the toolkit is terminated.

Parameters:

  • file_path (String)

    The absolute or relative path to the target file.

Returns:

  • (Hash)

    A hash containing the XMP metadata. The hash includes:

    • ‘“begin”`: The value of the `begin` attribute from the `xpacket` processing instruction.

    • ‘“packet_id”`: The value of the `id` attribute from the `xpacket` processing instruction.

    • ‘“xmp_data”`: The cleaned XMP XML string (core XMP metadata).

    • ‘“xmp_data_orig”`: The original, raw XMP data string as returned by the toolkit.

    • ‘“handler_flags”`: A descriptive representation of the handler flags (e.g., from XmpFileHandlerFlags).

    • ‘“handler_flags_orig”`: The original numerical handler flags from the toolkit.

    Returns an empty hash merged with cleanup and flag mapping results if the native call returns nil.

Raises:

  • (FileNotFoundError)

    If the file does not exist, is not readable, or ‘file_path` is nil.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/xmp_toolkit_ruby.rb', line 100

def xmp_from_file(file_path)
  check_file! file_path, need_to_read: true, need_to_write: false

  with_init do
    XmpToolkitRuby::XmpFile.with_xmp_file(
      file_path,
      open_flags: XmpToolkitRuby::XmpFileOpenFlags.bitmask_for(:open_for_read, :open_use_smart_handler),
      fallback_flags: XmpToolkitRuby::XmpFileOpenFlags.bitmask_for(:open_for_read, :open_use_packet_scanning)
    ) do |xmp_file|
      file_info = xmp_file.file_info
      packet_info = xmp_file.packet_info
      xmp_data = xmp_file.meta

      file_info.merge(packet_info).merge(xmp_data)
    end
  end
end

.xmp_to_file(file_path, xmp_data, override: false) ⇒ Object

Writes XMP metadata to a specified file.

This method checks if the file exists, is readable, and is writable. It then initializes the XMP Toolkit, writes the provided XMP data (either as a Hash or an XML String) to the file using the native extension, and ensures the toolkit is terminated.

The ‘override` parameter controls how existing XMP data in the file is handled:

  • If ‘true` (`:override`), existing XMP metadata is completely replaced.

  • If ‘false` (`:upsert`), the new XMP data is merged with existing metadata; new properties are added, and existing ones may be updated.

Parameters:

  • file_path (String)

    The absolute or relative path to the target file.

  • xmp_data (String)

    The XMP metadata to write. (which will be converted to XML by the native toolkit) or a pre-formatted XML String.

  • override (Boolean) (defaults to: false)

    (false) If ‘true`, existing XMP metadata in the file will be replaced. If `false`, the new data will be upserted (merged).

Raises:

  • (FileNotFoundError)

    If the file does not exist, is not readable/writable, or ‘file_path` is nil.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/xmp_toolkit_ruby.rb', line 136

def xmp_to_file(file_path, xmp_data, override: false)
  check_file! file_path, need_to_read: true, need_to_write: true

  with_init do
    XmpToolkitRuby::XmpFile.with_xmp_file(
      file_path,
      open_flags: XmpToolkitRuby::XmpFileOpenFlags.bitmask_for(:open_for_update, :open_use_smart_handler),
      fallback_flags: XmpToolkitRuby::XmpFileOpenFlags.bitmask_for(:open_for_update, :open_use_packet_scanning)
    ) do |xmp_file|
      xmp_file.update_meta xmp_data, mode: override ? :override : :upsert

      file_info = xmp_file.file_info
      packet_info = xmp_file.packet_info
      xmp_data = xmp_file.meta

      file_info.merge(packet_info).merge(xmp_data)
    end
  end
end