New revision of CCR Exif (0.9.7)

Another revision of CCR Exif is up. Changes:

  • The focus of this release was making the code more aware of what more recent versions of Windows Explorer (amongst other Microsoft applications) do when a person uses them to edit JPEG metadata. For some background, see my post here.
  • TExifData.SaveToStream now adds or updates the Microsoft-defined OffsetSchema tag if a MakerNote is defined (see
  • Various enumerated types have acquired a xxTagMissing value to more easily determine whether the underlying tag actually exists.
  • The following symbols have been added (see the documentation for more info):
    • PreserveXMPData property to TCustomExifData; default is False.
    • IsPadding and SetAsPadding methods to TExifTag; RemovePaddingTag method to TExifSection; RemovePaddingTags method to TCustomExifData. All these concern the padding tags Microsoft applications tend to write out.
    • RemoveMetaDataFromJPEG, StreamHasExifHeader and StreamHasXMPHeader  global functions to CCR.Exif.
    • DigitalZoomRatio, FocalLengthIn35mmFilm, GainControl, ImageUniqueID, MakerNoteDataOffset, Rendered, SceneCaptureType, SubjectDistanceRange, ThumbnailOrientation, ThumbnailResolution, and WhiteBalanceMode properties to TCustomExifData — all surface standard tags I missed out previously.
    • JPEGHeader global routine to CCR.Exif.JPEGUtils as a more flexible replacement for ParseJPEGHeader; unlike the latter, it doesn’t take a callback method, being used instead with the for/in syntax.
  • Deprecated:
    • The jmExif constant in CCR.Exif.JPEGUtils — use jmApp1 instead. The reason for this change is that Exif and XMP segments share the same marker number; the value of what was jmExif, then, isn’t unique to Exif.
    • DefJFIFData global variable in CCR.Exif.JPEGUtils — this hasn’t been used internally since v0.9.5.
    • ParseJPEGHeader function in CCR.Exif.JPEGUtils — use JPEGHeader with a for/in loop instead.
    • RemoveExifDataFromJPEG functions in CCR.Exif — use the new RemoveMetaDataFromJPEG functions instead. That said, a bug in the overload that takes a stream object has been fixed.
    • WriteJPEGMarkerToStream procedures in CCR.Exif.JPEGUtils — renamed WriteJPEGSegmentToStream.
  • Custom exception classes slightly rearranged and documented.
  • JPEG Dump and Exif List demos updated slightly.

14 thoughts on “New revision of CCR Exif (0.9.7)

  1. Hi Chris,

    thanks for the nice CCR.EXIF classes, which I’m currentlyusing instead of dEXIF.
    Anyhow, I’m struggeling with a couple of properties, which are not filled with any sensible values, though they’re there in the RAW dump.
    AddValue(‘Width in pixels ‘, ExifData.ExifImageWidth);
    AddValue(‘Height in pixels’, ExifData.ExifImageHeight);
    Always return 0, though the raw dump shows:
    ttExifImageWidth word (1) 4256
    ttExifImageHeight word (1) 2832
    Additionally, how can I read out the ISOSpeedRatings?
    What is the index parameter for? In theraw dump I get
    ttISOSpeedRatings word (1) 200
    Thanks for your help,

    • OK, the first problem spotted and change (=corrected?). It seems that the ExifImageWidth/Height properties are of type word, not longword. Changing that, I get proper values:

      property ExifImageWidth: Word index ttExifImageWidth read GetDetailsWord write SetDetailsWord stored False;
      property ExifImageHeight: Word index ttExifImageHeight read GetDetailsWord write SetDetailsWord stored False;

      Width in pixels 4256
      Height in pixels 2832

    • 2nd issue solved as well, defining:

      property ISOSpeedRatings: Word index ttISOSpeedRatings read GetDetailsWord write SetDetailsWord stored False;

      I get:
      ISO speed ratings 200

      Were these definitions done deliberately that way?
      Are my modifications wrong then?

      • OK, calling the original definition with parameter index=0, I get a correct value as well.
        The EXIF standards I saw have this as a single word value, not an indexed array. What standard/version did you use and where to get it?

  2. Stefan — thanks, I’ll have a look at these things later. With respect to ExifImageHeight/Width though, for the cameras I’ve seen, longwords are always outputted, though I could easily change TExifSection.GetLongWordValue to read a word-sized value if that exists, which would be a generic solution for reading (I’d rather do things on a case-by-case basis for writing). As for the other things, my only initial thought is that you can get to the raw data easily enough as a stopgap – it’s exposed as a raw pointer on TExifTag [get the TExifTag instance by calling Find on the relevant section – ExifData[esDetails].Find(ttMyTagID, ExifTag)].

    • Right, a quick follow up:
      – The version of the code I’m working on now reads/writes word-size ExifImageXXX values if that is what exists already, which is what you want (or really want…).
      – According to the Exif spec, the ISOSpeedRatings tag can contain more than one value, which is why the property was an array in my code. That said, my work-in-progress version changes it to be a new class type that includes a Count property.
      – The Exif spec I’ve more or less worked with is v2.2, in the form of a PDF file produced by the Japan Electronic Industry Development Association.

  3. Hi Chris,

    I develop a freeware picture viewer, with some EXIF functions, like
    -convert the digital photos to smaller (for mail, web, ect.) , but keep the EXIF information,
    -restore file date/time from EXIF,
    -show EXIF information,
    -write comment within the EXIF.

    I was very happy, when I found your CCR EXIF component, because I want to make my program comment technique compatible with Windos XP , and Vista. Currently I write the comment in to the EXIF comment segment area, it’s working fine, but my comments not visible in XP and Vista own picture preview program. With Your EXIF component it’s possible, and very simple task, but I found a bug. If I insert “Title” EXIF tag in the picture, which made with Canon camera, then the Canon specific extra EXIF informations will be corrupt.

    I think You working with this much mounts , I can’t debug your program, the ccr.exif.pas is too complicated for me. But I can tell you, how to reproduce the problem.

    1. Save to a temp directory, eg. C:\temp\ img_0969.jpg. This is the file, which contain the good EXIF informations, made by Canon camera.
    2. Download my freeware program, from . No need to install, only click, and run.
    3. Then select with NGVIEW the C:\temp\ img_0969.jpg.
    4. Turn on the in the View menu, the “EXIF data” option.
    5. Find the FirmwareVersion EXIF tag in the list, you will see “Firmware version 1.00” . It’s OK, this is the starting status.
    6. Run the next little test program in Delphi: (I use Delphi 2009)

    procedure TForm1.Button1Click(Sender: TObject);
    var jpg:TJpegImageEx;

    7. Select with NGVIEW the C:\temp\test.jpg.
    8. Find the FirmwareVersion EXIF tag in the list, you will see in place of “Firmware version 1.00” corrupted characters. And more other EXIF data corrupted.

    I think my freeware is working fine, I never don’t see corrupted EXIF data with NGVIEW, before this test.
    I will be very happy, if you have time, and you correct the CCR EXIF.


    • Gabor – by the looks of it, the ‘Firmware Version 1.0’ string is located in the MakerNote tag (correct me if I’m wrong by any means). If true, then you’ve just hit upon a standard problem when it comes to editing Exif data blocks with MakerNote tags: basically, you can either have freeform editing but say goodbye to guaranteed preservation of MakerNote integrity, or guarantee preserving MakerNote integrity but say goodbye to freeform editing. Googling will come up with the details — just search for MakerNote+corruption — but basically, the problem results from the combination of three things in particular:

      (1) Being based on the TIFF standard, Exif tag headers should be sorted by their tag IDs when saved. When declaring a new title tag, then, you can’t just add its declaration to the end of the Exif block – it has to go in the middle.

      (2) The format of MakerNote tags is proprietary, differing from manufacturer to manufacturer and even camera to camera from the same manufacturer.

      (3) MakerNote tags typically use internal offsets from the start of the Exif block, not the MakerNote tag – note how this interacts with (1).

      You can then add to these things the fact that my TExifData class, for reasons of simplicitly, writes out tag data afresh when saving, potentially moving the MakerNote block even when nothing has been changed. By comparison, the TExifDataPatcher class does not move anything around, but it doesn’t allow adding tags either because of (1).

      Nonetheless, if you have any ideas for a workaround, I’m open to suggestions. The problem with getting too clever, however, is that it will only heavily complicate the saving code, making it more prone to bugs…

      Lastly, and just out of interest, but what happens if you add a comment using Windows Explorer in Vista? Does the corruption happen then too?

    • Hi Jordi –

      Having looked into it, I’ve found the image in question contains a slightly corrupted Exif block – specifically, the ‘main IFD’ (= the ‘general section’ in my code’s terminology) ends with a link to another IFD/tag section that doesn’t actually exist, since it points to somewhere past the Exif block and into the JPEG image data. (By the by, the IFDs/sub-IFDs — the ‘sections’ — are in a funny order as well, albeit not an illegal one. Has the image’s metadata been edited since it came out the camera?)

      Anyhow, if you want to ‘fix’ my code, open up CCR.Exif.pas, go to the LoadTiffInfo global procedure, find your way to the ReadDirectory nested procedure, and wrap its call to LoadTiffDirectory in a try/except block like the following:

        Info.Directories[DirectoryCount] := LoadTiffDirectory(Info, Offset);
        on EReadError do

      Since EReadError is usually only raised by TStream.ReadBuffer when its own call to TStream.Read hasn’t got all the requested bytes, this is pretty safe to do. Also, remember that by default, even handled exceptions still have the IDE show an error dialog when the app is run through the debugger — add EReadError to the debugger’s ignored list to stop that.

      • Well, actually I don’t remember where the image came from. Maybe it has been edited. Anyway, your solution is good for me. Thanks a lot!

  4. Hi Chris, I’m now using the CCR.EXIF class without any problems. Right now I’m desperately trying to decode a NIKON makernote section. Would you be able to give some hints because it doesn’t work whatever header length I configure via the function GetIFDInfo that I’ve copied from the Panasonic example. The Nikon header has the length of 10 octets (Nikon type 3 header) and an 8 octet TIFF header follows. Here I’m stuck, because I never get the raw data decoded. Thanks, Stefan
    PS: Would communication via email suit you?

    • Stefan – sorry, but you would have to pay me for doing that – maker note internals can be complex.

Comments are closed.