How to preserve EXIF data when rotating/resizing JPEG images
.Net allows you to do some amazing things with graphics using GDI+. Its handling of image meta data leaves something to be desired though. It’s like they started to do something, but then abandoned it mid-way. In working on the Speerio File Manager Pro (for ASP.Net and DotNetNuke) product’s imaging functions, after considerable research, I decided that Omar Shahine’s PhotoLibrary was the best fit. It has some excellent functions especially in the EXIF area.
After playing with it for a while, I was disappointed to find that it, like just about every other EXIF-handler Google can find, does not preserve EXIF data on resize and rotate operations. I saw some commercial .Net components did it, so I knew could be done. But hours of searching later, all I knew for certain is that GDI+ can read EXIF data, but cannot write it to a new image. Bummer!
Not being one to give up easily, I experimenting a bit, and am pleased to have come up with a solution. Here is my JPEG resizing code which preserves EXIF data:
public static void ResizeJpeg(string filePath, int width, int height)
{
try
{
System.Drawing.Image imgPhoto = System.Drawing.Image.FromFile(filePath);
int sourceWidth = imgPhoto.Width;
int sourceHeight = imgPhoto.Height;
if (height == -1)
height = (int) ((float) sourceHeight * ((float) width/ (float) sourceWidth));
if (width == -1)
width = (int) ((float) sourceWidth * ((float) height/ (float) sourceHeight));
int sourceX = 0;
int sourceY = 0;
int destX = 0;
int destY = 0;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)width/(float)sourceWidth);
nPercentH = ((float)height/(float)sourceHeight);
//if we have to pad the height pad both the top and the bottom
//with the difference between the scaled height and the desired height
if(nPercentH < nPercentW)
{
nPercent = nPercentH;
destX = (int)((width – (sourceWidth * nPercent))/2);
}
else
{
nPercent = nPercentW;
destY = (int)((height – (sourceHeight * nPercent))/2);
}
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap bmPhoto = new Bitmap(imgPhoto, width, height);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(imgPhoto,
new Rectangle(destX,destY,destWidth,destHeight),
new Rectangle(sourceX,sourceY,sourceWidth,sourceHeight),
GraphicsUnit.Pixel);
grPhoto.Dispose();
try
{
foreach (PropertyItem propertyItem in imgPhoto.PropertyItems)
{
try
{
bmPhoto.SetPropertyItem(propertyItem);
}
catch{}
}
}
catch{}
System.Drawing.Imaging.Encoder enc = System.Drawing.Imaging.Encoder.Quality;
System.Drawing.Imaging.EncoderParameters encParms = new EncoderParameters(1);
System.Drawing.Imaging.EncoderParameter encParm = new EncoderParameter(enc,100L);
encParms.Param[0] = encParm;
ImageCodecInfo[] codecInfo = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo codecInfoJpeg = codecInfo[1];
bmPhoto.Save(filePath+“.tmp”,codecInfoJpeg, encParms);
bmPhoto.Dispose();
imgPhoto.Dispose();
FileInfo fileInfo = new FileInfo(filePath);
fileInfo.Delete();
fileInfo = new FileInfo(filePath+“.tmp”);
fileInfo.MoveTo(filePath);
}
catch{}
}