/** * Load and parse Exif data. * * This will populate the object with Exif data, contained as a * {@link PelTiff} object. This TIFF object can be accessed with * the {@link getTiff()} method. * * @param PelDataWindow $d */ public function load(PelDataWindow $d) { Pel::debug('Parsing %d bytes of Exif data...', $d->getSize()); /* There must be at least 6 bytes for the Exif header. */ if ($d->getSize() < 6) { throw new PelInvalidDataException('Expected at least 6 bytes of Exif ' . 'data, found just %d bytes.', $d->getSize()); } /* Verify the Exif header */ if ($d->strcmp(0, self::EXIF_HEADER)) { $d->setWindowStart(strlen(self::EXIF_HEADER)); } else { throw new PelInvalidDataException('Exif header not found.'); } /* The rest of the data is TIFF data. */ $this->tiff = new PelTiff(); $this->tiff->load($d); }
/** * Turn this directory into bytes. * * This directory will be turned into a byte string, with the * specified byte order. The offsets will be calculated from the * offset given. * * @param int the offset of the first byte of this directory. * * @param PelByteOrder the byte order that should be used when * turning integers into bytes. This should be one of {@link * PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}. */ function getBytes($offset, $order) { $bytes = ''; $extra_bytes = ''; Pel::debug('Bytes from IDF will start at offset %d within Exif data', $offset); $n = count($this->entries) + count($this->sub); if ($this->thumb_data != null) { /* We need two extra entries for the thumbnail offset and * length. */ $n += 2; } $bytes .= PelConvert::shortToBytes($n, $order); /* Initialize offset of extra data. This included the bytes * preceding this IFD, the bytes needed for the count of entries, * the entries themselves (and sub entries), the extra data in the * entries, and the IFD link. */ $end = $offset + 2 + 12 * $n + 4; foreach ($this->entries as $tag => $entry) { /* Each entry is 12 bytes long. */ $bytes .= PelConvert::shortToBytes($entry->getTag(), $order); $bytes .= PelConvert::shortToBytes($entry->getFormat(), $order); $bytes .= PelConvert::longToBytes($entry->getComponents(), $order); /* * Size? If bigger than 4 bytes, the actual data is not in * the entry but somewhere else. */ $data = $entry->getBytes($order); $s = strlen($data); if ($s > 4) { Pel::debug('Data size %d too big, storing at offset %d instead.', $s, $end); $bytes .= PelConvert::longToBytes($end, $order); $extra_bytes .= $data; $end += $s; } else { Pel::debug('Data size %d fits.', $s); /* Copy data directly, pad with NULL bytes as necessary to * fill out the four bytes available.*/ $bytes .= $data . str_repeat(chr(0), 4 - $s); } } if ($this->thumb_data != null) { Pel::debug('Appending %d bytes of thumbnail data at %d', $this->thumb_data->getSize(), $end); // TODO: make PelEntry a class that can be constructed with // arguments corresponding to the newt four lines. $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH, $order); $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); $bytes .= PelConvert::longToBytes(1, $order); $bytes .= PelConvert::longToBytes($this->thumb_data->getSize(), $order); $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT, $order); $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); $bytes .= PelConvert::longToBytes(1, $order); $bytes .= PelConvert::longToBytes($end, $order); $extra_bytes .= $this->thumb_data->getBytes(); $end += $this->thumb_data->getSize(); } /* Find bytes from sub IFDs. */ $sub_bytes = ''; foreach ($this->sub as $type => $sub) { if ($type == PelIfd::EXIF) { $tag = PelTag::EXIF_IFD_POINTER; } elseif ($type == PelIfd::GPS) { $tag = PelTag::GPS_INFO_IFD_POINTER; } elseif ($type == PelIfd::INTEROPERABILITY) { $tag = PelTag::INTEROPERABILITY_IFD_POINTER; } /* Make an aditional entry with the pointer. */ $bytes .= PelConvert::shortToBytes($tag, $order); /* Next the format, which is always unsigned long. */ $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); /* There is only one component. */ $bytes .= PelConvert::longToBytes(1, $order); $data = $sub->getBytes($end, $order); $s = strlen($data); $sub_bytes .= $data; $bytes .= PelConvert::longToBytes($end, $order); $end += $s; } /* Make link to next IFD, if any*/ if ($this->isLastIFD()) { $link = 0; } else { $link = $end; } Pel::debug('Link to next IFD: %d', $link); $bytes .= PelConvert::longtoBytes($link, $order); $bytes .= $extra_bytes . $sub_bytes; if (!$this->isLastIfd()) { $bytes .= $this->next->getBytes($end, $order); } return $bytes; }
/** * Check if data is valid TIFF data. * * This will read just enough data from the data window to determine * if the data could be a valid TIFF data. This means that the * check is more like a heuristic than a rigorous check. * * @param PelDataWindow $d * the bytes that will be examined. * * @return boolean true if the data looks like valid TIFF data, * false otherwise. * * @see PelJpeg::isValid() */ public static function isValid(PelDataWindow $d) { /* First check that we have enough data. */ if ($d->getSize() < 8) { return false; } /* Byte order */ if ($d->strcmp(0, 'II')) { $d->setByteOrder(PelConvert::LITTLE_ENDIAN); } elseif ($d->strcmp(0, 'MM')) { Pel::debug('Found Motorola byte order'); $d->setByteOrder(PelConvert::BIG_ENDIAN); } else { return false; } /* Verify the TIFF header */ return $d->getShort(2) == self::TIFF_HEADER; }
/** * Load data into a JPEG object. * * The data supplied will be parsed and turned into an object * structure representing the image. This structure can then be * manipulated and later turned back into an string of bytes. * * This methods can be called at any time after a JPEG object has * been constructed, also after the {@link appendSection()} has been * called to append custom sections. Loading several JPEG images * into one object will accumulate the sections, but there will only * be one {@link PelJpegMarker::SOS} section at any given time. * * @param PelDataWindow the data that will be turned into JPEG * sections. */ function load(PelDataWindow $d) { Pel::debug('Parsing %d bytes...', $d->getSize()); /* JPEG data is stored in big-endian format. */ $d->setByteOrder(PelConvert::BIG_ENDIAN); /* Run through the data to read the sections in the image. After * each section is read, the start of the data window will be * moved forward, and after the last section we'll terminate with * no data left in the window. */ while ($d->getSize() > 0) { /* JPEG sections start with 0xFF. The first byte that is not * 0xFF is a marker (hopefully). */ for ($i = 0; $i < 7; $i++) { if ($d->getByte($i) != 0xff) { break; } } $marker = $d->getByte($i); if (!PelJpegMarker::isValid($marker)) { throw new PelJpegInvalidMarkerException($marker, $i); } /* Move window so first byte becomes first byte in this * section. */ $d->setWindowStart($i + 1); if ($marker == PelJpegMarker::SOI || $marker == PelJpegMarker::EOI) { $content = new PelJpegContent(new PelDataWindow()); $this->appendSection($marker, $content); } else { /* Read the length of the section. The length includes the * two bytes used to store the length. */ $len = $d->getShort(0) - 2; Pel::debug('Found %s section of length %d', PelJpegMarker::getName($marker), $len); /* Skip past the length. */ $d->setWindowStart(2); if ($marker == PelJpegMarker::APP1) { try { $content = new PelExif(); $content->load($d->getClone(0, $len)); } catch (PelInvalidDataException $e) { /* We store the data as normal JPEG content if it could * not be parsed as Exif data. */ $content = new PelJpegContent($d->getClone(0, $len)); } $this->appendSection($marker, $content); /* Skip past the data. */ $d->setWindowStart($len); } elseif ($marker == PelJpegMarker::COM) { $content = new PelJpegComment(); $content->load($d->getClone(0, $len)); $this->appendSection($marker, $content); $d->setWindowStart($len); } else { $content = new PelJpegContent($d->getClone(0, $len)); $this->appendSection($marker, $content); /* Skip past the data. */ $d->setWindowStart($len); /* In case of SOS, image data will follow. */ if ($marker == PelJpegMarker::SOS) { /* Some images have some trailing (garbage?) following the * EOI marker. To handle this we seek backwards until we * find the EOI marker. Any trailing content is stored as * a PelJpegContent object. */ $length = $d->getSize(); while ($d->getByte($length - 2) != 0xff || $d->getByte($length - 1) != PelJpegMarker::EOI) { $length--; } $this->jpeg_data = $d->getClone(0, $length - 2); Pel::debug('JPEG data: ' . $this->jpeg_data->__toString()); /* Append the EOI. */ $this->appendSection(PelJpegMarker::EOI, new PelJpegContent(new PelDataWindow())); /* Now check to see if there are any trailing data. */ if ($length != $d->getSize()) { Pel::maybeThrow(new PelException('Found trailing content ' . 'after EOI: %d bytes', $d->getSize() - $length)); $content = new PelJpegContent($d->getClone($length)); /* We don't have a proper JPEG marker for trailing * garbage, so we just use 0x00... */ $this->appendSection(0x0, $content); } /* Done with the loop. */ break; } } } } /* while ($d->getSize() > 0) */ }
setlocale(LC_ALL, ''); /* Load the required files. One would normally just require the * PelJpeg.php file for dealing with JPEG images, but because this * example can handle both JPEG and TIFF it loads the PelDataWindow * class too. */ require_once dirname(__FILE__) . '/../PelDataWindow.php'; require_once dirname(__FILE__) . '/../PelJpeg.php'; require_once dirname(__FILE__) . '/../PelTiff.php'; /* Store the name of the script in $prog and remove this first part of * the command line. */ $prog = array_shift($argv); $error = false; /* The next argument could be -d to signal debug mode where lots of * extra information is printed out when the image is parsed. */ if (isset($argv[0]) && $argv[0] == '-d') { Pel::$debug = true; array_shift($argv); } /* The mandatory input filename. */ if (isset($argv[0])) { $input = array_shift($argv); } else { $error = true; } /* The mandatory output filename. */ if (isset($argv[0])) { $output = array_shift($argv); } else { $error = true; } /* Usage information is printed if an error was found in the command
/** * Enable/disable debugging output. * * @param boolean $flag * use true to enable debug output, false to * diable. */ public static function setDebug($flag) { self::$debug = $flag; }
/** * Enable/disable debugging output. * * @param boolean $flag use true to enable debug output, false to * diable. */ function setDebug($flag) { self::$debug = $flag; }