read one creator

This commit is contained in:
John Bintz 2011-03-11 13:27:17 -05:00
parent 29449d381b
commit efff98eb32
8 changed files with 217 additions and 23 deletions

View File

@ -7,10 +7,13 @@ module AVM
IPTC_CORE_FIELDS = [ :address, :city, :state, :zip, :country ] IPTC_CORE_FIELDS = [ :address, :city, :state, :zip, :country ]
PRIMARY_CONTACT_FIELDS = IPTC_CORE_FIELDS + [ :province, :postal_code ] PRIMARY_CONTACT_FIELDS = IPTC_CORE_FIELDS + [ :province, :postal_code ]
IPTC_MULTI_FIELD_MAP = [ [ :telephone, 'CiTelWork' ], [ :email, 'CiEmailWork' ] ]
IPTC_CORE_FIELD_ELEMENT_NAMES = %w{CiAdrExtadr CiAdrCity CiAdrRegion CiAdrPcode CiAdrCtry}
IPTC_CORE_FIELDS_AND_NAMES = IPTC_CORE_FIELDS.zip(IPTC_CORE_FIELD_ELEMENT_NAMES)
def initialize(image) def initialize(image, given_contacts = [])
@options = {} @options = {}
@contacts = [] @contacts = given_contacts
@image = image @image = image
end end
@ -18,6 +21,14 @@ module AVM
@options.merge!(hash) @options.merge!(hash)
end end
def length
contacts.length
end
def [](which)
contacts[which]
end
def method_missing(key, *opts) def method_missing(key, *opts)
if key.to_s[-1..-1] == '=' if key.to_s[-1..-1] == '='
@options[key.to_s[0..-2].to_sym] = opts.first @options[key.to_s[0..-2].to_sym] = opts.first
@ -31,7 +42,7 @@ module AVM
end end
def add_to_document(document) def add_to_document(document)
document.add_to_doc do |refs| document.get_refs do |refs|
creator = refs[:dublin_core].add_child('<dc:creator><rdf:Seq></rdf:Seq></dc:creator>') creator = refs[:dublin_core].add_child('<dc:creator><rdf:Seq></rdf:Seq></dc:creator>')
list = creator.at_xpath('.//rdf:Seq') list = creator.at_xpath('.//rdf:Seq')
@ -42,13 +53,13 @@ module AVM
end end
if primary_contact if primary_contact
[ [ :telephone, 'CiTelWork' ], [ :email, 'CiEmailWork' ] ].each do |key, element_name| IPTC_MULTI_FIELD_MAP.each do |key, element_name|
contact_info.add_child "<Iptc4xmpCore:#{element_name}>#{contacts.sort.collect(&key).join(',')}</Iptc4xmpCore:#{element_name}>" contact_info.add_child "<Iptc4xmpCore:#{element_name}>#{contacts.sort.collect(&key).join(',')}</Iptc4xmpCore:#{element_name}>"
end end
iptc_namespace = document.doc.root.namespace_scopes.find { |ns| ns.prefix == 'Iptc4xmpCore' } iptc_namespace = document.doc.root.namespace_scopes.find { |ns| ns.prefix == 'Iptc4xmpCore' }
IPTC_CORE_FIELDS.zip(%w{CiAdrExtadr CiAdrCity CiAdrRegion CiAdrPcode CiAdrCtry}).each do |key, element_name| IPTC_CORE_FIELDS_AND_NAMES.each do |key, element_name|
node = contact_info.document.create_element(element_name, primary_contact.send(key)) node = contact_info.document.create_element(element_name, primary_contact.send(key))
node.namespace = iptc_namespace node.namespace = iptc_namespace
contact_info.add_child node contact_info.add_child node
@ -57,6 +68,31 @@ module AVM
end end
end end
def from_xml(image, document)
contacts = []
document.get_refs do |refs|
refs[:dublin_core].search('.//rdf:li').each do |name|
contacts << { :name => name.text }
end
IPTC_MULTI_FIELD_MAP.each do |key, element_name|
if node = refs[:iptc].at_xpath("//Iptc4xmpCore:#{element_name}")
node.text.split(',').collect(&:strip).each_with_index do |value, index|
contacts[index][key] = value
end
end
end
IPTC_CORE_FIELDS_AND_NAMES.each do |key, element_name|
if node = refs[:iptc].at_xpath("//Iptc4xmpCore:#{element_name}")
contacts.first[key] = node.text.strip
end
end
end
@contacts = contacts.collect { |contact| Contact.new(contact) }
end
def primary_contact def primary_contact
@contacts.find(&:primary) || @contacts.sort.first @contacts.find(&:primary) || @contacts.sort.first
end end

View File

@ -16,6 +16,14 @@ module AVM
document.doc document.doc
end end
def self.from_xml(string)
document = AVM::XMP.from_string(string)
image = new
image.creator.from_xml(self, document)
image
end
end end
end end

View File

@ -4,15 +4,49 @@ module AVM
class XMP class XMP
attr_reader :doc attr_reader :doc
def initialize def initialize(doc = nil)
@doc = empty_xml_doc @doc = doc || empty_xml_doc
ensure_namespaces!
ensure_descriptions_findable!
end end
def add_to_doc def get_refs
yield Hash[[ :dublin_core, :iptc ].collect { |key| [ key, send(key) ] }] yield Hash[[ :dublin_core, :iptc ].collect { |key| [ key, send(key) ] }]
end end
def self.from_string(string)
new(Nokogiri::XML(string))
end
private private
def ensure_namespaces!
{
:x => "adobe:ns:meta/",
:rdf => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
:dc => "http://purl.org/dc/elements/1.1/",
:photoshop => "http://ns.adobe.com/photoshop/1.0/",
:avm => "http://www.communicatingastronomy.org/avm/1.0/",
:Iptc4xmpCore => "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
}.each do |namespace, url|
doc.root.add_namespace_definition(namespace.to_s, url)
end
end
def ensure_descriptions_findable!
doc.search('//rdf:Description').each do |description|
if first_child = description.first_element_child
if first_child.namespace
case first_child.namespace.prefix
when 'dc'
description['about'] = 'Dublin Core'
when 'Iptc4xmpCore'
description['about'] = 'IPTC'
end
end
end
end
end
def dublin_core def dublin_core
at_rdf_description "Dublin Core" at_rdf_description "Dublin Core"
end end
@ -26,7 +60,7 @@ module AVM
end end
def empty_xml_doc def empty_xml_doc
document = Nokogiri::XML(<<-XML) Nokogiri::XML(<<-XML)
<x:xmpmeta xmlns:x="adobe:ns:meta/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <x:xmpmeta xmlns:x="adobe:ns:meta/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:RDF> <rdf:RDF>
<rdf:Description about="Dublin Core"> <rdf:Description about="Dublin Core">
@ -38,17 +72,6 @@ module AVM
</rdf:RDF> </rdf:RDF>
</x:xmpmeta> </x:xmpmeta>
XML XML
{
:dc => "http://purl.org/dc/elements/1.1/",
:photoshop => "http://ns.adobe.com/photoshop/1.0/",
:avm => "http://www.communicatingastronomy.org/avm/1.0/",
:Iptc4xmpCore => "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
}.each do |namespace, url|
document.root.add_namespace_definition(namespace.to_s, url)
end
document
end end
end end
end end

View File

@ -91,6 +91,51 @@ describe AVM::Creator do
its(:name) { should == first_name } its(:name) { should == first_name }
end end
describe '#from_xml' do
let(:document) { image.to_xml }
let(:image) { AVM::Image.from_xml(File.read(file_path)) }
subject { image }
context 'no creator' do
let(:file_path) { 'spec/sample_files/creator/no_creator.xmp' }
its('creator.length') { should == 0 }
end
context 'one creator' do
let(:file_path) { 'spec/sample_files/creator/one_creator.xmp' }
its('creator.length') { should == 1 }
context 'creator one' do
subject { image.creator[0] }
let(:name) { 'John Bintz' }
let(:email) { 'bintz@stsci.edu' }
let(:telephone) { '800-555-1234' }
let(:address) { '3700 San Martin Drive' }
let(:city) { 'Baltimore' }
let(:state) { 'Maryland' }
let(:zip) { '21218' }
let(:country) { 'USA' }
its(:name) { should == name }
its(:address) { should == address }
its(:city) { should == city }
its(:state) { should == state }
its(:zip) { should == zip }
its(:country) { should == country }
its(:email) { should == email }
its(:telephone) { should == telephone }
end
end
context 'two creators' do
let(:file_path) { 'spec/sample_files/creator/two_creators.xmp' }
end
end
describe 'contact name node' do describe 'contact name node' do
let(:first_name) { 'John' } let(:first_name) { 'John' }
let(:second_name) { 'Zohn' } let(:second_name) { 'Zohn' }

View File

@ -4,9 +4,11 @@ require 'avm/xmp'
describe AVM::XMP do describe AVM::XMP do
let(:xmp) { self.class.describes.new } let(:xmp) { self.class.describes.new }
describe '#add_to_doc' do subject { xmp }
describe '#get_refs' do
before { before {
xmp.add_to_doc do |refs| xmp.get_refs do |refs|
refs[:dublin_core] << "<rdf:addedToDublinCore />" refs[:dublin_core] << "<rdf:addedToDublinCore />"
refs[:iptc] << "<rdf:addedToIPTC />" refs[:iptc] << "<rdf:addedToIPTC />"
end end
@ -15,4 +17,46 @@ describe AVM::XMP do
specify { xmp.doc.at_xpath('//rdf:Description[@about="Dublin Core"]//rdf:addedToDublinCore').should_not be_nil } specify { xmp.doc.at_xpath('//rdf:Description[@about="Dublin Core"]//rdf:addedToDublinCore').should_not be_nil }
specify { xmp.doc.at_xpath('//rdf:Description[@about="IPTC"]//rdf:addedToIPTC').should_not be_nil } specify { xmp.doc.at_xpath('//rdf:Description[@about="IPTC"]//rdf:addedToIPTC').should_not be_nil }
end end
describe '.from_string' do
let(:xmp) { self.class.describes.from_string(string) }
let(:string) { '<xml><node /></xml>' }
specify { xmp.doc.at_xpath('//node').should_not be_nil }
end
describe '#ensure_descriptions_findable!' do
let(:document) { <<-XML }
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
#{content}
</rdf:RDF>
</x:xmpmeta>
XML
let(:xmp) { self.class.describes.new(Nokogiri::XML(document)) }
context 'no nodes within' do
let(:content) { '' }
[ 'Dublin Core', 'IPTC' ].each do |which|
specify { xmp.doc.at_xpath(%{//rdf:Description[@about="#{which}"]}).should be_nil }
end
end
context 'has identifying nodes within' do
let(:content) { <<-XML }
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:creator />
</rdf:Description>
<rdf:Description rdf:about="" xmlns:Iptc4xmpCore="http://itpc.org/stf/Iptc4xmpCore/1.0/xmlns/">
<Iptc4xmpCore:CreatorContactInfo rdf:parseType="Resource" />
</rdf:Description>
XML
[ 'Dublin Core', 'IPTC' ].each do |which|
specify { xmp.doc.at_xpath(%{//rdf:Description[@about="#{which}"]}).should_not be_nil }
end
end
end
end end

View File

@ -0,0 +1,14 @@
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.2-c063 53.352624, 2008/07/30-18:05:41 ">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:creator />
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/">
<Iptc4xmpCore:CreatorContactInfo rdf:parseType="Resource">
</Iptc4xmpCore:CreatorContactInfo>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

@ -0,0 +1,25 @@
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.2-c063 53.352624, 2008/07/30-18:05:41 ">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:creator>
<rdf:Seq><rdf:li>John Bintz</rdf:li></rdf:Seq>
</dc:creator>
</rdf:Description>
<rdf:Description rdf:about=""
xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/">
<Iptc4xmpCore:CreatorContactInfo rdf:parseType="Resource">
<Iptc4xmpCore:CiAdrExtadr>3700 San Martin Drive</Iptc4xmpCore:CiAdrExtadr>
<Iptc4xmpCore:CiAdrCity>Baltimore</Iptc4xmpCore:CiAdrCity>
<Iptc4xmpCore:CiAdrRegion>Maryland</Iptc4xmpCore:CiAdrRegion>
<Iptc4xmpCore:CiAdrPcode>21218</Iptc4xmpCore:CiAdrPcode>
<Iptc4xmpCore:CiAdrCtry>USA</Iptc4xmpCore:CiAdrCtry>
<Iptc4xmpCore:CiTelWork>800-555-1234</Iptc4xmpCore:CiTelWork>
<Iptc4xmpCore:CiEmailWork>bintz@stsci.edu</Iptc4xmpCore:CiEmailWork>
<Iptc4xmpCore:CiUrlWork>http://hubblesite.org</Iptc4xmpCore:CiUrlWork>
</Iptc4xmpCore:CreatorContactInfo>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

@ -1,4 +1,3 @@
RSpec.configure do |config| RSpec.configure do |config|
config.mock_with :mocha config.mock_with :mocha
end end