Class: C2PA::Manifest
- Inherits:
-
Object
- Object
- C2PA::Manifest
- Defined in:
- lib/c2pa/manifest.rb
Constant Summary collapse
- INTENTS =
Intents this gem can express.
:edit — this asset derives from a parent. c2pa-rs generates the parent ingredient from the source file and adds a c2pa.opened action wired to it by hashed URI.
Omitting the intent produces a manifest for a newly created asset.
c2pa-rs also has an :update intent, a restricted edit for non-editorial changes. It is not offered here because it requires an ingredient with real content, and add_ingredient records metadata only — signing with it fails with "ingredient file not found". Tracked separately.
%i[edit].freeze
- GEM_FIELD =
c2pa-rs records itself in a namespaced field alongside the generator name, so the gem does the same when an application supplies its own.
"org.rubygems.ruby_c2pa".freeze
Instance Attribute Summary collapse
-
#intent ⇒ Symbol?
readonly
The builder intent, if any.
Instance Method Summary collapse
-
#add_action(action, when_time: nil, software_agent: nil, digital_source_type: nil, changed: nil, parameters: nil) ⇒ self
Add a C2PA action to this manifest.
-
#add_assertion(label:, data:) ⇒ self
Add an arbitrary assertion to this manifest.
-
#add_ingredient(title:, format:, instance_id:, relationship: "parentOf") ⇒ self
Add an ingredient (source asset) to this manifest.
-
#initialize(title:, intent: nil, generator_name: nil, generator_version: nil) ⇒ Manifest
constructor
A new instance of Manifest.
-
#to_json ⇒ String
Serialize to the JSON structure expected by c2pa-rs.
Constructor Details
#initialize(title:, intent: nil, generator_name: nil, generator_version: nil) ⇒ Manifest
Returns a new instance of Manifest.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/c2pa/manifest.rb', line 33 def initialize(title:, intent: nil, generator_name: nil, generator_version: nil) unless intent.nil? || INTENTS.include?(intent) raise InvalidManifestError, "unknown intent #{intent.inspect}. Valid options: #{INTENTS.map(&:inspect).join(', ')}" end @title = title @intent = intent @generator_name = generator_name @generator_version = generator_version @actions = [] @assertions = [] @ingredients = [] end |
Instance Attribute Details
#intent ⇒ Symbol? (readonly)
Returns the builder intent, if any.
24 25 26 |
# File 'lib/c2pa/manifest.rb', line 24 def intent @intent end |
Instance Method Details
#add_action(action, when_time: nil, software_agent: nil, digital_source_type: nil, changed: nil, parameters: nil) ⇒ self
Add a C2PA action to this manifest.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/c2pa/manifest.rb', line 58 def add_action(action, when_time: nil, software_agent: nil, digital_source_type: nil, changed: nil, parameters: nil) if action == Actions::OPENED raise InvalidManifestError, "#{Actions::OPENED} cannot be added directly. The specification requires it to " \ "reference a parentOf ingredient by hashed URI, and that hash is computed over " \ "the ingredient as c2pa-rs serialises it, so Ruby cannot construct one. Pass " \ "intent: :edit to C2PA::Manifest.new instead and the action will be added for you." end # Required as of c2pa-rs 0.90. Earlier versions accepted its absence, so # manifests signed by releases before 0.3.0 are rejected by current # verifiers. No default is supplied: c2pa-rs accepts any string here, so # a guess would validate while asserting something untrue about where the # asset came from. Use DigitalSourceTypes::UNSPECIFIED to decline. if action == Actions::CREATED && to_s_or_nil(digital_source_type).nil? raise InvalidManifestError, "#{Actions::CREATED} requires a digital_source_type. Choose the value that " \ "describes how the asset was produced — for example " \ "C2PA::DigitalSourceTypes::DIGITAL_CAPTURE for a camera original, or " \ "TRAINED_ALGORITHMIC_MEDIA for generative AI. If the origin is genuinely " \ "unknown, use C2PA::DigitalSourceTypes::UNSPECIFIED rather than guessing." end # Also new in c2pa-rs 0.90. if action == Actions::TRANSLATED missing = %w[sourceLanguage targetLanguage].reject { |key| param_present?(parameters, key) } unless missing.empty? raise InvalidManifestError, "#{Actions::TRANSLATED} requires #{missing.join(' and ')} in parameters, " \ "as RFC 5646 language codes" end end entry = { "action" => action } entry["when"] = when_time if when_time entry["softwareAgent"] = software_agent || "ruby-c2pa/#{VERSION}" entry["digitalSourceType"] = digital_source_type if digital_source_type entry["changed"] = changed if changed entry["parameters"] = parameters if parameters @actions << entry self end |
#add_assertion(label:, data:) ⇒ self
Add an arbitrary assertion to this manifest.
111 112 113 114 |
# File 'lib/c2pa/manifest.rb', line 111 def add_assertion(label:, data:) @assertions << { "label" => label, "data" => data } self end |
#add_ingredient(title:, format:, instance_id:, relationship: "parentOf") ⇒ self
Add an ingredient (source asset) to this manifest.
123 124 125 126 127 128 129 130 131 |
# File 'lib/c2pa/manifest.rb', line 123 def add_ingredient(title:, format:, instance_id:, relationship: "parentOf") @ingredients << { "title" => title, "format" => format, "instance_id" => instance_id, "relationship" => relationship } self end |
#to_json ⇒ String
Serialize to the JSON structure expected by c2pa-rs.
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/c2pa/manifest.rb', line 138 def to_json raise InvalidManifestError, "at least one action is required" if @actions.empty? manifest = { "title" => @title, "claim_generator_info" => [claim_generator_info], "assertions" => [ { "label" => "c2pa.actions.v2", "data" => { "actions" => @actions } }, *@assertions ] } manifest["ingredients"] = @ingredients unless @ingredients.empty? begin JSON.generate(manifest) rescue JSON::GeneratorError => e # Typically a string that is not valid UTF-8 — a filename or caption # read in another encoding and passed through untouched. Without this # the caller gets a JSON::GeneratorError, which is not a C2PA::Error # and so escapes `rescue C2PA::Error`. raise InvalidManifestError, "manifest contains text that cannot be encoded as JSON: #{e.}" end end |