diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aaaa7cfa..45f2ba4b 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] os: [ubuntu-latest, windows-latest] steps: diff --git a/composer.json b/composer.json index bfe528ea..18a07ea4 100755 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "keywords": ["module", "xp"], "require" : { "xp-framework/core": "^12.0 | ^11.0 | ^10.0", - "php" : ">=7.0.0" + "php" : ">=7.4.0" }, "require-dev" : { "xp-framework/test": "^2.0 | ^1.0" diff --git a/src/main/php/img/io/ImageMetaData.class.php b/src/main/php/img/io/ImageMetaData.class.php index 9b85a94d..2c03cd84 100755 --- a/src/main/php/img/io/ImageMetaData.class.php +++ b/src/main/php/img/io/ImageMetaData.class.php @@ -1,14 +1,12 @@ getName() : $type; $r= []; foreach ($this->segments as $segment) { - if (nameof($segment) === $class) $r[]= $segment; + if ($segment instanceof $type) $r[]= $segment; } return $r; } @@ -77,7 +74,7 @@ public function segmentsOf($type) { * @throws img.ImagingException if no information can be extracted */ public function imageDimensions() { - if (!($seg= $this->segmentsOf('img.io.SOFNSegment'))) { + if (!($seg= $this->segmentsOf(SOFNSegment::class))) { throw new ImagingException('Cannot load image information from '.$this->source); } @@ -88,10 +85,10 @@ public function imageDimensions() { * Returns an IptcData instance or NULL if this image does not contain * IPTC Data * - * @return img.util.IptcData + * @return ?img.util.IptcData */ public function iptcData() { - if (!($seg= $this->segmentsOf('img.io.IptcSegment'))) return null; + if (!($seg= $this->segmentsOf(IptcSegment::class))) return null; $data= new IptcData(); $iptc= $seg[0]->rawData(); @@ -103,38 +100,38 @@ public function iptcData() { $created= null; } - $data->setTitle($iptc['2#005'][0] ?? null); - $data->setUrgency($iptc['2#010'][0] ?? null); - $data->setCategory($iptc['2#015'][0] ?? null); - $data->setSupplementalCategories($iptc['2#020'] ?? null); - $data->setKeywords($iptc['2#025'] ?? null); - $data->setSpecialInstructions($iptc['2#040'][0] ?? null); - $data->setDateCreated($created); - $data->setAuthor($iptc['2#080'][0] ?? null); - $data->setAuthorPosition($iptc['2#085'][0] ?? null); - $data->setCity($iptc['2#090'][0] ?? null); - $data->setState($iptc['2#095'][0] ?? null); - $data->setCountry($iptc['2#101'][0] ?? null); - $data->setOriginalTransmissionReference($iptc['2#103'][0] ?? null); - $data->setHeadline($iptc['2#105'][0] ?? null); - $data->setCredit($iptc['2#110'][0] ?? null); - $data->setSource($iptc['2#115'][0] ?? null); - $data->setCopyrightNotice($iptc['2#116'][0] ?? null); - $data->setCaption($iptc['2#120'][0] ?? null); - $data->setWriter($iptc['2#122'][0] ?? null); - return $data; + return $data + ->withTitle($iptc['2#005'][0] ?? null) + ->withUrgency($iptc['2#010'][0] ?? null) + ->withCategory($iptc['2#015'][0] ?? null) + ->withSupplementalCategories($iptc['2#020'] ?? null) + ->withKeywords($iptc['2#025'] ?? null) + ->withSpecialInstructions($iptc['2#040'][0] ?? null) + ->withDateCreated($created) + ->withAuthor($iptc['2#080'][0] ?? null) + ->withAuthorPosition($iptc['2#085'][0] ?? null) + ->withCity($iptc['2#090'][0] ?? null) + ->withState($iptc['2#095'][0] ?? null) + ->withCountry($iptc['2#101'][0] ?? null) + ->withOriginalTransmissionReference($iptc['2#103'][0] ?? null) + ->withHeadline($iptc['2#105'][0] ?? null) + ->withCredit($iptc['2#110'][0] ?? null) + ->withSource($iptc['2#115'][0] ?? null) + ->withCopyrightNotice($iptc['2#116'][0] ?? null) + ->withCaption($iptc['2#120'][0] ?? null) + ->withWriter($iptc['2#122'][0] ?? null) + ; } /** * Lookup helper for exifData() method * * @param [:var] exif - * @param string... key - * @return string value or NULL + * @param string... $keys + * @return ?string value */ - protected static function lookup($exif) { - for ($i= 1, $s= func_num_args(); $i < $s; $i++) { - $key= func_get_arg($i); + protected static function lookup($exif, ... $keys) { + foreach ($keys as $key) { if (isset($exif[$key])) return $exif[$key]['data']; } return null; @@ -144,83 +141,78 @@ protected static function lookup($exif) { * Returns an ExifData instance or NULL if this image does not contain * EXIF Data * - * @return img.util.ExifData + * @return ?img.util.ExifData */ public function exifData() { - if (!($seg= $this->segmentsOf('img.io.ExifSegment'))) return null; + if (!($seg= $this->segmentsOf(ExifSegment::class))) return null; // Populate ExifData instance from ExifSegment's raw data with ($data= new ExifData(), $raw= $seg[0]->rawData()); { - $data->setFileName($this->source); - $data->setFileSize(-1); - $data->setMimeType('image/jpeg'); + $data->withFileName($this->source); + $data->withFileSize(-1); + $data->withMimeType('image/jpeg'); - $data->setMake(null === ($l= self::lookup($raw, 'Make')) ? null : trim($l)); - $data->setModel(null === ($l= self::lookup($raw, 'Model')) ? null : trim($l)); - $data->setSoftware(null === ($l= self::lookup($raw, 'Software')) ? null : trim($l)); + $data->withMake(null === ($l= self::lookup($raw, 'Make')) ? null : trim($l)); + $data->withModel(null === ($l= self::lookup($raw, 'Model')) ? null : trim($l)); + $data->withSoftware(null === ($l= self::lookup($raw, 'Software')) ? null : trim($l)); $exif= $raw['Exif_IFD_Pointer']['data']; - if ($sof= $this->segmentsOf('img.io.SOFNSegment')) { - $data->setWidth($sof[0]->width()); - $data->setHeight($sof[0]->height()); + if ($sof= $this->segmentsOf(SOFNSegment::class)) { + $data->withWidth($sof[0]->width()); + $data->withHeight($sof[0]->height()); } else { - $data->setWidth(self::lookup($exif, 'ExifImageWidth')); - $data->setHeight(self::lookup($exif, 'ExifImageLength')); + $data->withWidth(self::lookup($exif, 'ExifImageWidth')); + $data->withHeight(self::lookup($exif, 'ExifImageLength')); } // Aperture is either a FNumber (use directly), otherwise calculate from value if (null === ($a= self::lookup($exif, 'FNumber'))) { if (null === ($a= self::lookup($exif, 'ApertureValue', 'MaxApertureValue'))) { - $data->setApertureFNumber(null); + $data->withApertureFNumber(null); } else { sscanf($a, '%d/%d', $n, $frac); - $data->setApertureFNumber(sprintf('f/%.1F', exp($n / $frac * log(2) * 0.5))); + $data->withApertureFNumber(sprintf('f/%.1F', exp($n / $frac * log(2) * 0.5))); } } else { sscanf($a, '%d/%d', $n, $frac); - $data->setApertureFNumber(sprintf('f/%.1F', $n / $frac)); + $data->withApertureFNumber(sprintf('f/%.1F', $n / $frac)); } - $data->setExposureTime(self::lookup($exif, 'ExposureTime')); - $data->setExposureProgram(self::lookup($exif, 'ExposureProgram')); - $data->setMeteringMode(self::lookup($exif, 'MeteringMode')); - $data->setIsoSpeedRatings(self::lookup($exif, 'ISOSpeedRatings')); + $data->withExposureTime(self::lookup($exif, 'ExposureTime')); + $data->withExposureProgram(self::lookup($exif, 'ExposureProgram')); + $data->withMeteringMode(self::lookup($exif, 'MeteringMode')); + $data->withIsoSpeedRatings(self::lookup($exif, 'ISOSpeedRatings')); // Sometimes white balance is in MAKERNOTE - e.g. FUJIFILM's Finepix if (null !== ($w= self::lookup($exif, 'WhiteBalance'))) { - $data->setWhiteBalance($w); + $data->withWhiteBalance($w); } else if (isset($exif['MakerNote']) && null !== ($w= self::lookup($exif['MakerNote']['data'], 'WhiteBalance'))) { - $data->setWhiteBalance($w); + $data->withWhiteBalance($w); } else { - $data->setWhiteBalance(null); + $data->withWhiteBalance(null); } // Extract focal length. Some models store "80" as "80/1", rip off // the divisor "1" in this case. if (null !== ($l= self::lookup($exif, 'FocalLength'))) { sscanf($l, '%d/%d', $n, $frac); - $data->setFocalLength(1 == $frac ? $n : $n.'/'.$frac); + $data->withFocalLength(1 == $frac ? $n : $n.'/'.$frac); } else { - $data->setFocalLength(null); + $data->withFocalLength(null); } - // Check for Flash and flashUsed keys - if (null !== ($f= self::lookup($exif, 'Flash'))) { - $data->setFlash($f); - } else { - $data->setFlash(null); - } + $data->withFlash(self::lookup($exif, 'Flash')); if (null !== ($date= self::lookup($exif, 'DateTimeOriginal', 'DateTimeDigitized', 'DateTime'))) { $t= sscanf($date, '%4d:%2d:%2d %2d:%2d:%2d'); - $data->setDateTime(new \util\Date(mktime($t[3], $t[4], $t[5], $t[1], $t[2], $t[0]))); + $data->withDateTime(new Date(mktime($t[3], $t[4], $t[5], $t[1], $t[2], $t[0]))); } if (null !== ($o= self::lookup($exif, 'Orientation'))) { - $data->setOrientation($o); + $data->withOrientation($o); } else { - $data->setOrientation(($data->width / $data->height) > 1.0 + $data->withOrientation(($data->width / $data->height) > 1.0 ? 1 // normal : 5 // transpose ); diff --git a/src/main/php/img/util/ExifData.class.php b/src/main/php/img/util/ExifData.class.php index 430f46de..1c16b488 100755 --- a/src/main/php/img/util/ExifData.class.php +++ b/src/main/php/img/util/ExifData.class.php @@ -1,21 +1,15 @@ getURI(), $info)) { - $e= new ImagingException('Cannot read image information from '.$file->getURI()); - \xp::gc(__FILE__); - throw $e; - } - if (!isset($info['APP1'])) { - if (func_num_args() > 1) return func_get_arg(1); - throw new ElementNotFoundException( - 'Cannot get EXIF information from '.$file->getURI().' (no APP1 marker)' - ); - } - - if (!($info= exif_read_data($file->getURI(), 'COMPUTED,FILE,IFD0,EXIF,COMMENT,MAKERNOTE', true, false))) { - throw new FormatException('Cannot get EXIF information from '.$file->getURI()); - } - - // Change key case for lookups - foreach ($info as &$val) { - $val= array_change_key_case($val, CASE_LOWER); - } - - with ($e= new self()); { - - // COMPUTED info - $e->setWidth(self::lookup($info['COMPUTED'], 'width')); - $e->setHeight(self::lookup($info['COMPUTED'], 'height')); - $e->setApertureFNumber(self::lookup($info['COMPUTED'], 'aperturefnumber')); - - // IFD0 info - $e->setMake(trim(self::lookup($info['IFD0'], 'make'))); - $e->setModel(trim(self::lookup($info['IFD0'], 'model'))); - $software= self::lookup($info['IFD0'], 'software'); - $e->setSoftware(null === $software ? null : trim($software)); - - if (null !== ($o= self::lookup($info['IFD0'], 'orientation'))) { - $e->setOrientation($o); - } else { - $e->setOrientation(($e->width / $e->height) > 1.0 - ? 1 // normal - : 5 // transpose - ); - } - - // FILE info - $e->setFileName(self::lookup($info['FILE'], 'filename')); - $e->setFileSize(self::lookup($info['FILE'], 'filesize')); - $e->setMimeType(self::lookup($info['FILE'], 'mimetype')); - - // EXIF info - $e->setExposureTime(self::lookup($info['EXIF'], 'exposuretime')); - $e->setExposureProgram(self::lookup($info['EXIF'], 'exposureprogram')); - $e->setMeteringMode(self::lookup($info['EXIF'], 'meteringmode')); - $e->setIsoSpeedRatings(self::lookup($info['EXIF'], 'isospeedratings')); - - // Sometimes white balance is in MAKERNOTE - e.g. FUJIFILM's Finepix - if (null !== ($w= self::lookup($info['EXIF'], 'whitebalance'))) { - $e->setWhiteBalance($w); - } else if (isset($info['MAKERNOTE']) && null !== ($w= self::lookup($info['MAKERNOTE'], 'whitebalance'))) { - $e->setWhiteBalance($w); - } else { - $e->setWhiteBalance(null); - } - - // Extract focal length. Some models store "80" as "80/1", rip off - // the divisor "1" in this case. - if (null !== ($l= self::lookup($info['EXIF'], 'focallength'))) { - sscanf($l, '%d/%d', $n, $frac); - $e->setFocalLength(1 == $frac ? $n : $n.'/'.$frac); - } else { - $e->setFocalLength(null); - } - - // Check for Flash and flashUsed keys - if (null !== ($f= self::lookup($info['EXIF'], 'flash'))) { - $e->setFlash($f); - } else { - $e->setFlash(null); - } - - if (null !== ($date= self::lookup($info['EXIF'], 'datetimeoriginal', 'datetimedigitized'))) { - $t= sscanf($date, '%4d:%2d:%2d %2d:%2d:%2d'); - $e->setDateTime(new Date(mktime($t[3], $t[4], $t[5], $t[1], $t[2], $t[0]))); - } - } - return $e; - } - - /** - * Set Height - * - * @param int height - */ - public function setHeight($height) { - $this->height= $height; - } - /** * Set Height * * @param int height - * @return img.util.ExifData this + * @return self */ public function withHeight($height) { $this->height= $height; return $this; } - /** - * Get Height - * - * @return int - */ - public function getHeight() { - return $this->height; - } - - /** - * Set Width - * - * @param int width - */ - public function setWidth($width) { - $this->width= $width; - } - /** * Set Width * * @param int width - * @return img.util.ExifData this + * @return self */ public function withWidth($width) { $this->width= $width; return $this; } - /** - * Get Width - * - * @return int - */ - public function getWidth() { - return $this->width; - } - - /** - * Set Make - * - * @param string make - */ - public function setMake($make) { - $this->make= $make; - } - /** * Set Make * * @param string make - * @return img.util.ExifData this + * @return self */ public function withMake($make) { $this->make= $make; return $this; } - /** - * Get Make - * - * @return string - */ - public function getMake() { - return $this->make; - } - /** * Set Model * * @param string model - */ - public function setModel($model) { - $this->model= $model; - } - - /** - * Set Model - * - * @param string model - * @return img.util.ExifData this + * @return self */ public function withModel($model) { $this->model= $model; return $this; } - /** - * Get Model - * - * @return string - */ - public function getModel() { - return $this->model; - } - - /** - * Set Flash - * - * @param int flash - */ - public function setFlash($flash) { - $this->flash= $flash; - } - /** * Set Flash * * @param int flash - * @return img.util.ExifData this + * @return self */ public function withFlash($flash) { $this->flash= $flash; return $this; } - /** - * Get Flash. This is a bitmask: - *
-   *   0 = flash fired
-   *   1 = return detected
-   *   2 = return able to be detected
-   *   3 = unknown
-   *   4 = auto used
-   *   5 = unknown
-   *   6 = red eye reduction used
-   * 
- * - * @return int - */ - public function getFlash() { - return $this->flash; - } - - /** - * Set Orientation - * - * @param int orientation - */ - public function setOrientation($orientation) { - $this->orientation= $orientation; - } - /** * Set Orientation * * @param int orientation - * @return img.util.ExifData this + * @return self */ public function withOrientation($orientation) { $this->orientation= $orientation; return $this; } - /** - * Get Orientation - * - * @return int - */ - public function getOrientation() { - return $this->orientation; - } - /** * Set FileName * * @param string fileName + * @return self */ - public function setFileName($fileName) { + public function withFileName($fileName) { $this->fileName= $fileName; + return $this; } /** - * Set FileName + * Set FileSize * - * @param string fileName - * @return img.util.ExifData this + * @param int fileSize + * @return self */ - public function withFileName($fileName) { - $this->fileName= $fileName; + public function withFileSize($fileSize) { + $this->fileSize= $fileSize; return $this; } /** - * Get FileName + * Set MimeType * - * @return string + * @param string mimeType + * @return self */ - public function getFileName() { - return $this->fileName; + public function withMimeType($mimeType) { + $this->mimeType= $mimeType; + return $this; } /** - * Set FileSize + * Set DateTime * - * @param int fileSize + * @param util.Date dateTime + * @return self */ - public function setFileSize($fileSize) { - $this->fileSize= $fileSize; + public function withDateTime($dateTime) { + $this->dateTime= $dateTime; + return $this; } /** - * Set FileSize + * Set ApertureFNumber * - * @param int fileSize - * @return img.util.ExifData this + * @param string apertureFNumber + * @return self */ - public function withFileSize($fileSize) { - $this->fileSize= $fileSize; + public function withApertureFNumber($apertureFNumber) { + $this->apertureFNumber= $apertureFNumber; return $this; } /** - * Get FileSize + * Set Software * - * @return int + * @param string software + * @return self */ - public function getFileSize() { - return $this->fileSize; + public function withSoftware($software) { + $this->software= $software; + return $this; } /** - * Set MimeType + * Set ExposureTime * - * @param string mimeType + * @param string exposureTime + * @return self */ - public function setMimeType($mimeType) { - $this->mimeType= $mimeType; + public function withExposureTime($exposureTime) { + $this->exposureTime= $exposureTime; + return $this; } /** - * Set MimeType + * Set ExposureProgram * - * @param string mimeType - * @return img.util.ExifData this + * @param int exposureProgram + * @return self */ - public function withMimeType($mimeType) { - $this->mimeType= $mimeType; + public function withExposureProgram($exposureProgram) { + $this->exposureProgram= $exposureProgram; return $this; } /** - * Get MimeType + * Set MeteringMode * - * @return string + * @param int meteringMode + * @return self */ - public function getMimeType() { - return $this->mimeType; + public function withMeteringMode($meteringMode) { + $this->meteringMode= $meteringMode; + return $this; } /** - * Set DateTime + * Set Whitebalance * - * @param util.Date dateTime + * @param int whitebalance + * @return self */ - public function setDateTime($dateTime) { - $this->dateTime= $dateTime; + public function withWhitebalance($whitebalance) { + $this->whitebalance= $whitebalance; + return $this; } /** - * Set DateTime + * Set IsoSpeedRatings * - * @param util.Date dateTime - * @return img.util.ExifData this + * @param int isoSpeedRatings + * @return self */ - public function withDateTime($dateTime) { - $this->dateTime= $dateTime; + public function withIsoSpeedRatings($isoSpeedRatings) { + $this->isoSpeedRatings= $isoSpeedRatings; return $this; } /** - * Get DateTime + * Set FocalLength * - * @return util.Date + * @param int FocalLength + * @return self */ - public function getDateTime() { - return $this->dateTime; + public function withFocalLength($focallength) { + $this->focalLength= $focallength; + return $this; } - + /** * Retrieve whether the flash was used. * + * - 0 = flash fired + * - 1 = return detected + * - 2 = return able to be detected + * - 3 = unknown + * - 4 = auto used + * - 5 = unknown + * - 6 = red eye reduction used + * * @see http://www.drewnoakes.com/code/exif/ * @see http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/flash.html * @return bool @@ -536,122 +323,6 @@ public function getRotationDegree() { return $degree[$this->orientation] ?? 0; } - /** - * Set ApertureFNumber - * - * @param string apertureFNumber - */ - public function setApertureFNumber($apertureFNumber) { - $this->apertureFNumber= $apertureFNumber; - } - - /** - * Set ApertureFNumber - * - * @param string apertureFNumber - * @return img.util.ExifData this - */ - public function withApertureFNumber($apertureFNumber) { - $this->apertureFNumber= $apertureFNumber; - return $this; - } - - /** - * Get ApertureFNumber - * - * @return string - */ - public function getApertureFNumber() { - return $this->apertureFNumber; - } - - /** - * Set Software - * - * @param string software - */ - public function setSoftware($software) { - $this->software= $software; - } - - /** - * Set Software - * - * @param string software - * @return img.util.ExifData this - */ - public function withSoftware($software) { - $this->software= $software; - return $this; - } - - /** - * Get Software - * - * @return string - */ - public function getSoftware() { - return $this->software; - } - - /** - * Set ExposureTime - * - * @param string exposureTime - */ - public function setExposureTime($exposureTime) { - $this->exposureTime= $exposureTime; - } - - /** - * Set ExposureTime - * - * @param string exposureTime - * @return img.util.ExifData this - */ - public function withExposureTime($exposureTime) { - $this->exposureTime= $exposureTime; - return $this; - } - - /** - * Get ExposureTime - * - * @return string - */ - public function getExposureTime() { - return $this->exposureTime; - } - - /** - * Set ExposureProgram - * - * @param int exposureProgram - */ - public function setExposureProgram($exposureProgram) { - $this->exposureProgram= $exposureProgram; - } - - /** - * Set ExposureProgram - * - * @param int exposureProgram - * @return img.util.ExifData this - */ - public function withExposureProgram($exposureProgram) { - $this->exposureProgram= $exposureProgram; - return $this; - } - - /** - * Get ExposureProgram - * - * @return int - */ - public function getExposureProgram() { - return $this->exposureProgram; - } - /** * Get String describing exposureProgram value. * @@ -673,35 +344,6 @@ public function getExposureProgramString() { return $ep[$this->exposureProgram] ?? 'n/a'; } - /** - * Set MeteringMode - * - * @param int meteringMode - */ - public function setMeteringMode($meteringMode) { - $this->meteringMode= $meteringMode; - } - - /** - * Set MeteringMode - * - * @param int meteringMode - * @return img.util.ExifData this - */ - public function withMeteringMode($meteringMode) { - $this->meteringMode= $meteringMode; - return $this; - } - - /** - * Get MeteringMode - * - * @return int - */ - public function getMeteringMode() { - return $this->meteringMode; - } - /** * Get string describing meteringMode value. * @@ -722,107 +364,6 @@ public function getMeteringModeString() { return $mm[$this->meteringMode] ?? 'n/a'; } - /** - * Set Whitebalance - * - * @param int whitebalance - */ - public function setWhitebalance($whitebalance) { - $this->whitebalance= $whitebalance; - } - - /** - * Set Whitebalance - * - * @param int whitebalance - * @return img.util.ExifData this - */ - public function withWhitebalance($whitebalance) { - $this->whitebalance= $whitebalance; - return $this; - } - - /** - * Get Whitebalance. - * Values are 0 = auto white balance, 1 = manual white balance. - * - * @return int - */ - public function getWhitebalance() { - return $this->whitebalance; - } - - /** - * Set IsoSpeedRatings - * - * @param int isoSpeedRatings - */ - public function setIsoSpeedRatings($isoSpeedRatings) { - $this->isoSpeedRatings= $isoSpeedRatings; - } - - /** - * Set IsoSpeedRatings - * - * @param int isoSpeedRatings - * @return img.util.ExifData this - */ - public function withIsoSpeedRatings($isoSpeedRatings) { - $this->isoSpeedRatings= $isoSpeedRatings; - return $this; - } - - /** - * Get IsoSpeedRatings - * - * @return int - */ - public function getIsoSpeedRatings() { - return $this->isoSpeedRatings; - } - - /** - * Set FocalLength - * - * @param int FocalLength - */ - public function setFocalLength($focallength) { - $this->focalLength= $focallength; - } - - /** - * Set FocalLength - * - * @param int FocalLength - * @return img.util.ExifData this - */ - public function withFocalLength($focallength) { - $this->focalLength= $focallength; - return $this; - } - - /** - * Get FocalLength - * - * @return int - */ - public function getFocalLength() { - return $this->focalLength; - } - - /** - * Get Thumbnail - * - * @return img.Image - */ - public function getThumbnail() { - $s= new Stream(); - $s->open(STREAM_MODE_WRITE); - $s->write(exif_thumbnail($this->getFilename())); - $s->rewind(); - return Image::loadFrom(new StreamReader($s)); - } - /** * Retrieve a string representation * @@ -860,7 +401,7 @@ public function toString() { $this->orientation, $this->isHorizontal() ? 'horizontal' : 'vertical', $this->getOrientationString(), - \xp::stringOf($this->dateTime), + $this->dateTime ? $this->dateTime->toString() : 'null', $this->apertureFNumber, $this->exposureTime, $this->exposureProgram, diff --git a/src/main/php/img/util/IptcData.class.php b/src/main/php/img/util/IptcData.class.php index ff813107..7c97bea0 100755 --- a/src/main/php/img/util/IptcData.class.php +++ b/src/main/php/img/util/IptcData.class.php @@ -1,22 +1,12 @@ toString(); - * ``` + * IPTC headers from Photoshop-files JPEGs or TIFFs * * @test net.xp_framework.unittest.img.IptcDataTest - * @see php://iptcparse * @see http://photothumb.com/IPTCExt/ * @see http://www.controlledvocabulary.com/pdf/IPTC_mapped_fields.pdf */ @@ -48,615 +38,215 @@ static function __static() { self::$EMPTY= new self(); } - /** - * Read from a file - * - * @deprecated Use img.io.MetaDataReader instead - * @param io.File file - * @param var default default void what should be returned in case no data is found - * @return img.util.IptcData - * @throws lang.FormatException in case malformed meta data is encountered - * @throws lang.ElementNotFoundException in case no meta data is available - * @throws img.ImagingException in case reading meta data fails - */ - public static function fromFile(\io\File $file) { - if (false === getimagesize($file->getURI(), $info)) { - $e= new ImagingException('Cannot read image information from '.$file->getURI()); - \xp::gc(__FILE__); - throw $e; - } - if (!isset($info['APP13'])) { - if (func_num_args() > 1) return func_get_arg(1); - throw new ElementNotFoundException( - 'Cannot get IPTC information from '.$file->getURI().' (no APP13 marker)' - ); - } - if (!($iptc= iptcparse($info['APP13']))) { - throw new FormatException('Cannot parse IPTC information from '.$file->getURI()); - } - - // Parse creation date - if (3 == sscanf($iptc['2#055'][0] ?? '', '%4d%2d%d', $year, $month, $day)) { - $created= Date::create($year, $month, $day, 0, 0, 0); - } else { - $created= null; - } - - with ($i= new self()); { - $i->setTitle($iptc['2#005'][0] ?? null); - $i->setUrgency($iptc['2#010'][0] ?? null); - $i->setCategory($iptc['2#015'][0] ?? null); - $i->setSupplementalCategories($iptc['2#020'] ?? null); - $i->setKeywords($iptc['2#025'] ?? null); - $i->setSpecialInstructions($iptc['2#040'][0] ?? null); - $i->setDateCreated($created); - $i->setAuthor($iptc['2#080'][0] ?? null); - $i->setAuthorPosition($iptc['2#085'][0] ?? null); - $i->setCity($iptc['2#090'][0] ?? null); - $i->setState($iptc['2#095'][0] ?? null); - $i->setCountry($iptc['2#101'][0] ?? null); - $i->setOriginalTransmissionReference($iptc['2#103'][0] ?? null); - $i->setHeadline($iptc['2#105'][0] ?? null); - $i->setCredit($iptc['2#110'][0] ?? null); - $i->setSource($iptc['2#115'][0] ?? null); - $i->setCopyrightNotice($iptc['2#116'][0] ?? null); - $i->setCaption($iptc['2#120'][0] ?? null); - $i->setWriter($iptc['2#122'][0] ?? null); - } - return $i; - } - - /** - * Set Title - * - * @param string title - */ - public function setTitle($title) { - $this->title= $title; - } - /** * Set Title * * @param string title - * @return img.util.IptcData this + * @return self */ public function withTitle($title) { $this->title= $title; return $this; } - /** - * Get Title - * - * @return string - */ - public function getTitle() { - return $this->title; - } - /** * Set Urgency * * @param string urgency - */ - public function setUrgency($urgency) { - $this->urgency= $urgency; - } - - /** - * Set Urgency - * - * @param string urgency - * @return img.util.IptcData this + * @return self */ public function withUrgency($urgency) { $this->urgency= $urgency; return $this; } - /** - * Get Urgency - * - * @return string - */ - public function getUrgency() { - return $this->urgency; - } - /** * Set Category * * @param string category - */ - public function setCategory($category) { - $this->category= $category; - } - - /** - * Set Category - * - * @param string category - * @return img.util.IptcData this + * @return self */ public function withCategory($category) { $this->category= $category; return $this; } - /** - * Get Category - * - * @return string - */ - public function getCategory() { - return $this->category; - } - - /** - * Set Keywords - * - * @param string[] keywords - */ - public function setKeywords($keywords) { - $this->keywords= $keywords; - } - /** * Set Keywords * * @param string[] keywords - * @return img.util.IptcData this + * @return self */ public function withKeywords($keywords) { $this->keywords= $keywords; return $this; } - /** - * Get Keywords - * - * @return string[] - */ - public function getKeywords() { - return $this->keywords; - } - /** * Set DateCreated * - * @param util.Date dateCreated default NULL - */ - public function setDateCreated($dateCreated= null) { - $this->dateCreated= $dateCreated; - } - - /** - * Set DateCreated - * - * @param util.Date dateCreated default NULL - * @return img.util.IptcData this + * @param ?util.Date dateCreated default NULL + * @return self */ public function withDateCreated($dateCreated= null) { $this->dateCreated= $dateCreated; return $this; } - /** - * Get DateCreated - * - * @return util.Date - */ - public function getDateCreated() { - return $this->dateCreated; - } - - /** - * Set Author - * - * @param string author - */ - public function setAuthor($author) { - $this->author= $author; - } - /** * Set Author * * @param string author - * @return img.util.IptcData this + * @return self */ public function withAuthor($author) { $this->author= $author; return $this; } - /** - * Get Author - * - * @return string - */ - public function getAuthor() { - return $this->author; - } - /** * Set AuthorPosition * * @param string authorPosition - */ - public function setAuthorPosition($authorPosition) { - $this->authorPosition= $authorPosition; - } - - /** - * Set AuthorPosition - * - * @param string authorPosition - * @return img.util.IptcData this + * @return self */ public function withAuthorPosition($authorPosition) { $this->authorPosition= $authorPosition; return $this; } - /** - * Get AuthorPosition - * - * @return string - */ - public function getAuthorPosition() { - return $this->authorPosition; - } - /** * Set City * * @param string city - */ - public function setCity($city) { - $this->city= $city; - } - - /** - * Set City - * - * @param string city - * @return img.util.IptcData this + * @return self */ public function withCity($city) { $this->city= $city; return $this; } - /** - * Get City - * - * @return string - */ - public function getCity() { - return $this->city; - } - - /** - * Set State - * - * @param string state - */ - public function setState($state) { - $this->state= $state; - } - /** * Set State * * @param string state - * @return img.util.IptcData this + * @return self */ public function withState($state) { $this->state= $state; return $this; } - /** - * Get State - * - * @return string - */ - public function getState() { - return $this->state; - } - - /** - * Set Country - * - * @param string country - */ - public function setCountry($country) { - $this->country= $country; - } - /** * Set Country * * @param string country - * @return img.util.IptcData this + * @return self */ public function withCountry($country) { $this->country= $country; return $this; } - /** - * Get Country - * - * @return string - */ - public function getCountry() { - return $this->country; - } - - /** - * Set Headline - * - * @param string headline - */ - public function setHeadline($headline) { - $this->headline= $headline; - } - /** * Set Headline * * @param string headline - * @return img.util.IptcData this + * @return self */ public function withHeadline($headline) { $this->headline= $headline; return $this; } - /** - * Get Headline - * - * @return string - */ - public function getHeadline() { - return $this->headline; - } - - /** - * Set Credit - * - * @param string credit - */ - public function setCredit($credit) { - $this->credit= $credit; - } - /** * Set Credit * * @param string credit - * @return img.util.IptcData this + * @return self */ public function withCredit($credit) { $this->credit= $credit; return $this; } - /** - * Get Credit - * - * @return string - */ - public function getCredit() { - return $this->credit; - } - /** * Set Source * * @param string source - */ - public function setSource($source) { - $this->source= $source; - } - - /** - * Set Source - * - * @param string source - * @return img.util.IptcData this + * @return self */ public function withSource($source) { $this->source= $source; return $this; } - /** - * Get Source - * - * @return string - */ - public function getSource() { - return $this->source; - } - - /** - * Set CopyrightNotice - * - * @param string copyrightNotice - */ - public function setCopyrightNotice($copyrightNotice) { - $this->copyrightNotice= $copyrightNotice; - } - /** * Set CopyrightNotice * * @param string copyrightNotice - * @return img.util.IptcData this + * @return self */ public function withCopyrightNotice($copyrightNotice) { $this->copyrightNotice= $copyrightNotice; return $this; } - /** - * Get CopyrightNotice - * - * @return string - */ - public function getCopyrightNotice() { - return $this->copyrightNotice; - } - /** * Set Caption * * @param string caption - */ - public function setCaption($caption) { - $this->caption= $caption; - } - - /** - * Set Caption - * - * @param string caption - * @return img.util.IptcData this + * @return self */ public function withCaption($caption) { $this->caption= $caption; return $this; } - /** - * Get Caption - * - * @return string - */ - public function getCaption() { - return $this->caption; - } - - /** - * Set Writer - * - * @param string writer - */ - public function setWriter($writer) { - $this->writer= $writer; - } - /** * Set Writer * * @param string writer - * @return img.util.IptcData this + * @return self */ public function withWriter($writer) { $this->writer= $writer; return $this; } - /** - * Get Writer - * - * @return string - */ - public function getWriter() { - return $this->writer; - } - /** * Set SupplementalCategories * * @param string[] supplementalCategories - */ - public function setSupplementalCategories($supplementalCategories) { - $this->supplementalCategories= $supplementalCategories; - } - - /** - * Set SupplementalCategories - * - * @param string[] supplementalCategories - * @return img.util.IptcData this + * @return self */ public function withSupplementalCategories($supplementalCategories) { $this->supplementalCategories= $supplementalCategories; return $this; } - /** - * Get SupplementalCategories - * - * @return string[] - */ - public function getSupplementalCategories() { - return $this->supplementalCategories; - } - /** * Set SpecialInstructions * * @param string specialInstructions - */ - public function setSpecialInstructions($specialInstructions) { - $this->specialInstructions= $specialInstructions; - } - - /** - * Set SpecialInstructions - * - * @param string specialInstructions - * @return img.util.IptcData this + * @return self */ public function withSpecialInstructions($specialInstructions) { $this->specialInstructions= $specialInstructions; return $this; } - /** - * Get SpecialInstructions - * - * @return string - */ - public function getSpecialInstructions() { - return $this->specialInstructions; - } - - /** - * Set OriginalTransmissionReference - * - * @param string originalTransmissionReference - */ - public function setOriginalTransmissionReference($originalTransmissionReference) { - $this->originalTransmissionReference= $originalTransmissionReference; - } - /** * Set OriginalTransmissionReference * * @param string originalTransmissionReference - * @return img.util.IptcData this + * @return self */ public function withOriginalTransmissionReference($originalTransmissionReference) { $this->originalTransmissionReference= $originalTransmissionReference; return $this; } - /** - * Get OriginalTransmissionReference - * - * @return string - */ - public function getOriginalTransmissionReference() { - return $this->originalTransmissionReference; - } - /** @return string */ public function toString() { return sprintf( @@ -683,8 +273,8 @@ public function toString() { $this->title, $this->urgency, $this->category, - \xp::stringOf($this->keywords, ' '), - \xp::stringOf($this->dateCreated), + Objects::stringOf($this->keywords, ' '), + $this->dateCreated ? $this->dateCreated->toString() : 'null', $this->author, $this->authorPosition, $this->city, @@ -696,7 +286,7 @@ public function toString() { $this->copyrightNotice, $this->caption, $this->writer, - \xp::stringOf($this->supplementalCategories, ' '), + Objects::stringOf($this->supplementalCategories, ' '), $this->specialInstructions, $this->originalTransmissionReference ); diff --git a/src/test/php/img/unittest/ExifDataTest.class.php b/src/test/php/img/unittest/ExifDataTest.class.php index 9e480910..b7b7bedd 100755 --- a/src/test/php/img/unittest/ExifDataTest.class.php +++ b/src/test/php/img/unittest/ExifDataTest.class.php @@ -1,309 +1,32 @@ resourceAsFile('iptc-only.jpg'), null)); - } - - #[Test] - public function emptyExifData() { - Assert::equals(0, ExifData::$EMPTY->getWidth()); - } - - #[Test, Expect(ElementNotFoundException::class)] - public function fromFileWithIptcOnly() { - $this->extractFromFile($this->resourceAsFile('iptc-only.jpg')); - } - - #[Test, Ignore('https://bugs.php.net/bug.php?id=72819')] - public function fromFileWithExifAndIptc() { - $i= $this->extractFromFile($this->resourceAsFile('exif-and-iptc.jpg')); - Assert::equals(1, $i->getWidth()); - Assert::equals(1, $i->getHeight()); - } - - #[Test, Ignore('https://bugs.php.net/bug.php?id=72819')] - public function fromFileWithExifOnly() { - $i= $this->extractFromFile($this->resourceAsFile('exif-only.jpg')); - Assert::equals(1, $i->getWidth()); - Assert::equals(1, $i->getHeight()); - } - - #[Test, Ignore('https://bugs.php.net/bug.php?id=72819')] - public function exifSampleCanonIxus() { - $i= $this->extractFromFile($this->resourceAsFile('canon-ixus.jpg', 'exif_org')); - Assert::equals('1/350', $i->getExposureTime()); - Assert::equals('346/32', $i->getFocalLength()); - Assert::equals('Canon', $i->getMake()); - Assert::equals('f/4.0', $i->getApertureFNumber()); - Assert::equals(null, $i->getIsoSpeedRatings()); - Assert::equals(null, $i->getSoftware()); - Assert::equals(null, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(640, $i->getWidth()); - Assert::equals(480, $i->getHeight()); - Assert::equals('Canon DIGITAL IXUS', $i->getModel()); - Assert::equals('2001:06:09 15:17:32', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(2, $i->getMeteringMode()); - Assert::equals(0, $i->getFlash()); - Assert::false($i->flashUsed()); - } +class ExifDataTest { #[Test] - public function exifSampleFujifilmDx10() { - $i= $this->extractFromFile($this->resourceAsFile('fujifilm-dx10.jpg', 'exif_org')); - Assert::equals(null, $i->getExposureTime()); - Assert::equals('58/10', $i->getFocalLength()); - Assert::equals('FUJIFILM', $i->getMake()); - Assert::equals('f/4.2', $i->getApertureFNumber()); - Assert::equals(150, $i->getIsoSpeedRatings()); - Assert::equals('Digital Camera DX-10 Ver1.00', $i->getSoftware()); - Assert::equals(2, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(1024, $i->getWidth()); - Assert::equals(768, $i->getHeight()); - Assert::equals('DX-10', $i->getModel()); - Assert::equals('2001:04:12 20:33:14', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(5, $i->getMeteringMode()); - Assert::equals(1, $i->getFlash()); - Assert::true($i->flashUsed()); + public function can_create() { + new ExifData(); } - #[Test] - public function exifSampleFujifilmFinepix40i() { - $i= $this->extractFromFile($this->resourceAsFile('fujifilm-finepix40i.jpg', 'exif_org')); - Assert::equals(null, $i->getExposureTime()); - Assert::equals('870/100', $i->getFocalLength()); - Assert::equals('FUJIFILM', $i->getMake()); - Assert::equals('f/2.8', $i->getApertureFNumber()); - Assert::equals(200, $i->getIsoSpeedRatings()); - Assert::equals('Digital Camera FinePix40i Ver1.39', $i->getSoftware()); - Assert::equals(2, $i->getExposureProgram()); - Assert::equals(0, $i->getWhiteBalance()); - Assert::equals(600, $i->getWidth()); - Assert::equals(450, $i->getHeight()); - Assert::equals('FinePix40i', $i->getModel()); - Assert::equals('2000:08:04 18:22:57', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(5, $i->getMeteringMode()); - Assert::equals(1, $i->getFlash()); - Assert::true($i->flashUsed()); - } - - #[Test] - public function exifSampleFujifilmMx1700() { - $i= $this->extractFromFile($this->resourceAsFile('fujifilm-mx1700.jpg', 'exif_org')); - Assert::equals(null, $i->getExposureTime()); - Assert::equals('99/10', $i->getFocalLength()); - Assert::equals('FUJIFILM', $i->getMake()); - Assert::equals('f/7.0', $i->getApertureFNumber()); - Assert::equals(125, $i->getIsoSpeedRatings()); - Assert::equals('Digital Camera MX-1700ZOOM Ver1.00', $i->getSoftware()); - Assert::equals(2, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(640, $i->getWidth()); - Assert::equals(480, $i->getHeight()); - Assert::equals('MX-1700ZOOM', $i->getModel()); - Assert::equals('2000:09:02 14:30:10', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(5, $i->getMeteringMode()); - Assert::equals(0, $i->getFlash()); - Assert::false($i->flashUsed()); + #[Test, Values([[0, false], [1, true], [2, false], [3, true]])] + public function flashUsed($flash, $expected) { + Assert::equals($expected, (new ExifData())->withFlash($flash)->flashUsed()); } - #[Test, Ignore('Not reliable within XAR files, see issue #259')] - public function exifSampleKodakDC210() { - $i= $this->extractFromFile($this->resourceAsFile('kodak-dc210.jpg', 'exif_org')); - Assert::equals('1/30', $i->getExposureTime()); - Assert::equals('44/10', $i->getFocalLength()); - Assert::equals('Eastman Kodak Company', $i->getMake()); - Assert::equals('f/4.0', $i->getApertureFNumber()); - Assert::equals(null, $i->getIsoSpeedRatings()); - Assert::equals(null, $i->getSoftware()); - Assert::equals(null, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(640, $i->getWidth()); - Assert::equals(480, $i->getHeight()); - Assert::equals('DC210 Zoom (V05.00)', $i->getModel()); - Assert::equals('2000:10:26 16:46:51', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(2, $i->getMeteringMode()); - Assert::equals(1, $i->getFlash()); - Assert::true($i->flashUsed()); + #[Test, Values([[0, true], [4, true], [5, false]])] + public function isHorizontal($orientation, $expected) { + Assert::equals($expected, (new ExifData())->withOrientation($orientation)->isHorizontal()); } - #[Test] - public function exifSampleKodakDC240() { - $i= $this->extractFromFile($this->resourceAsFile('kodak-dc240.jpg', 'exif_org')); - Assert::equals('1/30', $i->getExposureTime()); - Assert::equals('140/10', $i->getFocalLength()); - Assert::equals('EASTMAN KODAK COMPANY', $i->getMake()); - Assert::equals('f/4.0', $i->getApertureFNumber()); - Assert::equals(null, $i->getIsoSpeedRatings()); - Assert::equals(null, $i->getSoftware()); - Assert::equals(null, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(640, $i->getWidth()); - Assert::equals(480, $i->getHeight()); - Assert::equals('KODAK DC240 ZOOM DIGITAL CAMERA', $i->getModel()); - Assert::equals('1999:05:25 21:00:09', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(1, $i->getMeteringMode()); - Assert::equals(1, $i->getFlash()); - Assert::true($i->flashUsed()); - } - - #[Test] - public function exifSampleNikonE950() { - $i= $this->extractFromFile($this->resourceAsFile('nikon-e950.jpg', 'exif_org')); - Assert::equals('10/770', $i->getExposureTime()); - Assert::equals('128/10', $i->getFocalLength()); - Assert::equals('NIKON', $i->getMake()); - Assert::equals('f/5.5', $i->getApertureFNumber()); - Assert::equals(80, $i->getIsoSpeedRatings()); - Assert::equals('v981-79', $i->getSoftware()); - Assert::equals(2, $i->getExposureProgram()); - Assert::equals(0, $i->getWhiteBalance()); - Assert::equals(800, $i->getWidth()); - Assert::equals(600, $i->getHeight()); - Assert::equals('E950', $i->getModel()); - Assert::equals('2001:04:06 11:51:40', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(5, $i->getMeteringMode()); - Assert::equals(0, $i->getFlash()); - Assert::false($i->flashUsed()); - } - - #[Test] - public function exifSampleOlympusC960() { - $i= $this->extractFromFile($this->resourceAsFile('olympus-c960.jpg', 'exif_org')); - Assert::equals('1/345', $i->getExposureTime()); - Assert::equals('56/10', $i->getFocalLength()); - Assert::equals('OLYMPUS OPTICAL CO.,LTD', $i->getMake()); - Assert::equals('f/8.0', $i->getApertureFNumber()); - Assert::equals(125, $i->getIsoSpeedRatings()); - Assert::equals('OLYMPUS CAMEDIA Master', $i->getSoftware()); - Assert::equals(2, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(640, $i->getWidth()); - Assert::equals(480, $i->getHeight()); - Assert::equals('C960Z,D460Z', $i->getModel()); - Assert::equals('2000:11:07 10:41:43', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(5, $i->getMeteringMode()); - Assert::equals(0, $i->getFlash()); - Assert::false($i->flashUsed()); - } - - #[Test] - public function exifSampleRicohrdc5300() { - $i= $this->extractFromFile($this->resourceAsFile('ricoh-rdc5300.jpg', 'exif_org')); - Assert::equals(null, $i->getExposureTime()); - Assert::equals('133/10', $i->getFocalLength()); - Assert::equals('RICOH', $i->getMake()); - Assert::equals('f/4.0', $i->getApertureFNumber()); - Assert::equals(null, $i->getIsoSpeedRatings()); - Assert::equals(null, $i->getSoftware()); - Assert::equals(null, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(896, $i->getWidth()); - Assert::equals(600, $i->getHeight()); - Assert::equals('RDC-5300', $i->getModel()); - Assert::equals('2000:05:31 21:50:40', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(null, $i->getMeteringMode()); - Assert::equals(1, $i->getFlash()); - Assert::true($i->flashUsed()); - } - - #[Test] - public function exifSampleSanyoVpcg250() { - $i= $this->extractFromFile($this->resourceAsFile('sanyo-vpcg250.jpg', 'exif_org')); - Assert::equals('1/171', $i->getExposureTime()); - Assert::equals('60/10', $i->getFocalLength()); - Assert::equals('SANYO Electric Co.,Ltd.', $i->getMake()); - Assert::equals('f/8.0', $i->getApertureFNumber()); - Assert::equals(null, $i->getIsoSpeedRatings()); - Assert::equals('V06P-74', $i->getSoftware()); - Assert::equals(null, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(640, $i->getWidth()); - Assert::equals(480, $i->getHeight()); - Assert::equals('SR6', $i->getModel()); - Assert::equals('1998:01:01 00:00:00', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(2, $i->getMeteringMode()); - Assert::equals(1, $i->getFlash()); - Assert::true($i->flashUsed()); - } - - #[Test] - public function exifSampleSanyovpcsx550() { - $i= $this->extractFromFile($this->resourceAsFile('sanyo-vpcsx550.jpg', 'exif_org')); - Assert::equals('10/483', $i->getExposureTime()); - Assert::equals('60/10', $i->getFocalLength()); - Assert::equals('SANYO Electric Co.,Ltd.', $i->getMake()); - Assert::equals('f/2.4', $i->getApertureFNumber()); - Assert::equals(400, $i->getIsoSpeedRatings()); - Assert::equals('V113p-73', $i->getSoftware()); - Assert::equals(null, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(640, $i->getWidth()); - Assert::equals(480, $i->getHeight()); - Assert::equals('SX113', $i->getModel()); - Assert::equals('2000:11:18 21:14:19', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(2, $i->getMeteringMode()); - Assert::equals(0, $i->getFlash()); - Assert::false($i->flashUsed()); - } - - #[Test] - public function exifSampleSonyCybershot() { - $i= $this->extractFromFile($this->resourceAsFile('sony-cybershot.jpg', 'exif_org')); - Assert::equals('1/197', $i->getExposureTime()); - Assert::equals('216/10', $i->getFocalLength()); - Assert::equals('SONY', $i->getMake()); - Assert::equals('f/4.0', $i->getApertureFNumber()); - Assert::equals(100, $i->getIsoSpeedRatings()); - Assert::equals(null, $i->getSoftware()); - Assert::equals(2, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(640, $i->getWidth()); - Assert::equals(480, $i->getHeight()); - Assert::equals('CYBERSHOT', $i->getModel()); - Assert::equals('2000:09:30 10:59:45', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(2, $i->getMeteringMode()); - Assert::equals(0, $i->getFlash()); - Assert::false($i->flashUsed()); + #[Test, Values([[0, false], [4, false], [5, true]])] + public function isVertical($orientation, $expected) { + Assert::equals($expected, (new ExifData())->withOrientation($orientation)->isVertical()); } #[Test] - public function exifSampleSonyD700() { - $i= $this->extractFromFile($this->resourceAsFile('sony-d700.jpg', 'exif_org')); - Assert::equals(null, $i->getExposureTime()); - Assert::equals(null, $i->getFocalLength()); - Assert::equals('SONY', $i->getMake()); - Assert::equals('f/2.4', $i->getApertureFNumber()); - Assert::equals(200, $i->getIsoSpeedRatings()); - Assert::equals(null, $i->getSoftware()); - Assert::equals(3, $i->getExposureProgram()); - Assert::equals(null, $i->getWhiteBalance()); - Assert::equals(672, $i->getWidth()); - Assert::equals(512, $i->getHeight()); - Assert::equals('DSC-D700', $i->getModel()); - Assert::equals('1998:12:01 14:22:36', $i->getDateTime()->toString('Y:m:d H:i:s')); - Assert::equals(2, $i->getMeteringMode()); - Assert::equals(0, $i->getFlash()); - Assert::false($i->flashUsed()); + public function string_representation() { + Assert::notEquals('', (new ExifData())->toString()); } } \ No newline at end of file diff --git a/src/test/php/img/unittest/IptcDataTest.class.php b/src/test/php/img/unittest/IptcDataTest.class.php index 28d14951..445b160e 100755 --- a/src/test/php/img/unittest/IptcDataTest.class.php +++ b/src/test/php/img/unittest/IptcDataTest.class.php @@ -1,52 +1,17 @@ resourceAsFile('exif-only.jpg'), null)); - } - - #[Test] - public function emptyIptcData() { - Assert::equals('', IptcData::$EMPTY->getTitle()); - } - - #[Test, Expect(ElementNotFoundException::class)] - public function fromFileWithoutIptc() { - $this->extractFromFile($this->resourceAsFile('exif-only.jpg')); - } +class IptcDataTest { #[Test] - public function fromFileWithExifAndIptc() { - $i= $this->extractFromFile($this->resourceAsFile('exif-and-iptc.jpg')); - Assert::equals('Unittest Image', $i->getTitle()); + public function can_create() { + new IptcData(); } #[Test] - public function fromFile() { - $i= $this->extractFromFile($this->resourceAsFile('iptc-only.jpg')); - Assert::equals('Unittest Image', $i->getTitle()); + public function string_representation() { + Assert::notEquals('', (new IptcData())->toString()); } } \ No newline at end of file diff --git a/src/test/php/img/unittest/MetaDataReaderTest.class.php b/src/test/php/img/unittest/MetaDataReaderTest.class.php index ff3a989f..a6af9067 100755 --- a/src/test/php/img/unittest/MetaDataReaderTest.class.php +++ b/src/test/php/img/unittest/MetaDataReaderTest.class.php @@ -2,22 +2,15 @@ use DOMDocument; use img\ImagingException; -use img\io\{CommentSegment, MetaDataReader, SOFNSegment, XMPSegment}; +use img\io\{Segment, CommentSegment, MetaDataReader, SOFNSegment, XMPSegment, ExifSegment, IptcSegment}; +use img\util\{ExifData, IptcData}; +use lang\ArrayType; use test\{Assert, Before, Expect, Test}; +use util\Date; class MetaDataReaderTest { private $fixture; - /** - * Sets up this unittest - * - * @throws unittest.PrerequisitesNotMetError - */ - #[Before] - public function setUp() { - $this->fixture= new MetaDataReader(); - } - /** * Returns a file for a classloader resource * @@ -51,10 +44,15 @@ protected function extractFromFile($name, $sub= null) { * @throws unittest.AssertionFailedError */ protected function assertArrayOf($type, $size, $value) { - Assert::instance(new \lang\ArrayType($type), $value); + Assert::instance(new ArrayType($type), $value); Assert::equals($size, sizeof($value)); } + #[Before] + public function fixture() { + $this->fixture= new MetaDataReader(); + } + #[Test, Expect(class: ImagingException::class, message: '/Could not find start of image/')] public function this_class_file() { $this->extractFromFile(basename(__FILE__)); @@ -67,12 +65,12 @@ public function empty_file() { #[Test] public function all_segments() { - $this->assertArrayOf('img.io.Segment', 9, $this->extractFromFile('1x1.jpg')->allSegments()); + $this->assertArrayOf(Segment::class, 9, $this->extractFromFile('1x1.jpg')->allSegments()); } #[Test] public function segments_named_dqt() { - $this->assertArrayOf('img.io.Segment', 2, $this->extractFromFile('1x1.jpg')->segmentsNamed('DQT')); + $this->assertArrayOf(Segment::class, 2, $this->extractFromFile('1x1.jpg')->segmentsNamed('DQT')); } #[Test] @@ -87,7 +85,7 @@ public function segment_named_sof0() { public function segment_of_sofn() { Assert::equals( [new SOFNSegment('SOF0', ['bits' => 8, 'height' => 1, 'width' => 1, 'channels' => 3])], - $this->extractFromFile('1x1.jpg')->segmentsOf('img.io.SOFNSegment') + $this->extractFromFile('1x1.jpg')->segmentsOf(SOFNSegment::class) ); } @@ -95,14 +93,14 @@ public function segment_of_sofn() { public function com_segment() { Assert::equals( [new CommentSegment('COM', 'Created with GIMP')], - $this->extractFromFile('1x1.jpg')->segmentsOf('img.io.CommentSegment') + $this->extractFromFile('1x1.jpg')->segmentsOf(CommentSegment::class) ); } #[Test] public function xmp_segment() { - $segments= $this->extractFromFile('xmp.jpg')->segmentsOf('img.io.XMPSegment'); - $this->assertArrayOf('img.io.XMPSegment', 1, $segments); + $segments= $this->extractFromFile('xmp.jpg')->segmentsOf(XMPSegment::class); + $this->assertArrayOf(XMPSegment::class, 1, $segments); Assert::matches('/^<.+/', $segments[0]->source); Assert::instance(DOMDocument::class, $segments[0]->document()); @@ -120,12 +118,12 @@ public function dimensions_of_xmp_image() { #[Test] public function no_exif_data_segments_in_1x1() { - Assert::equals([], $this->extractFromFile('1x1.jpg')->segmentsOf('img.io.ExifSegment')); + Assert::equals([], $this->extractFromFile('1x1.jpg')->segmentsOf(ExifSegment::class)); } #[Test] public function no_iptc_data_segments_in_1x1() { - Assert::equals([], $this->extractFromFile('1x1.jpg')->segmentsOf('img.io.IptcSegment')); + Assert::equals([], $this->extractFromFile('1x1.jpg')->segmentsOf(IptcSegment::class)); } #[Test] @@ -141,30 +139,30 @@ public function no_iptc_data_in_1x1() { #[Test] public function exif_data_segments() { $this->assertArrayOf( - 'img.io.ExifSegment', 1, - $this->extractFromFile('exif-only.jpg')->segmentsOf('img.io.ExifSegment') + ExifSegment::class, 1, + $this->extractFromFile('exif-only.jpg')->segmentsOf(ExifSegment::class) ); } #[Test] public function iptc_data_segments() { $this->assertArrayOf( - 'img.io.IptcSegment', 1, - $this->extractFromFile('iptc-only.jpg')->segmentsOf('img.io.IptcSegment') + IptcSegment::class, 1, + $this->extractFromFile('iptc-only.jpg')->segmentsOf(IptcSegment::class) ); } #[Test] public function exif_and_iptc_data_segments() { $meta= $this->extractFromFile('exif-and-iptc.jpg'); - $this->assertArrayOf('img.io.ExifSegment', 1, $meta->segmentsOf('img.io.ExifSegment')); - $this->assertArrayOf('img.io.IptcSegment', 1, $meta->segmentsOf('img.io.IptcSegment')); + $this->assertArrayOf(ExifSegment::class, 1, $meta->segmentsOf(ExifSegment::class)); + $this->assertArrayOf(IptcSegment::class, 1, $meta->segmentsOf(IptcSegment::class)); } #[Test] public function exif_dot_org_sample_CanonIxus() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('canon-ixus.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -180,7 +178,7 @@ public function exif_dot_org_sample_CanonIxus() { ->withWidth(640) ->withHeight(480) ->withModel('Canon DIGITAL IXUS') - ->withDateTime(new \util\Date('2001:06:09 15:17:32')) + ->withDateTime(new Date('2001:06:09 15:17:32')) ->withMeteringMode(2) ->withFlash(0) ->withOrientation(1) @@ -192,7 +190,7 @@ public function exif_dot_org_sample_CanonIxus() { #[Test] public function exif_dot_org_sample_FujifilmDx10() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('fujifilm-dx10.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -207,7 +205,7 @@ public function exif_dot_org_sample_FujifilmDx10() { ->withWidth(1024) ->withHeight(768) ->withModel('DX-10') - ->withDateTime(new \util\Date('2001:04:12 20:33:14')) + ->withDateTime(new Date('2001:04:12 20:33:14')) ->withMeteringMode(5) ->withFlash(1) ->withOrientation(1) @@ -219,7 +217,7 @@ public function exif_dot_org_sample_FujifilmDx10() { #[Test] public function exif_dot_org_sample_FujifilmFinepix40i() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('fujifilm-finepix40i.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -234,7 +232,7 @@ public function exif_dot_org_sample_FujifilmFinepix40i() { ->withWidth(600) ->withHeight(450) ->withModel('FinePix40i') - ->withDateTime(new \util\Date('2000:08:04 18:22:57')) + ->withDateTime(new Date('2000:08:04 18:22:57')) ->withMeteringMode(5) ->withFlash(1) ->withOrientation(1) @@ -246,7 +244,7 @@ public function exif_dot_org_sample_FujifilmFinepix40i() { #[Test] public function exif_dot_org_sample_FujifilmMx1700() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('fujifilm-mx1700.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -261,7 +259,7 @@ public function exif_dot_org_sample_FujifilmMx1700() { ->withWidth(640) ->withHeight(480) ->withModel('MX-1700ZOOM') - ->withDateTime(new \util\Date('2000:09:02 14:30:10')) + ->withDateTime(new Date('2000:09:02 14:30:10')) ->withMeteringMode(5) ->withFlash(0) ->withOrientation(1) @@ -273,7 +271,7 @@ public function exif_dot_org_sample_FujifilmMx1700() { #[Test] public function exif_dot_org_sample_KodakDC210() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('kodak-dc210.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -288,7 +286,7 @@ public function exif_dot_org_sample_KodakDC210() { ->withWidth(640) ->withHeight(480) ->withModel('DC210 Zoom (V05.00)') - ->withDateTime(new \util\Date('2000:10:26 16:46:51')) + ->withDateTime(new Date('2000:10:26 16:46:51')) ->withMeteringMode(2) ->withFlash(1) ->withOrientation(1) @@ -300,7 +298,7 @@ public function exif_dot_org_sample_KodakDC210() { #[Test] public function exif_dot_org_sample_KodakDC240() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('kodak-dc240.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -315,7 +313,7 @@ public function exif_dot_org_sample_KodakDC240() { ->withWidth(640) ->withHeight(480) ->withModel('KODAK DC240 ZOOM DIGITAL CAMERA') - ->withDateTime(new \util\Date('1999:05:25 21:00:09')) + ->withDateTime(new Date('1999:05:25 21:00:09')) ->withMeteringMode(1) ->withFlash(1) ->withOrientation(1) @@ -327,7 +325,7 @@ public function exif_dot_org_sample_KodakDC240() { #[Test] public function exif_dot_org_sample_NikonE950() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('nikon-e950.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -342,7 +340,7 @@ public function exif_dot_org_sample_NikonE950() { ->withWidth(800) ->withHeight(600) ->withModel('E950') - ->withDateTime(new \util\Date('2001:04:06 11:51:40')) + ->withDateTime(new Date('2001:04:06 11:51:40')) ->withMeteringMode(5) ->withFlash(0) ->withOrientation(1) @@ -354,7 +352,7 @@ public function exif_dot_org_sample_NikonE950() { #[Test] public function exif_dot_org_sample_OlympusC960() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('olympus-c960.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -369,7 +367,7 @@ public function exif_dot_org_sample_OlympusC960() { ->withWidth(640) ->withHeight(480) ->withModel('C960Z,D460Z') - ->withDateTime(new \util\Date('2000:11:07 10:41:43')) + ->withDateTime(new Date('2000:11:07 10:41:43')) ->withMeteringMode(5) ->withFlash(0) ->withOrientation(1) @@ -381,7 +379,7 @@ public function exif_dot_org_sample_OlympusC960() { #[Test] public function exif_dot_org_sample_Ricohrdc5300() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('ricoh-rdc5300.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -396,7 +394,7 @@ public function exif_dot_org_sample_Ricohrdc5300() { ->withWidth(896) ->withHeight(600) ->withModel('RDC-5300') - ->withDateTime(new \util\Date('2000:05:31 21:50:40')) + ->withDateTime(new Date('2000:05:31 21:50:40')) ->withMeteringMode(null) ->withFlash(1) ->withOrientation(1) @@ -408,7 +406,7 @@ public function exif_dot_org_sample_Ricohrdc5300() { #[Test] public function exif_dot_org_sample_SanyoVpcg250() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('sanyo-vpcg250.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -423,7 +421,7 @@ public function exif_dot_org_sample_SanyoVpcg250() { ->withWidth(640) ->withHeight(480) ->withModel('SR6') - ->withDateTime(new \util\Date('1998:01:01 00:00:00')) + ->withDateTime(new Date('1998:01:01 00:00:00')) ->withMeteringMode(2) ->withFlash(1) ->withOrientation(1) @@ -435,7 +433,7 @@ public function exif_dot_org_sample_SanyoVpcg250() { #[Test] public function exif_dot_org_sample_Sanyovpcsx550() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('sanyo-vpcsx550.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -450,7 +448,7 @@ public function exif_dot_org_sample_Sanyovpcsx550() { ->withWidth(640) ->withHeight(480) ->withModel('SX113') - ->withDateTime(new \util\Date('2000:11:18 21:14:19')) + ->withDateTime(new Date('2000:11:18 21:14:19')) ->withMeteringMode(2) ->withFlash(0) ->withOrientation(1) @@ -462,7 +460,7 @@ public function exif_dot_org_sample_Sanyovpcsx550() { #[Test] public function exif_dot_org_sample_SonyCybershot() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('sony-cybershot.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -477,7 +475,7 @@ public function exif_dot_org_sample_SonyCybershot() { ->withWidth(640) ->withHeight(480) ->withModel('CYBERSHOT') - ->withDateTime(new \util\Date('2000:09:30 10:59:45')) + ->withDateTime(new Date('2000:09:30 10:59:45')) ->withMeteringMode(2) ->withFlash(0) ->withOrientation(1) @@ -489,7 +487,7 @@ public function exif_dot_org_sample_SonyCybershot() { #[Test] public function exif_dot_org_sample_SonyD700() { Assert::equals( - (new \img\util\ExifData()) + (new ExifData()) ->withFileName('sony-d700.jpg') ->withFileSize(-1) ->withMimeType('image/jpeg') @@ -504,7 +502,7 @@ public function exif_dot_org_sample_SonyD700() { ->withWidth(672) ->withHeight(512) ->withModel('DSC-D700') - ->withDateTime(new \util\Date('1998:12:01 14:22:36')) + ->withDateTime(new Date('1998:12:01 14:22:36')) ->withMeteringMode(2) ->withFlash(0) ->withOrientation(1) @@ -516,12 +514,12 @@ public function exif_dot_org_sample_SonyD700() { #[Test] public function detailed_iptc_data() { Assert::equals( - (new \img\util\IptcData()) + (new IptcData()) ->withTitle('Unittest Image') ->withUrgency(null) ->withCategory(null) ->withKeywords(null) - ->withDateCreated(new \util\Date('2011-12-07 00:00:00')) + ->withDateCreated(new Date('2011-12-07 00:00:00')) ->withAuthor(null) ->withAuthorPosition(null) ->withCity(null) @@ -543,7 +541,7 @@ public function detailed_iptc_data() { #[Test] public function gps_data() { - $exif= $this->extractFromFile('gps-embedded.jpg')->segmentsOf('img.io.ExifSegment')[0]; + $exif= $this->extractFromFile('gps-embedded.jpg')->segmentsOf(ExifSegment::class)[0]; Assert::equals( [ 'Version' => '2/2/0/0',