xmp 0.2.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c71a383ba25e007195a1624a2d88311911d86323d6cb43d7107651904f91ec6f
4
+ data.tar.gz: f5d04040dd4905d29c7105d6aa20c3bf72b1feb8fad9ef93a719eacaece2f55b
5
+ SHA512:
6
+ metadata.gz: 1a4e7a6ff1563cf64298439ce866a8bf56fa4991d234f1e9b0c13859d87121d3eaf57d4bdf444ab4cd93e9a5d3be5ed935a5cde5381b43f1375f12abf42b74f4
7
+ data.tar.gz: 89d8d8e67dff63d882af773ce3053012deb25e4d92eb1a8645c1a2ba312a60a75b719499adfdb3667c0d258f6418ce067e7cef37ffba719dea07bf16e2b9d247
data/.gitignore CHANGED
@@ -1,4 +1,8 @@
1
+ *.swp
1
2
  *.gem
2
3
  .bundle
3
4
  Gemfile.lock
4
5
  pkg/*
6
+ /xmp.xml
7
+ .DS_Store
8
+ doc
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.3.5
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 2.6.10
3
+ - 2.7.8
4
+ - 3.1.6
5
+ - 3.2.5
6
+ - 3.3.5
7
+ - 3.4.0-preview1
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # xmp - Extensible Metadata Platform (XMP) parser
2
+
3
+ XMP provides an object oriented interface to [XMP data](http://en.wikipedia.org/wiki/Extensible_Metadata_Platform). XMP data can be found in PDF, JPEG, GIF, PNG, and many other formats.
4
+
5
+ ## Supported Formats
6
+
7
+ Format | File extension | Additional dependency
8
+ ----------|-----------------|-----------------------
9
+ JPEG | .jpeg, .jpg | [exifr/jpeg](https://github.com/remvee/exifr)
10
+ TIFF | .tiff, .tif | [exifr/tiff](https://github.com/remvee/exifr)
11
+ XMP Files | .xmp, .xml | none
12
+
13
+ ## Usage
14
+
15
+ ``` ruby
16
+ require 'xmp'
17
+ xmp = XMP.new('example.jpg')
18
+ xmp.dc.subject # => ["something interesting"]
19
+
20
+ xmp.namespaces.each do |namespace|
21
+ xmp[namespace].attributes.each do |attribute|
22
+ puts "#{namespace}.#{attribute}: #{xmp[namespace][attribute].inspect}"
23
+ end
24
+ end
25
+ ```
26
+
27
+ `XMP.new` accepts:
28
+ * Paths to files (it will choose based on file extension)
29
+ * File objects (it will choose based on file extension)
30
+ * Nokogiri documents or XML strings
31
+
32
+ Attributes can be accessed in the following ways:
33
+
34
+ ``` ruby
35
+ # assuming an XMP entry AttributeName within the NamespaceName namespace, these are all equivalent
36
+ xmp['NamespaceName']['AttributeName']
37
+ xmp.namespace_name.attribute_name
38
+ xmp.NamespaceName.AttributeName
39
+
40
+ # you can also mix them
41
+ xmp.namespace_name['AttributeName']
42
+
43
+ # or convert the data to an actual hash
44
+ xmp.to_h # => { 'NamespaceName' => { … }, … }
45
+ ```
46
+
47
+ ## Installation
48
+
49
+ ``` shell
50
+ $ gem install xmp
51
+ $ gem install exifr # optional, for jpeg/tiff support
52
+ ```
53
+
54
+ Or you can add it to your Gemfile:
55
+
56
+ ``` ruby
57
+ gem 'xmp', '~> 1.0'
58
+ gem 'exifr', '~> 1.3'
59
+ ```
60
+
61
+ ## Requirements
62
+ * Ruby 2 (supported on >= 2.6, though xmp may work on earlier versions) or Ruby 3
63
+ * nokogiri (>= 1.10). The xmp gem depends on this gem. It will be installed automatically.
64
+ * On Ruby 2.6, nokogiri 1.13.10 will be installed; nokogiri >= 1.14 require Ruby 2.7.
65
+ * On Ruby 2.7, nokogiri 1.15.5 will be installed; nokogiri >= 1.16 require Ruby 3.
66
+ * See https://nokogiri.org/CHANGELOG.html for nokogiri support for older or newer Ruby versions.
67
+ * exifr (>= 1.3). This gem is optional. If you need it, add it to your Gemfile.
68
+
69
+ ## Development
70
+
71
+ Fork it at https://github.com/amberbit/xmp
72
+
73
+ ``` shell
74
+ $ bundle install # install development dependencies
75
+ $ rake spec # run specs
76
+ ```
77
+
78
+ ## License
79
+ Ruby's license.
80
+
81
+ Copyright (c) 2011 Wojciech Piekutowski, AmberBit (<http://amberbit.com>) and contributors.
data/Rakefile CHANGED
@@ -8,5 +8,4 @@ desc "Run all specs"
8
8
  RSpec::Core::RakeTask.new do |t|
9
9
  t.ruby_opts = "-w"
10
10
  t.verbose = true
11
- t.skip_bundler = true
12
11
  end
@@ -0,0 +1,38 @@
1
+ module XMP::Convenience
2
+ def include?(key)
3
+ key_map.include? key
4
+ end
5
+
6
+ def [](key)
7
+ return unless key = key_map[key]
8
+ @entries ||= {}
9
+ @entries[key] ||= get(key)
10
+ end
11
+
12
+ def respond_to_missing?(method_name, include_private = false)
13
+ include?(method_name) or super
14
+ end
15
+
16
+ def to_h
17
+ list.map do |key|
18
+ result = self[key]
19
+ result = result.to_h if result.is_a? XMP::Convenience
20
+ [key, result]
21
+ end.to_h
22
+ end
23
+
24
+ private
25
+
26
+ def method_missing(method_name, *arguments, &block)
27
+ return super unless include? method_name
28
+ raise ArgumentError, "wrong number of arguments (given #{arguments.size}, expected 0)" if arguments.any?
29
+ self[method_name]
30
+ end
31
+
32
+ def key_map
33
+ @key_map ||= list.inject({}) do |map, key|
34
+ underscore_key = key.gsub(/([a-z])([^a-z])|(\d)(\D)|(\D)(\d)/, '\1\3\5_\2\4\6').tr('-', '_').downcase
35
+ map.merge!(key => key, key.to_sym => key, underscore_key => key, underscore_key.to_sym => key)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ class XMP::Document
2
+ include XMP::Convenience
3
+
4
+ attr_reader :namespaces
5
+ attr_reader :xml
6
+
7
+ def initialize(doc)
8
+ @xml = doc.root
9
+ @namespaces = doc.collect_namespaces.map do |ns, url|
10
+ @xml.add_namespace_definition ns, url
11
+ ns[/^(?:xmlns:)?xmlns:(.+)/, 1]
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def list
18
+ namespaces
19
+ end
20
+
21
+ def get(key)
22
+ XMP::Namespace.new(self, key)
23
+ end
24
+ end
data/lib/xmp/error.rb ADDED
@@ -0,0 +1,4 @@
1
+ module XMP
2
+ Error = Class.new(RuntimeError)
3
+ NoXMP = Class.new(Error)
4
+ end
@@ -0,0 +1,22 @@
1
+ begin
2
+ require 'exifr/jpeg'
3
+ require 'exifr/tiff'
4
+ rescue LoadError => exception
5
+ raise exception unless exception.path == 'exifr/jpeg'
6
+ end
7
+
8
+ module XMP::Handler
9
+ class Exifr
10
+ def call(object)
11
+ return unless defined?(EXIFR::JPEG)
12
+ case object
13
+ when EXIFR::JPEG then format, xmp_data = 'jpeg', object.app1s.find { |a| a =~ %r|\Ahttp://ns.adobe.com/xap/1.0/| }&.split("\000")[1]
14
+ when EXIFR::TIFF then format, xmp_data = 'tiff', object.xmp
15
+ else return
16
+ end
17
+
18
+ raise XMP::NoXMP, "XMP section missing from #{format}" unless xmp_data
19
+ xmp_data
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ require 'pathname'
2
+
3
+ module XMP::Handler
4
+ class File
5
+ SUPPORTED = [String, Pathname, ::File]
6
+ attr_reader :extensions, :callback
7
+
8
+ def initialize(*extensions, &callback)
9
+ @extensions = extensions
10
+ @callback = callback
11
+ end
12
+
13
+ def call(object)
14
+ return unless SUPPORTED.any? { |c| c === object }
15
+ return unless path = Pathname(object) and extensions.include? path.extname.downcase
16
+ return callback.call(object) if object.is_a? IO
17
+ return path.open(&callback) if path.exist? and path.readable?
18
+ raise XMP::Error, "cannot read file #{path}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ require 'nokogiri'
2
+
3
+ module XMP::Handler
4
+ class XML
5
+ def call(object)
6
+ object = Nokogiri::XML(object) if object.is_a? String
7
+ XMP::Document.new(object) if object.is_a? Nokogiri::XML::Document and object.root
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ module XMP::Handler
2
+ attr_accessor :handlers
3
+
4
+ def new(object)
5
+ last_error = nil
6
+ result = object
7
+
8
+ handlers.each do |handler|
9
+ begin
10
+ next unless new_result = handler.call(result)
11
+ return new_result if new_result.is_a? XMP::Document
12
+ result = new_result
13
+ rescue XMP::Error => error
14
+ last_error = error
15
+ end
16
+ end
17
+
18
+ raise last_error if last_error
19
+ raise XMP::Error, "cannot handle #{object.inspect}"
20
+ end
21
+
22
+ alias_method :parse, :new
23
+ end
data/lib/xmp/namespace.rb CHANGED
@@ -1,69 +1,52 @@
1
- class XMP
2
- class Namespace
3
- # available attributes
4
- attr_reader :attributes
1
+ class XMP::Namespace
2
+ include XMP::Convenience
3
+ attr_reader :document, :namespace, :standalone_attributes, :embedded_attributes
5
4
 
6
- def initialize(xmp, namespace) # :nodoc
7
- @xmp = xmp
8
- @namespace = namespace.to_s
9
-
10
- @attributes = []
11
- embedded_attributes =
12
- xml.xpath("//rdf:Description").map { |d|
13
- d.attributes.values.
14
- select { |attr| attr.namespace.prefix.to_s == @namespace }.
15
- map(&:name)
16
- }.flatten
17
- @attributes.concat embedded_attributes
18
- standalone_attributes = xml.xpath("//rdf:Description/#{@namespace}:*").
19
- map(&:name)
20
- @attributes.concat standalone_attributes
5
+ def initialize(document, namespace)
6
+ @document, @namespace = document, namespace
7
+ @standalone_attributes = xml.xpath("//rdf:Description/#{namespace}:*").map(&:name)
8
+ @embedded_attributes = xml.xpath('//rdf:Description').flat_map do |description|
9
+ description.attributes.values.select { |attr| attr.namespace.prefix.to_s == namespace }.map(&:name)
21
10
  end
11
+ end
12
+
13
+ def attributes
14
+ @attributes ||= (standalone_attributes + embedded_attributes).uniq
15
+ end
22
16
 
23
- def inspect
24
- "#<XMP::Namespace:#{@namespace}>"
25
- end
17
+ private
26
18
 
27
- def method_missing(method, *args)
28
- if has_attribute?(method)
29
- embedded_attribute(method) || standalone_attribute(method)
30
- else
31
- super
32
- end
33
- end
19
+ def xml
20
+ document.xml
21
+ end
34
22
 
35
- def respond_to?(method)
36
- has_attribute?(method) or super
37
- end
23
+ def list
24
+ attributes
25
+ end
38
26
 
39
- private
27
+ def get(name)
28
+ embedded_attributes.include?(name) ? get_embedded(name) : get_standalone(name)
29
+ end
40
30
 
41
- def embedded_attribute(name)
42
- element = xml.at("//rdf:Description[@#{@namespace}:#{name}]")
43
- return unless element
44
- element.attribute(name.to_s).text
45
- end
31
+ def get_embedded(name)
32
+ return unless element = xml.at("//rdf:Description[@#{namespace}:#{name}]")
33
+ element.attribute(name.to_s).text
34
+ end
46
35
 
47
- def has_attribute?(name)
48
- attributes.include?(name.to_s)
36
+ def get_standalone(name)
37
+ return unless attribute = xml.xpath("//#{namespace}:#{name}").first
38
+ if list = attribute.xpath("./rdf:Bag | ./rdf:Seq | ./rdf:Alt").first
39
+ return list.xpath("./rdf:li").map(&:text)
49
40
  end
50
41
 
51
- def standalone_attribute(name)
52
- attribute_xpath = "//#{@namespace}:#{name}"
53
- attribute = xml.xpath(attribute_xpath).first
54
- return unless attribute
42
+ hash = {}
43
+ attribute.element_children.each { |c| hash[c.name] = c.text }
44
+ attribute.attributes.each { |k, v| hash[k] = v.value } if hash.empty?
45
+ return hash unless hash.empty?
55
46
 
56
- array_value = attribute.xpath("./rdf:Bag | ./rdf:Seq | ./rdf:Alt").first
57
- if array_value
58
- items = array_value.xpath("./rdf:li")
59
- items.map { |i| i.text }
60
- else
61
- raise "Don't know how to handle: \n" + attribute.to_s
62
- end
63
- end
47
+ text = attribute.text.to_s.strip
48
+ return text if text.length > 0
64
49
 
65
- def xml
66
- @xmp.xml
67
- end
50
+ raise XMP::Error, "Don't know how to handle: \n" + attribute.to_s
68
51
  end
69
- end
52
+ end
data/lib/xmp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
- class XMP
2
- VERSION = "0.2.0"
1
+ module XMP
2
+ VERSION = '1.0.1'
3
3
  end
data/lib/xmp.rb CHANGED
@@ -1,70 +1,20 @@
1
- require 'xmp/silencer'
2
- XMP::Silencer.silently { require 'nokogiri' }
3
- require 'xmp/namespace'
4
-
5
- # = XMP XML parser
6
- #
7
- # == Example
8
- # xmp = XMP.new(File.read('xmp.xml'))
9
- # xmp.dc.title # => "Amazing Photo"
10
- # xmp.photoshop.Category # => "summer"
11
- # xmp.photoshop.SupplementalCategories # => ["morning", "sea"]
12
- class XMP
13
- # underlying XML content
14
- attr_reader :xml
15
- # available namespace names
16
- attr_reader :namespaces
17
-
18
- # accepts valid XMP XML
19
- def initialize(xml)
20
- doc = Nokogiri::XML(xml)
21
- @xml = doc.root
22
-
23
- available_namespaces = doc.collect_namespaces
24
- # let nokogiri know about all namespaces
25
- available_namespaces.each do |ns, url|
26
- @xml.add_namespace_definition ns, url
27
- end
28
-
29
- # collect namespace names
30
- @namespaces = available_namespaces.collect do |ns, _|
31
- ns =~ /^xmlns:(.+)/
32
- $1
33
- end
34
- end
35
-
36
- def inspect
37
- "#<XMP:@namespaces=#{@namespaces.inspect}>"
38
- end
39
-
40
- # returns Namespace object if namespace exists, otherwise tries to call a method
41
- def method_missing(namespace, *args)
42
- if has_namespace?(namespace)
43
- Namespace.new(self, namespace)
44
- else
45
- super
46
- end
47
- end
48
-
49
- def respond_to?(method)
50
- has_namespace?(method) or super
51
- end
52
-
53
- def self.parse(doc)
54
- case doc.class.to_s
55
- when 'EXIFR::JPEG'
56
- if xmp_chunk = doc.app1s.find { |a| a =~ %r|\Ahttp://ns.adobe.com/xap/1.0/| }
57
- xmp_data = xmp_chunk.split("\000")[1]
58
- XMP.new(xmp_data)
59
- end
60
- else
61
- raise "Document not supported:\n#{doc.inspect}"
62
- end
63
- end
64
-
65
- private
66
-
67
- def has_namespace?(namespace)
68
- namespaces.include?(namespace.to_s)
69
- end
70
- end
1
+ module XMP
2
+ require 'xmp/convenience'
3
+ require 'xmp/document'
4
+ require 'xmp/error'
5
+ require 'xmp/handler'
6
+ require 'xmp/handler/exifr'
7
+ require 'xmp/handler/file'
8
+ require 'xmp/handler/xml'
9
+ require 'xmp/namespace'
10
+ require 'xmp/version'
11
+
12
+ extend Handler
13
+ self.handlers = [
14
+ Handler::File.new('.jpg', '.jpeg') { |file| EXIFR::JPEG.new(file) },
15
+ Handler::File.new('.tif', '.tiff') { |file| EXIFR::TIFF.new(file) },
16
+ Handler::File.new('.xmp', '.xml') { |file| Nokogiri::XML(file) },
17
+ Handler::Exifr.new,
18
+ Handler::XML.new
19
+ ]
20
+ end
@@ -1,13 +1,23 @@
1
1
  require './spec/spec_helper.rb'
2
+ require 'exifr/jpeg'
2
3
 
3
4
  describe "XMP with EXIFR::JPEG" do
4
- before do
5
- XMP::Silencer.silently { @img = EXIFR::JPEG.new('spec/fixtures/multiple-app1.jpg') }
5
+ it "should parse image given as path" do
6
+ xmp = XMP.parse('spec/fixtures/multiple-app1.jpg')
7
+ xmp.should be_instance_of(XMP::Document)
8
+ xmp.namespaces.should =~ %w{dc iX pdf photoshop rdf tiff x xap xapRights}
9
+ end
10
+
11
+ it "should parse image given as path with upper case extension" do
12
+ xmp = XMP.parse('spec/fixtures/UPPERCASE.JPG')
13
+ xmp.should be_instance_of(XMP::Document)
14
+ xmp.namespaces.should =~ %w{dc iX pdf photoshop rdf tiff x xap xapRights}
6
15
  end
7
16
 
8
- it "should parse image" do
9
- xmp = XMP.parse(@img)
10
- xmp.should be_instance_of(XMP)
17
+ it "should parse image given as EXIFR::JPEG" do
18
+ img = EXIFR::JPEG.new('spec/fixtures/multiple-app1.jpg')
19
+ xmp = XMP.parse(img)
20
+ xmp.should be_instance_of(XMP::Document)
11
21
  xmp.namespaces.should =~ %w{dc iX pdf photoshop rdf tiff x xap xapRights}
12
22
  end
13
23
  end
@@ -0,0 +1,17 @@
1
+ require './spec/spec_helper.rb'
2
+ require 'exifr/tiff'
3
+
4
+ describe "XMP with EXIFR::TIFF" do
5
+ it "should parse image given as path" do
6
+ xmp = XMP.parse('spec/fixtures/multiple-app1.tiff')
7
+ xmp.should be_instance_of(XMP::Document)
8
+ xmp.namespaces.should =~ %w{dc iX pdf photoshop rdf tiff x xap xapRights}
9
+ end
10
+
11
+ it "should parse image given as EXIFR::TIFF" do
12
+ img = EXIFR::TIFF.new('spec/fixtures/multiple-app1.tiff')
13
+ xmp = XMP.parse(img)
14
+ xmp.should be_instance_of(XMP::Document)
15
+ xmp.namespaces.should =~ %w{dc iX pdf photoshop rdf tiff x xap xapRights}
16
+ end
17
+ end
Binary file
Binary file
@@ -192,6 +192,7 @@
192
192
  <rdf:li>255, 255</rdf:li>
193
193
  </rdf:Seq>
194
194
  </crs:ToneCurve>
195
+ <Iptc4xmpCore:CreatorContactInfo Iptc4xmpCore:CiAdrCtry="Germany" Iptc4xmpCore:CiAdrCity="Berlin" />
195
196
  </rdf:Description>
196
197
  </rdf:RDF>
197
198
  </x:xmpmeta>
@@ -0,0 +1,62 @@
1
+ <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.4.0">
2
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
3
+ <rdf:Description xmlns:aux="http://ns.adobe.com/exif/1.0/aux/" xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmpRights="http://ns.adobe.com/xap/1.0/rights/" rdf:about="">
4
+ <aux:SerialNumber>1981003444</aux:SerialNumber>
5
+ <aux:FlashCompensation>0/1</aux:FlashCompensation>
6
+ <aux:ImageNumber>0</aux:ImageNumber>
7
+ <aux:LensID>237</aux:LensID>
8
+ <aux:Firmware>2.0.3</aux:Firmware>
9
+ <aux:LensInfo>24/1 105/1 0/0 0/0</aux:LensInfo>
10
+ <aux:Lens>EF24-105mm f/4L IS USM</aux:Lens>
11
+ <Iptc4xmpCore:Location>Ph&#x1EA1;m &#x110;&#xEC;nh H&#x1ED3;</Iptc4xmpCore:Location>
12
+ <Iptc4xmpCore:CreatorContactInfo rdf:parseType="Resource">
13
+ <Iptc4xmpCore:CiAdrCtry>Germany</Iptc4xmpCore:CiAdrCtry>
14
+ <Iptc4xmpCore:CiAdrCity>Berlin</Iptc4xmpCore:CiAdrCity>
15
+ </Iptc4xmpCore:CreatorContactInfo>
16
+ <xmp:ModifyDate>2014-07-06T17:54:07</xmp:ModifyDate>
17
+ <xmp:CreateDate>2012-12-23T10:38:45</xmp:CreateDate>
18
+ <xmp:CreatorTool>Adobe Photoshop Lightroom 4.4 (Macintosh)</xmp:CreatorTool>
19
+ <xmp:Rating>2</xmp:Rating>
20
+ <dc:subject>
21
+ <rdf:Bag>
22
+ <rdf:li>2012</rdf:li>
23
+ <rdf:li>Asia</rdf:li>
24
+ <rdf:li>Hanoi</rdf:li>
25
+ <rdf:li>Vietnam</rdf:li>
26
+ <rdf:li>food</rdf:li>
27
+ <rdf:li>frog</rdf:li>
28
+ <rdf:li>travel</rdf:li>
29
+ </rdf:Bag>
30
+ </dc:subject>
31
+ <dc:description>
32
+ <rdf:Alt>
33
+ <rdf:li xml:lang="x-default">This is the Caption</rdf:li>
34
+ </rdf:Alt>
35
+ </dc:description>
36
+ <dc:rights>
37
+ <rdf:Alt>
38
+ <rdf:li xml:lang="x-default">&#xA9; Jens Kr&#xE4;mer</rdf:li>
39
+ </rdf:Alt>
40
+ </dc:rights>
41
+ <dc:title>
42
+ <rdf:Alt>
43
+ <rdf:li xml:lang="x-default">The title</rdf:li>
44
+ </rdf:Alt>
45
+ </dc:title>
46
+ <dc:creator>
47
+ <rdf:Seq>
48
+ <rdf:li>Jens Kr&#xE4;mer</rdf:li>
49
+ </rdf:Seq>
50
+ </dc:creator>
51
+ <photoshop:City>Hanoi</photoshop:City>
52
+ <photoshop:State>Hanoi</photoshop:State>
53
+ <photoshop:Country>Vietnam</photoshop:Country>
54
+ <photoshop:DateCreated>2012-12-23T10:38:45</photoshop:DateCreated>
55
+ <xmpRights:UsageTerms>
56
+ <rdf:Alt>
57
+ <rdf:li xml:lang="x-default">All rights reserved</rdf:li>
58
+ </rdf:Alt>
59
+ </xmpRights:UsageTerms>
60
+ </rdf:Description>
61
+ </rdf:RDF>
62
+ </x:xmpmeta>
@@ -0,0 +1,66 @@
1
+ <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.5-c002 1.148022, 2012/07/15-18:06:45 ">
2
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
3
+ <rdf:Description xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:aux="http://ns.adobe.com/exif/1.0/aux/" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpRights="http://ns.adobe.com/xap/1.0/rights/" xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/" rdf:about="" xmp:ModifyDate="2014-06-22T17:42:46+02:00" xmp:CreateDate="2012-12-23T23:39:14" xmp:MetadataDate="2014-06-22T17:42:46+02:00" xmp:Rating="2" xmp:CreatorTool="Adobe Photoshop Lightroom 4.4 (Macintosh)" aux:LensInfo="5200/1000 26000/1000 0/0 0/0" aux:Lens="5.2-26.0 mm" aux:ApproximateFocusDistance="298/100" aux:FlashCompensation="0/1" aux:Firmware="1.01" photoshop:DateCreated="2012-12-23T23:39:14" photoshop:City="Hanoi" photoshop:State="Hanoi" photoshop:Country="Vietnam" xmpMM:DocumentID="xmp.did:28b12b53-d653-4f0a-80ba-67c9f60d68bb" xmpMM:OriginalDocumentID="01AD727BA286F4EF2A360236E69DDE89" xmpMM:InstanceID="xmp.iid:28b12b53-d653-4f0a-80ba-67c9f60d68bb" dc:format="image/jpeg" xmpRights:Marked="True" Iptc4xmpCore:Location="Ph&#x1EA1;m &#x110;&#xEC;nh H&#x1ED3;" crs:Version="7.4" crs:ProcessVersion="6.7" crs:WhiteBalance="As Shot" crs:Temperature="3100" crs:Tint="+18" crs:Saturation="0" crs:Sharpness="30" crs:LuminanceSmoothing="50" crs:ColorNoiseReduction="25" crs:VignetteAmount="0" crs:ShadowTint="0" crs:RedHue="0" crs:RedSaturation="0" crs:GreenHue="0" crs:GreenSaturation="0" crs:BlueHue="0" crs:BlueSaturation="0" crs:Vibrance="+10" crs:HueAdjustmentRed="0" crs:HueAdjustmentOrange="0" crs:HueAdjustmentYellow="0" crs:HueAdjustmentGreen="0" crs:HueAdjustmentAqua="0" crs:HueAdjustmentBlue="0" crs:HueAdjustmentPurple="0" crs:HueAdjustmentMagenta="0" crs:SaturationAdjustmentRed="0" crs:SaturationAdjustmentOrange="0" crs:SaturationAdjustmentYellow="0" crs:SaturationAdjustmentGreen="0" crs:SaturationAdjustmentAqua="0" crs:SaturationAdjustmentBlue="0" crs:SaturationAdjustmentPurple="0" crs:SaturationAdjustmentMagenta="0" crs:LuminanceAdjustmentRed="0" crs:LuminanceAdjustmentOrange="0" crs:LuminanceAdjustmentYellow="0" crs:LuminanceAdjustmentGreen="0" crs:LuminanceAdjustmentAqua="0" crs:LuminanceAdjustmentBlue="0" crs:LuminanceAdjustmentPurple="0" crs:LuminanceAdjustmentMagenta="0" crs:SplitToningShadowHue="0" crs:SplitToningShadowSaturation="0" crs:SplitToningHighlightHue="0" crs:SplitToningHighlightSaturation="0" crs:SplitToningBalance="0" crs:ParametricShadows="0" crs:ParametricDarks="0" crs:ParametricLights="0" crs:ParametricHighlights="0" crs:ParametricShadowSplit="25" crs:ParametricMidtoneSplit="50" crs:ParametricHighlightSplit="75" crs:SharpenRadius="+1.0" crs:SharpenDetail="25" crs:SharpenEdgeMasking="0" crs:PostCropVignetteAmount="0" crs:GrainAmount="0" crs:LuminanceNoiseReductionDetail="50" crs:ColorNoiseReductionDetail="50" crs:LuminanceNoiseReductionContrast="0" crs:LensProfileEnable="1" crs:LensManualDistortionAmount="0" crs:PerspectiveVertical="0" crs:PerspectiveHorizontal="0" crs:PerspectiveRotate="0.0" crs:PerspectiveScale="100" crs:AutoLateralCA="1" crs:Exposure2012="0.00" crs:Contrast2012="0" crs:Highlights2012="-28" crs:Shadows2012="+28" crs:Whites2012="+4" crs:Blacks2012="+26" crs:Clarity2012="+15" crs:DefringePurpleAmount="0" crs:DefringePurpleHueLo="30" crs:DefringePurpleHueHi="70" crs:DefringeGreenAmount="0" crs:DefringeGreenHueLo="40" crs:DefringeGreenHueHi="60" crs:ConvertToGrayscale="False" crs:ToneCurveName2012="Medium Contrast" crs:CameraProfile="Adobe Standard" crs:LensProfileSetup="LensDefaults" crs:LensProfileName="Adobe (Canon PowerShot S100)" crs:LensProfileFilename="Canon PowerShot S100 - RAW.lcp" crs:LensProfileDigest="D95499C6F0BF984F0C805DC729DE5F48" crs:LensProfileDistortionScale="100" crs:LensProfileChromaticAberrationScale="100" crs:LensProfileVignettingScale="100" crs:HasSettings="True" crs:HasCrop="False" crs:AlreadyApplied="True">
4
+ <xmpMM:History>
5
+ <rdf:Seq>
6
+ <rdf:li stEvt:action="derived" stEvt:parameters="converted from image/x-canon-cr2 to image/jpeg, saved to new location"/>
7
+ <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:28b12b53-d653-4f0a-80ba-67c9f60d68bb" stEvt:when="2014-06-22T17:42:46+02:00" stEvt:softwareAgent="Adobe Photoshop Lightroom 4.4 (Macintosh)" stEvt:changed="/"/>
8
+ </rdf:Seq>
9
+ </xmpMM:History>
10
+ <xmpMM:DerivedFrom stRef:documentID="01AD727BA286F4EF2A360236E69DDE89" stRef:originalDocumentID="01AD727BA286F4EF2A360236E69DDE89"/>
11
+ <dc:creator>
12
+ <rdf:Seq>
13
+ <rdf:li>Jens Kr&#xE4;mer</rdf:li>
14
+ </rdf:Seq>
15
+ </dc:creator>
16
+ <dc:rights>
17
+ <rdf:Alt>
18
+ <rdf:li xml:lang="x-default">&#xA9; Jens Kr&#xE4;mer</rdf:li>
19
+ </rdf:Alt>
20
+ </dc:rights>
21
+ <dc:subject>
22
+ <rdf:Bag>
23
+ <rdf:li>2012</rdf:li>
24
+ <rdf:li>Asia</rdf:li>
25
+ <rdf:li>Hanoi</rdf:li>
26
+ <rdf:li>Vietnam</rdf:li>
27
+ <rdf:li>travel</rdf:li>
28
+ </rdf:Bag>
29
+ </dc:subject>
30
+ <xmpRights:UsageTerms>
31
+ <rdf:Alt>
32
+ <rdf:li xml:lang="x-default">All rights reserved</rdf:li>
33
+ </rdf:Alt>
34
+ </xmpRights:UsageTerms>
35
+ <Iptc4xmpCore:CreatorContactInfo Iptc4xmpCore:CiAdrCtry="Germany" Iptc4xmpCore:CiAdrCity="Berlin"/>
36
+ <crs:ToneCurvePV2012>
37
+ <rdf:Seq>
38
+ <rdf:li>0, 0</rdf:li>
39
+ <rdf:li>32, 22</rdf:li>
40
+ <rdf:li>64, 56</rdf:li>
41
+ <rdf:li>128, 128</rdf:li>
42
+ <rdf:li>192, 196</rdf:li>
43
+ <rdf:li>255, 255</rdf:li>
44
+ </rdf:Seq>
45
+ </crs:ToneCurvePV2012>
46
+ <crs:ToneCurvePV2012Red>
47
+ <rdf:Seq>
48
+ <rdf:li>0, 0</rdf:li>
49
+ <rdf:li>255, 255</rdf:li>
50
+ </rdf:Seq>
51
+ </crs:ToneCurvePV2012Red>
52
+ <crs:ToneCurvePV2012Green>
53
+ <rdf:Seq>
54
+ <rdf:li>0, 0</rdf:li>
55
+ <rdf:li>255, 255</rdf:li>
56
+ </rdf:Seq>
57
+ </crs:ToneCurvePV2012Green>
58
+ <crs:ToneCurvePV2012Blue>
59
+ <rdf:Seq>
60
+ <rdf:li>0, 0</rdf:li>
61
+ <rdf:li>255, 255</rdf:li>
62
+ </rdf:Seq>
63
+ </crs:ToneCurvePV2012Blue>
64
+ </rdf:Description>
65
+ </rdf:RDF>
66
+ </x:xmpmeta>
@@ -0,0 +1,63 @@
1
+ <x:xmpmeta xmlns:x="adobe:ns:meta/" xmlns:xmlns:x="adobe:ns:meta/" xmlns:xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/" xmlns:xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#" xmlns:xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#" xmlns:xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/" xmlns:xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" xmlns:xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmlns:exifEX="http://cipa.jp/exif/1.0/" xmlns:xmlns:aux="http://ns.adobe.com/exif/1.0/aux/" xmlns:xmlns:xmp="http://ns.adobe.com/xap/1.0/" x:xmptk="Adobe XMP Core 5.6-c140 79.160451, 2017/05/06-01:08:21 ">
2
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
3
+ <rdf:Description xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:aux="http://ns.adobe.com/exif/1.0/aux/" xmlns:exifEX="http://cipa.jp/exif/1.0/" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/" xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#" xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#" xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/" rdf:about="" xmp:ModifyDate="2020-11-04T18:52:39+01:00" xmp:CreateDate="2019-05-24T15:45:35.65+01:00" xmp:MetadataDate="2020-11-04T18:52:39+01:00" xmp:Label="2019 - 006 - Wedding Sabine &amp; Nils" xmp:CreatorTool="Adobe Photoshop Lightroom Classic 10.0 (Windows)" aux:SerialNumber="053021002918" aux:LensInfo="50/1 50/1 0/0 0/0" aux:Lens="RF50mm F1.2 L USM" aux:LensID="61182" aux:LensSerialNumber="0000000000" aux:ImageNumber="0" aux:ApproximateFocusDistance="249/100" aux:FlashCompensation="0/1" aux:Firmware="1.0.0" exifEX:LensModel="RF50mm F1.2 L USM" photoshop:DateCreated="2019-05-24T15:45:35.65+01:00" xmpMM:DocumentID="xmp.did:f20f4ff3-ea80-f04e-8064-c9ccb27a1133" xmpMM:PreservedFileName="IMG_0259.CR3" xmpMM:OriginalDocumentID="DDD8F3AA8B7D1C0F1BD0D46063A8AA0D" xmpMM:InstanceID="xmp.iid:f20f4ff3-ea80-f04e-8064-c9ccb27a1133" dc:format="image/jpeg" crs:RawFileName="IMG_0259.CR3" crs:Version="13.0" crs:ProcessVersion="11.0" crs:WhiteBalance="As Shot" crs:Temperature="6250" crs:Tint="+9" crs:Exposure2012="0.00" crs:Contrast2012="-43" crs:Highlights2012="0" crs:Shadows2012="+26" crs:Whites2012="+13" crs:Blacks2012="0" crs:Texture="0" crs:Clarity2012="0" crs:Dehaze="-3" crs:Vibrance="+38" crs:Saturation="-20" crs:ParametricShadows="0" crs:ParametricDarks="0" crs:ParametricLights="0" crs:ParametricHighlights="0" crs:ParametricShadowSplit="25" crs:ParametricMidtoneSplit="50" crs:ParametricHighlightSplit="75" crs:Sharpness="25" crs:SharpenRadius="+1.0" crs:SharpenDetail="25" crs:SharpenEdgeMasking="0" crs:LuminanceSmoothing="0" crs:ColorNoiseReduction="25" crs:ColorNoiseReductionDetail="50" crs:ColorNoiseReductionSmoothness="50" crs:HueAdjustmentRed="+5" crs:HueAdjustmentOrange="+3" crs:HueAdjustmentYellow="+31" crs:HueAdjustmentGreen="+15" crs:HueAdjustmentAqua="-40" crs:HueAdjustmentBlue="+15" crs:HueAdjustmentPurple="+2" crs:HueAdjustmentMagenta="-10" crs:SaturationAdjustmentRed="-4" crs:SaturationAdjustmentOrange="+7" crs:SaturationAdjustmentYellow="-54" crs:SaturationAdjustmentGreen="-72" crs:SaturationAdjustmentAqua="-24" crs:SaturationAdjustmentBlue="-30" crs:SaturationAdjustmentPurple="-23" crs:SaturationAdjustmentMagenta="+1" crs:LuminanceAdjustmentRed="+16" crs:LuminanceAdjustmentOrange="+22" crs:LuminanceAdjustmentYellow="+27" crs:LuminanceAdjustmentGreen="+47" crs:LuminanceAdjustmentAqua="+15" crs:LuminanceAdjustmentBlue="-10" crs:LuminanceAdjustmentPurple="+2" crs:LuminanceAdjustmentMagenta="+31" crs:SplitToningShadowHue="31" crs:SplitToningShadowSaturation="4" crs:SplitToningHighlightHue="138" crs:SplitToningHighlightSaturation="3" crs:SplitToningBalance="-62" crs:ColorGradeMidtoneHue="0" crs:ColorGradeMidtoneSat="0" crs:ColorGradeShadowLum="0" crs:ColorGradeMidtoneLum="0" crs:ColorGradeHighlightLum="0" crs:ColorGradeBlending="100" crs:ColorGradeGlobalHue="0" crs:ColorGradeGlobalSat="0" crs:ColorGradeGlobalLum="0" crs:AutoLateralCA="0" crs:LensProfileEnable="0" crs:LensManualDistortionAmount="0" crs:VignetteAmount="0" crs:DefringePurpleAmount="0" crs:DefringePurpleHueLo="30" crs:DefringePurpleHueHi="70" crs:DefringeGreenAmount="0" crs:DefringeGreenHueLo="40" crs:DefringeGreenHueHi="60" crs:PerspectiveUpright="0" crs:PerspectiveVertical="0" crs:PerspectiveHorizontal="0" crs:PerspectiveRotate="0.0" crs:PerspectiveAspect="0" crs:PerspectiveScale="100" crs:PerspectiveX="0.00" crs:PerspectiveY="0.00" crs:GrainAmount="0" crs:PostCropVignetteAmount="0" crs:ShadowTint="0" crs:RedHue="0" crs:RedSaturation="0" crs:GreenHue="0" crs:GreenSaturation="0" crs:BlueHue="0" crs:BlueSaturation="0" crs:ConvertToGrayscale="False" crs:OverrideLookVignette="False" crs:ToneCurveName2012="Custom" crs:CameraProfile="Adobe Standard" crs:CameraProfileDigest="38E531B7F49849F74EF6FA15106BD31D" crs:HasSettings="True" crs:CropTop="0" crs:CropLeft="0" crs:CropBottom="1" crs:CropRight="1" crs:CropAngle="0" crs:CropConstrainToWarp="0" crs:HasCrop="False" crs:AlreadyApplied="True">
4
+ <xmpMM:History>
5
+ <rdf:Seq>
6
+ <rdf:li stEvt:action="derived" stEvt:parameters="converted from image/x-canon-cr3 to image/jpeg, saved to new location"/>
7
+ <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:f20f4ff3-ea80-f04e-8064-c9ccb27a1133" stEvt:when="2020-11-04T18:52:39+01:00" stEvt:softwareAgent="Adobe Photoshop Lightroom Classic 10.0 (Windows)" stEvt:changed="/"/>
8
+ </rdf:Seq>
9
+ </xmpMM:History>
10
+ <xmpMM:DerivedFrom stRef:documentID="DDD8F3AA8B7D1C0F1BD0D46063A8AA0D" stRef:originalDocumentID="DDD8F3AA8B7D1C0F1BD0D46063A8AA0D"/>
11
+ <dc:subject>
12
+ <rdf:Bag>
13
+ <rdf:li>A Person</rdf:li>
14
+ </rdf:Bag>
15
+ </dc:subject>
16
+ <Iptc4xmpExt:PersonInImage>
17
+ <rdf:Bag>
18
+ <rdf:li>A Person</rdf:li>
19
+ </rdf:Bag>
20
+ </Iptc4xmpExt:PersonInImage>
21
+ <mwg-rs:Regions rdf:parseType="Resource">
22
+ <mwg-rs:AppliedToDimensions stDim:w="4500" stDim:h="3000" stDim:unit="pixel"/>
23
+ <mwg-rs:RegionList>
24
+ <rdf:Bag>
25
+ <rdf:li>
26
+ <rdf:Description mwg-rs:Rotation="0.00000" mwg-rs:Name="A Person" mwg-rs:Type="Face">
27
+ <mwg-rs:Area stArea:h="0.12757" stArea:w="0.08496" stArea:x="0.49268" stArea:y="0.42009"/>
28
+ </rdf:Description>
29
+ </rdf:li>
30
+ </rdf:Bag>
31
+ </mwg-rs:RegionList>
32
+ </mwg-rs:Regions>
33
+ <crs:ToneCurvePV2012>
34
+ <rdf:Seq>
35
+ <rdf:li>0, 22</rdf:li>
36
+ <rdf:li>48, 32</rdf:li>
37
+ <rdf:li>78, 56</rdf:li>
38
+ <rdf:li>103, 100</rdf:li>
39
+ <rdf:li>151, 166</rdf:li>
40
+ <rdf:li>255, 219</rdf:li>
41
+ </rdf:Seq>
42
+ </crs:ToneCurvePV2012>
43
+ <crs:ToneCurvePV2012Red>
44
+ <rdf:Seq>
45
+ <rdf:li>0, 0</rdf:li>
46
+ <rdf:li>255, 255</rdf:li>
47
+ </rdf:Seq>
48
+ </crs:ToneCurvePV2012Red>
49
+ <crs:ToneCurvePV2012Green>
50
+ <rdf:Seq>
51
+ <rdf:li>0, 0</rdf:li>
52
+ <rdf:li>255, 255</rdf:li>
53
+ </rdf:Seq>
54
+ </crs:ToneCurvePV2012Green>
55
+ <crs:ToneCurvePV2012Blue>
56
+ <rdf:Seq>
57
+ <rdf:li>0, 0</rdf:li>
58
+ <rdf:li>255, 255</rdf:li>
59
+ </rdf:Seq>
60
+ </crs:ToneCurvePV2012Blue>
61
+ </rdf:Description>
62
+ </rdf:RDF>
63
+ </x:xmpmeta>
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,8 @@
1
- $LOAD_PATH.unshift 'lib'
2
- require 'xmp/silencer'
1
+ require 'bundler/setup'
2
+ Bundler.require(:development)
3
3
 
4
- XMP::Silencer.silently {
5
- require 'bundler/setup'
6
- Bundler.require(:development)
7
- }
4
+ RSpec.configure { |c| c.expect_with(:rspec) { |c| c.syntax = :should } }
8
5
 
9
6
  # load whole montgomery after bundler loads required gems
10
7
  require 'xmp'
11
-
12
8
  require 'pp'
data/spec/xmp_spec.rb CHANGED
@@ -3,16 +3,39 @@ require './spec/spec_helper.rb'
3
3
 
4
4
  describe XMP do
5
5
  describe "with xmp.xml" do
6
- before { @xmp = XMP.new(File.read('spec/fixtures/xmp.xml')) }
6
+ before { @xmp = XMP.new('spec/fixtures/xmp.xml') }
7
7
 
8
8
  it "should return all namespace names" do
9
9
  @xmp.namespaces.should =~ %w{rdf x tiff exif xap aux Iptc4xmpCore photoshop crs dc}
10
10
  end
11
11
 
12
+ it "should support converting to hash via to_h" do
13
+ hash = @xmp.to_h
14
+ hash.should be_a(Hash)
15
+ hash['dc'].should be_a(Hash)
16
+ hash.keys.should =~ %w{rdf x tiff exif xap aux Iptc4xmpCore photoshop crs dc}
17
+ hash['dc'].keys.should =~ %w{creator title rights subject description}
18
+ hash['dc']['title'].should eq(['Tytuł zdjęcia'])
19
+ end
20
+
12
21
  it "should return standalone attribute" do
13
22
  @xmp.dc.title.should eq(['Tytuł zdjęcia'])
14
23
  @xmp.dc.subject.should eq(['Słowa kluczowe i numery startowe.'])
15
24
  @xmp.photoshop.SupplementalCategories.should eq(['Nazwa imprezy'])
25
+ @xmp.photoshop.supplemental_categories.should eq(['Nazwa imprezy'])
26
+ @xmp['photoshop']['SupplementalCategories'].should eq(['Nazwa imprezy'])
27
+ end
28
+
29
+ it "should respond to standalone attributes" do
30
+ @xmp.should respond_to(:dc)
31
+ @xmp.dc.should respond_to(:title)
32
+ @xmp.should respond_to(:photoshop)
33
+ @xmp.photoshop.should respond_to(:SupplementalCategories)
34
+ @xmp.photoshop.should respond_to(:supplemental_categories)
35
+ end
36
+
37
+ it "should return standalone attribute hash" do
38
+ @xmp.Iptc4xmpCore.CreatorContactInfo.should eq({'CiAdrCtry' => 'Germany', 'CiAdrCity' => 'Berlin'})
16
39
  end
17
40
 
18
41
  it "should return embedded attribute" do
@@ -22,6 +45,7 @@ describe XMP do
22
45
 
23
46
  it "should raise NoMethodError on unknown attribute" do
24
47
  lambda { @xmp.photoshop.UnknownAttribute }.should raise_error(NoMethodError)
48
+ @xmp.photoshop['UnknownAttribute'].should eq(nil)
25
49
  end
26
50
 
27
51
  describe "namespace 'tiff'" do
@@ -58,4 +82,47 @@ describe XMP do
58
82
  @xmp.photoshop.Credit.should eq('Remco')
59
83
  end
60
84
  end
85
+
86
+ # metadata after lightroom -> preview (resize)
87
+ # this one has only standalone attributes
88
+ describe "with xmp3.xml" do
89
+ before { @xmp = XMP.new(File.read('spec/fixtures/xmp3.xml')) }
90
+
91
+ it "should return attributes" do
92
+ @xmp.Iptc4xmpCore.Location.should eq('Phạm Đình Hồ')
93
+ @xmp.photoshop.City.should eq('Hanoi')
94
+ @xmp.aux.Lens.should eq('EF24-105mm f/4L IS USM')
95
+ end
96
+
97
+ it "should return standalone attribute hash" do
98
+ @xmp.Iptc4xmpCore.CreatorContactInfo.should eq({'CiAdrCtry' => 'Germany', 'CiAdrCity' => 'Berlin'})
99
+ end
100
+
101
+ end
102
+
103
+ # metadata after lightroom
104
+ describe "with xmp4.xml" do
105
+ before { @xmp = XMP.new(File.read('spec/fixtures/xmp4.xml')) }
106
+
107
+ it "should return dc:format" do
108
+ @xmp.dc.format.should eq('image/jpeg')
109
+ end
110
+
111
+ it "should return standalone attribute hash" do
112
+ @xmp.Iptc4xmpCore.CreatorContactInfo.should eq({'CiAdrCtry' => 'Germany', 'CiAdrCity' => 'Berlin'})
113
+ end
114
+ end
115
+
116
+ # metadata after lightroom 10 with face recognition
117
+ describe "with xmp5.xml" do
118
+ before { @xmp = XMP.new(File.read('spec/fixtures/xmp5.xml')) }
119
+
120
+ it "should return dc:format" do
121
+ @xmp.dc.format.should eq('image/jpeg')
122
+ end
123
+
124
+ it "should be able to read all attribute" do
125
+ @xmp.to_h.keys.should =~ %w{Iptc4xmpExt aux crs dc exifEX mwg-rs photoshop rdf stArea stDim stEvt stRef x xmp xmpMM}
126
+ end
127
+ end
61
128
  end
data/xmp.gemspec CHANGED
@@ -9,20 +9,23 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Wojciech Piekutowski"]
10
10
  s.email = ["wojciech.piekutowski@amberbit.com"]
11
11
  s.homepage = "https://github.com/amberbit/xmp"
12
- s.summary = %q{Extensible Metadata Platform (XMP) parser}
13
- s.description = %q{Extensible Metadata Platform (XMP) parser}
14
-
15
- s.rubyforge_project = "xmp"
12
+ s.summary = %q{Extensible Metadata Platform (XMP) parser for JPEG, TIFF and raw XML files}
13
+ s.description = %q{Extensible Metadata Platform (XMP) parser extracts metadata from JPED and TIFF image files. It also supports parsing raw XML files containing XMP.}
14
+ s.licenses = ["MIT"]
16
15
 
17
16
  s.files = `git ls-files`.split("\n")
18
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
19
  s.require_paths = ["lib"]
21
20
 
22
- s.extra_rdoc_files = %w(README.rdoc)
21
+ s.extra_rdoc_files = %w(README.md)
22
+
23
+ s.required_ruby_version = '>= 2.6.0'
23
24
 
24
- s.add_dependency 'nokogiri', '~>1.5.0'
25
+ s.add_dependency 'nokogiri', '~> 1.0'
25
26
 
26
- s.add_development_dependency 'exifr', '>=1.0.4'
27
- s.add_development_dependency 'rspec', '~>2.0'
27
+ s.add_development_dependency 'exifr', '>= 1.0.4', '~> 1.0'
28
+ s.add_development_dependency 'rspec', '~> 3.0'
29
+ s.add_development_dependency 'logger', '~> 1.0'
30
+ s.add_development_dependency 'rake'
28
31
  end
metadata CHANGED
@@ -1,98 +1,163 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xmp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
5
- prerelease:
4
+ version: 1.0.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Wojciech Piekutowski
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2011-07-28 00:00:00.000000000 +02:00
13
- default_executable:
11
+ date: 2024-09-14 00:00:00.000000000 Z
14
12
  dependencies:
15
13
  - !ruby/object:Gem::Dependency
16
14
  name: nokogiri
17
- requirement: &14726140 !ruby/object:Gem::Requirement
18
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
19
16
  requirements:
20
- - - ~>
17
+ - - "~>"
21
18
  - !ruby/object:Gem::Version
22
- version: 1.5.0
19
+ version: '1.0'
23
20
  type: :runtime
24
21
  prerelease: false
25
- version_requirements: *14726140
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
26
27
  - !ruby/object:Gem::Dependency
27
28
  name: exifr
28
- requirement: &14725640 !ruby/object:Gem::Requirement
29
- none: false
29
+ requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ! '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 1.0.4
34
+ - - "~>"
35
+ - !ruby/object:Gem::Version
36
+ version: '1.0'
34
37
  type: :development
35
38
  prerelease: false
36
- version_requirements: *14725640
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.4
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.0'
37
47
  - !ruby/object:Gem::Dependency
38
48
  name: rspec
39
- requirement: &14725180 !ruby/object:Gem::Requirement
40
- none: false
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: logger
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rake
77
+ requirement: !ruby/object:Gem::Requirement
41
78
  requirements:
42
- - - ~>
79
+ - - ">="
43
80
  - !ruby/object:Gem::Version
44
- version: '2.0'
81
+ version: '0'
45
82
  type: :development
46
83
  prerelease: false
47
- version_requirements: *14725180
48
- description: Extensible Metadata Platform (XMP) parser
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description: Extensible Metadata Platform (XMP) parser extracts metadata from JPED
90
+ and TIFF image files. It also supports parsing raw XML files containing XMP.
49
91
  email:
50
92
  - wojciech.piekutowski@amberbit.com
51
93
  executables: []
52
94
  extensions: []
53
95
  extra_rdoc_files:
54
- - README.rdoc
96
+ - README.md
55
97
  files:
56
- - .gitignore
57
- - .rspec
58
- - .rvmrc
98
+ - ".gitignore"
99
+ - ".rspec"
100
+ - ".tool-versions"
101
+ - ".travis.yml"
59
102
  - Gemfile
60
- - README.rdoc
103
+ - README.md
61
104
  - Rakefile
62
105
  - lib/xmp.rb
106
+ - lib/xmp/convenience.rb
107
+ - lib/xmp/document.rb
108
+ - lib/xmp/error.rb
109
+ - lib/xmp/handler.rb
110
+ - lib/xmp/handler/exifr.rb
111
+ - lib/xmp/handler/file.rb
112
+ - lib/xmp/handler/xml.rb
63
113
  - lib/xmp/namespace.rb
64
- - lib/xmp/silencer.rb
65
114
  - lib/xmp/version.rb
66
115
  - spec/exifr_jpeg_spec.rb
116
+ - spec/exifr_tiff_spec.rb
117
+ - spec/fixtures/UPPERCASE.JPG
67
118
  - spec/fixtures/multiple-app1.jpg
119
+ - spec/fixtures/multiple-app1.tiff
68
120
  - spec/fixtures/xmp.xml
69
121
  - spec/fixtures/xmp2.xml
122
+ - spec/fixtures/xmp3.xml
123
+ - spec/fixtures/xmp4.xml
124
+ - spec/fixtures/xmp5.xml
70
125
  - spec/spec_helper.rb
71
126
  - spec/xmp_spec.rb
72
127
  - xmp.gemspec
73
- has_rdoc: true
74
128
  homepage: https://github.com/amberbit/xmp
75
- licenses: []
76
- post_install_message:
129
+ licenses:
130
+ - MIT
131
+ metadata: {}
132
+ post_install_message:
77
133
  rdoc_options: []
78
134
  require_paths:
79
135
  - lib
80
136
  required_ruby_version: !ruby/object:Gem::Requirement
81
- none: false
82
137
  requirements:
83
- - - ! '>='
138
+ - - ">="
84
139
  - !ruby/object:Gem::Version
85
- version: '0'
140
+ version: 2.6.0
86
141
  required_rubygems_version: !ruby/object:Gem::Requirement
87
- none: false
88
142
  requirements:
89
- - - ! '>='
143
+ - - ">="
90
144
  - !ruby/object:Gem::Version
91
145
  version: '0'
92
146
  requirements: []
93
- rubyforge_project: xmp
94
- rubygems_version: 1.6.2
95
- signing_key:
96
- specification_version: 3
97
- summary: Extensible Metadata Platform (XMP) parser
98
- test_files: []
147
+ rubygems_version: 3.5.16
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Extensible Metadata Platform (XMP) parser for JPEG, TIFF and raw XML files
151
+ test_files:
152
+ - spec/exifr_jpeg_spec.rb
153
+ - spec/exifr_tiff_spec.rb
154
+ - spec/fixtures/UPPERCASE.JPG
155
+ - spec/fixtures/multiple-app1.jpg
156
+ - spec/fixtures/multiple-app1.tiff
157
+ - spec/fixtures/xmp.xml
158
+ - spec/fixtures/xmp2.xml
159
+ - spec/fixtures/xmp3.xml
160
+ - spec/fixtures/xmp4.xml
161
+ - spec/fixtures/xmp5.xml
162
+ - spec/spec_helper.rb
163
+ - spec/xmp_spec.rb
data/.rvmrc DELETED
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- # http://rvm.beginrescueend.com/workflow/rvmrc/
4
-
5
- ruby_string="ruby-1.9.2"
6
- gemset_name="xmp"
7
-
8
- if rvm list strings | grep -q "${ruby_string}" ; then
9
-
10
- # Load or create the specified environment
11
- if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
12
- && -s "${rvm_path:-$HOME/.rvm}/environments/${ruby_string}@${gemset_name}" ]] ; then
13
- \. "${rvm_path:-$HOME/.rvm}/environments/${ruby_string}@${gemset_name}"
14
- else
15
- rvm --create "${ruby_string}@${gemset_name}"
16
- fi
17
-
18
- # (
19
- # Ensure that Bundler is installed, install it if it is not.
20
- if ! command -v bundle &> /dev/null; then
21
- echo "Installing bundler and creating bundle"
22
- gem install bundler
23
- bundle
24
- fi
25
- # )&
26
-
27
- else
28
-
29
- # Notify the user to install the desired interpreter before proceeding.
30
- echo "${ruby_string} was not found, please run 'rvm install ${ruby_string}' and then cd back into the project directory."
31
-
32
- fi
33
-
34
-
data/README.rdoc DELETED
@@ -1,53 +0,0 @@
1
- = xmp - Extensible Metadata Platform (XMP) parser
2
-
3
- XMP provides object oriented interface to XMP data (http://en.wikipedia.org/wiki/Extensible_Metadata_Platform). XMP data can be found in PDF, JPEG, GIF, PNG, and many other formats.
4
-
5
- == Supported formats
6
-
7
- Currently only JPEG is supported through exifr gem.
8
-
9
- == JPEG example
10
-
11
- # gem install xmp exifr
12
- require 'xmp'
13
- require 'exifr'
14
- require 'open-uri'
15
-
16
- img = EXIFR::JPEG.new('spec/fixtures/multiple-app1.jpg')
17
- xmp = XMP.parse(img)
18
- xmp.dc.subject #=> ["something interesting"]
19
-
20
- # explore XMP data
21
- xmp.namespaces.each do |namespace_name|
22
- namespace = xmp.send(namespace_name)
23
- namespace.attributes.each do |attr|
24
- puts "#{namespace_name}.#{attr}: " + namespace.send(attr).inspect
25
- end
26
- end
27
-
28
- == Installation
29
-
30
- gem install xmp
31
- # for JPEG support
32
- gem install exifr -v ">=1.0.4"
33
-
34
- == Requirements
35
-
36
- * Ruby 1.8.7, 1.9.2
37
- * Nokogiri 1.4
38
- * EXIFR >= 1.0.4
39
-
40
- == Development
41
-
42
- Fork it at https://github.com/amberbit/xmp
43
-
44
- # install development dependencies
45
- bundle install
46
- # run specs
47
- rake spec
48
-
49
- == License
50
-
51
- Ruby's license.
52
-
53
- Copyright (c) 2011 Wojciech Piekutowski, AmberBit (http://amberbit.com)
data/lib/xmp/silencer.rb DELETED
@@ -1,11 +0,0 @@
1
- class XMP
2
- module Silencer
3
- def self.silently
4
- verbosity = $VERBOSE
5
- $VERBOSE = nil
6
- result = yield
7
- $VERBOSE = verbosity
8
- result
9
- end
10
- end
11
- end