Index: trunk/src/com/drew/imaging/ImageProcessingException.java
===================================================================
--- trunk/src/com/drew/imaging/ImageProcessingException.java	(revision 8243)
+++ trunk/src/com/drew/imaging/ImageProcessingException.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
Index: trunk/src/com/drew/imaging/PhotographicConversions.java
===================================================================
--- trunk/src/com/drew/imaging/PhotographicConversions.java	(revision 8243)
+++ trunk/src/com/drew/imaging/PhotographicConversions.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
Index: trunk/src/com/drew/imaging/jpeg/JpegMetadataReader.java
===================================================================
--- trunk/src/com/drew/imaging/jpeg/JpegMetadataReader.java	(revision 8243)
+++ trunk/src/com/drew/imaging/jpeg/JpegMetadataReader.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
@@ -39,6 +39,8 @@
 import com.drew.metadata.iptc.IptcReader;
 //import com.drew.metadata.jfif.JfifReader;
+//import com.drew.metadata.jfxx.JfxxReader;
 import com.drew.metadata.jpeg.JpegCommentReader;
 import com.drew.metadata.jpeg.JpegReader;
+//import com.drew.metadata.photoshop.DuckyReader;
 //import com.drew.metadata.photoshop.PhotoshopReader;
 //import com.drew.metadata.xmp.XmpReader;
@@ -55,8 +57,10 @@
             new JpegCommentReader(),
             //new JfifReader(),
+            //new JfxxReader(),
             new ExifReader(),
             //new XmpReader(),
             //new IccReader(),
             //new PhotoshopReader(),
+            //new DuckyReader(),
             new IptcReader()//,
             //new AdobeJpegReader()
Index: trunk/src/com/drew/imaging/jpeg/JpegProcessingException.java
===================================================================
--- trunk/src/com/drew/imaging/jpeg/JpegProcessingException.java	(revision 8243)
+++ trunk/src/com/drew/imaging/jpeg/JpegProcessingException.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
Index: trunk/src/com/drew/imaging/jpeg/JpegSegmentData.java
===================================================================
--- trunk/src/com/drew/imaging/jpeg/JpegSegmentData.java	(revision 8243)
+++ trunk/src/com/drew/imaging/jpeg/JpegSegmentData.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
@@ -52,5 +52,4 @@
      * @param segmentBytes the byte array holding data for the segment being added
      */
-    @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
     public void addSegment(byte segmentType, @NotNull byte[] segmentBytes)
     {
@@ -207,5 +206,4 @@
      * @param occurrence  the zero-based index of the segment occurrence to remove.
      */
-    @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
     public void removeSegmentOccurrence(@NotNull JpegSegmentType segmentType, int occurrence)
     {
@@ -220,5 +218,4 @@
      * @param occurrence  the zero-based index of the segment occurrence to remove.
      */
-    @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
     public void removeSegmentOccurrence(byte segmentType, int occurrence)
     {
Index: trunk/src/com/drew/imaging/jpeg/JpegSegmentReader.java
===================================================================
--- trunk/src/com/drew/imaging/jpeg/JpegSegmentReader.java	(revision 8243)
+++ trunk/src/com/drew/imaging/jpeg/JpegSegmentReader.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
@@ -43,4 +43,9 @@
 public class JpegSegmentReader
 {
+    /**
+     * The 0xFF byte that signals the start of a segment.
+     */
+    private static final byte SEGMENT_IDENTIFIER = (byte) 0xFF;
+
     /**
      * Private, because this segment crashes my algorithm, and searching for it doesn't work (yet).
@@ -112,17 +117,12 @@
             // by a 0xFF and then a byte not equal to 0x00 or 0xFF.
 
-            final short segmentIdentifier = reader.getUInt8();
+            byte segmentIdentifier = reader.getInt8();
+            byte segmentType = reader.getInt8();
 
-            // We must have at least one 0xFF byte
-            if (segmentIdentifier != 0xFF)
-                throw new JpegProcessingException("Expected JPEG segment start identifier 0xFF, not 0x" + Integer.toHexString(segmentIdentifier).toUpperCase());
-
-            // Read until we have a non-0xFF byte. This identifies the segment type.
-            byte segmentType = reader.getInt8();
-            while (segmentType == (byte)0xFF)
-                segmentType = reader.getInt8();
-
-            if (segmentType == 0)
-                throw new JpegProcessingException("Expected non-zero byte as part of JPEG marker identifier");
+            // Read until we have a 0xFF byte followed by a byte that is not 0xFF or 0x00
+            while (segmentIdentifier != SEGMENT_IDENTIFIER || segmentType == SEGMENT_IDENTIFIER || segmentType == 0) {
+            	segmentIdentifier = segmentType;
+            	segmentType = reader.getInt8();
+            }
 
             if (segmentType == SEGMENT_SOS) {
Index: trunk/src/com/drew/imaging/jpeg/JpegSegmentType.java
===================================================================
--- trunk/src/com/drew/imaging/jpeg/JpegSegmentType.java	(revision 8243)
+++ trunk/src/com/drew/imaging/jpeg/JpegSegmentType.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,15 +30,20 @@
  * An enumeration of the known segment types found in JPEG files.
  *
+ * <ul>
+ *     <li>http://www.ozhiker.com/electronics/pjmt/jpeg_info/app_segments.html</li>
+ *     <li>http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html</li>
+ * </ul>
+ *
  * @author Drew Noakes https://drewnoakes.com
  */
 public enum JpegSegmentType
 {
-    /** APP0 JPEG segment identifier -- JFIF data (also JFXX apparently). */
+    /** APP0 JPEG segment identifier. Commonly contains JFIF, JFXX. */
     APP0((byte)0xE0, true),
 
-    /** APP1 JPEG segment identifier -- where Exif data is kept.  XMP data is also kept in here, though usually in a second instance. */
+    /** APP1 JPEG segment identifier. Commonly contains Exif. XMP data is also kept in here, though usually in a second instance. */
     APP1((byte)0xE1, true),
 
-    /** APP2 JPEG segment identifier. */
+    /** APP2 JPEG segment identifier. Commonly contains ICC. */
     APP2((byte)0xE2, true),
 
@@ -64,5 +69,5 @@
     APP9((byte)0xE9, true),
 
-    /** APPA (App10) JPEG segment identifier -- can hold Unicode comments. */
+    /** APPA (App10) JPEG segment identifier. Can contain Unicode comments, though {@link JpegSegmentType#COM} is more commonly used for comments. */
     APPA((byte)0xEA, true),
 
@@ -73,8 +78,8 @@
     APPC((byte)0xEC, true),
 
-    /** APPD (App13) JPEG segment identifier -- IPTC data in here. */
+    /** APPD (App13) JPEG segment identifier. Commonly contains IPTC, Photoshop data. */
     APPD((byte)0xED, true),
 
-    /** APPE (App14) JPEG segment identifier. */
+    /** APPE (App14) JPEG segment identifier. Commonly contains Adobe data. */
     APPE((byte)0xEE, true),
 
Index: trunk/src/com/drew/imaging/jpeg/package.html
===================================================================
--- trunk/src/com/drew/imaging/jpeg/package.html	(revision 8243)
+++ trunk/src/com/drew/imaging/jpeg/package.html	(revision 10862)
@@ -1,4 +1,4 @@
 <!--
-  ~ Copyright 2002-2015 Drew Noakes
+  ~ Copyright 2002-2016 Drew Noakes
   ~
   ~    Licensed under the Apache License, Version 2.0 (the "License");
Index: trunk/src/com/drew/imaging/package.html
===================================================================
--- trunk/src/com/drew/imaging/package.html	(revision 8243)
+++ trunk/src/com/drew/imaging/package.html	(revision 10862)
@@ -1,4 +1,4 @@
 <!--
-  ~ Copyright 2002-2015 Drew Noakes
+  ~ Copyright 2002-2016 Drew Noakes
   ~
   ~    Licensed under the Apache License, Version 2.0 (the "License");
Index: trunk/src/com/drew/imaging/tiff/TiffDataFormat.java
===================================================================
--- trunk/src/com/drew/imaging/tiff/TiffDataFormat.java	(revision 8243)
+++ trunk/src/com/drew/imaging/tiff/TiffDataFormat.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
Index: trunk/src/com/drew/imaging/tiff/TiffHandler.java
===================================================================
--- trunk/src/com/drew/imaging/tiff/TiffHandler.java	(revision 8243)
+++ trunk/src/com/drew/imaging/tiff/TiffHandler.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,4 +24,5 @@
 import com.drew.lang.Rational;
 import com.drew.lang.annotations.NotNull;
+import com.drew.lang.annotations.Nullable;
 
 import java.io.IOException;
@@ -46,5 +47,5 @@
     void setTiffMarker(int marker) throws TiffProcessingException;
 
-    boolean isTagIfdPointer(int tagType);
+    boolean tryEnterSubIfd(int tagId);
     boolean hasFollowerIfd();
 
@@ -52,4 +53,7 @@
 
     void completed(@NotNull final RandomAccessReader reader, final int tiffHeaderOffset);
+
+    @Nullable
+    Long tryCustomProcessFormat(int tagId, int formatCode, long componentCount);
 
     boolean customProcessTag(int tagOffset,
Index: trunk/src/com/drew/imaging/tiff/TiffProcessingException.java
===================================================================
--- trunk/src/com/drew/imaging/tiff/TiffProcessingException.java	(revision 8243)
+++ trunk/src/com/drew/imaging/tiff/TiffProcessingException.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
Index: trunk/src/com/drew/imaging/tiff/TiffReader.java
===================================================================
--- trunk/src/com/drew/imaging/tiff/TiffReader.java	(revision 8243)
+++ trunk/src/com/drew/imaging/tiff/TiffReader.java	(revision 10862)
@@ -1,4 +1,4 @@
 /*
- * Copyright 2002-2015 Drew Noakes
+ * Copyright 2002-2016 Drew Noakes
  *
  *    Licensed under the Apache License, Version 2.0 (the "License");
@@ -110,4 +110,5 @@
                                   final int tiffHeaderOffset) throws IOException
     {
+        Boolean resetByteOrder = null;
         try {
             // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
@@ -126,4 +127,14 @@
             // First two bytes in the IFD are the number of tags in this directory
             int dirTagCount = reader.getUInt16(ifdOffset);
+
+            // Some software modifies the byte order of the file, but misses some IFDs (such as makernotes).
+            // The entire test image repository doesn't contain a single IFD with more than 255 entries.
+            // Here we detect switched bytes that suggest this problem, and temporarily swap the byte order.
+            // This was discussed in GitHub issue #136.
+            if (dirTagCount > 0xFF && (dirTagCount & 0xFF) == 0) {
+                resetByteOrder = reader.isMotorolaByteOrder();
+                dirTagCount >>= 8;
+                reader.setMotorolaByteOrder(!reader.isMotorolaByteOrder());
+            }
 
             int dirLength = (2 + (12 * dirTagCount) + 4);
@@ -147,29 +158,30 @@
                 final TiffDataFormat format = TiffDataFormat.fromTiffFormatCode(formatCode);
 
+                // 4 bytes dictate the number of components in this tag's data
+                final long componentCount = reader.getUInt32(tagOffset + 4);
+
+                final long byteCount;
                 if (format == null) {
-                    // This error suggests that we are processing at an incorrect index and will generate
-                    // rubbish until we go out of bounds (which may be a while).  Exit now.
-                    handler.error("Invalid TIFF tag format code: " + formatCode);
-                    // TODO specify threshold as a parameter, or provide some other external control over this behaviour
-                    if (++invalidTiffFormatCodeCount > 5) {
-                        handler.error("Stopping processing as too many errors seen in TIFF IFD");
-                        return;
+                    Long byteCountOverride = handler.tryCustomProcessFormat(tagId, formatCode, componentCount);
+                    if (byteCountOverride == null) {
+                        // This error suggests that we are processing at an incorrect index and will generate
+                        // rubbish until we go out of bounds (which may be a while).  Exit now.
+                        handler.error(String.format("Invalid TIFF tag format code %d for tag 0x%04X", formatCode, tagId));
+                        // TODO specify threshold as a parameter, or provide some other external control over this behaviour
+                        if (++invalidTiffFormatCodeCount > 5) {
+                            handler.error("Stopping processing as too many errors seen in TIFF IFD");
+                            return;
+                        }
+                        continue;
                     }
-                    continue;
-                }
-
-                // 4 bytes dictate the number of components in this tag's data
-                final int componentCount = reader.getInt32(tagOffset + 4);
-                if (componentCount < 0) {
-                    handler.error("Negative TIFF tag component count");
-                    continue;
-                }
-
-                final int byteCount = componentCount * format.getComponentSizeBytes();
-
-                final int tagValueOffset;
+                    byteCount = byteCountOverride;
+                } else {
+                    byteCount = componentCount * format.getComponentSizeBytes();
+                }
+
+                final long tagValueOffset;
                 if (byteCount > 4) {
                     // If it's bigger than 4 bytes, the dir entry contains an offset.
-                    final int offsetVal = reader.getInt32(tagOffset + 8);
+                    final long offsetVal = reader.getUInt32(tagOffset + 8);
                     if (offsetVal + byteCount > reader.getLength()) {
                         // Bogus pointer offset and / or byteCount value
@@ -195,14 +207,20 @@
                 }
 
-                //
-                // Special handling for tags that point to other IFDs
-                //
-                if (byteCount == 4 && handler.isTagIfdPointer(tagId)) {
-                    final int subDirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset);
-                    processIfd(handler, reader, processedIfdOffsets, subDirOffset, tiffHeaderOffset);
-                } else {
-                    if (!handler.customProcessTag(tagValueOffset, processedIfdOffsets, tiffHeaderOffset, reader, tagId, byteCount)) {
-                        processTag(handler, tagId, tagValueOffset, componentCount, formatCode, reader);
+                // Some tags point to one or more additional IFDs to process
+                boolean isIfdPointer = false;
+                if (byteCount == 4 * componentCount) {
+                    for (int i = 0; i < componentCount; i++) {
+                        if (handler.tryEnterSubIfd(tagId)) {
+                            isIfdPointer = true;
+                            int subDirOffset = tiffHeaderOffset + reader.getInt32((int) (tagValueOffset + i * 4));
+                            processIfd(handler, reader, processedIfdOffsets, subDirOffset, tiffHeaderOffset);
+                        }
                     }
+                }
+
+                // If it wasn't an IFD pointer, allow custom tag processing to occur
+                if (!isIfdPointer && !handler.customProcessTag((int) tagValueOffset, processedIfdOffsets, tiffHeaderOffset, reader, tagId, (int) byteCount)) {
+                    // If no custom processing occurred, process the tag in the standard fashion
+                    processTag(handler, tagId, (int) tagValueOffset, (int) componentCount, formatCode, reader);
                 }
             }
@@ -229,4 +247,6 @@
         } finally {
             handler.endingIFD();
+            if (resetByteOrder != null)
+                reader.setMotorolaByteOrder(resetByteOrder);
         }
     }
@@ -350,5 +370,5 @@
                 break;
             default:
-                handler.error(String.format("Unknown format code %d for tag %d", formatCode, tagId));
+                handler.error(String.format("Invalid TIFF tag format code %d for tag 0x%04X", formatCode, tagId));
         }
     }
Index: trunk/src/com/drew/imaging/tiff/package.html
===================================================================
--- trunk/src/com/drew/imaging/tiff/package.html	(revision 8243)
+++ trunk/src/com/drew/imaging/tiff/package.html	(revision 10862)
@@ -1,4 +1,4 @@
 <!--
-  ~ Copyright 2002-2015 Drew Noakes
+  ~ Copyright 2002-2016 Drew Noakes
   ~
   ~    Licensed under the Apache License, Version 2.0 (the "License");
