ImageFileDirectory(BufferedInputStream bufferedIn, long positionAfterTIFFHeader, boolean reverse) throws IOException { bufferedIn.reset(); if (JPEGMarkerInputStream.skipFully(bufferedIn, positionAfterTIFFHeader) != positionAfterTIFFHeader) throw new IOException(); byte[] array = new byte[4]; if (JPEGMarkerInputStream.readFully(bufferedIn, array, 2, reverse) != 2) throw new IOException("Corrupt image file directory."); int numberOfEntries = (array[0] & 0xff) * 256 + (array[1] & 0xff); entries = new DirectoryEntry[numberOfEntries]; for (int a = 0; a < numberOfEntries; a++) { try { DirectoryEntry entry = new DirectoryEntry(bufferedIn, reverse); entries[a] = (entry); } catch (RuntimeException e) { System.err.println(a + " / " + numberOfEntries); throw e; } } /** * According to the specs: what is supposed to follow is a 4-byte pointer to the next IFD. * However I found an APP1 block that omits these 4 bytes and instead contains some * DirectoryEntries. * * <p>So first I'll scan through the entries and see if any of them believe they should start * here, and if so we just assume there is no next IFD to point to. (If anything this errs on * the side of omitting data, instead of trying to read data where the isn't any.) */ long positionToExpectIFDPtr = positionAfterTIFFHeader + 2 + 12 * numberOfEntries; boolean missingIFDPtr = false; for (int a = 0; a < numberOfEntries && (!missingIFDPtr); a++) { int k = entries[a].positionAfterTIFFHeader(); if (k != -1 && k == positionToExpectIFDPtr) { missingIFDPtr = true; } } if (missingIFDPtr) { // there is no space for the IFD position to be, so assume it's zero. nextIFDPosition = 0; } else { if (JPEGMarkerInputStream.readFully(bufferedIn, array, 4, reverse) != 4) throw new IOException("Corrupt image file directory."); nextIFDPosition = ((array[0] & 0xff) << 24) + ((array[1] & 0xff) << 16) + ((array[2] & 0xff) << 8) + (array[3] & 0xff); } }
DirectoryEntry(InputStream in, boolean reverse) throws IOException { byte[] array = new byte[4]; if (JPEGMarkerInputStream.readFully(in, array, 2, reverse) != 2) throw new IOException(); tagNumber = (array[0] & 0xff) * 256 + (array[1] & 0xff); if (JPEGMarkerInputStream.readFully(in, array, 2, reverse) != 2) throw new IOException(); dataFormat = (array[0] & 0xff) * 256 + (array[1] & 0xff); if (JPEGMarkerInputStream.readFully(in, array, 4, reverse) != 4) throw new IOException(); componentCount = ((array[0] & 0xff) << 24) + ((array[1] & 0xff) << 16) + ((array[2] & 0xff) << 8) + ((array[3] & 0xff) << 0); fieldValue = new byte[4]; if (JPEGMarkerInputStream.readFully(in, fieldValue, 4, reverse) != 4) throw new IOException(); }
/** * @param in a BufferedInputStream that was last marked at the start of the TIFF header. * @throws IOException */ void resolveValue(BufferedInputStream in) throws IOException { int byteLength = getValueByteLength(); byte[] data; if (byteLength > 4) { // we have to read this data: byte[] newData = new byte[byteLength]; int positionAfterTIFFHeader = readLong(fieldValue, 0); in.reset(); if (JPEGMarkerInputStream.skipFully(in, positionAfterTIFFHeader) != positionAfterTIFFHeader) throw new IOException(); JPEGMarkerInputStream.readFully(in, newData, byteLength, false); data = newData; } else { data = fieldValue; } if (dataFormat == 7 || dataFormat == 0) { // UNDEFINED value = data; return; } else if (dataFormat == 2) { // ASCII StringBuffer buffer = new StringBuffer(); for (int a = 0; a < componentCount; a++) { int i = (data[a] & 0xff); if (i == 0) { break; } char c = (char) i; buffer.append(c); } value = buffer.toString(); return; } Object[] valueArray; if (dataFormat == 1 || // BYTE dataFormat == 6 || // SIGNED BYTE dataFormat == 3 || // SHORT dataFormat == 8 || // SIGNED SHORT dataFormat == 4 || // LONG dataFormat == 9) { // SIGNED LONG valueArray = new Integer[componentCount]; } else if (dataFormat == 5 || // RATIONAL dataFormat == 10) { // SIGNED RATIONAL valueArray = new Double[componentCount]; } else { throw new RuntimeException("unexpected data format (" + dataFormat + ")"); } for (int a = 0; a < componentCount; a++) { switch (dataFormat) { case 1: // byte valueArray[a] = new Integer(readByte(data, a * 1)); break; case 6: // signed byte valueArray[a] = new Integer(readSignedByte(data, a * 1)); break; case 3: // short valueArray[a] = new Integer(readShort(data, a * 2)); break; case 8: // signed short valueArray[a] = new Integer(readSignedShort(data, a * 2)); break; case 4: // long valueArray[a] = new Integer(readLong(data, a * 4)); break; case 9: // signed long valueArray[a] = new Integer(readSignedLong(data, a * 4)); break; case 5: // rational double numerator = readLong(data, a * 4); double denominator = readLong(data, a * 4 + 4); valueArray[a] = new Double(numerator / denominator); break; case 10: // signed rational double numerator2 = readSignedLong(data, a * 4); double denominator2 = readSignedLong(data, a * 4 + 4); valueArray[a] = new Double(numerator2 / denominator2); break; default: throw new RuntimeException("Unexpected condition."); } } if (valueArray.length == 1) { value = valueArray[0]; } else { value = valueArray; } }