Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
85.71% |
90 / 105 |
|
75.00% |
18 / 24 |
CRAP | |
0.00% |
0 / 1 |
MetaData | |
85.71% |
90 / 105 |
|
75.00% |
18 / 24 |
57.29 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
25 / 25 |
|
100.00% |
1 / 1 |
2 | |||
getResolution | |
50.00% |
5 / 10 |
|
0.00% |
0 / 1 |
4.12 | |||
getResolutionHeight | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getResolutionWidth | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getFormat | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDepth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getColorspace | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getGeometry | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getHeight | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getWidth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
listRaw | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
5.02 | |||
parseFilename | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
parseFileformat | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
parseGeometry | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
parseColorspace | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
parseDepth | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
parseResolution | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
parseVerbose | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
parse | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
parseUnits | |
62.50% |
10 / 16 |
|
0.00% |
0 / 1 |
7.90 | |||
getUnit | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
getHash | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
ppc2ppi | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
ppi2ppc | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | /** |
4 | * Karla ImageMagick wrapper library |
5 | * |
6 | * PHP Version 8.0< |
7 | * |
8 | * @category Utility |
9 | * @author Johannes Skov Frandsen <jsf@greenoak.dk> |
10 | * @license http://www.opensource.org/licenses/mit-license.php MIT |
11 | * @link https://github.com/localgod/karla Karla |
12 | * @since 2012-04-05 |
13 | */ |
14 | |
15 | declare(strict_types=1); |
16 | |
17 | namespace Karla; |
18 | |
19 | /** |
20 | * Class for wrapping image metadata |
21 | * |
22 | * @category Utility |
23 | * @author Johannes Skov Frandsen <jsf@greenoak.dk> |
24 | * @license http://www.opensource.org/licenses/mit-license.php MIT |
25 | * @link https://github.com/localgod/karla Karla |
26 | */ |
27 | class MetaData extends \SplFileInfo |
28 | { |
29 | /** |
30 | * Raw image info |
31 | * |
32 | * @var string[] |
33 | */ |
34 | private array $verboseImageinfo; |
35 | |
36 | /** |
37 | * Raw image info |
38 | * |
39 | * @var array |
40 | */ |
41 | private array $imageinfo; |
42 | |
43 | /** |
44 | * The image format. |
45 | * |
46 | * @var string |
47 | */ |
48 | private string $format; |
49 | |
50 | /** |
51 | * The image depth. |
52 | * |
53 | * @var integer |
54 | */ |
55 | private int $depth; |
56 | |
57 | /** |
58 | * The image colorspace. |
59 | * |
60 | * @var string |
61 | */ |
62 | private string $colorspace; |
63 | |
64 | /** |
65 | * The image geometry |
66 | * |
67 | * @var array |
68 | */ |
69 | private array $geometry; |
70 | |
71 | /** |
72 | * The image resolution |
73 | * |
74 | * @var array |
75 | */ |
76 | private array $resolution; |
77 | |
78 | /** |
79 | * The resolution unit is measured in Pixels Per Inch |
80 | * |
81 | * @var boolean |
82 | */ |
83 | private bool $isPpi; |
84 | |
85 | /** |
86 | * The resolution unit is measured in Pixels Per Centimeter |
87 | * |
88 | * @var boolean |
89 | */ |
90 | private bool $isPpc; |
91 | |
92 | /** |
93 | * Are we scanning in verbose output from identify |
94 | * |
95 | * @var boolean |
96 | */ |
97 | private bool $verbose; |
98 | |
99 | /** |
100 | * Construct a new image file. |
101 | * |
102 | * @param string $imageinfo |
103 | * Image info as string |
104 | * @param boolean $verbose |
105 | * Should input be parsed as verbose |
106 | */ |
107 | public function __construct(string $imageinfo, bool $verbose = false) |
108 | { |
109 | $this->verbose = $verbose; |
110 | if ($this->verbose) { |
111 | $this->verboseImageinfo = explode("\n", $imageinfo); |
112 | $this->parseFileformat(); |
113 | $this->parseGeometry(); |
114 | $this->parseResolution(); |
115 | $this->parseUnits(); |
116 | $this->parseDepth(); |
117 | $this->parseColorspace(); |
118 | parent::__construct($this->parseFilename()); |
119 | } else { |
120 | $info = explode(" ", $imageinfo); |
121 | $this->imageinfo['Image'] = $info[0]; |
122 | $this->imageinfo['Format'] = $info[1]; |
123 | $this->imageinfo['Geometry'] = $info[3]; |
124 | $this->imageinfo['Depth'] = $info[4]; |
125 | $this->imageinfo['Colorspace'] = $info[5]; |
126 | $this->imageinfo['Resolution'] = 'x'; |
127 | $this->imageinfo['Units'] = ''; |
128 | $this->parseFileformat(); |
129 | $this->parseGeometry(); |
130 | $this->parseResolution(); |
131 | $this->parseUnits(); |
132 | $this->parseDepth(); |
133 | $this->parseColorspace(); |
134 | parent::__construct($this->parseFilename()); |
135 | } |
136 | } |
137 | |
138 | /** |
139 | * Get the image resolution. |
140 | * If the resolution is in ppc we convert it to ppi |
141 | * |
142 | * @return array |
143 | */ |
144 | private function getResolution(): array |
145 | { |
146 | if ($this->verbose) { |
147 | if ($this->isPpc) { |
148 | // Here we convert to ppi |
149 | $this->resolution = array( |
150 | $this->ppc2ppi($this->resolution[0]), |
151 | $this->ppc2ppi($this->resolution[1]) |
152 | ); |
153 | $this->isPpc = false; |
154 | $this->isPpi = true; |
155 | } |
156 | } else { |
157 | $this->resolution = array(); |
158 | } |
159 | |
160 | return $this->resolution; |
161 | } |
162 | |
163 | /** |
164 | * Get the resolution height. |
165 | * |
166 | * @return integer |
167 | */ |
168 | public function getResolutionHeight(): int|null |
169 | { |
170 | $res = $this->getResolution(); |
171 | return isset($res[1]) ? (int) $res[1] : null; |
172 | } |
173 | |
174 | /** |
175 | * Get the resolution width. |
176 | * |
177 | * @return integer |
178 | */ |
179 | public function getResolutionWidth(): int|null |
180 | { |
181 | $res = $this->getResolution(); |
182 | |
183 | return isset($res[0]) ? (int) $res[0] : null; |
184 | } |
185 | |
186 | /** |
187 | * Get file format |
188 | * |
189 | * @return string |
190 | */ |
191 | public function getFormat(): string |
192 | { |
193 | return $this->format; |
194 | } |
195 | |
196 | /** |
197 | * Get image depth |
198 | * |
199 | * @return integer |
200 | */ |
201 | public function getDepth(): int |
202 | { |
203 | return $this->depth; |
204 | } |
205 | |
206 | /** |
207 | * Get colorspace |
208 | * |
209 | * @return string |
210 | */ |
211 | public function getColorspace(): string |
212 | { |
213 | return $this->colorspace; |
214 | } |
215 | |
216 | /** |
217 | * Get the image geometry |
218 | * |
219 | * @return array |
220 | */ |
221 | public function getGeometry(): array |
222 | { |
223 | return $this->geometry; |
224 | } |
225 | |
226 | /** |
227 | * Get the image height |
228 | * |
229 | * @return integer |
230 | */ |
231 | public function getHeight(): int |
232 | { |
233 | return (int) $this->geometry[1]; |
234 | } |
235 | |
236 | /** |
237 | * Get the image width |
238 | * |
239 | * @return integer |
240 | */ |
241 | public function getWidth(): int |
242 | { |
243 | return (int) $this->geometry[0]; |
244 | } |
245 | |
246 | /** |
247 | * List the raw image information as a unorder list |
248 | * |
249 | * @return string |
250 | */ |
251 | public function listRaw(): string |
252 | { |
253 | $output = array(); |
254 | $output[] = "<ul>"; |
255 | $list = $this->verbose ? $this->verboseImageinfo : $this->imageinfo; |
256 | if (is_array($list)) { |
257 | foreach ($list as $key => $line) { |
258 | if ($this->verbose) { |
259 | $output[] = "<li>" . $line . "</li>"; |
260 | } else { |
261 | $output[] = "<li>" . $key . ' : ' . $line . "</li>"; |
262 | } |
263 | } |
264 | } else { |
265 | $output[] = "<li>" . $this->imageinfo . "</li>"; |
266 | } |
267 | $output[] = "<ul>"; |
268 | |
269 | return implode("\n", $output); |
270 | } |
271 | |
272 | /** |
273 | * Search for the name of the file |
274 | * |
275 | * @return string |
276 | */ |
277 | private function parseFilename(): string |
278 | { |
279 | return $this->verbose ? $this->parseVerbose('Image') : $this->parse('Image'); |
280 | } |
281 | |
282 | /** |
283 | * Search for file format |
284 | * |
285 | * @return void |
286 | */ |
287 | private function parseFileformat(): void |
288 | { |
289 | $format = $this->verbose ? $this->parseVerbose('Format') : $this->parse('Format'); |
290 | $matches = []; |
291 | preg_match("/^[\s]*[A-Z0-9]+/", $format, $matches); |
292 | if (count($matches) == 1) { |
293 | $this->format = strtolower(trim($matches[0])); |
294 | } |
295 | } |
296 | |
297 | /** |
298 | * Search for image geometry |
299 | * |
300 | * @todo output not sanetized yet |
301 | * |
302 | * @return void |
303 | */ |
304 | private function parseGeometry(): void |
305 | { |
306 | $geometry = $this->verbose ? $this->parseVerbose('Geometry') : $this->parse('Geometry'); |
307 | $matches = []; |
308 | preg_match("/^[0-9]*x[0-9]*/", $geometry, $matches); |
309 | if (count($matches) == 1) { |
310 | $this->geometry = explode("x", $matches[0]); |
311 | } |
312 | } |
313 | |
314 | /** |
315 | * Search for image colorspace |
316 | * |
317 | * @return void |
318 | */ |
319 | private function parseColorspace(): void |
320 | { |
321 | $colorspace = $this->verbose ? $this->parseVerbose('Colorspace') : $this->parse('Colorspace'); |
322 | $matches = []; |
323 | preg_match("/^[a-zA-Z0-9]*/", $colorspace, $matches); |
324 | $this->colorspace = strtolower(trim($matches[0])); |
325 | } |
326 | |
327 | /** |
328 | * Search for image depth |
329 | * |
330 | * @return void |
331 | */ |
332 | private function parseDepth(): void |
333 | { |
334 | $depth = $this->verbose ? $this->parseVerbose('Depth') : $this->parse('Depth'); |
335 | $matches = []; |
336 | preg_match("/^[0-9]*/", $depth, $matches); |
337 | $this->depth = (int) $matches[0]; |
338 | } |
339 | |
340 | /** |
341 | * Search for image resolution |
342 | * |
343 | * @return void |
344 | */ |
345 | private function parseResolution(): void |
346 | { |
347 | $resolution = $this->verbose ? $this->parseVerbose('Resolution') : $this->parse('Resolution'); |
348 | $this->resolution = explode("x", $resolution); |
349 | } |
350 | |
351 | /** |
352 | * Parse the image info array for the search line and |
353 | * return it for futher processing |
354 | * |
355 | * @param string $search |
356 | * Search string |
357 | * |
358 | * @return string |
359 | */ |
360 | private function parseVerbose($search): string |
361 | { |
362 | foreach ($this->verboseImageinfo as $line) { |
363 | if (preg_match("/^" . $search . ":/", trim($line))) { |
364 | return trim(str_replace($search . ':', '', $line)); |
365 | } |
366 | } |
367 | } |
368 | |
369 | /** |
370 | * Parse the image info string for the search line and |
371 | * return it for futher processing |
372 | * |
373 | * @param string $search |
374 | * Search string |
375 | * |
376 | * @return string |
377 | */ |
378 | private function parse($search): string |
379 | { |
380 | return $this->imageinfo[$search]; |
381 | } |
382 | |
383 | /** |
384 | * Search for print unit. |
385 | * |
386 | * If not found we default to Pixels Per Inch. |
387 | * |
388 | * @return void |
389 | */ |
390 | private function parseUnits(): void |
391 | { |
392 | $units = $this->verbose ? $this->parseVerbose('Units') : $this->parse('Units'); |
393 | switch ($units) { |
394 | case 'PixelsPerCentimeter': |
395 | $this->isPpc = true; |
396 | $this->isPpi = false; |
397 | break; |
398 | |
399 | case 'PixelsPerInch': |
400 | $this->isPpc = false; |
401 | $this->isPpi = true; |
402 | break; |
403 | |
404 | case 'Undefined': |
405 | $this->isPpc = false; |
406 | $this->isPpi = true; |
407 | break; |
408 | |
409 | default: |
410 | $this->isPpc = false; |
411 | $this->isPpi = true; |
412 | break; |
413 | } |
414 | } |
415 | |
416 | /** |
417 | * Get the resolution unit |
418 | * |
419 | * @return string |
420 | */ |
421 | public function getUnit(): string |
422 | { |
423 | if ($this->isPpi) { |
424 | return 'Pixels Per Inch'; |
425 | } else { |
426 | return 'Pixels Per Centimeter'; |
427 | } |
428 | } |
429 | |
430 | /** |
431 | * Get hash of file |
432 | * |
433 | * @param string $hash |
434 | * Name of hash algorithm to use; default is md5 |
435 | * |
436 | * @return string |
437 | */ |
438 | public function getHash(string $hash = 'md5'): string |
439 | { |
440 | if ($hash == 'md5') { |
441 | return md5_file($this->getPathname()); |
442 | } else { |
443 | throw new \InvalidArgumentException($hash . ' is not a supported hash algorithm'); |
444 | } |
445 | } |
446 | |
447 | /** |
448 | * Convert Pixels Per Centimeter (ppc) to Pixels Per Inch (ppi) |
449 | * |
450 | * @param integer $value |
451 | * Input value |
452 | * |
453 | * @return integer |
454 | */ |
455 | private function ppc2ppi(int $value): int |
456 | { |
457 | return intval(floor($value * 0.3937008)); |
458 | } |
459 | |
460 | /** |
461 | * Convert Pixels Per Inch (ppi) to Pixels Per Centimeter (ppc) |
462 | * |
463 | * @param integer $value |
464 | * Input value |
465 | * |
466 | * @return integer |
467 | */ |
468 | private function ppi2ppc(int $value): int |
469 | { |
470 | return intval(floor($value * 2.54)); |
471 | } |
472 | } |