//
// $Id: PictureFileData.java 287 2007-06-17 09:07:04 +0000 (dim., 17 juin 2007)
// felfert $
//
// jupload - A file upload applet.
// Copyright 2007 The JUpload Team
//
// Created: 2006-05-09
// Creator: etienne_sf
// Last modified: $Date: 2008-06-05 05:25:47 -0700 (Thu, 05 Jun 2008) $
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version. This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details. You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation, Inc.,
// 675 Mass Ave, Cambridge, MA 02139, USA.
package wjhk.jupload2.filedata;
import java.awt.Canvas;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.FileImageInputStream;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import wjhk.jupload2.exception.JUploadException;
import wjhk.jupload2.exception.JUploadIOException;
import wjhk.jupload2.filedata.helper.ImageHelper;
import wjhk.jupload2.filedata.helper.ImageReaderWriterHelper;
import wjhk.jupload2.policies.PictureUploadPolicy;
import wjhk.jupload2.policies.UploadPolicy;
/**
* This class contains all data about files to upload as a picture. It adds the
* following elements to the {@link wjhk.jupload2.filedata.FileData} class :<BR>
* <UL>
* <LI> Ability to define a target format (to convert pictures to JPG before
* upload, for instance)
* <LI> Optional definition of a maximal width and/or height.
* <LI> Ability to rotate a picture, with {@link #addRotation(int)}
* <LI> Ability to store a picture into a BufferedImage. This is actualy a bad
* idea within an applet (should run within a java application) : the applet
* runs very quickly out of memory. With pictures from my Canon EOS20D (3,5M), I
* can only display two pictures. The third one generates an out of memory
* error, despite the System.finalize and System.gc I've put everywhere in the
* code!
* </UL>
*
* @author etienne_sf
* @version $Revision: 476 $
*/
public class PictureFileData extends DefaultFileData {
/**
* Indicate whether the data for this fileData has already been intialized.
*/
// private boolean initialized = false;
/**
* Indicates if this file is a picture or not. This is bases on the return
* of ImageIO.getImageReadersByFormatName().
*/
private boolean isPicture = false;
/**
* If set to true, the PictureFileData will keep the BufferedImage in
* memory. That is: it won't load it again from the hard drive, and resize
* and/or rotate it (if necessary) when the user select this picture. When
* picture are big this is nice. <BR>
* <BR>
* <B>Caution:</B> this parameter is currently unused, as the navigator
* applet runs quickly out of memory (after three or four picture for my
* Canon EOS 20D, 8,5 Mega pixels).
*
* @see UploadPolicy
*/
boolean storeBufferedImage = UploadPolicy.DEFAULT_STORE_BUFFERED_IMAGE;
// Will be erased in the constructor.
/*
* bufferedImage contains a preloaded picture. This buffer is used according
* to PictureFileDataPolicy.storeBufferedImage.
*
* @see PictureUploadPolicy#storeBufferedImage
*/
// private BufferedImage bufferedImage = null;
// Currently commented, as it leads to memory leaks.
/**
* This picture is precalculated, and stored to avoid to calculate it each
* time the user select this picture again, or each time the use switch from
* an application to another.
*/
private Image offscreenImage = null;
/**
* quarterRotation contains the current rotation that will be applied to the
* picture. Its value should be one of 0, 1, 2, 3. It is controled by the
* {@link #addRotation(int)} method.
* <UL>
* <LI>0 means no rotation.
* <LI>1 means a rotation of 90� clockwise (word = Ok ??).
* <LI>2 means a rotation of 180�.
* <LI>3 means a rotation of 900 counterclockwise (word = Ok ??).
* </UL>
*/
int quarterRotation = 0;
/**
* Width of the original picture. The width is taken from the first image of
* the file. We expect that all pictures in a file are of the same size (for
* instance, for animated gif). Calculated in the
* {@link #PictureFileData(File, File, PictureUploadPolicy)} constructor.
*/
int originalWidth;
/**
* Same as {@link #originalWidth}, for the height of the fisst image in the
* picture file.
*/
int originalHeight;
/**
* transformedPictureFile contains the reference to the temporary file that
* stored the transformed picture, during upload. It is created by
* {@link #getInputStream()} and freed by {@link #afterUpload()}.
*/
private File transformedPictureFile = null;
/**
* uploadLength contains the uploadLength, which is : <BR> - The size of the
* original file, if no transformation is needed. <BR> - The size of the
* transformed file, if a transformation were made. <BR>
* <BR>
* It is set to -1 whenever the user ask for a rotation (current only action
* that need to recalculate the picture).
*/
private long uploadLength = -1;
/**
* Contains the reference to a copy of the original picture files.
* Originally created because a SUN bug would prevent picture to be
* correctly resized if the original picture filename contains accents (or
* any non-ASCII characters).
*/
private File workingCopyTempFile = null;
/**
* Standard constructor: needs a PictureFileDataPolicy.
*
* @param file The files which data are to be handled by this instance.
* @param root The root directory, to calculate the relative dir (see
* {@link #getRelativeDir()}.
* @param uploadPolicy The current upload policy
* @throws JUploadIOException Encapsulation of the IOException, if any would
* occurs.
*/
public PictureFileData(File file, File root,
PictureUploadPolicy uploadPolicy) throws JUploadIOException {
super(file, root, uploadPolicy);
// EGR Should be useless
// this.uploadPolicy = (PictureUploadPolicy) super.uploadPolicy;
this.storeBufferedImage = uploadPolicy.hasToStoreBufferedImage();
String fileExtension = getFileExtension();
// Is it a picture?
Iterator<ImageReader> iter = ImageIO
.getImageReadersByFormatName(fileExtension);
if (iter.hasNext()) {
// It's a picture: we store its original width and height, for
// further calculation (rescaling and rotation).
this.isPicture = true;
try {
FileImageInputStream fiis = new FileImageInputStream(getFile());
ImageReader ir = iter.next();
ir.setInput(fiis);
this.originalHeight = ir.getHeight(0);
this.originalWidth = ir.getWidth(0);
ir.dispose();
fiis.close();
} catch (IOException e) {
throw new JUploadIOException("PictureFileData()", e);
}
} else {
this.isPicture = false;
}
// Let's log the test result
uploadPolicy.displayDebug("isPicture=" + this.isPicture + " ("
+ file.getName() + "), extension=" + fileExtension, 75);
// If it's a picture, we override the default mime type:
if (this.isPicture) {
setMimeTypeByExtension(fileExtension);
}
}
/**
* Free any available memory. This method is called very often here, to be
* sure that we don't use too much memory. But we still run out of memory in
* some case.
*
* @param caller Indicate the method or treatment from which this method is
* called.
*/
public void freeMemory(String caller) {
Runtime rt = Runtime.getRuntime();
/*
* uploadPolicy.displayDebug("freeMemory : " + caller, 80);
* uploadPolicy.displayDebug("freeMemory (before " + caller + ") : " +
* rt.freeMemory(), 80); uploadPolicy.displayDebug("maxMemory (before " +
* caller + ") : " + rt.maxMemory(), 80);
*/
// rt.runFinalization();
rt.gc();
this.uploadPolicy.displayDebug("freeMemory (after " + caller + ") : "
+ rt.freeMemory(), 80);
/*
* uploadPolicy.displayDebug("maxMemory (after " + caller + ") : " +
* rt.maxMemory(), 80);
*/
}
/**
* If this pictures needs transformation, a temporary file is created. This
* can occurs if the original picture is bigger than the maxWidth or
* maxHeight, of if it has to be rotated. This temporary file contains the
* transformed picture. <BR>
* The call to this method is optional, if the caller calls
* {@link #getUploadLength()}. This method calls beforeUpload() if the
* uploadLength is unknown.
*/
@Override
public void beforeUpload() throws JUploadException {
if (this.uploadLength < 0) {
try {
// Get the transformed picture file, if needed.
initTransformedPictureFile();
} catch (OutOfMemoryError e) {
// Oups ! My EOS 20D has too big pictures to handle more than
// two pictures in a navigator applet !!!!!
// :-(
//
// We don't transform it. We clean the file, if it has been
// created.
deleteTransformedPictureFile();
//
tooBigPicture();
}
// If the transformed picture is correctly created, we'll upload it.
// Else we upload the original file.
if (this.transformedPictureFile != null) {
this.uploadLength = this.transformedPictureFile.length();
} else {
this.uploadLength = getFile().length();
}
}
// Let's check that everything is Ok
super.beforeUpload();
}
/**
* Returns the number of bytes, for this upload. If needed, that is, if
* uploadlength is unknown, {@link #beforeUpload()} is called.
*
* @return The length of upload. In this class, this is ... the size of the
* original file, or the transformed file!
*/
@Override
public long getUploadLength() throws JUploadException {
if (this.uploadLength < 0) {
// Hum, beforeUpload should have been called before. Let's correct
// that.
beforeUpload();
}
return this.uploadLength;
}
/**
* This function create an input stream for this file. The caller is
* responsible for closing this input stream. <BR>
* This function assumes that the {@link #getUploadLength()} method has
* already be called : it is responsible for creating the temporary file (if
* needed). If not called, the original file will be sent.
*
* @return An inputStream
*/
@Override
public InputStream getInputStream() throws JUploadException {
// Do we have to transform the picture ?
if (this.transformedPictureFile != null) {
try {
return new FileInputStream(this.transformedPictureFile);
} catch (FileNotFoundException e) {
throw new JUploadIOException(e);
}
}
// Otherwise : we read the file, in the standard way.
return super.getInputStream();
}
/**
* Cleaning of the temporary file on the hard drive, if any. <BR>
* <B>Note:</B> if the debugLevel is 100 (or more) this temporary file is
* not removed. This allow control of this created file.
*/
@Override
public void afterUpload() {
super.afterUpload();
// Free the temporary file ... if any.
if (this.transformedPictureFile != null) {
// for debug : if the debugLevel is enough, we keep the temporary
// file (for check).
if (this.uploadPolicy.getDebugLevel() >= 100) {
this.uploadPolicy.displayWarn("Temporary file not deleted");
} else {
deleteTransformedPictureFile();
}
}
}
/**
* This method creates a new Image, from the current picture. The resulting
* width and height will be less or equal than the given maximum width and
* height. The scale is maintained. Thus the width or height may be inferior
* than the given values.
*
* @param canvas The canvas on which the picture will be displayed.
* @param shadow True if the pictureFileData should store this picture.
* False if the pictureFileData instance should not store this
* picture. Store this picture avoid calculating the image each
* time the user selects it in the file panel.
* @return The rescaled image.
* @throws JUploadException Encapsulation of the Exception, if any would
* occurs.
*/
public Image getImage(Canvas canvas, boolean shadow)
throws JUploadException {
Image localImage = null;
if (canvas == null) {
throw new JUploadException(
"canvas null in PictureFileData.getImage");
}
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
if (canvasWidth <= 0 || canvasHeight <= 0) {
this.uploadPolicy
.displayDebug(
"canvas width and/or height null in PictureFileData.getImage()",
1);
} else if (shadow && this.offscreenImage != null) {
// We take and return the previous calculated image for this
// PictureFileData.
localImage = this.offscreenImage;
} else if (this.isPicture) {
try {
// First: load the picture.
ImageReaderWriterHelper irwh = new ImageReaderWriterHelper(
(PictureUploadPolicy) uploadPolicy, this);
BufferedImage sourceImage = irwh.readImage(0);
irwh.dispose();
irwh = null;
ImageHelper ih = new ImageHelper(
(PictureUploadPolicy) this.uploadPolicy, this,
canvasWidth, canvasHeight, this.quarterRotation);
localImage = ih.getBufferedImage(
((PictureUploadPolicy) this.uploadPolicy)
.getHighQualityPreview(), sourceImage);
// We free memory ASAP.
sourceImage.flush();
sourceImage = null;
} catch (OutOfMemoryError e) {
// Too bad
localImage = null;
tooBigPicture();
}
} // If isPicture
// We store it, if asked to.
if (shadow) {
this.offscreenImage = localImage;
}
freeMemory("end of " + this.getClass().getName() + ".getImage()");
// The picture is now loaded. We clear the progressBar
this.uploadPolicy.getApplet().getUploadPanel().getProgressBar()
.setValue(0);
return localImage;
}// getImage
/**
* This function is used to rotate the picture. The current rotation state
* is kept in the quarterRotation private attribute.
*
* @param quarter Number of quarters (90�) the picture should rotate. 1
* means rotating of 90� clockwise. Can be negative.
*/
public void addRotation(int quarter) {
this.quarterRotation += quarter;
// We'll have to recalculate the upload length, as the resulting file is
// different.
this.uploadLength = -1;
// We keep the 'quarter' in the segment [0;4[
while (this.quarterRotation < 0) {
this.quarterRotation += 4;
}
while (this.quarterRotation >= 4) {
this.quarterRotation -= 4;
}
// We need to change the precalculated picture, if any
if (this.offscreenImage != null) {
this.offscreenImage.flush();
this.offscreenImage = null;
}
}
/**
* Indicates if this file is actually a picture or not.
*
* @return the isPicture flag.
*/
public boolean isPicture() {
return this.isPicture;
}
/** @see FileData#getMimeType() */
@Override
public String getMimeType() {
return this.mimeType;
}
// ///////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////// private METHODS
// ///////////////////////////////////////////////////////////////////////////////////////////
/**
* File.deleteOnExit() is pretty unreliable, especially in applets.
* Therefore the applet provides a callback which is executed during applet
* termination. This method performs the actual cleanup.
*/
public void deleteTransformedPictureFile() {
if (null != this.transformedPictureFile) {
this.transformedPictureFile.delete();
this.transformedPictureFile = null;
this.uploadLength = -1;
}
}
/**
* Creation of a temporary file, that contains the transformed picture. For
* instance, it can be resized or rotated. This method doesn't throw
* exception when there is an IOException within its procedure. If an
* exception occurs while building the temporary file, the exception is
* caught, a warning is displayed, the temporary file is deleted (if it was
* created), and the upload will go on with the original file. <BR>
* Note: any JUploadException thrown by a method called within
* getTransformedPictureFile() will be thrown within this method.
*/
private void initTransformedPictureFile() throws JUploadException {
int targetMaxWidth;
int targetMaxHeight;
// If the image is rotated, we compare to realMaxWidth and
// realMaxHeight, instead of maxWidth and maxHeight. This allows
// to have a different picture size for rotated and not rotated
// pictures. See the UploadPolicy javadoc for details ... and a
// good reason ! ;-)
if (this.quarterRotation == 0) {
targetMaxWidth = ((PictureUploadPolicy) this.uploadPolicy)
.getMaxWidth();
targetMaxHeight = ((PictureUploadPolicy) this.uploadPolicy)
.getMaxHeight();
} else {
targetMaxWidth = ((PictureUploadPolicy) this.uploadPolicy)
.getRealMaxWidth();
targetMaxHeight = ((PictureUploadPolicy) this.uploadPolicy)
.getRealMaxHeight();
}
// Some Helper will .. help us !
// I like useful comment :-)
ImageHelper imageHelper = new ImageHelper(
(PictureUploadPolicy) uploadPolicy, this, targetMaxWidth,
targetMaxHeight, this.quarterRotation);
// Should transform the file, and do we already created the transformed
// file ?
if (imageHelper.hasToTransformPicture()
&& this.transformedPictureFile == null) {
// We have to create a resized or rotated picture file, and all
// needed information.
// ...let's do it
createTranformedPictureFile(imageHelper);
}
}// end of initTransformedPictureFile
/**
* Creates a transformed picture file of the given max width and max height.
* If the {@link #transformedPictureFile} attribute is not set before
* calling this method, it will be set. If set before, the existing
* {@link #transformedPictureFile} is replaced by the newly transformed
* picture file. It is cleared if an error occured. <BR>
*
* @param imageHelper The {@link ImageHelper} that was initialized with
* current parameters.
*/
void createTranformedPictureFile(ImageHelper imageHelper)
throws JUploadException {
IIOMetadata metadata = null;
IIOImage iioImage = null;
BufferedImage originalImage = null;
BufferedImage transformedImage = null;
ImageReaderWriterHelper imageWriterHelper = new ImageReaderWriterHelper(
(PictureUploadPolicy) uploadPolicy, this);
boolean transmitMetadata = ((PictureUploadPolicy) uploadPolicy)
.getPictureTransmitMetadata();
// Creation of the transformed picture file.
createTransformedTempFile();
imageWriterHelper.setOutput(this.transformedPictureFile);
// How many picture should we read from the input file.
// Default number of pictures is one.
int nbPictures = 1;
// For gif file, we put a max to MAX_VALUE, and we check the
// IndexOutOfBoundsException to identify when we've read all pictures
if (getExtension(getFile()).equalsIgnoreCase("gif")) {
nbPictures = Integer.MAX_VALUE;
}
uploadPolicy.displayDebug(
"Reading image with imageWriterHelper.readImage(i)", 50);
// Now, we have to read each picture from the original file, apply
// the calculated transformation, and write each transformed picture
// to the writer.
// As indicated in javadoc for ImageReader.getNumImages(), we go
// through pictures, until we get an IndexOutOfBoundsException.
try {
for (int i = 0; i < nbPictures; i += 1) {
originalImage = imageWriterHelper.readImage(i);
transformedImage = imageHelper.getBufferedImage(true,
originalImage);
// If necessary, we load the metadata for the current
// picture
if (transmitMetadata) {
metadata = imageWriterHelper.getImageMetadata(i);
}
iioImage = new IIOImage(transformedImage, null, metadata);
imageWriterHelper.write(iioImage);
// Let's clear picture, to force getBufferedImage to read a new
// one,
// in the next loop.
if (originalImage != null) {
originalImage.flush();
originalImage = null;
}
}// for
} catch (IndexOutOfBoundsException e) {
// Was sent by imageWriterHelper.readImage(i)
// Ok, no more picture to read. We just want to go out of
// the loop. No error.
uploadPolicy.displayDebug(
"IndexOutOfBoundsException catched: end of reading for file "
+ getFileName(), 50);
}
if (originalImage != null) {
originalImage.flush();
originalImage = null;
}
// Let's free any used resource.
imageWriterHelper.dispose();
}
/**
* This method is called when an OutOfMemoryError occurs. This can easily
* happen within the navigator, with big pictures: I've put a lot of
* freeMemory calls within the code, but they don't seem to work very well.
* When running from eclipse, the memory is freed Ok !
*/
private void tooBigPicture() {
String msg = String.format(
this.uploadPolicy.getString("tooBigPicture"), getFileName());
JOptionPane.showMessageDialog(null, msg, "Warning",
JOptionPane.WARNING_MESSAGE);
this.uploadPolicy.displayWarn(msg);
}
/**
* This methods set the {@link DefaultFileData#mimeType} to the image mime
* type, that should be associate with the picture.
*/
private void setMimeTypeByExtension(String fileExtension) {
String ext = fileExtension.toLowerCase();
if (ext.equals("jpg")) {
ext = "jpeg";
}
this.mimeType = "image/" + ext;
}
/**
* If {@link #transformedPictureFile} is null, create a new temporary file,
* and assign it to {@link #transformedPictureFile}. Otherwise, no action.
*
* @throws IOException
*/
private void createTransformedTempFile() throws JUploadIOException {
if (this.transformedPictureFile == null) {
try {
this.transformedPictureFile = File.createTempFile("jupload_",
".tmp");
} catch (IOException e) {
throw new JUploadIOException(
"PictureFileData.createTransformedTempFile()", e);
}
this.uploadPolicy.getApplet().registerUnload(this,
"deleteTransformedPictureFile");
this.uploadPolicy.displayDebug("Using transformed temp file "
+ this.transformedPictureFile.getAbsolutePath() + " for "
+ getFileName(), 50);
}
}
// ////////////////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////// static methods
// ////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Returns an ImageIcon for the given file, resized according to the given
* dimensions. If the original file contains a pictures smaller than these
* width and height, the picture is returned as is (nor resized).
*
* @param pictureFile The file, containing a picture, from which the user
* wants to extract a static picture.
* @param maxWidth The maximum allowed width for the static picture to
* generate.
* @param maxHeight The maximum allowed height for the static picture to
* generate.
* @return The created static picture, or null if the file is null.
*/
public static ImageIcon getImageIcon(File pictureFile, int maxWidth,
int maxHeight) {
ImageIcon thumbnail = null;
if (pictureFile != null) {
ImageIcon tmpIcon = new ImageIcon(pictureFile.getPath());
if (tmpIcon != null) {
double scaleWidth = ((double) maxWidth)
/ tmpIcon.getIconWidth();
double scaleHeight = ((double) maxHeight)
/ tmpIcon.getIconHeight();
double scale = Math.min(scaleWidth, scaleHeight);
if (scale < 1) {
thumbnail = new ImageIcon(tmpIcon.getImage()
.getScaledInstance(
(int) (scale * tmpIcon.getIconWidth()),
(int) (scale * tmpIcon.getIconHeight()),
Image.SCALE_FAST));
} else { // no need to miniaturize
thumbnail = tmpIcon;
}
}
}
return thumbnail;
}
/**
* If {@link #workingCopyTempFile} is null, create a new temporary file, and
* assign it to {@link #transformedPictureFile}. Otherwise, no action.
*
* @throws IOException
*/
private void createWorkingCopyTempFile() throws IOException {
if (this.workingCopyTempFile == null) {
// The temporary file must have the correct extension, so that
// native Java method works on it.
this.workingCopyTempFile = File.createTempFile("jupload_", ".tmp."
+ DefaultFileData.getExtension(getFile()));
this.uploadPolicy.getApplet().registerUnload(this,
"deleteWorkingCopyPictureFile");
this.uploadPolicy.displayDebug("Using working copy temp file "
+ this.workingCopyTempFile.getAbsolutePath() + " for "
+ getFileName(), 50);
}
}
/**
* File.deleteOnExit() is pretty unreliable, especially in applets.
* Therefore the applet provides a callback which is executed during applet
* termination. This method performs the actual cleanup.
*/
public void deleteWorkingCopyPictureFile() {
if (null != this.workingCopyTempFile) {
this.workingCopyTempFile.delete();
this.workingCopyTempFile = null;
}
}
/**
* Get the file that contains the original picture. This is used as a
* workaround for the following JVM bug: once in the navigator, it can't
* transform picture read from a file whose name contains non-ASCII
* characters, like French accents.
*
* @return The file that contains the original picture, as the source for
* picture transformation
* @throws JUploadIOException
*/
public File getWorkingSourceFile() throws JUploadIOException {
if (this.workingCopyTempFile == null) {
uploadPolicy.displayDebug(
"[getWorkingSourceFile] Creating a copy of "
+ getFileName() + " as a source working target.",
20);
FileInputStream is = null;
FileOutputStream os = null;
try {
createWorkingCopyTempFile();
is = new FileInputStream(getFile());
os = new FileOutputStream(this.workingCopyTempFile);
byte b[] = new byte[1024];
int l;
while ((l = is.read(b)) > 0) {
os.write(b, 0, l);
}
} catch (IOException e) {
throw new JUploadIOException(
"ImageReaderWriterHelper.getWorkingSourceFile()", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
uploadPolicy
.displayWarn(e.getClass().getName()
+ " while trying to close FileInputStream, in PictureUploadPolicy.copyOriginalToWorkingCopyTempFile.");
} finally {
is = null;
}
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
uploadPolicy
.displayWarn(e.getClass().getName()
+ " while trying to close FileOutputStream, in PictureUploadPolicy.copyOriginalToWorkingCopyTempFile.");
} finally {
os = null;
}
}
}
}
return this.workingCopyTempFile;
}// getWorkingSourceFile()
/**
* @return the originalWidth of the picture
*/
public int getOriginalWidth() {
return originalWidth;
}
/**
* @return the originalHeight of the picture
*/
public int getOriginalHeight() {
return originalHeight;
}
}