Chapter 7: Metadata Extraction

Contents

7.1 Metadata Overview

Most digital cameras nowadays embed various pieces of information into the JPEG images they generate, including the current date/time, shooting conditions (e.g. whether a flash was used), camera settings (shutter, aperture, focal length), etc. Adobe Photoshop, the leading image editing package, is also capable of embedding various pieces of metadata into the JPEGs it produces, such as an author, title, copyright notice, etc.

The format for camera-embedded metadata is called EXIF, which stands for Exchangeable Image File Format. The format used by Photoshop is described by the International Press Telecommunications Council (www.iptc.org) and usually referred to as IPTC.

AspJpeg.NET is capable of extracting EXIF and IPTC metadata from JPEG images via the OpenInfo method. This method can open a source image from a disk file or byte array.

UPDATE: As of Version 2.9.0.12503, AspJpeg.NET is capable of extracting metadata from PNG images as well. Metadata preservation and editing (described below) is still limited to JPEG files, however.

The OpenInfo method returns an instance of the JpegInfo object which is an IEnumerable-based collection of JpegInfoItem objects, each representing a separate EXIF or IPTC field. The JpegInfoItem object supports the following properties: Name (string), Value (object), Tag (int), and Description (string).

The following code sample extracts and displays all metadata fields from a JPEG image:

<%@ Page Language="C#" debug="true" %>

<%@ Import Namespace="Persits.Jpeg"%>
<html>
<head>
<title>AspJpeg.NET User Manual Chapter 7 - Metadata Extraction</title>
<script runat="server" languge="C#">

void Page_Load( Object Source, EventArgs E)
{
  // Create instance of JpegManager
  JpegManager objJpeg = new JpegManager();

  string strPath = Server.MapPath("../images/photo.jpg");

  // Obtain metadata from image
  JpegInfo objInfo = objJpeg.OpenInfo( strPath );

  foreach( JpegInfoItem objItem in objInfo)
  {
    TableRow objRow = new TableRow();

    TableCell objCell1 = new TableCell();
    objCell1.Controls.Add( new LiteralControl( objItem.Name ) );

    TableCell objCell2 = new TableCell();
    objCell2.Controls.Add(new LiteralControl(objItem.Description));

    TableCell objCell3 = new TableCell();
    objCell3.Controls.Add(
    new LiteralControl( objItem.Value.ToString() ) );

    objRow.Cells.Add( objCell1 );
    objRow.Cells.Add( objCell2 );
    objRow.Cells.Add( objCell3 );
    objTable.Rows.Add( objRow );
  }
}
</script>
</head>

<form runat="server">
<asp:Table cellspacing="0" runat="server" ID="objTable" BORDER="1"/>
</form>
</html>
<%@ Page Language="vb" debug="true" %>

<%@ Import Namespace="Persits.Jpeg"%>

<html>
<head>
<title>AspJpeg.NET User Manual Chapter 7 - Metadata Extraction</title>
<script runat="server" languge="vb">

Sub Page_Load(Source As Object, E As EventArgs)
  ' Create instance of JpegManager
  Dim objJpeg As JpegManager = New JpegManager()

  Dim strPath As String = Server.MapPath("../images/photo.jpg")

  ' Obtain metadata from image
  Dim objInfo As JpegInfo = objJpeg.OpenInfo(strPath)

  For Each objItem As JpegInfoItem In objInfo
    Dim objRow As TableRow = New TableRow()

    Dim objCell1 As TableCell = New TableCell()
    objCell1.Controls.Add(New LiteralControl(objItem.Name))

    Dim objCell2 As TableCell = New TableCell()
    objCell2.Controls.Add(New LiteralControl(objItem.Description))

    Dim objCell3 As TableCell = New TableCell()
    objCell3.Controls.Add(
    New LiteralControl(objItem.Value.ToString()))

    objRow.Cells.Add(objCell1)
    objRow.Cells.Add(objCell2)
    objRow.Cells.Add(objCell3)
    objTable.Rows.Add(objRow)
  Next
End Sub
</script>
</head>

<form runat="server">
<asp:Table cellspacing="0" runat="server" ID="objTable" BORDER="1"/>
</form>
</html>

Click the links below to run this code sample:

To obtain the value for a specific metadata field, such as a camera make, you should use JpegInfo's this[ ] indexer property which accepts this field's name as the index. If the specified field name is not found, this property returns null. The following code snippet obtains camera make information and assigns it to a string variable:

string s = (string)objInfo["Make"].Value;
Dim s As String = objInfo("Make").Value

All valid EXIF and IPTC field names are listed below.

7.2 EXIF Item List

The following table lists all valid EXIF field names and descriptions. Most real-world images only contain a small subset of these items, if any. Note that in addition to general image and camera setting information, EXIF also provides for GPS-related data.

Name
Description
NewSubfileType
Subfile type
ImageWidth
Image width
ImageLength
Image height
BitsPerSample
Number of bits per component
Compression
Compression scheme
PhotometricInterpretation
Pixel composition
ImageDescription
Image title
Make
Manufacturer of image input equipment
Model
Model of image input equipment
StripOffsets
Image data location
Orientation
Orientation of image
SamplesPerPixel
Number of components
RowsPerStrip
Number of rows per strip
StripByteCounts
Bytes per compressed strip
XResolution
Image resolution in width direction
YResolution
Image resolution in height direction
PlanarConfiguration
Image data arrangement
ResolutionUnit
Unit of X and Y resolution
TransferFunction
Transfer function
Software
Software used
DateTime
File change date and time
Artist
Person who created the image
HostComputer
The computer and/or operating system in use
WhitePoint
White point chromaticity
PrimaryChromaticities
Chromaticities of primaries
JPEGInterchangeFormat
Offset to JPEG SOI
JPEGInterchangeFormatLength
Bytes of JPEG data
YCbCrCoefficients
Color space transformation matrix coefficients
YCbCrSubSampling
Subsampling ratio of Y to C
YCbCrPositioning
Y and C positioning
ReferenceBlackWhite
Pair of black and white reference values
Copyright
Copyright holder
ExifTag
Exif IFD Pointer
GPSTag
GPSInfo IFD Pointer
ExposureTime
Exposure time
FNumber
F number
ExposureProgram
Exposure program
SpectralSensitivity
Spectral sensitivity
ISOSpeedRatings
ISO speed ratings
OECF
Optoelectric coefficient
ExifVersion
Exif Version
DateTimeOriginal
Date and time original image was generated
DateTimeDigitized
Date and time image was made digital data
ComponentsConfiguration
Meaning of each component
CompressedBitsPerPixel
Image compression mode
ShutterSpeedValue
Shutter speed
ApertureValue
Aperture
BrightnessValue
Brightness
ExposureBiasValue
Exposure bias
MaxApertureValue
Maximum lens aperture
SubjectDistance
Subject distance
MeteringMode
Metering mode
LightSource
Light source
Flash
Flash
FocalLength
Lens focal length
SubjectArea
Subject area
MakerNote
Manufacturer notes
UserComment
User comments
SubSecTime
DateTime subseconds
SubSecTimeOriginal
DateTimeOriginal subseconds
SubSecTimeDigitized
DateTimeDigitized subseconds
FlashpixVersion
Supported Flashpix version
ColorSpace
Color space information
PixelXDimension
Valid image width
PixelYDimension
Valid image height
RelatedSoundFile
Related audio file
InteroperabilityTag
Interoperability IFD Pointer
FlashEnergy
Flash energy
SpatialFrequencyResponse
Spatial frequency response
FocalPlaneXResolution
Focal plane X resolution
FocalPlaneYResolution
Focal plane Y resolution
FocalPlaneResolutionUnit
Focal plane resolution unit
SubjectLocation
Subject location
ExposureIndex
Exposure index
SensingMethod
Sensing method
FileSource
File source
SceneType
Scene type
CFAPattern
CFA pattern
CustomRendered
Custom image processing
ExposureMode
Exposure mode
WhiteBalance
White balance
DigitalZoomRatio
Digital zoom ratio
FocalLengthIn35mmFilm
Focal length in 35 mm film
SceneCaptureType
Scene capture type
GainControl
Gain control
Contrast
Contrast
Saturation
Saturation
Sharpness
Sharpness
DeviceSettingDescription
Device settings description
SubjectDistanceRange
Subject distance range
ImageUniqueID
Unique image ID
RelatedImageFileFormat
File format of image file
RelatedImageWidth
Image width
RelatedImageLength
Image height
GPSVersionID
GPS tag version
GPSLatitudeRef
North or South Latitude
GPSLatitude
Latitude
GPSLongitudeRef
East or West Longitude
GPSLongitude
Longitude
GPSAltitudeRef
Altitude reference
GPSAltitude
Altitude
GPSTimeStamp
GPS time (atomic clock)
GPSSatellites
GPS satellites used for measurement
GPSStatus
GPS receiver status
GPSMeasureMode
GPS measurement mode
GPSDOP
Measurement precision
GPSSpeedRef
Speed unit
GPSSpeed
Speed of GPS receiver
GPSTrackRef
Reference for direction of movement
GPSTrack
Direction of movement
GPSImgDirectionRef
Reference for direction of image
GPSImgDirection
Direction of image
GPSMapDatum
Geodetic survey data used
GPSDestLatitudeRef
Reference for latitude of destination
GPSDestLatitude
Latitude of destination
GPSDestLongitudeRef
Reference for longitude of destination
GPSDestLongitude
Longitude of destination
GPSDestBearingRef
Reference for bearing of destination
GPSDestBearing
Bearing of destination
GPSDestDistanceRef
Reference for distance to destination
GPSDestDistance
Distance to destination
GPSProcessingMethod
Name of GPS processing method
GPSAreaInformation
Name of GPS area
GPSDateStamp
GPS date
GPSDifferential
GPS differential correction
WinTitle
Windows Explorer Title
WinAuthor
Windows Explorer Author
WinSubject
Windows Explorer Subject
WinComments
Windows Explorer Comments
WinKeywords
Windows Explorer Keywords
LensSpecification
Focal and aperture ranges
PanasonicTitle
Panasonic Title
PanasonicTitle2
Panasonic Title 2
PrintIM
Print Image Matching
CameraOwnerName
Owner of the camera
BodySerialNumber
Camera body serial number
LensMake
Lens manufacturer
LensModel
Lens model
Gamma
Gamma coefficient value
Rating
Rating tag used by Windows
TimeZoneOffset
Time zone of the camera clock relative to GMT
ExposureIndex
Camera exposure index setting

In addition to the pre-defined fields, some images may contain custom fields not described in the EXIF specifications. Custom field names have the form Tag#nnn where nnn is a decimal number, e.g. Tag#37388.

7.3 IPTC Item List

The following IPTC fields are supported by AspJpeg:

IptcByline
IptcBylineTitle
IptcCredits
IptcSource
IptcObjectName
IptcDateCreated
IptcCity
IptcState
IptcCountry
IptcOriginalTransmissionReference
IptcCopyrightNotice
IptcCaption
IptcCaptionWriter
IptcHeadline
IptcSpecialInstructions
IptcCategory
IptcSupplementalCategories
IptcUrgency
IptcKeywords
IptcTimeCreated
IptcDigitalCreationDate
IptcDigitalCreationTime
IptcOriginatingProgram
IptcProgramVersion
IptcUno
IptcEditStatus
IptcFixtureIdentifier
IptcReleaseDate
IptcReleaseTime
IptcObjectCycle
IptcImageNotes
IptcTextSaved
IptcCustom1
IptcCustom2
IptcCustom3
IptcCustom4
IptcCustom5
IptcCustom6
IptcCustom7
IptcCustom8
IptcCustom9
IptcCustom10
IptcCustom11
IptcCustom12
IptcCustom13
IptcCustom14
IptcCustom15
IptcCustom16
IptcCustom17
IptcCustom18
IptcCustom19
IptcCustom20
IptcImageURL
IptcCopyrighted
IptcXMP

Note: The IptcKeywords field may appear multiple times in the Info collection. IptcCopyrighted was introduced in version 2.0 of AspJpeg and is a True/False value. IptcXMP was introduced in version 2.7.0.5 and is described below.

7.4 TIFF Support

If the image being opened is a TIFF, the JpegInfo collection will contain a special field by the name of "TiffPages" which contains the number of pages (images) in that TIFF file. This value can be used to iterate through all images in a multi-page TIFF. To open a TIFF page with an index other than 1, the property TiffIndex should be used. The index is 1-based. The following code snippet saves each page of a TIFF image as a separate JPEG image.

JpegManager objJpeg = new JpegManager();
JpegInfo objInfo = objJpeg.OpenInfo(@"c:\path\Multipage.tif");
JpegInfoItem item = objInfo["TiffPages"];
if( item != null )
{
   int num = (int)item.Value;
   for( int i = 1; i <= num; i++)
   {
      objJpeg.TiffIndex = i; // One-based
      JpegImage objImage = objJpeg.OpenImage(@"c:\path\Multipage.tif");
      objImage.Save(@"c:\myprojects\aspdf\asp\files\out_" + i + ".jpg");
   }
}

7.5 Metadata Preservation

AspJpeg.NET is capable of preserving the EXIF and IPTC metadata of the original image when the thumbnail is created. To enable this functionality, you need to set the property PreserveMetadata to true before opening the image, as follows:

JpegManager objJpeg = new JpegManager();
objJpeg.PreserveMetadata = true;
JpegImage objImage = objJpeg.OpenImage(Path);
...

AspJpeg.NET is also capable of preserving the ICC profile of the original image. For more information on the International Color Consortium and ICC profiles, visit www.color.org.

JpegManager objJpeg = new JpegManager();
objJpeg.PreserveICCProfile = true;
JpegImage objImage = objJpeg.OpenImage(Path);
...

The PreserveMetadata and PreserveICCProfile properties must be set to true before calling OpenImage for them to take effect.

Note that preserving metadata and ICC profiles in a thumbnail can considerably increase its file size.

7.6 Metadata Editing

AspJpeg.NET can be used to add or replace IPTC values in an image via the method AddMetadataItem. This method accepts two arguments: an IPTC tag from the table shown in Section 7.3 above, and a string value to be inserted under that tag. This method can be called multiple times, if necessary. To use AddMetadataItem, the property PreserveMetadata described in the previous section must be set to true before the image is opened:

JpegManager objJpeg = new JpegManager();
objJpeg.PreserveMetadata = true;
JpegImage objImage = objJpeg.OpenImage(Path);

objImage.AddMetadataItem( "IptcCaption", "New York City Skyline" );
objImage.AddMetadataItem( "IptcCaptionWriter", "John Smith" );
...
objImage.Save( ... );

All IPTC fields are strings with one exception: the IptcCopyrighted tag is an on/off flag. Use the string "True" to set this flag or "False" to clear it.

You can add multiple IptcKeywords entries to an image by using the special tag "IptcKeywordsAdd". Unlike "IptcKeywords", it adds a new keyword entry instead of replacing an existing one. For example, the code snippet

objImage.AddMetadataItem( "IptcKeywordsAdd", "Art" );
objImage.AddMetadataItem( "IptcKeywordsAdd", "Portrait" );

adds two keyword entries to an image, "Art" and "Portrait", whereas the snippet

objImage.AddMetadataItem( "IptcKeywords", "Art" );
objImage.AddMetadataItem( "IptcKeywords", "Portrait" );

only adds a single entry, "Portrait" ("Art" is overwritten.)

There is another special tag, "IptcKeywordsRemove", which removes all keyword entries. The IptcKeywordsRemove tag is to be used as follows:

objImage.AddMetadataItem( "IptcKeywordsRemove", "" );

Most EXIF tags can be modified as well. See Section 7.8 below for more information.

7.7 Adobe XMP Support

In addition to EXIF and IPTC information described above, JPEG images often contain metadata based on Adobe's Extensible Metadata Platform (XMP) specifications. XMP metadata uses the XML format.

Typical XMP data embedded in a JPEG image may look as follows (the right side of the XML code is truncated for brevity):

AspJpeg.NET is capable of retrieving and setting the XMP metadata in its entirety as if it were a regular IPTC tag, via the JpegInfo collection and AddMetadataItem method. The name of the tag is "IptcXMP". It is the application developer's responsibility to perform XML parsing to retrieve and set various components of the XMP metadata. XML processing can be performed quite easily using .NET's built-in XmlDocument object.

The following code sample retrieves XMP data from an image, changes the value of the dc:creator item and plugs the modified XML string back into the image.

<%@ Page Language="C#" debug="true" %>

<%@ Import Namespace="System.IO"%>
<%@ Import Namespace="System.Xml"%>
<%@ Import Namespace="Persits.Jpeg"%>

<html>
<head>
<script runat="server" languge="C#">

void Page_Load( Object Source, EventArgs E)
{
  JpegManager objJpeg = new JpegManager();

  string strPath = @"c:\path\image.jpg";

  JpegInfo objInfo = objJpeg.OpenInfo(strPath);

  XmlDocument XmlDom = new XmlDocument();
  XmlDom.LoadXml(objInfo["IptcXMP"].Value.ToString());

  XmlNamespaceManager objMgr = new XmlNamespaceManager(XmlDom.NameTable);
  objMgr.AddNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
  objMgr.AddNamespace("dc", "http://purl.org/dc/elements/1.1/");
  objMgr.AddNamespace("x", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");

  XmlNode node = XmlDom.DocumentElement.SelectSingleNode(
    "rdf:RDF/rdf:Description/dc:creator/rdf:Seq/rdf:li", objMgr);
  node.InnerText = "John Smith";

  objJpeg.PreserveMetadata = true;
  JpegImage objImage = objJpeg.OpenImage(strPath);
  objImage.AddMetadataItem("IptcXMP", XmlDom.InnerXml);

  objImage.SaveUnique(@"c:\path\out.jpg");
}
</script>
</head>

<form runat="server">
<asp:image runat="server" id="OutputImage"/>
</form>
</html>
<%@ Page Language="vb" debug="true" %>

<%@ Import Namespace="System.IO"%>
<%@ Import Namespace="System.Xml"%>
<%@ Import Namespace="Persits.Jpeg"%>

<html>
<head>
<script runat="server" languge="vb">

Sub Page_Load(Source As Object, E As EventArgs)
  Dim objJpeg As JpegManager = New JpegManager()
  Dim strPath As String = "c:\myprojects\aspdf\asp\files\aviv.jpg"
  Dim objInfo As JpegInfo = objJpeg.OpenInfo(strPath)

  Dim XmlDom As XmlDocument = new XmlDocument()
  XmlDom.LoadXml(objInfo("IptcXMP").Value.ToString())

  Dim objMgr As XmlNamespaceManager =
    new XmlNamespaceManager(XmlDom.NameTable)
  objMgr.AddNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
  objMgr.AddNamespace("dc", "http://purl.org/dc/elements/1.1/")
  objMgr.AddNamespace("x", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")

  Dim node As XmlNode = XmlDom.DocumentElement.SelectSingleNode(
    "rdf:RDF/rdf:Description/dc:creator/rdf:Seq/rdf:li", objMgr)
  node.InnerText = "John Smith"

  objJpeg.PreserveMetadata = true
  Dim objImage As JpegImage = objJpeg.OpenImage(strPath)
  objImage.AddMetadataItem("IptcXMP", XmlDom.InnerXml)

  objImage.SaveUnique("c:\path\out.jpg")
End Sub
</script>
</head>

<form runat="server">
<asp:image runat="server" id="OutputImage"/>
</form>
</html>

The following code snippet adds a new keyword to the dc:subject list:

...
XmlNode XmlNode = XmlDom.DocumentElement.SelectSingleNode( "rdf:RDF/rdf:Description/dc:subject/rdf:Bag", objMgr);
XmlNode NewNode = XmlDom.CreateNode( XmlNodeType.Element, "rdf:li", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
NewNode.InnerText = "John Smith";
XmlNode.AppendChild( NewNode );
...
..
Dim XmlNode As XmlNode = XmlDom.DocumentElement.SelectSingleNode( "rdf:RDF/rdf:Description/dc:subject/rdf:Bag", objMgr)
Dim NewNode As XmlNode = XmlDom.CreateNode( XmlNodeType.Element, "rdf:li", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
NewNode.InnerText = "John Smith"
XmlNode.AppendChild( NewNode )
...

7.8 EXIF Editing

7.8.1 ChangeExifItem Method

AspJpeg.NET is capable of setting or modifying most EXIF fields in an image via the method ChangeExifItem. This method expects two arguments: the EXIF field name to modify (see Section 7.3 for the list of valid names) and the double-precision or string value to set the field to. The 2rd argument must be a string if the EXIF field specified by the first argument is of the EXIF type ASCII or UNDEFINED. To be able to use the ChangeExifItem method, the property PreserveMetadata must be set to true before opening the image.

The image does not need to have existing EXIF information for the ChangeExifItem method to work. If the image has no EXIF data block to begin with, the method creates it. Note that only JPEG format, not PNG, GIF or BMP, can contain EXIF data.

The GPS-related coordinate and timestamp values (GPSLatitude, GPSLongitude, GPSDestLatitude, GPSDestLongitude, and GPSTimeStamp) must be specified in a decimal form. Positive values correspond to the North latitudes and East longitudes, and negative values to the South latitudes and West longitudes. The latitude/longitude reference fields such as GPSLongitudeRef or GPSDestLongitudeRef should not be set directly.

The following code snippet sets the Artist and Software information for the image to arbitrary text values. It also sets the GPS coordinates of the image to the center of Canberra, Australia, GPS timestamp to 14:30 and GPS date stamp, a text field, to "2016:08:22".

JpegManager objJpeg = new JpegManager();
objJpeg.PreserveMetadata = true;
JpegImage objImage = objJpeg.OpenImage( strPath );

objImage.ChangeExifItem( "Artist", "John Smith" );
objImage.ChangeExifItem( "Software", "Persits Software, Inc." );

objImage.ChangeExifItem( "GPSLatitude", -35.2809d );
objImage.ChangeExifItem( "GPSLongitude", 149.13d );
objImage.ChangeExifItem( "GPSTimeStamp", 14.5d );
objImage.ChangeExifItem( "GPSDateStamp", "2016:08:22" );

objImage.SaveUnique( @"c:\path\out.jpg" );
Dim objJpeg As JpegManager = New JpegManager()
objJpeg.PreserveMetadata = True
Dim objImage As JpegImage = objJpeg.OpenImage( strPath )

objImage.ChangeExifItem( "Artist", "John Smith" )
objImage.ChangeExifItem( "Software", "Persits Software, Inc." )

objImage.ChangeExifItem( "GPSLatitude", -35.2809d )
objImage.ChangeExifItem( "GPSLongitude", 149.13d )
objImage.ChangeExifItem( "GPSTimeStamp", 14.5d )
objImage.ChangeExifItem( "GPSDateStamp", "2016:08:22" )

objImage.SaveUnique( "c:\path\out.jpg" )

EXIF editing is demonstated by our Live Demo #9.

7.8.2 ApplyOrientation Method

Most digital images store their pixel information in "standard" orientation: the 0th row of pixels corresponds to the top of the image, and the 0th column of pixels to the left side of the image. However, when the camera or mobile device taking the picture is turned sideways or upside-down, the resultant image may have its pixels stored differently (for example, its 0th row of pixels corresponds to the right side of the image and the 0th column to the top side.)

When that happens, the camera stores the camera orientation information in the EXIF "Orientation" tag. When the image is in standard orientation, this EXIF value is 1. The values of 2 to 8 correspond to various other possible orientations as summarized by the following table:

Value
0th Row
0th Column
1
top
left side
2
top
right side
3
bottom
right side
4
bottom
left side
5
left side
top
6
right side
top
7
right side
bottom
8
left side
bottom

The problem with images with the "Orientation" tag set to anything other than 1 is that some image viewers and browsers take this tag into account when displaying the image (such as Google Chrome), while others ignore it altogether. As a result, the same image may come up correctly in some viewers while appear rotated and/or flipped in others.

AspJpeg.NET offers the method JpegImage.ApplyOrientation which rotates and/or flips the image according to its Orientation tag, and then sets this tag to 1. As a result, the image is always displayed consistently across all viewers and browsers. To be able to use the ApplyOrientation method, the property PreserveMetadata must be set to true before opening the image. This method expects no arguments, and its return value is the original Orientation value of the image. If the image contains no Orientation tag, the method does nothing.

JpegManager objJpeg = new JpegManager();
objJpeg.PreserveMetadata = true;
JpegImage objImage = objJpeg.OpenImage( strPath );
objImage.ApplyOrientation();
objImage.SaveUnique( @"c:\path\out.jpg" );
Dim objJpeg As JpegManager = New JpegManager()
objJpeg.PreserveMetadata = True
Dim objImage As JpegImage = objJpeg.OpenImage( strPath )
objImage.ApplyOrientation()
objImage.SaveUnique( "c:\path\out.jpg" )