Another update to my Exif (and now IPTC) code (v1.0.0)

Having been stuck on v0.9.x for ages due to my somewhat arbrary versioning scheme (or lack of one!), I thought I might as well get a v1.0.0 out, so here it is. Changes since v0.9.9 are as thus:

  • Added support for IPTC metadata as stored in Adobe APP13 segments —
    • Implemented an IPTC reader/writer class, TIPTCData, in a new unit, CCR.Exif.IPTC.pas.
    • The interface of TIPTCData is broadly modelled on TExifData’s — thus, there are ‘sections’ and ‘tags’, with high level tag properties on TIPTCData itself.
    • At a lower level, the RemoveMetadataFromJPEG global routine can now delete IPTC data, and you can enumerate the data blocks of an Adobe APP13 segment from an IJPEGSegment instance.
  • TJPEGImageEx has received a few amendments —
    • Added an IPTCData property.
    • Added an overload to Assign that allows for the preservation of any metadata, interpreted by my code or not, when a bitmap is assigned.
    • Fixed a bug in which calling the regular Assign didn’t cause the ExifData property to be updated.
  • Two more Nikon maker note types now parsed. Thanks goes to Stefan Grube for updating the Exif List demo’s MakerNotes.ini for this.
  • Fixed bug of JPEG parsing code not realising a segment with a marker number of 0 has no data.
  • Fixed typo in TStreamHelper.ReadLongInt spotted by Jeff Hamblin.
  • Changed the types of the ExifImageWidth, ExifImageHeight and FocalLengthIn35mmFilm properties of TCustomExifData so as to give them MissingOrInvalid and AsString sub-properties. (Basically, they now use custom record types that have methods and operator overloads.)
  • Changed behaviour of TCustomExifData’s enumerator to not skip empty sections.
  • The LoadFromJPEG methods of TExifData are now procedures rather than functions.
  • Added a couple more demos, namely an IPTC editor and a console app to strip specified types of metadata from one or more JPEG files.
  • Removed all previously deprecated symbols.

[Update 19/1/10 — grr, gremlins. Try downloading again to get a version compilable in D2009 or D2010 (CCR.Exif.JPEGUtils.pas and CCR.Exif.IPTCUtils.pas should now be marked v1.0.0a).]

Added IPTC support —
Implemented an IPTC reader/writer class, TIPTCData, in a new unit, CCR.Exif.IPTC.pas.
The interface of TIPTCData is broadly modelled on TExifData’s — thus, there are ‘sections’ and ‘tags’, with high level tag properties on TIPTCData itself. (See http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf for the IPTC specification.)
At a lower level, the RemoveMetadataFromJPEG global routine can now delete IPTC data, and you can enumerate the data blocks of an Adobe APP13 segment from an IJPEGSegment instance.
TJPEGImageEx has received a few amendments —
Added an IPTCData property.
Added an overload to Assign that allows for the preservation of any metadata, interpreted by my code or not, when a bitmap is assigned.
Fixed a bug in which calling the regular Assign didn’t cause the ExifData property to be updated.
Two more Nikon maker note types now parsed. Thanks goes to Stefan Grube for updating the Exif List demo’s MakerNotes.ini for this.
Fixed bug of JPEG parsing code not realising a segment with a marker number of 0 has no data.
Fixed typo in TStreamHelper.ReadLongInt spotted by Jeff Hamblin.
Changed the types of the ExifImageWidth, ExifImageHeight and FocalLengthIn35mmFilm properties of TCustomExifData so as to give them MissingOrInvalid and AsString sub-properties. (Basically, they now use custom record types that have methods and operator overloads.)
Changed behaviour of TCustomExifData’s enumerator to now not skip empty sections.
The LoadFromJPEG methods of TExifData are now procedures rather than functions.
Added a couple more demos, namely an IPTC editor and a console app to strip specified types of metadata from one or more JPEG files.
Removed all previously deprecated symbols.
Advertisements

22 thoughts on “Another update to my Exif (and now IPTC) code (v1.0.0)

  1. Chris, so far I cannot compile anything with the new version, even not your demos.
    D2010 complains in CCR.Exif.JPEGUtils.pas(251): E2003 about undeclared ‘GetSignature’, ‘GetName’, ‘SetName’.
    Am I missing something?
    Stefan

    • After getting rid of that by using a couple of ansistrings a the right places, I ran into the next problems in CCR.Exif.IPTC.pas:

      [DCC Fehler] CCR.Exif.IPTC.pas(41): E2394 Parameterlose Konstruktoren sind für Record-Typen nicht zulässig
      [DCC Fehler] CCR.Exif.IPTC.pas(64): E2394 Parameterlose Konstruktoren sind für Record-Typen nicht zulässig
      [DCC Fehler] CCR.Exif.IPTC.pas(87): E2394 Parameterlose Konstruktoren sind für Record-Typen nicht zulässig
      [DCC Fehler] CCR.Exif.IPTC.pas(715): E2250 Es gibt keine überladene Version von ‘ReadString’, die man mit diesen Argumenten aufrufen kann

      Sorry, only in german, but you have the compiler error codes…

      • The first three are compiler over-eagerness IMO – there’s no real error, since the constructors in question are given a custom name, so you’d *obviously* have to explicitly call them. Anyhow, I can get roughly the same semantics by using a static class function, so that’s what I’ve now done. Nothing outside of CCR.Exif.IPTC.pas itself should need changing.

        As for fourth, changing the method parameter to AnsiString might be better, rather than changing the variable type to RawByteString.

        Anyhow, thanks for the swift feedback – I should have thought of asking you to try the new version before I put it up… As soon as CodeCentral stops mucking me about (I’m having trouble uploading at the moment), the revised version will be up.

  2. OK, done that as well:

    1a. Change all constructors to have a fake parameter:
    constructor CreateMissingOrInvalid(const AString: string);
    1b. Call to all constructors with:
    CreateMissingOrInvalid(”);
    2. Change in function “Readstring” AnsiString to RawByteString to:
    function TIPTCTag.ReadString: string;
    {$IFDEF UNICODE}
    var
    AnsiStr: RawByteString;

    I hope the code still does what it should do…

    To be confirmed.

    • We could also do a little detour for the readstring:

      function TIPTCTag.ReadString: string;
      {$IFDEF UNICODE}
      var
      RBString: RawByteString;
      AnsiStr: AnsiString;
      begin
      ReadString(RBString);
      AnsiStr:=RBString;
      Result := string(AnsiStr);
      end;

      • Hmm… not so keen on that. Changing the method parameter to AnsiString is a better approach. That said, I’ve managed to upload my revised version now, so check it out.

  3. Hello Chris and thanks a lot for this great library!

    I have questions regarding EXIF and XMP, could you help me in making them clear?

    1st: I looked at your ExifList demo and found that you are showing only the predefined tags. If we will modify the code to print out absolutely all tags, we spot that some of them can appear in different sections. For example, I have images where the Custom Rendered (0xA401) and ExposureMode (0xA402) tags can be met in the Exif sub-IFD section, and the other where they are actually in the Main IFD section. What is that? Manufacturers’ going against the specification, mailforming or what?

    2nd: I tried the XMP browser demo and have problems with it. When I load any of the “photoshoped” files into it, I get a very poor report in the Properties tab:

    While the raw version contains a lot of information:

    64

    How to force the code showing all that info in a more convenient (tree-like) way?

    • Hi Helmut. With respect to your first question, well the Exif specification says those tags should be in the Exif sub-IFD (‘details section’ in the jargon of TExifData). You can download the spec from http://www.exif.org/. Can I ask whether the problem images are straight from a camera or have been edited in some way?

      As for the XMP issue, it looks like WordPress has eaten your XML! Try reposting, but wrapping it in sourcecode tags –

      Posting XML image

      • Thank you for the reply and sorry for my delay!

        1. I’m not sure, please look at the sample image: http://i49.tinypic.com/6q8le0.jpg
        2. Here is the raw listing of my XMP:

        <?xpacket begin="?" id="W5M0MpCehiHzreSzNTczkc9d"?>
        <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.1-c036 46.276720, Mon Feb 19 2007 22:40:08        ">
        	<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
        		<rdf:Description xmlns:tiff="http://ns.adobe.com/tiff/1.0/" xmlns:xap="http://ns.adobe.com/xap/1.0/" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xapMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" tiff:Orientation="1" tiff:YCbCrPositioning="2" tiff:XResolution="720000/10000" tiff:YResolution="720000/10000" tiff:ResolutionUnit="2" tiff:Make="SONY" tiff:Model="DSC-H1" tiff:NativeDigest="256,257,258,259,262,274,277,284,530,531,282,283,296,301,318,319,529,532,306,270,271,272,305,315,33432;858828A82A1B655EF2972EDA9A7FD1F6" xap:ModifyDate="2009-05-12T02:23+05:00" xap:CreateDate="2009-05-12T02:23+05:00" xap:MetadataDate="2009-05-12T02:23+05:00" xap:CreatorTool="Adobe Photoshop CS3 Windows" exif:ExifVersion="0220" exif:FlashpixVersion="0100" exif:ColorSpace="1" exif:CompressedBitsPerPixel="8/1" exif:PixelXDimension="1600" exif:PixelYDimension="1200" exif:DateTimeOriginal="2009-05-09T09:51:37+05:00" exif:DateTimeDigitized="2009-05-09T09:51:37+05:00" exif:ExposureTime="10/6400" exif:FNumber="40/10" exif:ExposureProgram="2" exif:ExposureBiasValue="0/10" exif:MaxApertureValue="48/16" exif:MeteringMode="5" exif:LightSource="0" exif:FocalLength="91/10" exif:FileSource="3" exif:SceneType="1" exif:CustomRendered="0" exif:ExposureMode="0" exif:WhiteBalance="0" exif:SceneCaptureType="0" exif:Contrast="0" exif:Saturation="0" exif:Sharpness="0" exif:NativeDigest="36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;750D341FDF2293D29C45BA9D3B44EE7A" dc:format="image/jpeg" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1" photoshop:History="" xapMM:InstanceID="uuid:C7A896DC713EDE11BDED93DDFB982110" xapMM:DocumentID="uuid:C6A896DC713EDE11BDED93DDFB982110" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">
        			<exif:ISOSpeedRatings>
        				<rdf:Seq>
        					<rdf:li>64</rdf:li>
        				</rdf:Seq>
        			</exif:ISOSpeedRatings>
        			<exif:Flash exif:Fired="False" exif:Return="0" exif:Mode="3" exif:Function="False" exif:RedEyeMode="False"/>
        			<dc:description>
        				<rdf:Alt>
        					<rdf:li xml:lang="x-default">
        					</rdf:li>
        				</rdf:Alt>
        			</dc:description>
        			<xapMM:DerivedFrom stRef:instanceID="uuid:65CA4E2C713EDE11BDED93DDFB982110" stRef:documentID="uuid:8AE0F35F693EDE11BDED93DDFB982110"/>
        		</rdf:Description>
        	</rdf:RDF>
        </x:xmpmeta>
        <?xpacket end="w"?>
        

        I did not try the new CCRExif v1.0.1 for handling it yet.

        • Helmut –

          Thanks for reposting the XML (v1.0.1 of my code wouldn’t have changed anything here BTW). The issue here is that my code only looks for XMP tags written out as XML nodes rather than as nodes or attributes. Can I ask what version of Photoshop is involved here?

          • Hello again and thanks a lot for your efforts, Chris!

            I’m using Photoshop CS 3 for Windows, and as far as I know the Creative Suite 4 version produces the same structure. BTW, u could check it out in the CreatorTool field of the raw XML (that I posted above).

            And I tested the new unit and found that the whole XMPBrowser demo (now) works fine. Thank you!

          • I tested the new unit and found that the whole XMPBrowser demo (now) works fine

            Unfortunately that’s not true, since I’ve now noticed sub-properties (like those on exif:Flash) can of course be written out as attributes too. In light of that, I quickly added reading support for them, but then found writing (or rather re-writing) a different matter, given the way the code is currently structured. Fixing it properly will therefore take a bit longer…

  4. Hi Chris,

    if got a jpg-file without exifs, which causes problems:

    I think, it was safed by “Picture Publisher”.

    Bertram

    • Bertram – actually, that file *does* contain Exif data. The issue is that it claims to be in big endian format when it is in fact small endian. As this is trivial to test for however, I’ve changed the code to do it — check out the revised version of the library (now at v1.0.1) I’ve just put up on CodeCentral.

Comments are closed.