Skip to content

Commit 2c9b9c0

Browse files
authored
Merge pull request #31 from klaari/fix/creating-png-and-webp-from-string
Fix Image::fromString to support PNG and WebP files
2 parents 6e287e9 + 99d28e9 commit 2c9b9c0

File tree

9 files changed

+464
-140
lines changed

9 files changed

+464
-140
lines changed

src/Format/JPEG.php

+10-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ public static function fromString($string)
8585
fwrite($stream, $string);
8686
rewind($stream);
8787

88-
return self::fromStream($stream);
88+
$jpeg = self::fromStream($stream);
89+
$jpeg->setSizeFromString($string);
90+
91+
return $jpeg;
8992
}
9093

9194
/**
@@ -108,6 +111,8 @@ public static function fromImagick(\Imagick $imagick)
108111
*
109112
* @return self
110113
* @throws \Exception
114+
*
115+
* @todo calculate and set image size
111116
*/
112117
public static function fromStream($fileHandle)
113118
{
@@ -189,12 +194,14 @@ public static function fromStream($fileHandle)
189194
public static function fromFile($filename)
190195
{
191196
$fileHandle = @fopen($filename, 'rb');
192-
193197
if (!$fileHandle) {
194198
throw new \Exception(sprintf('Could not open file %s', $filename));
195199
}
196200

197-
return self::fromStream($fileHandle);
201+
$jpeg = self::fromStream($fileHandle);
202+
$jpeg->setSizeFromFile($filename);
203+
204+
return $jpeg;
198205
}
199206

200207
/**

src/Format/PNG.php

+18-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public function __construct($contents)
4040
}
4141

4242
$this->chunks = $this->getChunksFromContents($contents);
43+
44+
$this->setSizeFromString($contents);
4345
}
4446

4547
/**
@@ -101,7 +103,22 @@ public function getIptc()
101103
*/
102104
public static function fromFile($filename)
103105
{
104-
return new self(file_get_contents($filename));
106+
$contents = file_get_contents($filename);
107+
if ($contents === false) {
108+
throw new \Exception(sprintf('Could not open file %s', $filename));
109+
}
110+
111+
return new self($contents);
112+
}
113+
114+
/**
115+
* @param $string
116+
*
117+
* @return PNG
118+
*/
119+
public static function fromString($string)
120+
{
121+
return new self($string);
105122
}
106123

107124
/**

src/Format/PSD.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use CSD\Image\Metadata\Exif;
66
use CSD\Image\Metadata\Iptc;
7+
use CSD\Image\Metadata\UnsupportedException;
78
use CSD\Image\Metadata\Xmp;
89
use CSD\Image\Image;
910

@@ -70,6 +71,8 @@ public static function fromResource($gd)
7071

7172
/**
7273
* Load PSD from string.
74+
*
75+
* @todo calculate and set image size
7376
*/
7477
public static function fromString($string)
7578
{
@@ -96,6 +99,8 @@ public static function fromImagick(\Imagick $imagick)
9699
*
97100
* @return self
98101
* @throws \Exception
102+
*
103+
* @todo calculate and set image size
99104
*/
100105
public static function fromStream($fileHandle)
101106
{
@@ -194,7 +199,9 @@ public static function fromFile($filename)
194199
throw new \Exception(sprintf('Could not open file %s', $filename));
195200
}
196201

197-
return self::fromStream($fileHandle);
202+
$psd = self::fromStream($fileHandle);
203+
$psd->setSizeFromFile($filename);
204+
return $psd;
198205
}
199206

200207
/**

src/Format/WebP.php

+19-2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public function __construct($contents)
4747
if (!$this->isExtendedFormat()) {
4848
// throw new \Exception('Only extended WebP format is supported');
4949
}
50+
51+
$this->setSizeFromString($contents);
5052
}
5153

5254
/**
@@ -144,8 +146,23 @@ public function getIptc()
144146
*/
145147
public static function fromFile($filename)
146148
{
147-
// var_dump($filename);
148-
return new self(file_get_contents($filename));
149+
$contents = file_get_contents($filename);
150+
if ($contents === false) {
151+
throw new \Exception(sprintf('Could not open file %s', $filename));
152+
}
153+
154+
return new self($contents);
155+
}
156+
157+
158+
/**
159+
* @param $string
160+
*
161+
* @return PNG
162+
*/
163+
public static function fromString($string)
164+
{
165+
return new self($string);
149166
}
150167

151168
/**

src/Image.php

+32-21
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,6 @@ public static function fromFile($fileName)
198198
if (!$result) {
199199
throw new \Exception('Unrecognised file name');
200200
}
201-
202-
$size = getimagesize($fileName);
203-
$result->width = $size[0];
204-
$result->height = $size[1];
205201
return $result;
206202
}
207203

@@ -210,31 +206,46 @@ public static function fromFile($fileName)
210206
*
211207
* @return JPEG|WebP|PNG|false
212208
*/
209+
213210
public static function fromString($string)
214211
{
215-
$len = strlen($string);
212+
$imageInfo = getimagesizefromstring($string);
216213

217-
// try JPEG
218-
if ($len >= 2) {
219-
if (JPEG::SOI === substr($string, 0, 2)) {
220-
return JPEG::fromString($string);
221-
}
214+
if (!$imageInfo) {
215+
return false;
222216
}
223217

224-
// try WebP
225-
if ($len >= 4) {
226-
if ('RIFF' === substr($string, 0, 4) && 'WEBP' === substr($string, 8, 4)) {
227-
return WebP::fromString($string);
228-
}
229-
}
218+
$mime = $imageInfo['mime'];
230219

231-
// try PNG
232-
if ($len >= 8) {
233-
if (PNG::SIGNATURE === substr($string, 0, 8)) {
234-
return PNG::fromString($string);
235-
}
220+
$mimeToClass = [
221+
'image/jpeg' => JPEG::class,
222+
'image/png' => PNG::class,
223+
'image/webp' => WebP::class,
224+
];
225+
226+
if (isset($mimeToClass[$mime])) {
227+
$class = $mimeToClass[$mime];
228+
$image = $class::fromString($string);
229+
return $image;
236230
}
237231

238232
return false;
239233
}
234+
235+
protected function setSizeFromFile($fileName)
236+
{
237+
$imageSize = getimagesize($fileName);
238+
if ($imageSize === false) {
239+
throw new \Exception(sprintf('Could not get image size for %s', $fileName));
240+
}
241+
$this->width = $imageSize[0];
242+
$this->height = $imageSize[1];
243+
}
244+
245+
protected function setSizeFromString($string)
246+
{
247+
$size = getimagesizefromstring($string);
248+
$this->width = $size[0];
249+
$this->height = $size[1];
250+
}
240251
}

tests/Format/JPEGTest.php

+35-26
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,62 @@
11
<?php
2-
32
namespace CSD\Image\Tests\Format;
43

54
use CSD\Image\Format\JPEG;
65
use CSD\Image\Metadata\Xmp;
76

87
/**
9-
* @author Daniel Chesterton <[email protected]>
10-
*
118
* @coversDefaultClass \CSD\Image\Format\JPEG
129
*/
1310
class JPEGTest extends \PHPUnit\Framework\TestCase
1411
{
1512
/**
16-
* Test that JPEG can read XMP embedded with Photo Mechanic.
13+
* Data provider for testGetXmp method.
14+
*
15+
* @return array
1716
*/
18-
public function testGetXmpPhotoMechanic()
17+
public function providerTestGetXmp()
1918
{
20-
$jpeg = JPEG::fromFile(__DIR__ . '/../Fixtures/metapm.jpg');
21-
22-
$xmp = $jpeg->getXmp();
23-
24-
$this->assertInstanceOf(Xmp::class, $xmp);
25-
$this->assertSame('Headline', $xmp->getHeadline());
19+
return [
20+
// [method, filename, expectedHeadline]
21+
['fromFile', 'metapm.jpg', 'Headline'],
22+
['fromString', 'metapm.jpg', 'Headline'],
23+
['fromFile', 'metaphotoshop.jpg', 'Headline'],
24+
['fromString', 'metaphotoshop.jpg', 'Headline'],
25+
['fromFile', 'nometa.jpg', null],
26+
['fromString', 'nometa.jpg', null],
27+
];
2628
}
2729

2830
/**
29-
* Test that JPEG can read XMP embedded with Photoshop.
31+
* Test that JPEG can read XMP data using both fromFile and fromString methods.
32+
*
33+
* @dataProvider providerTestGetXmp
34+
*
35+
* @param string $method The method to use ('fromFile' or 'fromString')
36+
* @param string $filename The filename of the test image
37+
* @param string|null $expectedHeadline The expected headline in the XMP data
3038
*/
31-
public function testGetXmpPhotoshop()
39+
public function testGetXmp($method, $filename, $expectedHeadline)
3240
{
33-
$jpeg = JPEG::fromFile(__DIR__ . '/../Fixtures/metaphotoshop.jpg');
41+
$filePath = __DIR__ . '/../Fixtures/' . $filename;
3442

35-
$xmp = $jpeg->getXmp();
43+
if ($method === 'fromFile') {
44+
$jpeg = JPEG::fromFile($filePath);
45+
} elseif ($method === 'fromString') {
46+
$string = file_get_contents($filePath);
47+
$jpeg = JPEG::fromString($string);
48+
} else {
49+
throw new \InvalidArgumentException("Invalid method: $method");
50+
}
3651

37-
$this->assertInstanceOf(Xmp::class, $xmp);
38-
$this->assertSame('Headline', $xmp->getHeadline());
39-
}
40-
41-
/**
42-
* Test that JPEG class returns an empty XMP object when there is no XMP data.
43-
*/
44-
public function testGetXmpNoMeta()
45-
{
46-
$jpeg = JPEG::fromFile(__DIR__ . '/../Fixtures/nometa.jpg');
52+
$this->assertInstanceOf(JPEG::class, $jpeg);
53+
$this->assertGreaterThan(0, $jpeg->getSize()["width"]);
54+
$this->assertGreaterThan(0, $jpeg->getSize()["height"]);
4755

4856
$xmp = $jpeg->getXmp();
4957

5058
$this->assertInstanceOf(Xmp::class, $xmp);
51-
$this->assertNull($xmp->getHeadline());
59+
$this->assertSame($expectedHeadline, $xmp->getHeadline());
5260
}
5361
}
62+

0 commit comments

Comments
 (0)