You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2010/09/10 18:33:42 UTC

svn commit: r995859 [11/30] - in /commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan: ./ color/ common/ common/byteSources/ common/mylzw/ formats/bmp/ formats/bmp/pixelparsers/ formats/bmp/writers/ formats/gif/ formats/ico/ formats/jpeg/ f...

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java?rev=995859&r1=995858&r2=995859&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java Fri Sep 10 16:33:35 2010
@@ -51,987 +51,987 @@ import org.apache.sanselan.formats.tiff.
 import org.apache.sanselan.util.Debug;
 
 public class JpegImageParser extends ImageParser implements JpegConstants,
-		TiffTagConstants
+        TiffTagConstants
 {
-	public JpegImageParser()
-	{
-		setByteOrder(BYTE_ORDER_NETWORK);
-		// setDebug(true);
-	}
-
-	protected ImageFormat[] getAcceptedTypes()
-	{
-		return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_JPEG, //
-		};
-	}
-
-	public String getName()
-	{
-		return "Jpeg-Custom";
-	}
-
-	public String getDefaultExtension()
-	{
-		return DEFAULT_EXTENSION;
-	}
-
-	private static final String DEFAULT_EXTENSION = ".jpg";
-
-	public static final String AcceptedExtensions[] = { ".jpg", ".jpeg", };
-
-	protected String[] getAcceptedExtensions()
-	{
-		return AcceptedExtensions;
-	}
-
-	public final BufferedImage getBufferedImage(ByteSource byteSource,
-			Map params) throws ImageReadException, IOException
-	{
-		throw new ImageReadException(
-				"Sanselan cannot read or write JPEG images.");
-	}
-
-	private boolean keepMarker(int marker, int markers[])
-	{
-		if (markers == null)
-			return true;
-
-		for (int i = 0; i < markers.length; i++)
-		{
-			if (markers[i] == marker)
-				return true;
-		}
-
-		return false;
-	}
-
-	public ArrayList readSegments(ByteSource byteSource, final int markers[],
-			final boolean returnAfterFirst, boolean readEverything)
-			throws ImageReadException, IOException
-	{
-		final ArrayList result = new ArrayList();
-		final JpegImageParser parser = this;
-
-		JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
-			// return false to exit before reading image data.
-			public boolean beginSOS()
-			{
-				return false;
-			}
-
-			public void visitSOS(int marker, byte markerBytes[],
-					byte imageData[])
-			{
-			}
-
-			// return false to exit traversal.
-			public boolean visitSegment(int marker, byte markerBytes[],
-					int markerLength, byte markerLengthBytes[],
-					byte segmentData[]) throws ImageReadException, IOException
-			{
-				if (marker == 0xffd9)
-					return false;
-
-				// Debug.debug("visitSegment marker", marker);
-				// // Debug.debug("visitSegment keepMarker(marker, markers)",
-				// keepMarker(marker, markers));
-				// Debug.debug("visitSegment keepMarker(marker, markers)",
-				// keepMarker(marker, markers));
-
-				if (!keepMarker(marker, markers))
-					return true;
-
-				if (marker == JPEG_APP13_Marker)
-				{
-					// Debug.debug("app 13 segment data", segmentData.length);
-					result.add(new App13Segment(parser, marker, segmentData));
-				} else if (marker == JPEG_APP2_Marker)
-				{
-					result.add(new App2Segment(marker, segmentData));
-				} else if (marker == JFIFMarker)
-				{
-					result.add(new JFIFSegment(marker, segmentData));
-				} else if ((marker >= SOF0Marker) && (marker <= SOF15Marker))
-				{
-					result.add(new SOFNSegment(marker, segmentData));
-				} else if ((marker >= JPEG_APP1_Marker)
-						&& (marker <= JPEG_APP15_Marker))
-				{
-					result.add(new UnknownSegment(marker, segmentData));
-				}
-
-				if (returnAfterFirst)
-					return false;
-
-				return true;
-			}
-		};
-
-		new JpegUtils().traverseJFIF(byteSource, visitor);
-
-		return result;
-	}
-
-	public static final boolean permissive = true;
-
-	private byte[] assembleSegments(ArrayList v) throws ImageReadException
-	{
-		try
-		{
-			return assembleSegments(v, false);
-		} catch (ImageReadException e)
-		{
-			return assembleSegments(v, true);
-		}
-	}
-
-	private byte[] assembleSegments(ArrayList v, boolean start_with_zero)
-			throws ImageReadException
-	{
-		if (v.size() < 1)
-			throw new ImageReadException("No App2 Segments Found.");
-
-		int markerCount = ((App2Segment) v.get(0)).num_markers;
-
-		// if (permissive && (markerCount == 0))
-		// markerCount = v.size();
-
-		if (v.size() != markerCount)
-			throw new ImageReadException("App2 Segments Missing.  Found: "
-					+ v.size() + ", Expected: " + markerCount + ".");
-
-		Collections.sort(v);
-
-		int offset = start_with_zero ? 0 : 1;
-
-		int total = 0;
-		for (int i = 0; i < v.size(); i++)
-		{
-			App2Segment segment = (App2Segment) v.get(i);
-
-			if ((i + offset) != segment.cur_marker)
-			{
-				dumpSegments(v);
-				throw new ImageReadException(
-						"Incoherent App2 Segment Ordering.  i: " + i
-								+ ", segment[" + i + "].cur_marker: "
-								+ segment.cur_marker + ".");
-			}
-
-			if (markerCount != segment.num_markers)
-			{
-				dumpSegments(v);
-				throw new ImageReadException(
-						"Inconsistent App2 Segment Count info.  markerCount: "
-								+ markerCount + ", segment[" + i
-								+ "].num_markers: " + segment.num_markers + ".");
-			}
-
-			total += segment.icc_bytes.length;
-		}
-
-		byte result[] = new byte[total];
-		int progress = 0;
-
-		for (int i = 0; i < v.size(); i++)
-		{
-			App2Segment segment = (App2Segment) v.get(i);
-
-			System.arraycopy(segment.icc_bytes, 0, result, progress,
-					segment.icc_bytes.length);
-			progress += segment.icc_bytes.length;
-		}
-
-		return result;
-	}
-
-	private void dumpSegments(ArrayList v)
-	{
-		Debug.debug();
-		Debug.debug("dumpSegments", v.size());
-
-		for (int i = 0; i < v.size(); i++)
-		{
-			App2Segment segment = (App2Segment) v.get(i);
-
-			Debug.debug((i) + ": " + segment.cur_marker + " / "
-					+ segment.num_markers);
-		}
-		Debug.debug();
-	}
-
-	public ArrayList readSegments(ByteSource byteSource, int markers[],
-			boolean returnAfterFirst) throws ImageReadException, IOException
-	{
-		return readSegments(byteSource, markers, returnAfterFirst, false);
-	}
-
-	public byte[] getICCProfileBytes(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		ArrayList segments = readSegments(byteSource,
-				new int[] { JPEG_APP2_Marker, }, false);
-
-		if (segments != null)
-		{
-			// throw away non-icc profile app2 segments.
-			ArrayList filtered = new ArrayList();
-			for (int i = 0; i < segments.size(); i++)
-			{
-				App2Segment segment = (App2Segment) segments.get(i);
-				if (segment.icc_bytes != null)
-					filtered.add(segment);
-			}
-			segments = filtered;
-		}
-
-		if ((segments == null) || (segments.size() < 1))
-			return null;
-
-		byte bytes[] = assembleSegments(segments);
-
-		if (debug)
-			System.out.println("bytes" + ": "
-					+ ((bytes == null) ? null : "" + bytes.length));
-
-		if (debug)
-			System.out.println("");
-
-		return (bytes);
-	}
-
-	public IImageMetadata getMetadata(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		TiffImageMetadata exif = getExifMetadata(byteSource, params);
-
-		JpegPhotoshopMetadata photoshop = getPhotoshopMetadata(byteSource,
-				params);
-
-		if (null == exif && null == photoshop)
-			return null;
-
-		JpegImageMetadata result = new JpegImageMetadata(photoshop, exif);
-
-		return result;
-	}
-
-	public static boolean isExifAPP1Segment(GenericSegment segment)
-	{
-		return byteArrayHasPrefix(segment.bytes, EXIF_IDENTIFIER_CODE);
-	}
-
-	private ArrayList filterAPP1Segments(ArrayList v)
-	{
-		ArrayList result = new ArrayList();
-
-		for (int i = 0; i < v.size(); i++)
-		{
-			GenericSegment segment = (GenericSegment) v.get(i);
-			if (isExifAPP1Segment(segment))
-				result.add(segment);
-		}
-
-		return result;
-	}
-
-	// TODO unused
-	private ArrayList filterSegments(ArrayList v, List markers)
-	{
-		ArrayList result = new ArrayList();
-
-		for (int i = 0; i < v.size(); i++)
-		{
-			Segment segment = (Segment) v.get(i);
-			Integer marker = new Integer(segment.marker);
-			if (markers.contains(marker))
-				result.add(segment);
-		}
-
-		return result;
-	}
-
-	public TiffImageMetadata getExifMetadata(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		byte bytes[] = getExifRawData(byteSource);
-		if (null == bytes)
-			return null;
-
-		if (params == null)
-			params = new HashMap();
-		if (!params.containsKey(PARAM_KEY_READ_THUMBNAILS))
-			params.put(PARAM_KEY_READ_THUMBNAILS, Boolean.TRUE);
-
-		return (TiffImageMetadata) new TiffImageParser().getMetadata(bytes,
-				params);
-	}
-
-	public byte[] getExifRawData(ByteSource byteSource)
-			throws ImageReadException, IOException
-	{
-		ArrayList segments = readSegments(byteSource,
-				new int[] { JPEG_APP1_Marker, }, false);
-
-		if ((segments == null) || (segments.size() < 1))
-			return null;
-
-		ArrayList exifSegments = filterAPP1Segments(segments);
-		if (debug)
-			System.out.println("exif_segments.size" + ": "
-					+ exifSegments.size());
-
-		// Debug.debug("segments", segments);
-		// Debug.debug("exifSegments", exifSegments);
-
-		// TODO: concatenate if multiple segments, need example.
-		if (exifSegments.size() < 1)
-			return null;
-		if (exifSegments.size() > 1)
-			throw new ImageReadException(
-					"Sanselan currently can't parse EXIF metadata split across multiple APP1 segments.  "
-							+ "Please send this image to the Sanselan project.");
-
-		GenericSegment segment = (GenericSegment) exifSegments.get(0);
-		byte bytes[] = segment.bytes;
-
-		// byte head[] = readBytearray("exif head", bytes, 0, 6);
-		//
-		// Debug.debug("head", head);
-
-		return getByteArrayTail("trimmed exif bytes", bytes, 6);
-	}
-
-	public boolean hasExifSegment(ByteSource byteSource)
-			throws ImageReadException, IOException
-	{
-		final boolean result[] = { false, };
-
-		JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
-			// return false to exit before reading image data.
-			public boolean beginSOS()
-			{
-				return false;
-			}
-
-			public void visitSOS(int marker, byte markerBytes[],
-					byte imageData[])
-			{
-			}
-
-			// return false to exit traversal.
-			public boolean visitSegment(int marker, byte markerBytes[],
-					int markerLength, byte markerLengthBytes[],
-					byte segmentData[]) throws ImageReadException, IOException
-			{
-				if (marker == 0xffd9)
-					return false;
-
-				if (marker == JPEG_APP1_Marker)
-				{
-					if (byteArrayHasPrefix(segmentData, EXIF_IDENTIFIER_CODE))
-					{
-						result[0] = true;
-						return false;
-					}
-				}
-
-				return true;
-			}
-		};
-
-		new JpegUtils().traverseJFIF(byteSource, visitor);
-
-		return result[0];
-	}
-
-	public boolean hasIptcSegment(ByteSource byteSource)
-			throws ImageReadException, IOException
-	{
-		final boolean result[] = { false, };
-
-		JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
-			// return false to exit before reading image data.
-			public boolean beginSOS()
-			{
-				return false;
-			}
-
-			public void visitSOS(int marker, byte markerBytes[],
-					byte imageData[])
-			{
-			}
-
-			// return false to exit traversal.
-			public boolean visitSegment(int marker, byte markerBytes[],
-					int markerLength, byte markerLengthBytes[],
-					byte segmentData[]) throws ImageReadException, IOException
-			{
-				if (marker == 0xffd9)
-					return false;
-
-				if (marker == JPEG_APP13_Marker)
-				{
-					if (new IPTCParser().isPhotoshopJpegSegment(segmentData))
-					{
-						result[0] = true;
-						return false;
-					}
-				}
-
-				return true;
-			}
-		};
-
-		new JpegUtils().traverseJFIF(byteSource, visitor);
-
-		return result[0];
-	}
-
-	public boolean hasXmpSegment(ByteSource byteSource)
-			throws ImageReadException, IOException
-	{
-		final boolean result[] = { false, };
-
-		JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
-			// return false to exit before reading image data.
-			public boolean beginSOS()
-			{
-				return false;
-			}
-
-			public void visitSOS(int marker, byte markerBytes[],
-					byte imageData[])
-			{
-			}
-
-			// return false to exit traversal.
-			public boolean visitSegment(int marker, byte markerBytes[],
-					int markerLength, byte markerLengthBytes[],
-					byte segmentData[]) throws ImageReadException, IOException
-			{
-				if (marker == 0xffd9)
-					return false;
-
-				if (marker == JPEG_APP1_Marker)
-				{
-					if (new JpegXmpParser().isXmpJpegSegment(segmentData))
-					{
-						result[0] = true;
-						return false;
-					}
-				}
-
-				return true;
-			}
-		};
-		new JpegUtils().traverseJFIF(byteSource, visitor);
-
-		return result[0];
-	}
-
-	/**
-	 * Extracts embedded XML metadata as XML string.
-	 * <p>
-	 * 
-	 * @param byteSource
-	 *            File containing image data.
-	 * @param params
-	 *            Map of optional parameters, defined in SanselanConstants.
-	 * @return Xmp Xml as String, if present. Otherwise, returns null.
-	 */
-	public String getXmpXml(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-
-		final List result = new ArrayList();
-
-		JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
-			// return false to exit before reading image data.
-			public boolean beginSOS()
-			{
-				return false;
-			}
-
-			public void visitSOS(int marker, byte markerBytes[],
-					byte imageData[])
-			{
-			}
-
-			// return false to exit traversal.
-			public boolean visitSegment(int marker, byte markerBytes[],
-					int markerLength, byte markerLengthBytes[],
-					byte segmentData[]) throws ImageReadException, IOException
-			{
-				if (marker == 0xffd9)
-					return false;
-
-				if (marker == JPEG_APP1_Marker)
-				{
-					if (new JpegXmpParser().isXmpJpegSegment(segmentData))
-					{
-						result.add(new JpegXmpParser()
-								.parseXmpJpegSegment(segmentData));
-						return false;
-					}
-				}
-
-				return true;
-			}
-		};
-		new JpegUtils().traverseJFIF(byteSource, visitor);
-
-		if (result.size() < 1)
-			return null;
-		if (result.size() > 1)
-			throw new ImageReadException(
-					"Jpeg file contains more than one XMP segment.");
-		return (String) result.get(0);
-	}
-
-	public JpegPhotoshopMetadata getPhotoshopMetadata(ByteSource byteSource,
-			Map params) throws ImageReadException, IOException
-	{
-		ArrayList segments = readSegments(byteSource,
-				new int[] { JPEG_APP13_Marker, }, false);
-
-		if ((segments == null) || (segments.size() < 1))
-			return null;
-
-		PhotoshopApp13Data photoshopApp13Data = null;
-
-		for (int i = 0; i < segments.size(); i++)
-		{
-			App13Segment segment = (App13Segment) segments.get(i);
-
-			PhotoshopApp13Data data = segment.parsePhotoshopSegment(params);
-			if (data != null && photoshopApp13Data != null)
-				throw new ImageReadException(
-						"Jpeg contains more than one Photoshop App13 segment.");
-
-			photoshopApp13Data = data;
-		}
-
-		if(null==photoshopApp13Data)
-			return null;
-		return new JpegPhotoshopMetadata(photoshopApp13Data);
-	}
-
-	public Dimension getImageSize(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		ArrayList segments = readSegments(byteSource, new int[] {
-				// kJFIFMarker,
-				SOF0Marker,
-
-				SOF1Marker, SOF2Marker, SOF3Marker, SOF5Marker, SOF6Marker,
-				SOF7Marker, SOF9Marker, SOF10Marker, SOF11Marker, SOF13Marker,
-				SOF14Marker, SOF15Marker,
-
-		}, true);
-
-		if ((segments == null) || (segments.size() < 1))
-			throw new ImageReadException("No JFIF Data Found.");
-
-		if (segments.size() > 1)
-			throw new ImageReadException("Redundant JFIF Data Found.");
-
-		SOFNSegment fSOFNSegment = (SOFNSegment) segments.get(0);
-
-		return new Dimension(fSOFNSegment.width, fSOFNSegment.height);
-	}
-
-	public byte[] embedICCProfile(byte image[], byte profile[])
-	{
-		return null;
-	}
-
-	public boolean embedICCProfile(File src, File dst, byte profile[])
-	{
-		return false;
-	}
-
-	public ImageInfo getImageInfo(ByteSource byteSource, Map params)
-			throws ImageReadException, IOException
-	{
-		// ArrayList allSegments = readSegments(byteSource, null, false);
-
-		ArrayList SOF_segments = readSegments(byteSource, new int[] {
-				// kJFIFMarker,
-
-				SOF0Marker, SOF1Marker, SOF2Marker, SOF3Marker, SOF5Marker,
-				SOF6Marker, SOF7Marker, SOF9Marker, SOF10Marker, SOF11Marker,
-				SOF13Marker, SOF14Marker, SOF15Marker,
-
-		}, false);
-
-		if (SOF_segments == null)
-			throw new ImageReadException("No SOFN Data Found.");
-
-		// if (SOF_segments.size() != 1)
-		// System.out.println("Incoherent SOFN Data Found: "
-		// + SOF_segments.size());
-
-		ArrayList jfifSegments = readSegments(byteSource,
-				new int[] { JFIFMarker, }, true);
-
-		SOFNSegment fSOFNSegment = (SOFNSegment) SOF_segments.get(0);
-		// SOFNSegment fSOFNSegment = (SOFNSegment) findSegment(segments,
-		// SOFNmarkers);
-
-		if (fSOFNSegment == null)
-			throw new ImageReadException("No SOFN Data Found.");
-
-		int Width = fSOFNSegment.width;
-		int Height = fSOFNSegment.height;
-
-		JFIFSegment jfifSegment = null;
-
-		if ((jfifSegments != null) && (jfifSegments.size() > 0))
-			jfifSegment = (JFIFSegment) jfifSegments.get(0);
-
-		// JFIFSegment fTheJFIFSegment = (JFIFSegment) findSegment(segments,
-		// kJFIFMarker);
-
-		double x_density = -1.0;
-		double y_density = -1.0;
-		double units_per_inch = -1.0;
-		// int JFIF_major_version;
-		// int JFIF_minor_version;
-		String FormatDetails;
-
-		if (jfifSegment != null)
-		{
-			x_density = jfifSegment.xDensity;
-			y_density = jfifSegment.yDensity;
-			int density_units = jfifSegment.densityUnits;
-			// JFIF_major_version = fTheJFIFSegment.JFIF_major_version;
-			// JFIF_minor_version = fTheJFIFSegment.JFIF_minor_version;
-
-			FormatDetails = "Jpeg/JFIF v." + jfifSegment.jfifMajorVersion + "."
-					+ jfifSegment.jfifMinorVersion;
-
-			switch (density_units)
-			{
-			case 0:
-				break;
-			case 1: // inches
-				units_per_inch = 1.0;
-				break;
-			case 2: // cms
-				units_per_inch = 2.54;
-				break;
-			default:
-				break;
-			}
-		} else
-		{
-			JpegImageMetadata metadata = (JpegImageMetadata) getMetadata(
-					byteSource, params);
-
-			if (metadata != null)
-			{
-				{
-					TiffField field = metadata
-							.findEXIFValue(TIFF_TAG_XRESOLUTION);
-					if (field != null)
-						x_density = ((Number) field.getValue()).doubleValue();
-				}
-				{
-					TiffField field = metadata
-							.findEXIFValue(TIFF_TAG_YRESOLUTION);
-					if (field != null)
-						y_density = ((Number) field.getValue()).doubleValue();
-				}
-				{
-					TiffField field = metadata
-							.findEXIFValue(TIFF_TAG_RESOLUTION_UNIT);
-					if (field != null)
-					{
-						int density_units = ((Number) field.getValue())
-								.intValue();
-
-						switch (density_units)
-						{
-						case 1:
-							break;
-						case 2: // inches
-							units_per_inch = 1.0;
-							break;
-						case 3: // cms
-							units_per_inch = 2.54;
-							break;
-						default:
-							break;
-						}
-					}
-
-				}
-			}
-
-			FormatDetails = "Jpeg/DCM";
-
-		}
-
-		int PhysicalHeightDpi = -1;
-		float PhysicalHeightInch = -1;
-		int PhysicalWidthDpi = -1;
-		float PhysicalWidthInch = -1;
-
-		if (units_per_inch > 0)
-		{
-			PhysicalWidthDpi = (int) Math.round(x_density / units_per_inch);
-			PhysicalWidthInch = (float) (Width / (x_density * units_per_inch));
-			PhysicalHeightDpi = (int) Math.round(y_density 	* units_per_inch);
-			PhysicalHeightInch = (float) (Height / (y_density * units_per_inch));
-		}
-
-		ArrayList Comments = new ArrayList();
-		// TODO: comments...
-
-		int Number_of_components = fSOFNSegment.numberOfComponents;
-		int Precision = fSOFNSegment.precision;
-
-		int BitsPerPixel = Number_of_components * Precision;
-		ImageFormat Format = ImageFormat.IMAGE_FORMAT_JPEG;
-		String FormatName = "JPEG (Joint Photographic Experts Group) Format";
-		String MimeType = "image/jpeg";
-		// we ought to count images, but don't yet.
-		int NumberOfImages = 1;
-		// not accurate ... only reflects first
-		boolean isProgressive = fSOFNSegment.marker == SOF2Marker;
-
-		boolean isTransparent = false; // TODO: inaccurate.
-		boolean usesPalette = false; // TODO: inaccurate.
-		int ColorType;
-		if (Number_of_components == 1)
-			ColorType = ImageInfo.COLOR_TYPE_BW;
-		else if (Number_of_components == 3)
-			ColorType = ImageInfo.COLOR_TYPE_RGB;
-		else if (Number_of_components == 4)
-			ColorType = ImageInfo.COLOR_TYPE_CMYK;
-		else
-			ColorType = ImageInfo.COLOR_TYPE_UNKNOWN;
-
-		String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG;
-
-		ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments,
-				Format, FormatName, Height, MimeType, NumberOfImages,
-				PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi,
-				PhysicalWidthInch, Width, isProgressive, isTransparent,
-				usesPalette, ColorType, compressionAlgorithm);
-
-		return result;
-	}
-
-	// public ImageInfo getImageInfo(ByteSource byteSource, Map params)
-	// throws ImageReadException, IOException
-	// {
-	//
-	// ArrayList allSegments = readSegments(byteSource, null, false);
-	//
-	// final int SOF_MARKERS[] = new int[]{
-	// SOF0Marker, SOF1Marker, SOF2Marker, SOF3Marker, SOF5Marker,
-	// SOF6Marker, SOF7Marker, SOF9Marker, SOF10Marker, SOF11Marker,
-	// SOF13Marker, SOF14Marker, SOF15Marker,
-	// };
-	//
-	// ArrayList sofMarkers = new ArrayList();
-	// for(int i=0;i<SOF_MARKERS.length;i++)
-	// sofMarkers.add(new Integer(SOF_MARKERS[i]));
-	// ArrayList SOFSegments = filterSegments(allSegments, sofMarkers);
-	// if (SOFSegments == null || SOFSegments.size()<1)
-	// throw new ImageReadException("No SOFN Data Found.");
-	//
-	// List jfifMarkers = new ArrayList();
-	// jfifMarkers.add(new Integer(JFIFMarker));
-	// ArrayList jfifSegments = filterSegments(allSegments, jfifMarkers);
-	//
-	// SOFNSegment firstSOFNSegment = (SOFNSegment) SOFSegments.get(0);
-	//
-	// int Width = firstSOFNSegment.width;
-	// int Height = firstSOFNSegment.height;
-	//
-	// JFIFSegment jfifSegment = null;
-	//
-	// if (jfifSegments != null && jfifSegments.size() > 0)
-	// jfifSegment = (JFIFSegment) jfifSegments.get(0);
-	//
-	// double x_density = -1.0;
-	// double y_density = -1.0;
-	// double units_per_inch = -1.0;
-	// // int JFIF_major_version;
-	// // int JFIF_minor_version;
-	// String FormatDetails;
-	//
-	// if (jfifSegment != null)
-	// {
-	// x_density = jfifSegment.xDensity;
-	// y_density = jfifSegment.yDensity;
-	// int density_units = jfifSegment.densityUnits;
-	// // JFIF_major_version = fTheJFIFSegment.JFIF_major_version;
-	// // JFIF_minor_version = fTheJFIFSegment.JFIF_minor_version;
-	//
-	// FormatDetails = "Jpeg/JFIF v." + jfifSegment.jfifMajorVersion
-	// + "." + jfifSegment.jfifMinorVersion;
-	//
-	// switch (density_units)
-	// {
-	// case 0 :
-	// break;
-	// case 1 : // inches
-	// units_per_inch = 1.0;
-	// break;
-	// case 2 : // cms
-	// units_per_inch = 2.54;
-	// break;
-	// default :
-	// break;
-	// }
-	// }
-	// else
-	// {
-	// JpegImageMetadata metadata = (JpegImageMetadata) getMetadata(byteSource,
-	// params);
-	//
-	// {
-	// TiffField field = metadata
-	// .findEXIFValue(TiffField.TIFF_TAG_XRESOLUTION);
-	// if (field == null)
-	// throw new ImageReadException("No XResolution");
-	//
-	// x_density = ((Number) field.getValue()).doubleValue();
-	// }
-	// {
-	// TiffField field = metadata
-	// .findEXIFValue(TiffField.TIFF_TAG_YRESOLUTION);
-	// if (field == null)
-	// throw new ImageReadException("No YResolution");
-	//
-	// y_density = ((Number) field.getValue()).doubleValue();
-	// }
-	// {
-	// TiffField field = metadata
-	// .findEXIFValue(TiffField.TIFF_TAG_RESOLUTION_UNIT);
-	// if (field == null)
-	// throw new ImageReadException("No ResolutionUnits");
-	//
-	// int density_units = ((Number) field.getValue()).intValue();
-	//
-	// switch (density_units)
-	// {
-	// case 1 :
-	// break;
-	// case 2 : // inches
-	// units_per_inch = 1.0;
-	// break;
-	// case 3 : // cms
-	// units_per_inch = 2.54;
-	// break;
-	// default :
-	// break;
-	// }
-	//
-	// }
-	//
-	// FormatDetails = "Jpeg/DCM";
-	//
-	// }
-	//
-	// int PhysicalHeightDpi = -1;
-	// float PhysicalHeightInch = -1;
-	// int PhysicalWidthDpi = -1;
-	// float PhysicalWidthInch = -1;
-	//
-	// if (units_per_inch > 0)
-	// {
-	// PhysicalWidthDpi = (int) Math.round((double) x_density
-	// / units_per_inch);
-	// PhysicalWidthInch = (float) ((double) Width / (x_density *
-	// units_per_inch));
-	// PhysicalHeightDpi = (int) Math.round((double) y_density
-	// * units_per_inch);
-	// PhysicalHeightInch = (float) ((double) Height / (y_density *
-	// units_per_inch));
-	// }
-	//
-	// ArrayList Comments = new ArrayList();
-	// // TODO: comments...
-	//
-	// int Number_of_components = firstSOFNSegment.numberOfComponents;
-	// int Precision = firstSOFNSegment.precision;
-	//
-	// int BitsPerPixel = Number_of_components * Precision;
-	// ImageFormat Format = ImageFormat.IMAGE_FORMAT_JPEG;
-	// String FormatName = "JPEG (Joint Photographic Experts Group) Format";
-	// String MimeType = "image/jpeg";
-	// // we ought to count images, but don't yet.
-	// int NumberOfImages = -1;
-	// // not accurate ... only reflects first
-	// boolean isProgressive = firstSOFNSegment.marker == SOF2Marker;
-	//
-	// boolean isTransparent = false; // TODO: inaccurate.
-	// boolean usesPalette = false; // TODO: inaccurate.
-	// int ColorType;
-	// if (Number_of_components == 1)
-	// ColorType = ImageInfo.COLOR_TYPE_BW;
-	// else if (Number_of_components == 3)
-	// ColorType = ImageInfo.COLOR_TYPE_RGB;
-	// else if (Number_of_components == 4)
-	// ColorType = ImageInfo.COLOR_TYPE_CMYK;
-	// else
-	// ColorType = ImageInfo.COLOR_TYPE_UNKNOWN;
-	//
-	// String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG;
-	//
-	// ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments,
-	// Format, FormatName, Height, MimeType, NumberOfImages,
-	// PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi,
-	// PhysicalWidthInch, Width, isProgressive, isTransparent,
-	// usesPalette, ColorType, compressionAlgorithm);
-	//
-	// return result;
-	// }
-
-	public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
-			throws ImageReadException, IOException
-	{
-		pw.println("tiff.dumpImageFile");
-
-		{
-			ImageInfo imageInfo = getImageInfo(byteSource);
-			if (imageInfo == null)
-				return false;
-
-			imageInfo.toString(pw, "");
-		}
-
-		pw.println("");
-
-		{
-			ArrayList segments = readSegments(byteSource, null, false);
-
-			if (segments == null)
-				throw new ImageReadException("No Segments Found.");
-
-			for (int d = 0; d < segments.size(); d++)
-			{
-
-				Segment segment = (Segment) segments.get(d);
-
-				NumberFormat nf = NumberFormat.getIntegerInstance();
-				// this.debugNumber("found, marker: ", marker, 4);
-				pw.println(d + ": marker: "
-						+ Integer.toHexString(segment.marker) + ", "
-						+ segment.getDescription() + " (length: "
-						+ nf.format(segment.length) + ")");
-				segment.dump(pw);
-			}
+    public JpegImageParser()
+    {
+        setByteOrder(BYTE_ORDER_NETWORK);
+        // setDebug(true);
+    }
+
+    protected ImageFormat[] getAcceptedTypes()
+    {
+        return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_JPEG, //
+        };
+    }
+
+    public String getName()
+    {
+        return "Jpeg-Custom";
+    }
+
+    public String getDefaultExtension()
+    {
+        return DEFAULT_EXTENSION;
+    }
+
+    private static final String DEFAULT_EXTENSION = ".jpg";
+
+    public static final String AcceptedExtensions[] = { ".jpg", ".jpeg", };
+
+    protected String[] getAcceptedExtensions()
+    {
+        return AcceptedExtensions;
+    }
+
+    public final BufferedImage getBufferedImage(ByteSource byteSource,
+            Map params) throws ImageReadException, IOException
+    {
+        throw new ImageReadException(
+                "Sanselan cannot read or write JPEG images.");
+    }
+
+    private boolean keepMarker(int marker, int markers[])
+    {
+        if (markers == null)
+            return true;
+
+        for (int i = 0; i < markers.length; i++)
+        {
+            if (markers[i] == marker)
+                return true;
+        }
+
+        return false;
+    }
+
+    public ArrayList readSegments(ByteSource byteSource, final int markers[],
+            final boolean returnAfterFirst, boolean readEverything)
+            throws ImageReadException, IOException
+    {
+        final ArrayList result = new ArrayList();
+        final JpegImageParser parser = this;
+
+        JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
+            // return false to exit before reading image data.
+            public boolean beginSOS()
+            {
+                return false;
+            }
+
+            public void visitSOS(int marker, byte markerBytes[],
+                    byte imageData[])
+            {
+            }
+
+            // return false to exit traversal.
+            public boolean visitSegment(int marker, byte markerBytes[],
+                    int markerLength, byte markerLengthBytes[],
+                    byte segmentData[]) throws ImageReadException, IOException
+            {
+                if (marker == 0xffd9)
+                    return false;
+
+                // Debug.debug("visitSegment marker", marker);
+                // // Debug.debug("visitSegment keepMarker(marker, markers)",
+                // keepMarker(marker, markers));
+                // Debug.debug("visitSegment keepMarker(marker, markers)",
+                // keepMarker(marker, markers));
+
+                if (!keepMarker(marker, markers))
+                    return true;
+
+                if (marker == JPEG_APP13_Marker)
+                {
+                    // Debug.debug("app 13 segment data", segmentData.length);
+                    result.add(new App13Segment(parser, marker, segmentData));
+                } else if (marker == JPEG_APP2_Marker)
+                {
+                    result.add(new App2Segment(marker, segmentData));
+                } else if (marker == JFIFMarker)
+                {
+                    result.add(new JFIFSegment(marker, segmentData));
+                } else if ((marker >= SOF0Marker) && (marker <= SOF15Marker))
+                {
+                    result.add(new SOFNSegment(marker, segmentData));
+                } else if ((marker >= JPEG_APP1_Marker)
+                        && (marker <= JPEG_APP15_Marker))
+                {
+                    result.add(new UnknownSegment(marker, segmentData));
+                }
+
+                if (returnAfterFirst)
+                    return false;
+
+                return true;
+            }
+        };
+
+        new JpegUtils().traverseJFIF(byteSource, visitor);
+
+        return result;
+    }
+
+    public static final boolean permissive = true;
+
+    private byte[] assembleSegments(ArrayList v) throws ImageReadException
+    {
+        try
+        {
+            return assembleSegments(v, false);
+        } catch (ImageReadException e)
+        {
+            return assembleSegments(v, true);
+        }
+    }
+
+    private byte[] assembleSegments(ArrayList v, boolean start_with_zero)
+            throws ImageReadException
+    {
+        if (v.size() < 1)
+            throw new ImageReadException("No App2 Segments Found.");
+
+        int markerCount = ((App2Segment) v.get(0)).num_markers;
+
+        // if (permissive && (markerCount == 0))
+        // markerCount = v.size();
+
+        if (v.size() != markerCount)
+            throw new ImageReadException("App2 Segments Missing.  Found: "
+                    + v.size() + ", Expected: " + markerCount + ".");
+
+        Collections.sort(v);
+
+        int offset = start_with_zero ? 0 : 1;
+
+        int total = 0;
+        for (int i = 0; i < v.size(); i++)
+        {
+            App2Segment segment = (App2Segment) v.get(i);
+
+            if ((i + offset) != segment.cur_marker)
+            {
+                dumpSegments(v);
+                throw new ImageReadException(
+                        "Incoherent App2 Segment Ordering.  i: " + i
+                                + ", segment[" + i + "].cur_marker: "
+                                + segment.cur_marker + ".");
+            }
+
+            if (markerCount != segment.num_markers)
+            {
+                dumpSegments(v);
+                throw new ImageReadException(
+                        "Inconsistent App2 Segment Count info.  markerCount: "
+                                + markerCount + ", segment[" + i
+                                + "].num_markers: " + segment.num_markers + ".");
+            }
+
+            total += segment.icc_bytes.length;
+        }
+
+        byte result[] = new byte[total];
+        int progress = 0;
+
+        for (int i = 0; i < v.size(); i++)
+        {
+            App2Segment segment = (App2Segment) v.get(i);
+
+            System.arraycopy(segment.icc_bytes, 0, result, progress,
+                    segment.icc_bytes.length);
+            progress += segment.icc_bytes.length;
+        }
+
+        return result;
+    }
+
+    private void dumpSegments(ArrayList v)
+    {
+        Debug.debug();
+        Debug.debug("dumpSegments", v.size());
+
+        for (int i = 0; i < v.size(); i++)
+        {
+            App2Segment segment = (App2Segment) v.get(i);
+
+            Debug.debug((i) + ": " + segment.cur_marker + " / "
+                    + segment.num_markers);
+        }
+        Debug.debug();
+    }
+
+    public ArrayList readSegments(ByteSource byteSource, int markers[],
+            boolean returnAfterFirst) throws ImageReadException, IOException
+    {
+        return readSegments(byteSource, markers, returnAfterFirst, false);
+    }
+
+    public byte[] getICCProfileBytes(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        ArrayList segments = readSegments(byteSource,
+                new int[] { JPEG_APP2_Marker, }, false);
+
+        if (segments != null)
+        {
+            // throw away non-icc profile app2 segments.
+            ArrayList filtered = new ArrayList();
+            for (int i = 0; i < segments.size(); i++)
+            {
+                App2Segment segment = (App2Segment) segments.get(i);
+                if (segment.icc_bytes != null)
+                    filtered.add(segment);
+            }
+            segments = filtered;
+        }
+
+        if ((segments == null) || (segments.size() < 1))
+            return null;
+
+        byte bytes[] = assembleSegments(segments);
+
+        if (debug)
+            System.out.println("bytes" + ": "
+                    + ((bytes == null) ? null : "" + bytes.length));
+
+        if (debug)
+            System.out.println("");
+
+        return (bytes);
+    }
+
+    public IImageMetadata getMetadata(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        TiffImageMetadata exif = getExifMetadata(byteSource, params);
+
+        JpegPhotoshopMetadata photoshop = getPhotoshopMetadata(byteSource,
+                params);
+
+        if (null == exif && null == photoshop)
+            return null;
+
+        JpegImageMetadata result = new JpegImageMetadata(photoshop, exif);
+
+        return result;
+    }
+
+    public static boolean isExifAPP1Segment(GenericSegment segment)
+    {
+        return byteArrayHasPrefix(segment.bytes, EXIF_IDENTIFIER_CODE);
+    }
+
+    private ArrayList filterAPP1Segments(ArrayList v)
+    {
+        ArrayList result = new ArrayList();
+
+        for (int i = 0; i < v.size(); i++)
+        {
+            GenericSegment segment = (GenericSegment) v.get(i);
+            if (isExifAPP1Segment(segment))
+                result.add(segment);
+        }
+
+        return result;
+    }
+
+    // TODO unused
+    private ArrayList filterSegments(ArrayList v, List markers)
+    {
+        ArrayList result = new ArrayList();
+
+        for (int i = 0; i < v.size(); i++)
+        {
+            Segment segment = (Segment) v.get(i);
+            Integer marker = new Integer(segment.marker);
+            if (markers.contains(marker))
+                result.add(segment);
+        }
+
+        return result;
+    }
+
+    public TiffImageMetadata getExifMetadata(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        byte bytes[] = getExifRawData(byteSource);
+        if (null == bytes)
+            return null;
+
+        if (params == null)
+            params = new HashMap();
+        if (!params.containsKey(PARAM_KEY_READ_THUMBNAILS))
+            params.put(PARAM_KEY_READ_THUMBNAILS, Boolean.TRUE);
+
+        return (TiffImageMetadata) new TiffImageParser().getMetadata(bytes,
+                params);
+    }
+
+    public byte[] getExifRawData(ByteSource byteSource)
+            throws ImageReadException, IOException
+    {
+        ArrayList segments = readSegments(byteSource,
+                new int[] { JPEG_APP1_Marker, }, false);
+
+        if ((segments == null) || (segments.size() < 1))
+            return null;
+
+        ArrayList exifSegments = filterAPP1Segments(segments);
+        if (debug)
+            System.out.println("exif_segments.size" + ": "
+                    + exifSegments.size());
+
+        // Debug.debug("segments", segments);
+        // Debug.debug("exifSegments", exifSegments);
+
+        // TODO: concatenate if multiple segments, need example.
+        if (exifSegments.size() < 1)
+            return null;
+        if (exifSegments.size() > 1)
+            throw new ImageReadException(
+                    "Sanselan currently can't parse EXIF metadata split across multiple APP1 segments.  "
+                            + "Please send this image to the Sanselan project.");
+
+        GenericSegment segment = (GenericSegment) exifSegments.get(0);
+        byte bytes[] = segment.bytes;
+
+        // byte head[] = readBytearray("exif head", bytes, 0, 6);
+        //
+        // Debug.debug("head", head);
+
+        return getByteArrayTail("trimmed exif bytes", bytes, 6);
+    }
+
+    public boolean hasExifSegment(ByteSource byteSource)
+            throws ImageReadException, IOException
+    {
+        final boolean result[] = { false, };
+
+        JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
+            // return false to exit before reading image data.
+            public boolean beginSOS()
+            {
+                return false;
+            }
+
+            public void visitSOS(int marker, byte markerBytes[],
+                    byte imageData[])
+            {
+            }
+
+            // return false to exit traversal.
+            public boolean visitSegment(int marker, byte markerBytes[],
+                    int markerLength, byte markerLengthBytes[],
+                    byte segmentData[]) throws ImageReadException, IOException
+            {
+                if (marker == 0xffd9)
+                    return false;
+
+                if (marker == JPEG_APP1_Marker)
+                {
+                    if (byteArrayHasPrefix(segmentData, EXIF_IDENTIFIER_CODE))
+                    {
+                        result[0] = true;
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        };
+
+        new JpegUtils().traverseJFIF(byteSource, visitor);
+
+        return result[0];
+    }
+
+    public boolean hasIptcSegment(ByteSource byteSource)
+            throws ImageReadException, IOException
+    {
+        final boolean result[] = { false, };
+
+        JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
+            // return false to exit before reading image data.
+            public boolean beginSOS()
+            {
+                return false;
+            }
+
+            public void visitSOS(int marker, byte markerBytes[],
+                    byte imageData[])
+            {
+            }
+
+            // return false to exit traversal.
+            public boolean visitSegment(int marker, byte markerBytes[],
+                    int markerLength, byte markerLengthBytes[],
+                    byte segmentData[]) throws ImageReadException, IOException
+            {
+                if (marker == 0xffd9)
+                    return false;
+
+                if (marker == JPEG_APP13_Marker)
+                {
+                    if (new IPTCParser().isPhotoshopJpegSegment(segmentData))
+                    {
+                        result[0] = true;
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        };
+
+        new JpegUtils().traverseJFIF(byteSource, visitor);
+
+        return result[0];
+    }
+
+    public boolean hasXmpSegment(ByteSource byteSource)
+            throws ImageReadException, IOException
+    {
+        final boolean result[] = { false, };
+
+        JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
+            // return false to exit before reading image data.
+            public boolean beginSOS()
+            {
+                return false;
+            }
+
+            public void visitSOS(int marker, byte markerBytes[],
+                    byte imageData[])
+            {
+            }
+
+            // return false to exit traversal.
+            public boolean visitSegment(int marker, byte markerBytes[],
+                    int markerLength, byte markerLengthBytes[],
+                    byte segmentData[]) throws ImageReadException, IOException
+            {
+                if (marker == 0xffd9)
+                    return false;
+
+                if (marker == JPEG_APP1_Marker)
+                {
+                    if (new JpegXmpParser().isXmpJpegSegment(segmentData))
+                    {
+                        result[0] = true;
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        };
+        new JpegUtils().traverseJFIF(byteSource, visitor);
+
+        return result[0];
+    }
+
+    /**
+     * Extracts embedded XML metadata as XML string.
+     * <p>
+     *
+     * @param byteSource
+     *            File containing image data.
+     * @param params
+     *            Map of optional parameters, defined in SanselanConstants.
+     * @return Xmp Xml as String, if present. Otherwise, returns null.
+     */
+    public String getXmpXml(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+
+        final List result = new ArrayList();
+
+        JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
+            // return false to exit before reading image data.
+            public boolean beginSOS()
+            {
+                return false;
+            }
+
+            public void visitSOS(int marker, byte markerBytes[],
+                    byte imageData[])
+            {
+            }
+
+            // return false to exit traversal.
+            public boolean visitSegment(int marker, byte markerBytes[],
+                    int markerLength, byte markerLengthBytes[],
+                    byte segmentData[]) throws ImageReadException, IOException
+            {
+                if (marker == 0xffd9)
+                    return false;
+
+                if (marker == JPEG_APP1_Marker)
+                {
+                    if (new JpegXmpParser().isXmpJpegSegment(segmentData))
+                    {
+                        result.add(new JpegXmpParser()
+                                .parseXmpJpegSegment(segmentData));
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        };
+        new JpegUtils().traverseJFIF(byteSource, visitor);
+
+        if (result.size() < 1)
+            return null;
+        if (result.size() > 1)
+            throw new ImageReadException(
+                    "Jpeg file contains more than one XMP segment.");
+        return (String) result.get(0);
+    }
+
+    public JpegPhotoshopMetadata getPhotoshopMetadata(ByteSource byteSource,
+            Map params) throws ImageReadException, IOException
+    {
+        ArrayList segments = readSegments(byteSource,
+                new int[] { JPEG_APP13_Marker, }, false);
+
+        if ((segments == null) || (segments.size() < 1))
+            return null;
+
+        PhotoshopApp13Data photoshopApp13Data = null;
+
+        for (int i = 0; i < segments.size(); i++)
+        {
+            App13Segment segment = (App13Segment) segments.get(i);
+
+            PhotoshopApp13Data data = segment.parsePhotoshopSegment(params);
+            if (data != null && photoshopApp13Data != null)
+                throw new ImageReadException(
+                        "Jpeg contains more than one Photoshop App13 segment.");
+
+            photoshopApp13Data = data;
+        }
+
+        if(null==photoshopApp13Data)
+            return null;
+        return new JpegPhotoshopMetadata(photoshopApp13Data);
+    }
+
+    public Dimension getImageSize(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        ArrayList segments = readSegments(byteSource, new int[] {
+                // kJFIFMarker,
+                SOF0Marker,
+
+                SOF1Marker, SOF2Marker, SOF3Marker, SOF5Marker, SOF6Marker,
+                SOF7Marker, SOF9Marker, SOF10Marker, SOF11Marker, SOF13Marker,
+                SOF14Marker, SOF15Marker,
+
+        }, true);
+
+        if ((segments == null) || (segments.size() < 1))
+            throw new ImageReadException("No JFIF Data Found.");
+
+        if (segments.size() > 1)
+            throw new ImageReadException("Redundant JFIF Data Found.");
+
+        SOFNSegment fSOFNSegment = (SOFNSegment) segments.get(0);
+
+        return new Dimension(fSOFNSegment.width, fSOFNSegment.height);
+    }
+
+    public byte[] embedICCProfile(byte image[], byte profile[])
+    {
+        return null;
+    }
+
+    public boolean embedICCProfile(File src, File dst, byte profile[])
+    {
+        return false;
+    }
+
+    public ImageInfo getImageInfo(ByteSource byteSource, Map params)
+            throws ImageReadException, IOException
+    {
+        // ArrayList allSegments = readSegments(byteSource, null, false);
+
+        ArrayList SOF_segments = readSegments(byteSource, new int[] {
+                // kJFIFMarker,
+
+                SOF0Marker, SOF1Marker, SOF2Marker, SOF3Marker, SOF5Marker,
+                SOF6Marker, SOF7Marker, SOF9Marker, SOF10Marker, SOF11Marker,
+                SOF13Marker, SOF14Marker, SOF15Marker,
+
+        }, false);
+
+        if (SOF_segments == null)
+            throw new ImageReadException("No SOFN Data Found.");
+
+        // if (SOF_segments.size() != 1)
+        // System.out.println("Incoherent SOFN Data Found: "
+        // + SOF_segments.size());
+
+        ArrayList jfifSegments = readSegments(byteSource,
+                new int[] { JFIFMarker, }, true);
+
+        SOFNSegment fSOFNSegment = (SOFNSegment) SOF_segments.get(0);
+        // SOFNSegment fSOFNSegment = (SOFNSegment) findSegment(segments,
+        // SOFNmarkers);
+
+        if (fSOFNSegment == null)
+            throw new ImageReadException("No SOFN Data Found.");
+
+        int Width = fSOFNSegment.width;
+        int Height = fSOFNSegment.height;
+
+        JFIFSegment jfifSegment = null;
+
+        if ((jfifSegments != null) && (jfifSegments.size() > 0))
+            jfifSegment = (JFIFSegment) jfifSegments.get(0);
+
+        // JFIFSegment fTheJFIFSegment = (JFIFSegment) findSegment(segments,
+        // kJFIFMarker);
+
+        double x_density = -1.0;
+        double y_density = -1.0;
+        double units_per_inch = -1.0;
+        // int JFIF_major_version;
+        // int JFIF_minor_version;
+        String FormatDetails;
+
+        if (jfifSegment != null)
+        {
+            x_density = jfifSegment.xDensity;
+            y_density = jfifSegment.yDensity;
+            int density_units = jfifSegment.densityUnits;
+            // JFIF_major_version = fTheJFIFSegment.JFIF_major_version;
+            // JFIF_minor_version = fTheJFIFSegment.JFIF_minor_version;
+
+            FormatDetails = "Jpeg/JFIF v." + jfifSegment.jfifMajorVersion + "."
+                    + jfifSegment.jfifMinorVersion;
+
+            switch (density_units)
+            {
+            case 0:
+                break;
+            case 1: // inches
+                units_per_inch = 1.0;
+                break;
+            case 2: // cms
+                units_per_inch = 2.54;
+                break;
+            default:
+                break;
+            }
+        } else
+        {
+            JpegImageMetadata metadata = (JpegImageMetadata) getMetadata(
+                    byteSource, params);
+
+            if (metadata != null)
+            {
+                {
+                    TiffField field = metadata
+                            .findEXIFValue(TIFF_TAG_XRESOLUTION);
+                    if (field != null)
+                        x_density = ((Number) field.getValue()).doubleValue();
+                }
+                {
+                    TiffField field = metadata
+                            .findEXIFValue(TIFF_TAG_YRESOLUTION);
+                    if (field != null)
+                        y_density = ((Number) field.getValue()).doubleValue();
+                }
+                {
+                    TiffField field = metadata
+                            .findEXIFValue(TIFF_TAG_RESOLUTION_UNIT);
+                    if (field != null)
+                    {
+                        int density_units = ((Number) field.getValue())
+                                .intValue();
+
+                        switch (density_units)
+                        {
+                        case 1:
+                            break;
+                        case 2: // inches
+                            units_per_inch = 1.0;
+                            break;
+                        case 3: // cms
+                            units_per_inch = 2.54;
+                            break;
+                        default:
+                            break;
+                        }
+                    }
+
+                }
+            }
+
+            FormatDetails = "Jpeg/DCM";
+
+        }
+
+        int PhysicalHeightDpi = -1;
+        float PhysicalHeightInch = -1;
+        int PhysicalWidthDpi = -1;
+        float PhysicalWidthInch = -1;
+
+        if (units_per_inch > 0)
+        {
+            PhysicalWidthDpi = (int) Math.round(x_density / units_per_inch);
+            PhysicalWidthInch = (float) (Width / (x_density * units_per_inch));
+            PhysicalHeightDpi = (int) Math.round(y_density     * units_per_inch);
+            PhysicalHeightInch = (float) (Height / (y_density * units_per_inch));
+        }
+
+        ArrayList Comments = new ArrayList();
+        // TODO: comments...
+
+        int Number_of_components = fSOFNSegment.numberOfComponents;
+        int Precision = fSOFNSegment.precision;
+
+        int BitsPerPixel = Number_of_components * Precision;
+        ImageFormat Format = ImageFormat.IMAGE_FORMAT_JPEG;
+        String FormatName = "JPEG (Joint Photographic Experts Group) Format";
+        String MimeType = "image/jpeg";
+        // we ought to count images, but don't yet.
+        int NumberOfImages = 1;
+        // not accurate ... only reflects first
+        boolean isProgressive = fSOFNSegment.marker == SOF2Marker;
+
+        boolean isTransparent = false; // TODO: inaccurate.
+        boolean usesPalette = false; // TODO: inaccurate.
+        int ColorType;
+        if (Number_of_components == 1)
+            ColorType = ImageInfo.COLOR_TYPE_BW;
+        else if (Number_of_components == 3)
+            ColorType = ImageInfo.COLOR_TYPE_RGB;
+        else if (Number_of_components == 4)
+            ColorType = ImageInfo.COLOR_TYPE_CMYK;
+        else
+            ColorType = ImageInfo.COLOR_TYPE_UNKNOWN;
+
+        String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG;
+
+        ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments,
+                Format, FormatName, Height, MimeType, NumberOfImages,
+                PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi,
+                PhysicalWidthInch, Width, isProgressive, isTransparent,
+                usesPalette, ColorType, compressionAlgorithm);
+
+        return result;
+    }
+
+    // public ImageInfo getImageInfo(ByteSource byteSource, Map params)
+    // throws ImageReadException, IOException
+    // {
+    //
+    // ArrayList allSegments = readSegments(byteSource, null, false);
+    //
+    // final int SOF_MARKERS[] = new int[]{
+    // SOF0Marker, SOF1Marker, SOF2Marker, SOF3Marker, SOF5Marker,
+    // SOF6Marker, SOF7Marker, SOF9Marker, SOF10Marker, SOF11Marker,
+    // SOF13Marker, SOF14Marker, SOF15Marker,
+    // };
+    //
+    // ArrayList sofMarkers = new ArrayList();
+    // for(int i=0;i<SOF_MARKERS.length;i++)
+    // sofMarkers.add(new Integer(SOF_MARKERS[i]));
+    // ArrayList SOFSegments = filterSegments(allSegments, sofMarkers);
+    // if (SOFSegments == null || SOFSegments.size()<1)
+    // throw new ImageReadException("No SOFN Data Found.");
+    //
+    // List jfifMarkers = new ArrayList();
+    // jfifMarkers.add(new Integer(JFIFMarker));
+    // ArrayList jfifSegments = filterSegments(allSegments, jfifMarkers);
+    //
+    // SOFNSegment firstSOFNSegment = (SOFNSegment) SOFSegments.get(0);
+    //
+    // int Width = firstSOFNSegment.width;
+    // int Height = firstSOFNSegment.height;
+    //
+    // JFIFSegment jfifSegment = null;
+    //
+    // if (jfifSegments != null && jfifSegments.size() > 0)
+    // jfifSegment = (JFIFSegment) jfifSegments.get(0);
+    //
+    // double x_density = -1.0;
+    // double y_density = -1.0;
+    // double units_per_inch = -1.0;
+    // // int JFIF_major_version;
+    // // int JFIF_minor_version;
+    // String FormatDetails;
+    //
+    // if (jfifSegment != null)
+    // {
+    // x_density = jfifSegment.xDensity;
+    // y_density = jfifSegment.yDensity;
+    // int density_units = jfifSegment.densityUnits;
+    // // JFIF_major_version = fTheJFIFSegment.JFIF_major_version;
+    // // JFIF_minor_version = fTheJFIFSegment.JFIF_minor_version;
+    //
+    // FormatDetails = "Jpeg/JFIF v." + jfifSegment.jfifMajorVersion
+    // + "." + jfifSegment.jfifMinorVersion;
+    //
+    // switch (density_units)
+    // {
+    // case 0 :
+    // break;
+    // case 1 : // inches
+    // units_per_inch = 1.0;
+    // break;
+    // case 2 : // cms
+    // units_per_inch = 2.54;
+    // break;
+    // default :
+    // break;
+    // }
+    // }
+    // else
+    // {
+    // JpegImageMetadata metadata = (JpegImageMetadata) getMetadata(byteSource,
+    // params);
+    //
+    // {
+    // TiffField field = metadata
+    // .findEXIFValue(TiffField.TIFF_TAG_XRESOLUTION);
+    // if (field == null)
+    // throw new ImageReadException("No XResolution");
+    //
+    // x_density = ((Number) field.getValue()).doubleValue();
+    // }
+    // {
+    // TiffField field = metadata
+    // .findEXIFValue(TiffField.TIFF_TAG_YRESOLUTION);
+    // if (field == null)
+    // throw new ImageReadException("No YResolution");
+    //
+    // y_density = ((Number) field.getValue()).doubleValue();
+    // }
+    // {
+    // TiffField field = metadata
+    // .findEXIFValue(TiffField.TIFF_TAG_RESOLUTION_UNIT);
+    // if (field == null)
+    // throw new ImageReadException("No ResolutionUnits");
+    //
+    // int density_units = ((Number) field.getValue()).intValue();
+    //
+    // switch (density_units)
+    // {
+    // case 1 :
+    // break;
+    // case 2 : // inches
+    // units_per_inch = 1.0;
+    // break;
+    // case 3 : // cms
+    // units_per_inch = 2.54;
+    // break;
+    // default :
+    // break;
+    // }
+    //
+    // }
+    //
+    // FormatDetails = "Jpeg/DCM";
+    //
+    // }
+    //
+    // int PhysicalHeightDpi = -1;
+    // float PhysicalHeightInch = -1;
+    // int PhysicalWidthDpi = -1;
+    // float PhysicalWidthInch = -1;
+    //
+    // if (units_per_inch > 0)
+    // {
+    // PhysicalWidthDpi = (int) Math.round((double) x_density
+    // / units_per_inch);
+    // PhysicalWidthInch = (float) ((double) Width / (x_density *
+    // units_per_inch));
+    // PhysicalHeightDpi = (int) Math.round((double) y_density
+    // * units_per_inch);
+    // PhysicalHeightInch = (float) ((double) Height / (y_density *
+    // units_per_inch));
+    // }
+    //
+    // ArrayList Comments = new ArrayList();
+    // // TODO: comments...
+    //
+    // int Number_of_components = firstSOFNSegment.numberOfComponents;
+    // int Precision = firstSOFNSegment.precision;
+    //
+    // int BitsPerPixel = Number_of_components * Precision;
+    // ImageFormat Format = ImageFormat.IMAGE_FORMAT_JPEG;
+    // String FormatName = "JPEG (Joint Photographic Experts Group) Format";
+    // String MimeType = "image/jpeg";
+    // // we ought to count images, but don't yet.
+    // int NumberOfImages = -1;
+    // // not accurate ... only reflects first
+    // boolean isProgressive = firstSOFNSegment.marker == SOF2Marker;
+    //
+    // boolean isTransparent = false; // TODO: inaccurate.
+    // boolean usesPalette = false; // TODO: inaccurate.
+    // int ColorType;
+    // if (Number_of_components == 1)
+    // ColorType = ImageInfo.COLOR_TYPE_BW;
+    // else if (Number_of_components == 3)
+    // ColorType = ImageInfo.COLOR_TYPE_RGB;
+    // else if (Number_of_components == 4)
+    // ColorType = ImageInfo.COLOR_TYPE_CMYK;
+    // else
+    // ColorType = ImageInfo.COLOR_TYPE_UNKNOWN;
+    //
+    // String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG;
+    //
+    // ImageInfo result = new ImageInfo(FormatDetails, BitsPerPixel, Comments,
+    // Format, FormatName, Height, MimeType, NumberOfImages,
+    // PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi,
+    // PhysicalWidthInch, Width, isProgressive, isTransparent,
+    // usesPalette, ColorType, compressionAlgorithm);
+    //
+    // return result;
+    // }
+
+    public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
+            throws ImageReadException, IOException
+    {
+        pw.println("tiff.dumpImageFile");
+
+        {
+            ImageInfo imageInfo = getImageInfo(byteSource);
+            if (imageInfo == null)
+                return false;
+
+            imageInfo.toString(pw, "");
+        }
+
+        pw.println("");
+
+        {
+            ArrayList segments = readSegments(byteSource, null, false);
+
+            if (segments == null)
+                throw new ImageReadException("No Segments Found.");
+
+            for (int d = 0; d < segments.size(); d++)
+            {
+
+                Segment segment = (Segment) segments.get(d);
+
+                NumberFormat nf = NumberFormat.getIntegerInstance();
+                // this.debugNumber("found, marker: ", marker, 4);
+                pw.println(d + ": marker: "
+                        + Integer.toHexString(segment.marker) + ", "
+                        + segment.getDescription() + " (length: "
+                        + nf.format(segment.length) + ")");
+                segment.dump(pw);
+            }
 
-			pw.println("");
-		}
+            pw.println("");
+        }
 
-		return true;
-	}
+        return true;
+    }
 
 }
\ No newline at end of file

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegPhotoshopMetadata.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegPhotoshopMetadata.java?rev=995859&r1=995858&r2=995859&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegPhotoshopMetadata.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegPhotoshopMetadata.java Fri Sep 10 16:33:35 2010
@@ -26,28 +26,28 @@ import org.apache.sanselan.formats.jpeg.
 import org.apache.sanselan.util.Debug;
 
 public class JpegPhotoshopMetadata extends ImageMetadata implements
-		IPTCConstants
+        IPTCConstants
 {
 
-	public final PhotoshopApp13Data photoshopApp13Data;
+    public final PhotoshopApp13Data photoshopApp13Data;
 
-	public JpegPhotoshopMetadata(final PhotoshopApp13Data photoshopApp13Data)
-	{
-		this.photoshopApp13Data = photoshopApp13Data;
-
-		List records = photoshopApp13Data.getRecords();
-		Collections.sort(records, IPTCRecord.COMPARATOR);
-		for (int j = 0; j < records.size(); j++)
-		{
-			IPTCRecord element = (IPTCRecord) records.get(j);
-			if (element.iptcType.type != IPTC_TYPE_RECORD_VERSION.type)
-				add(element.getIptcTypeName(), element.getValue());
-		}
-	}
-
-	public void dump()
-	{
-		Debug.debug(this.toString());
-	}
+    public JpegPhotoshopMetadata(final PhotoshopApp13Data photoshopApp13Data)
+    {
+        this.photoshopApp13Data = photoshopApp13Data;
+
+        List records = photoshopApp13Data.getRecords();
+        Collections.sort(records, IPTCRecord.COMPARATOR);
+        for (int j = 0; j < records.size(); j++)
+        {
+            IPTCRecord element = (IPTCRecord) records.get(j);
+            if (element.iptcType.type != IPTC_TYPE_RECORD_VERSION.type)
+                add(element.getIptcTypeName(), element.getValue());
+        }
+    }
+
+    public void dump()
+    {
+        Debug.debug(this.toString());
+    }
 
 }
\ No newline at end of file

Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java?rev=995859&r1=995858&r2=995859&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java Fri Sep 10 16:33:35 2010
@@ -27,184 +27,184 @@ import org.apache.sanselan.util.Debug;
 
 public class JpegUtils extends BinaryFileParser implements JpegConstants
 {
-	public JpegUtils()
-	{
-		setByteOrder(BYTE_ORDER_NETWORK);
-	}
-
-	public static interface Visitor
-	{
-		// return false to exit before reading image data.
-		public boolean beginSOS();
-
-		public void visitSOS(int marker, byte markerBytes[], byte imageData[]);
-
-		// return false to exit traversal.
-		public boolean visitSegment(int marker, byte markerBytes[],
-				int segmentLength, byte segmentLengthBytes[],
-				byte segmentData[]) throws ImageReadException,
-		// ImageWriteException,
-				IOException;
-	}
-
-	public void traverseJFIF(ByteSource byteSource, Visitor visitor)
-			throws ImageReadException,
-			// ImageWriteException,
-			IOException
-	{
-		InputStream is = null;
-
-		try
-		{
-			is = byteSource.getInputStream();
-
-			readAndVerifyBytes(is, SOI,
-					"Not a Valid JPEG File: doesn't begin with 0xffd8");
-
-			int byteOrder = getByteOrder();
-
-			for (int markerCount = 0; true; markerCount++)
-			{
-				byte markerBytes[] = readByteArray("markerBytes", 2, is,
-						"markerBytes");
-				int marker = convertByteArrayToShort("marker", markerBytes,
-						byteOrder);
-
-//				Debug.debug("marker", marker + " (0x" + Integer.toHexString(marker) + ")");
-//				Debug.debug("markerBytes", markerBytes);
-				
-				if (marker == 0xffd9 || marker == SOS_Marker)
-				{
-					if (!visitor.beginSOS())
-						return;
-
-					byte imageData[] = getStreamBytes(is);
-					visitor.visitSOS(marker, markerBytes, imageData);
-					break;
-				}
-
-				byte segmentLengthBytes[] = readByteArray("segmentLengthBytes",
-						2, is, "segmentLengthBytes");
-				int segmentLength = convertByteArrayToShort("segmentLength",
-						segmentLengthBytes, byteOrder);
-
-//				Debug.debug("segmentLength", segmentLength + " (0x" + Integer.toHexString(segmentLength) + ")");
-//				Debug.debug("segmentLengthBytes", segmentLengthBytes);
-
-				byte segmentData[] = readByteArray("Segment Data",
-						segmentLength - 2, is,
-						"Invalid Segment: insufficient data");
-
-				// Debug.debug("segmentLength", segmentLength);
-
-				if (!visitor.visitSegment(marker, markerBytes, segmentLength,
-						segmentLengthBytes, segmentData))
-					return;
-			}
-
-		} finally
-		{
-			try
-			{
-			    if (is != null) {
-			        is.close();
-			    }
-			} catch (Exception e)
-			{
-				Debug.debug(e);
-			}
-		}
-	}
-
-	public static String getMarkerName(int marker)
-	{
-		switch (marker)
-		{
-		case SOS_Marker:
-			return "SOS_Marker";
-			// case JPEG_APP0 :
-			// return "JPEG_APP0";
-			// case JPEG_APP0_Marker :
-			// return "JPEG_APP0_Marker";
-		case JPEG_APP1_Marker:
-			return "JPEG_APP1_Marker";
-		case JPEG_APP2_Marker:
-			return "JPEG_APP2_Marker";
-		case JPEG_APP13_Marker:
-			return "JPEG_APP13_Marker";
-		case JPEG_APP14_Marker:
-			return "JPEG_APP14_Marker";
-		case JPEG_APP15_Marker:
-			return "JPEG_APP15_Marker";
-		case JFIFMarker:
-			return "JFIFMarker";
-		case SOF0Marker:
-			return "SOF0Marker";
-		case SOF1Marker:
-			return "SOF1Marker";
-		case SOF2Marker:
-			return "SOF2Marker";
-		case SOF3Marker:
-			return "SOF3Marker";
-		case SOF4Marker:
-			return "SOF4Marker";
-		case SOF5Marker:
-			return "SOF5Marker";
-		case SOF6Marker:
-			return "SOF6Marker";
-		case SOF7Marker:
-			return "SOF7Marker";
-		case SOF8Marker:
-			return "SOF8Marker";
-		case SOF9Marker:
-			return "SOF9Marker";
-		case SOF10Marker:
-			return "SOF10Marker";
-		case SOF11Marker:
-			return "SOF11Marker";
-		case SOF12Marker:
-			return "SOF12Marker";
-		case SOF13Marker:
-			return "SOF13Marker";
-		case SOF14Marker:
-			return "SOF14Marker";
-		case SOF15Marker:
-			return "SOF15Marker";
-		default:
-			return "Unknown";
-		}
-	}
-
-	public void dumpJFIF(ByteSource byteSource) throws ImageReadException,
-			IOException, ImageWriteException
-	{
-		Visitor visitor = new Visitor() {
-			// return false to exit before reading image data.
-			public boolean beginSOS()
-			{
-				return true;
-			}
-
-			public void visitSOS(int marker, byte markerBytes[],
-					byte imageData[])
-			{
-				Debug.debug("SOS marker.  " + imageData.length
-						+ " bytes of image data.");
-				Debug.debug("");
-			}
-
-			// return false to exit traversal.
-			public boolean visitSegment(int marker, byte markerBytes[],
-					int segmentLength, byte segmentLengthBytes[],
-					byte segmentData[])
-			{
-				Debug.debug("Segment marker: " + Integer.toHexString(marker)
-						+ " (" + getMarkerName(marker) + "), "
-						+ segmentData.length + " bytes of segment data.");
-				return true;
-			}
-		};
+    public JpegUtils()
+    {
+        setByteOrder(BYTE_ORDER_NETWORK);
+    }
+
+    public static interface Visitor
+    {
+        // return false to exit before reading image data.
+        public boolean beginSOS();
+
+        public void visitSOS(int marker, byte markerBytes[], byte imageData[]);
+
+        // return false to exit traversal.
+        public boolean visitSegment(int marker, byte markerBytes[],
+                int segmentLength, byte segmentLengthBytes[],
+                byte segmentData[]) throws ImageReadException,
+        // ImageWriteException,
+                IOException;
+    }
+
+    public void traverseJFIF(ByteSource byteSource, Visitor visitor)
+            throws ImageReadException,
+            // ImageWriteException,
+            IOException
+    {
+        InputStream is = null;
+
+        try
+        {
+            is = byteSource.getInputStream();
+
+            readAndVerifyBytes(is, SOI,
+                    "Not a Valid JPEG File: doesn't begin with 0xffd8");
+
+            int byteOrder = getByteOrder();
+
+            for (int markerCount = 0; true; markerCount++)
+            {
+                byte markerBytes[] = readByteArray("markerBytes", 2, is,
+                        "markerBytes");
+                int marker = convertByteArrayToShort("marker", markerBytes,
+                        byteOrder);
+
+//                Debug.debug("marker", marker + " (0x" + Integer.toHexString(marker) + ")");
+//                Debug.debug("markerBytes", markerBytes);
+
+                if (marker == 0xffd9 || marker == SOS_Marker)
+                {
+                    if (!visitor.beginSOS())
+                        return;
+
+                    byte imageData[] = getStreamBytes(is);
+                    visitor.visitSOS(marker, markerBytes, imageData);
+                    break;
+                }
+
+                byte segmentLengthBytes[] = readByteArray("segmentLengthBytes",
+                        2, is, "segmentLengthBytes");
+                int segmentLength = convertByteArrayToShort("segmentLength",
+                        segmentLengthBytes, byteOrder);
+
+//                Debug.debug("segmentLength", segmentLength + " (0x" + Integer.toHexString(segmentLength) + ")");
+//                Debug.debug("segmentLengthBytes", segmentLengthBytes);
+
+                byte segmentData[] = readByteArray("Segment Data",
+                        segmentLength - 2, is,
+                        "Invalid Segment: insufficient data");
+
+                // Debug.debug("segmentLength", segmentLength);
+
+                if (!visitor.visitSegment(marker, markerBytes, segmentLength,
+                        segmentLengthBytes, segmentData))
+                    return;
+            }
+
+        } finally
+        {
+            try
+            {
+                if (is != null) {
+                    is.close();
+                }
+            } catch (Exception e)
+            {
+                Debug.debug(e);
+            }
+        }
+    }
+
+    public static String getMarkerName(int marker)
+    {
+        switch (marker)
+        {
+        case SOS_Marker:
+            return "SOS_Marker";
+            // case JPEG_APP0 :
+            // return "JPEG_APP0";
+            // case JPEG_APP0_Marker :
+            // return "JPEG_APP0_Marker";
+        case JPEG_APP1_Marker:
+            return "JPEG_APP1_Marker";
+        case JPEG_APP2_Marker:
+            return "JPEG_APP2_Marker";
+        case JPEG_APP13_Marker:
+            return "JPEG_APP13_Marker";
+        case JPEG_APP14_Marker:
+            return "JPEG_APP14_Marker";
+        case JPEG_APP15_Marker:
+            return "JPEG_APP15_Marker";
+        case JFIFMarker:
+            return "JFIFMarker";
+        case SOF0Marker:
+            return "SOF0Marker";
+        case SOF1Marker:
+            return "SOF1Marker";
+        case SOF2Marker:
+            return "SOF2Marker";
+        case SOF3Marker:
+            return "SOF3Marker";
+        case SOF4Marker:
+            return "SOF4Marker";
+        case SOF5Marker:
+            return "SOF5Marker";
+        case SOF6Marker:
+            return "SOF6Marker";
+        case SOF7Marker:
+            return "SOF7Marker";
+        case SOF8Marker:
+            return "SOF8Marker";
+        case SOF9Marker:
+            return "SOF9Marker";
+        case SOF10Marker:
+            return "SOF10Marker";
+        case SOF11Marker:
+            return "SOF11Marker";
+        case SOF12Marker:
+            return "SOF12Marker";
+        case SOF13Marker:
+            return "SOF13Marker";
+        case SOF14Marker:
+            return "SOF14Marker";
+        case SOF15Marker:
+            return "SOF15Marker";
+        default:
+            return "Unknown";
+        }
+    }
+
+    public void dumpJFIF(ByteSource byteSource) throws ImageReadException,
+            IOException, ImageWriteException
+    {
+        Visitor visitor = new Visitor() {
+            // return false to exit before reading image data.
+            public boolean beginSOS()
+            {
+                return true;
+            }
+
+            public void visitSOS(int marker, byte markerBytes[],
+                    byte imageData[])
+            {
+                Debug.debug("SOS marker.  " + imageData.length
+                        + " bytes of image data.");
+                Debug.debug("");
+            }
+
+            // return false to exit traversal.
+            public boolean visitSegment(int marker, byte markerBytes[],
+                    int segmentLength, byte segmentLengthBytes[],
+                    byte segmentData[])
+            {
+                Debug.debug("Segment marker: " + Integer.toHexString(marker)
+                        + " (" + getMarkerName(marker) + "), "
+                        + segmentData.length + " bytes of segment data.");
+                return true;
+            }
+        };
 
-		traverseJFIF(byteSource, visitor);
-	}
+        traverseJFIF(byteSource, visitor);
+    }
 }
\ No newline at end of file