using XML elements instead of attributes when writing XMP tags
Added by Linxiao Geng over 5 years ago
I was trying to write some customized XMP tags into a JPG file and noticed that, with the following code:
Exiv2::XmpData xmpData; Exiv2::XmpProperties::registerNs("http://ns.exampledomainn.com/Camera/1.0/","Camera"); xmpData["Xmp.Camera.GPSXYAccuracy"]="2.0001"; xmpData["Xmp.Camera.GPSZAccuracy"]="2.0001"; Exiv2::XmpTextValue s; s.read("2.0"); xmpData.add(Exiv2::XmpKey("Xmp.Camera.GPSXYAccuracy"), &s); xmpData.add(Exiv2::XmpKey("Xmp.Camera.GPSZAccuracy"), &s); image->setXmpData(xmpData); image->writeMetadata();
I got XMP written in the JPG like this:
<rdf:Description rdf:about="" xmlns:Camera="http://ns.exampledomainn.com/" Camera:GPSXYAccuracy="2.0001" Camera:GPSZAccuracy="2.0001" /> </rdf:RDF>
But what I wanted to achieve is:
<rdf:Description rdf:about="" xmlns:Camera="http://ns.exampledomainn.com/"> <Camera:GPSXYAccuracy>"2.0001"</Camera:GPSXYAccuracy> <Camera:GPSZAccuracy>"2.0001"</Camera:GPSZAccuracy> </rdf:RDF>
That is, instead of using XML attributes, using XML elements for my tags.
I googled the web and didn't find anything, maybe someone here can help me out? Thanks!
Replies (4)
RE: using XML elements instead of attributes when writing XMP tags - Added by Robin Mills over 5 years ago
Alan's our XMP expert. I've been able to change the behaviour from an attribute to a tag using XMPseq:
693 rmills@rmillsmbp:~/gnu/exiv2/trunk $ curl -O --silent http://clanmills.com/Stonehenge.jpg 694 rmills@rmillsmbp:~/gnu/exiv2/trunk $ exiv2 -M"reg Camera http://clanmills.com/Camera/1.0/" \ -M"add Xmp.Camera.Accuracy XmpSeq 2.001" Stonehenge.jpg 695 rmills@rmillsmbp:~/gnu/exiv2/trunk $ exiv2 -pX Stonehenge.jpg | xmllint --pretty 1 - <?xml version="1.0"?> <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:Camera="http://clanmills.com/Camera/1.0/" rdf:about="" xmp:Rating="0" xmp:ModifyDate="2015-07-16T20:25:28+01:00"> <dc:description> <rdf:Alt> <rdf:li xml:lang="x-default">Classic View</rdf:li> </rdf:Alt> </dc:description> <Camera:Accuracy> <rdf:Seq> <rdf:li>2.001</rdf:li> </rdf:Seq> </Camera:Accuracy> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="w"?> 696 rmills@rmillsmbp:~/gnu/exiv2/trunk $Of course it's gratuitously embellished with embedded tags:
<rdf:Seq><rdf:li>
and encoded within <rdf:Description>
An alternative approach is to extract the XMP to a sidecar with
exiv2 -pX Stonehenge.jpg > Stonehenge.xmpEdit the sidecar with an external application and replace the XMP with
exiv2 -iX Stonehenge.jpg
RE: using XML elements instead of attributes when writing XMP tags - Added by Linxiao Geng over 5 years ago
Robin, thanks for responding! That looks promising. However, for performance considerations, I'm looking for achieving this through C++ code instead of command line. Any idea?
RE: using XML elements instead of attributes when writing XMP tags - Added by Robin Mills over 5 years ago
Two answers:
1) The exiv2 command-line app is a thin wrapper over the API. So, your C++ probably becomes something like: warning, not compiled or tested in any way:
Exiv2::XmpData xmpData; Exiv2::XmpProperties::registerNs("http://ns.exampledomainn.com/Camera/1.0/","Camera"); XmpArrayValue s(xmpSeq); s.read("2.0"); xmpData.add(Exiv2::XmpKey("Xmp.Camera.GPSXYAccuracy"), &s); xmpData.add(Exiv2::XmpKey("Xmp.Camera.GPSZAccuracy"), &s); image->setXmpData(xmpData); image->writeMetadata();
2) You can read and write streams using the API. exiv2 -pR uses the API image->printStructure(....);
I can't remember off-hand how -iX is implemented - you'll have to read the code. I implemented -pR. I don't know who wrote -iX. However I recommend that you discover a solution with the exiv2 application and then convert it into C++. The debugger will be your best friend to discover the APIs being used.
RE: using XML elements instead of attributes when writing XMP tags - Added by Robin Mills over 5 years ago
I've discovered something else about this. I've used the type XmpAlt to add Camera.Accuracy.
$ curl --silent -O http://clanmills.com/Stonehenge.jpg $ exiv2 -M"reg Camera http://clanmills.com/Camera/1.0/" \ -M"set Xmp.Camera.Accuracy XmpAlt 9.11" Stonehenge.jpg $ exiv2 -pX Stonehenge.jpg | xmllint --pretty 1 - <?xml version="1.0"?> <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:Camera="http://clanmills.com/Camera/1.0/" rdf:about="" xmp:Rating="0" xmp:ModifyDate="2015-07-16T20:25:28+01:00"> <dc:description> <rdf:Alt> <rdf:li xml:lang="x-default">Classic View</rdf:li> </rdf:Alt> </dc:description> <Camera:Accuracy> <rdf:Alt> <rdf:li>9.11</rdf:li> </rdf:Alt> </Camera:Accuracy> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="w"?>$The tag
<Camera::Accuracy>
is beautiful enclosed by tags <rdf:Alt><rdf:li>
This seems to perfectly match the pattern of XMP/xml in the original image which was written by Google's Picasa application.
I'm not an expert in metadata and know very little about XMP. However I'm wondering if your target code is valid XMP:
<rdf:Description rdf:about="" xmlns:Camera="http://ns.exampledomainn.com/"> <Camera:GPSXYAccuracy>"2.0001"</Camera:GPSXYAccuracy> <Camera:GPSZAccuracy>"2.0001"</Camera:GPSZAccuracy> </rdf:RDF>For certain this isn't valid XML, however I thing that's just a typo and the closing tag should be </rdf:Description>.
I'd like to reiterate my recommendation that you use the exiv2 application to obtain the result you desire and then figure out the C++ API to generate that from your code.
I've also investigated the behaviour of the test application xmpparser-test. It uses a side car which does use XMP/xml similar to your pattern.
600 rmills@rmillsmbp:~/gnu/exiv2/trunk $ xmllint --pretty 1 test/data/xmpsdk.xmp <?xml version="1.0"?> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description xmlns:ns1="ns:test1/" xmlns:ns2="ns:test2/" rdf:about=""> <ns1:SimpleProp1>Simple1 value</ns1:SimpleProp1> <ns1:SimpleProp2 xml:lang="x-default">Simple2 value</ns1:SimpleProp2> <ns1:ArrayProp1> <rdf:Bag> <rdf:li>Item1.1 value</rdf:li> <rdf:li>Item1.2 value</rdf:li> </rdf:Bag> </ns1:ArrayProp1> <ns1:ArrayProp2> <rdf:Alt> <rdf:li xml:lang="x-one">Item2.1 value</rdf:li> <rdf:li xml:lang="x-two">Item2.2 value</rdf:li> </rdf:Alt> </ns1:ArrayProp2> <ns1:StructProp rdf:parseType="Resource"> <ns2:Field1>Field1 value</ns2:Field1> <ns2:Field2>Field2 value</ns2:Field2> </ns1:StructProp> <ns1:QualProp1 rdf:parseType="Resource"> <rdf:value>Prop value</rdf:value> <ns2:Qual>Qual value</ns2:Qual> </ns1:QualProp1> <ns1:QualProp2 rdf:parseType="Resource"> <rdf:value xml:lang="x-default">Prop value</rdf:value> <ns2:Qual>Qual value</ns2:Qual> </ns1:QualProp2> <ns1:NestedStructProp rdf:parseType="Resource"> <ns2:Outer rdf:parseType="Resource"> <ns2:Middle rdf:parseType="Resource"> <ns2:Inner rdf:parseType="Resource"> <ns2:Field1>Field1 value</ns2:Field1> <ns2:Field2>Field2 value</ns2:Field2> </ns2:Inner> </ns2:Middle> </ns2:Outer> </ns1:NestedStructProp> </rdf:Description> </rdf:RDF> 601 rmills@rmillsmbp:~/gnu/exiv2/trunk $The test application bin/xmpparser-test reads test/data/xmpsdk.xmp into memory then write the XMP as the sidecar test/data/xmpsdk.xmp-new.
602 rmills@rmillsmbp:~/gnu/exiv2/trunk $ bin/xmpparser-test test/data/xmpsdk.xmp -----> Decoding XMP data read from test/data/xmpsdk.xmp <----- Xmp.ns1.SimpleProp1 XmpText 13 Simple1 value Xmp.ns1.SimpleProp2 XmpText 13 Simple2 value Xmp.ns1.SimpleProp2/?xml:lang XmpText 9 x-default Xmp.ns1.ArrayProp1 XmpBag 2 Item1.1 value, Item1.2 value Xmp.ns1.ArrayProp2 LangAlt 2 lang="x-two" Item2.2 value, lang="x-one" Item2.1 value Xmp.ns1.StructProp XmpText 0 type="Struct" Xmp.ns1.StructProp/ns2:Field1 XmpText 12 Field1 value Xmp.ns1.StructProp/ns2:Field2 XmpText 12 Field2 value Xmp.ns1.QualProp1 XmpText 10 Prop value Xmp.ns1.QualProp1/?ns2:Qual XmpText 10 Qual value Xmp.ns1.QualProp2 XmpText 10 Prop value Xmp.ns1.QualProp2/?xml:lang XmpText 9 x-default Xmp.ns1.QualProp2/?ns2:Qual XmpText 10 Qual value Xmp.ns1.NestedStructProp XmpText 0 type="Struct" Xmp.ns1.NestedStructProp/ns2:Outer XmpText 0 type="Struct" Xmp.ns1.NestedStructProp/ns2:Outer/ns2:Middle XmpText 0 type="Struct" Xmp.ns1.NestedStructProp/ns2:Outer/ns2:Middle/ns2:Inner XmpText 0 type="Struct" Xmp.ns1.NestedStructProp/ns2:Outer/ns2:Middle/ns2:Inner/ns2:Field1 XmpText 12 Field1 value Xmp.ns1.NestedStructProp/ns2:Outer/ns2:Middle/ns2:Inner/ns2:Field2 XmpText 12 Field2 value -----> Encoding XMP data to write to test/data/xmpsdk.xmp-new <----- 603 rmills@rmillsmbp:~/gnu/exiv2/trunk $ ls -alt test/data/xmpsdk.xml-new ls: test/data/xmpsdk.xml-new: No such file or directory 604 rmills@rmillsmbp:~/gnu/exiv2/trunk $ ls -alt test/data/xmpsdk.xmp-new -rw-r--r--+ 1 rmills staff 3474 25 Jun 08:08 test/data/xmpsdk.xmp-new 605 rmills@rmillsmbp:~/gnu/exiv2/trunk $ xmllint --pretty 1 test/data/xmpsdk.xmp-new <?xml version="1.0"?> <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description xmlns:ns1="ns:test1/" xmlns:ns2="ns:test2/" rdf:about="" ns1:SimpleProp1="Simple1 value"> <ns1:SimpleProp2 xml:lang="x-default">Simple2 value</ns1:SimpleProp2> <ns1:ArrayProp1> <rdf:Bag> <rdf:li>Item1.1 value</rdf:li> <rdf:li>Item1.2 value</rdf:li> </rdf:Bag> </ns1:ArrayProp1> <ns1:ArrayProp2> <rdf:Alt> <rdf:li xml:lang="x-two">Item2.2 value</rdf:li> <rdf:li xml:lang="x-one">Item2.1 value</rdf:li> </rdf:Alt> </ns1:ArrayProp2> <ns1:StructProp ns2:Field1="Field1 value" ns2:Field2="Field2 value"/> <ns1:QualProp1 rdf:parseType="Resource"> <rdf:value>Prop value</rdf:value> <ns2:Qual>Qual value</ns2:Qual> </ns1:QualProp1> <ns1:QualProp2 xml:lang="x-default" rdf:parseType="Resource"> <rdf:value>Prop value</rdf:value> <ns2:Qual>Qual value</ns2:Qual> </ns1:QualProp2> <ns1:NestedStructProp rdf:parseType="Resource"> <ns2:Outer rdf:parseType="Resource"> <ns2:Middle rdf:parseType="Resource"> <ns2:Inner ns2:Field1="Field1 value" ns2:Field2="Field2 value"/> </ns2:Middle> </ns2:Outer> </ns1:NestedStructProp> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="w"?> 606 rmills@rmillsmbp:~/gnu/exiv2/trunk $As you can see
ns1:SimpleProp1
is now an attribute and this matches your original report.
As you can also see, ns1:SimpleProp2
is written as a tag. The key difference between SimpleProp1 and SimpleProp2 is xml:lang="x-default"
. I haven't yet discovered how to use the exiv2 application to generate that pattern of XML in Stonehenge.jpg. I've run out of time at present for further investigation. Here's my current best shot. XmpAlt and LangAlt seem to behave identically.
$ curl --silent -O http://clanmills.com/Stonehenge.jpg $ exiv2 -M"reg Camera http://clanmills.com/Camera/1.0/" -M"set Xmp.Camera.Accuracy XmpAlt lang=x-default 9.11" Stonehenge.jpg $ exiv2 -pX Stonehenge.jpg | xmllint --pretty 1 - <?xml version="1.0"?> <?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:Camera="http://clanmills.com/Camera/1.0/" rdf:about="" xmp:Rating="0" xmp:ModifyDate="2015-07-16T20:25:28+01:00"> <dc:description> <rdf:Alt> <rdf:li xml:lang="x-default">Classic View</rdf:li> </rdf:Alt> </dc:description> <Camera:Accuracy> <rdf:Alt> <rdf:li>lang=x-default 9.11</rdf:li> </rdf:Alt> </Camera:Accuracy> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="w"?> $