Friday, February 15, 2013

EXIF alteration madness

Lately for a project of mine I had to alter the EXIF metadata of a JPEG file using Java. After googleing and improvising, I finally wrote a code sample that demonstrates how to change several metadata of a JPEG file, using the Sanselan library from Apache; you can download the jar file and source here. The code below changes Camera Model, Camera Maker and X Resolution, which are included in the Root IFD0 of a JPEG image as well as Digital Zoom Ratio and Image Width, stored in the EXIF Sub IFD. 

Notice: You will be able to view the changes from the image properties of your operating system, except from the Image Width. Use this code to view the raw image metadata.

References: 


import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.Sanselan;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.formats.jpeg.JpegImageMetadata;
import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter;
import org.apache.sanselan.formats.tiff.TiffImageMetadata;
import org.apache.sanselan.formats.tiff.constants.ExifTagConstants;
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
import org.apache.sanselan.formats.tiff.constants.TiffFieldTypeConstants;
import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
import org.apache.sanselan.formats.tiff.write.TiffOutputField;
import org.apache.sanselan.formats.tiff.write.TiffOutputSet;

public class TestWriteExif {

  /**
  * @param args
  */
  public static void main(String[] args) {
  File file = new File("test.jpg"); //Change filename here
  File dst = new File("OutputImage.jpg");  
  changeMetadata(file, dst);
  System.out.println("DONE");

  }
 
  public static void changeMetadata(File file, File output) {
        IImageMetadata metadata = null;
        JpegImageMetadata jpegMetadata = null;
        TiffImageMetadata exif = null;
        OutputStream os = null;
        TiffOutputSet outputSet = new TiffOutputSet();

        // establish metadata
        try {
            metadata = Sanselan.getMetadata(file);
        } catch (ImageReadException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // establish jpegMedatadata
        if (metadata != null) {
            jpegMetadata = (JpegImageMetadata) metadata;
        }

        // establish exif
        if (jpegMetadata != null) {
            exif = jpegMetadata.getExif();
        }

        // establish outputSet
        if (exif != null) {
            try {
                outputSet = exif.getOutputSet();
            } catch (ImageWriteException e) {
                e.printStackTrace();
            }
        }

        if (outputSet != null) {      
         //CAMERA MODEL CHANGE

            //check if field already EXISTS - if so remove         
            TiffOutputField cameraModelPre = outputSet.findField(TiffConstants.EXIF_TAG_MODEL);
            if (cameraModelPre != null) {
                outputSet.removeField(TiffConstants.EXIF_TAG_MODEL);
            }         
            
            //add field 
            try {               
                
             String model = "Canon IXUS 115HS";
                TiffOutputField cameraModel = new TiffOutputField(
                           ExifTagConstants.EXIF_TAG_MODEL, 
                           TiffFieldTypeConstants.FIELD_TYPE_ASCII, 
                           model.length(), 
                           model.getBytes());
                TiffOutputDirectory exifDirectory = outputSet.getOrCreateRootDirectory(); //Since camera model is included in the Root directory
                                    //we use the appropriate function
                exifDirectory.add(cameraModel);
            } catch (ImageWriteException e) {

                e.printStackTrace();
            }
                
     
            //CAMERA MAKER CHANGE

            TiffOutputField cameraMakerPre = outputSet.findField(TiffConstants.EXIF_TAG_MAKE);
            if (cameraMakerPre != null) {
                outputSet.removeField(TiffConstants.EXIF_TAG_MAKE);
            }         
            
            // add field 
            try {               
                
             String model = "HP";
                TiffOutputField cameraMaker = new TiffOutputField(
                           ExifTagConstants.EXIF_TAG_MAKE, 
                           TiffFieldTypeConstants.FIELD_TYPE_ASCII, 
                           model.length(), 
                           model.getBytes());
                TiffOutputDirectory exifDirectory = outputSet.getOrCreateRootDirectory(); //Since camera model is included in the Root directory
                                    //we use the appropriate function
                exifDirectory.add(cameraMaker);
            } catch (ImageWriteException e) {

                e.printStackTrace();
            }
          
             // XRESOLUTION CHANGE - Using different method         
          
            TiffOutputField xresPre = outputSet.findField(TiffConstants.EXIF_TAG_XRESOLUTION);
            if (xresPre != null) {
                outputSet.removeField(TiffConstants.EXIF_TAG_XRESOLUTION);
            }         
            
            // add field 
            try {               
                         
             TiffOutputField xres = TiffOutputField.create(
               ExifTagConstants.EXIF_TAG_XRESOLUTION, 
               outputSet.byteOrder, 
               new Double(74));
             
                TiffOutputDirectory exifDirectory = outputSet.getOrCreateRootDirectory(); //Since camera XRes is included in the Root directory
                                    //we use the appropriate function
                exifDirectory.add(xres);
            } catch (ImageWriteException e) {

                e.printStackTrace();
            }

            // DIGITAL ZOOM RATIO - Stored in EXIF directory
            
            TiffOutputField zoomPre = outputSet.findField(TiffConstants.EXIF_TAG_DIGITAL_ZOOM_RATIO);
            if (zoomPre != null) {
                outputSet.removeField(TiffConstants.EXIF_TAG_DIGITAL_ZOOM_RATIO);
            }         
            
            // add field 
            try {               
                         
             TiffOutputField zoom = TiffOutputField.create(
               TiffConstants.EXIF_TAG_DIGITAL_ZOOM_RATIO, 
               outputSet.byteOrder, 
               new Double(61146.071));
             
                TiffOutputDirectory exifDirectory = outputSet.getOrCreateExifDirectory(); //Since camera XRes is included in the Root directory
                                    //we use the appropriate function
                exifDirectory.add(zoom);
            } catch (ImageWriteException e) {

                e.printStackTrace();
            }
            
           
            //EXIF IMAGE WIDTH - Stored in EXIF directory
            
            
            TiffOutputField imageWid = outputSet.findField(TiffConstants.EXIF_TAG_EXIF_IMAGE_WIDTH);
            if (imageWid != null) {
                outputSet.removeField(TiffConstants.EXIF_TAG_EXIF_IMAGE_WIDTH);
            }         
            
            // add field 
           try {               
                         
             TiffOutputField xres = TiffOutputField.create(
               TiffConstants.EXIF_TAG_EXIF_IMAGE_WIDTH, 
               outputSet.byteOrder, 
               new Integer(2590));
             
                TiffOutputDirectory exifDirectory = outputSet.getOrCreateExifDirectory(); //Since camera XRes is included in the Root directory
                                    //we use the appropriate function
                exifDirectory.add(xres);
            } catch (ImageWriteException e) {

                e.printStackTrace();
            }
            
        }       
        
        // create stream using temp file for dst
        try {
            os = new FileOutputStream(output);
            os = new BufferedOutputStream(os);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // write/update EXIF metadata to output stream
        try {
            new ExifRewriter().updateExifMetadataLossless(file, os, outputSet);
        } catch (ImageReadException e) {
            e.printStackTrace();
        } catch (ImageWriteException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                }
            }
        } 
    }
}