Completed
Push — master ( 9984eb...7cae75 )
by Lukas
10:46
created

OC_Image::crop()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 29
Code Lines 21

Duplication

Lines 14
Ratio 48.28 %

Importance

Changes 0
Metric Value
cc 6
eloc 21
nc 6
nop 4
dl 14
loc 29
rs 8.439
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Andreas Fischer <[email protected]>
6
 * @author Bartek Przybylski <[email protected]>
7
 * @author Bart Visscher <[email protected]>
8
 * @author Björn Schießle <[email protected]>
9
 * @author Byron Marohn <[email protected]>
10
 * @author Christopher Schäpers <[email protected]>
11
 * @author Georg Ehrke <[email protected]>
12
 * @author j-ed <[email protected]>
13
 * @author Joas Schilling <[email protected]>
14
 * @author Johannes Willnecker <[email protected]>
15
 * @author Jörn Friedrich Dreyer <[email protected]>
16
 * @author Lukas Reschke <[email protected]>
17
 * @author Morris Jobke <[email protected]>
18
 * @author Olivier Paroz <[email protected]>
19
 * @author Robin Appelman <[email protected]>
20
 * @author Thomas Müller <[email protected]>
21
 * @author Thomas Tanghus <[email protected]>
22
 *
23
 * @license AGPL-3.0
24
 *
25
 * This code is free software: you can redistribute it and/or modify
26
 * it under the terms of the GNU Affero General Public License, version 3,
27
 * as published by the Free Software Foundation.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License, version 3,
35
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
36
 *
37
 */
38
39
/**
40
 * Class for basic image manipulation
41
 */
42
class OC_Image implements \OCP\IImage {
43
	/** @var false|resource */
44
	protected $resource = false; // tmp resource.
45
	/** @var int */
46
	protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident.
47
	/** @var string */
48
	protected $mimeType = 'image/png'; // Default to png
49
	/** @var int */
50
	protected $bitDepth = 24;
51
	/** @var null|string */
52
	protected $filePath = null;
53
	/** @var finfo */
54
	private $fileInfo;
55
	/** @var \OCP\ILogger */
56
	private $logger;
57
	/** @var array */
58
	private $exif;
59
60
	/**
61
	 * Get mime type for an image file.
62
	 *
63
	 * @param string|null $filePath The path to a local image file.
64
	 * @return string The mime type if the it could be determined, otherwise an empty string.
65
	 */
66
	static public function getMimeTypeForFile($filePath) {
67
		// exif_imagetype throws "read error!" if file is less than 12 byte
68
		if ($filePath !== null && filesize($filePath) > 11) {
69
			$imageType = exif_imagetype($filePath);
70
		} else {
71
			$imageType = false;
72
		}
73
		return $imageType ? image_type_to_mime_type($imageType) : '';
74
	}
75
76
	/**
77
	 * Constructor.
78
	 *
79
	 * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by
80
	 * an imagecreate* function.
81
	 * @param \OCP\ILogger $logger
82
	 */
83
	public function __construct($imageRef = null, $logger = null) {
84
		$this->logger = $logger;
85
		if (is_null($logger)) {
86
			$this->logger = \OC::$server->getLogger();
87
		}
88
89
		if (\OC_Util::fileInfoLoaded()) {
90
			$this->fileInfo = new finfo(FILEINFO_MIME_TYPE);
91
		}
92
93
		if (!is_null($imageRef)) {
94
			$this->load($imageRef);
95
		}
96
	}
97
98
	/**
99
	 * Determine whether the object contains an image resource.
100
	 *
101
	 * @return bool
102
	 */
103
	public function valid() { // apparently you can't name a method 'empty'...
104
		return is_resource($this->resource);
105
	}
106
107
	/**
108
	 * Returns the MIME type of the image or an empty string if no image is loaded.
109
	 *
110
	 * @return string
111
	 */
112
	public function mimeType() {
113
		return $this->valid() ? $this->mimeType : '';
114
	}
115
116
	/**
117
	 * Returns the width of the image or -1 if no image is loaded.
118
	 *
119
	 * @return int
120
	 */
121
	public function width() {
122
		return $this->valid() ? imagesx($this->resource) : -1;
123
	}
124
125
	/**
126
	 * Returns the height of the image or -1 if no image is loaded.
127
	 *
128
	 * @return int
129
	 */
130
	public function height() {
131
		return $this->valid() ? imagesy($this->resource) : -1;
132
	}
133
134
	/**
135
	 * Returns the width when the image orientation is top-left.
136
	 *
137
	 * @return int
138
	 */
139 View Code Duplication
	public function widthTopLeft() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
		$o = $this->getOrientation();
141
		$this->logger->debug('OC_Image->widthTopLeft() Orientation: ' . $o, array('app' => 'core'));
142
		switch ($o) {
143
			case -1:
144
			case 1:
145
			case 2: // Not tested
146
			case 3:
147
			case 4: // Not tested
148
				return $this->width();
149
			case 5: // Not tested
150
			case 6:
151
			case 7: // Not tested
152
			case 8:
153
				return $this->height();
154
		}
155
		return $this->width();
156
	}
157
158
	/**
159
	 * Returns the height when the image orientation is top-left.
160
	 *
161
	 * @return int
162
	 */
163 View Code Duplication
	public function heightTopLeft() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164
		$o = $this->getOrientation();
165
		$this->logger->debug('OC_Image->heightTopLeft() Orientation: ' . $o, array('app' => 'core'));
166
		switch ($o) {
167
			case -1:
168
			case 1:
169
			case 2: // Not tested
170
			case 3:
171
			case 4: // Not tested
172
				return $this->height();
173
			case 5: // Not tested
174
			case 6:
175
			case 7: // Not tested
176
			case 8:
177
				return $this->width();
178
		}
179
		return $this->height();
180
	}
181
182
	/**
183
	 * Outputs the image.
184
	 *
185
	 * @param string $mimeType
186
	 * @return bool
187
	 */
188
	public function show($mimeType = null) {
189
		if ($mimeType === null) {
190
			$mimeType = $this->mimeType();
191
		}
192
		header('Content-Type: ' . $mimeType);
193
		return $this->_output(null, $mimeType);
194
	}
195
196
	/**
197
	 * Saves the image.
198
	 *
199
	 * @param string $filePath
200
	 * @param string $mimeType
201
	 * @return bool
202
	 */
203
204
	public function save($filePath = null, $mimeType = null) {
205
		if ($mimeType === null) {
206
			$mimeType = $this->mimeType();
207
		}
208
		if ($filePath === null && $this->filePath === null) {
209
			$this->logger->error(__METHOD__ . '(): called with no path.', array('app' => 'core'));
210
			return false;
211
		} elseif ($filePath === null && $this->filePath !== null) {
212
			$filePath = $this->filePath;
213
		}
214
		return $this->_output($filePath, $mimeType);
215
	}
216
217
	/**
218
	 * Outputs/saves the image.
219
	 *
220
	 * @param string $filePath
221
	 * @param string $mimeType
222
	 * @return bool
223
	 * @throws Exception
224
	 */
225
	private function _output($filePath = null, $mimeType = null) {
226
		if ($filePath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filePath of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
227
			if (!file_exists(dirname($filePath)))
228
				mkdir(dirname($filePath), 0777, true);
229
			if (!is_writable(dirname($filePath))) {
230
				$this->logger->error(__METHOD__ . '(): Directory \'' . dirname($filePath) . '\' is not writable.', array('app' => 'core'));
231
				return false;
232
			} elseif (is_writable(dirname($filePath)) && file_exists($filePath) && !is_writable($filePath)) {
233
				$this->logger->error(__METHOD__ . '(): File \'' . $filePath . '\' is not writable.', array('app' => 'core'));
234
				return false;
235
			}
236
		}
237
		if (!$this->valid()) {
238
			return false;
239
		}
240
241
		$imageType = $this->imageType;
242
		if ($mimeType !== null) {
243
			switch ($mimeType) {
244
				case 'image/gif':
245
					$imageType = IMAGETYPE_GIF;
246
					break;
247
				case 'image/jpeg':
248
					$imageType = IMAGETYPE_JPEG;
249
					break;
250
				case 'image/png':
251
					$imageType = IMAGETYPE_PNG;
252
					break;
253
				case 'image/x-xbitmap':
254
					$imageType = IMAGETYPE_XBM;
255
					break;
256
				case 'image/bmp':
257
				case 'image/x-ms-bmp':
258
					$imageType = IMAGETYPE_BMP;
259
					break;
260
				default:
261
					throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format');
262
			}
263
		}
264
265
		switch ($imageType) {
266
			case IMAGETYPE_GIF:
267
				$retVal = imagegif($this->resource, $filePath);
268
				break;
269
			case IMAGETYPE_JPEG:
270
				$retVal = imagejpeg($this->resource, $filePath);
271
				break;
272
			case IMAGETYPE_PNG:
273
				$retVal = imagepng($this->resource, $filePath);
274
				break;
275
			case IMAGETYPE_XBM:
276
				if (function_exists('imagexbm')) {
277
					$retVal = imagexbm($this->resource, $filePath);
278
				} else {
279
					throw new Exception('\OC_Image::_output(): imagexbm() is not supported.');
280
				}
281
282
				break;
283
			case IMAGETYPE_WBMP:
284
				$retVal = imagewbmp($this->resource, $filePath);
285
				break;
286
			case IMAGETYPE_BMP:
287
				$retVal = imagebmp($this->resource, $filePath, $this->bitDepth);
0 ignored issues
show
Security Bug introduced by
It seems like $this->resource can also be of type false; however, imagebmp() does only seem to accept resource, did you maybe forget to handle an error condition?
Loading history...
288
				break;
289
			default:
290
				$retVal = imagepng($this->resource, $filePath);
291
		}
292
		return $retVal;
293
	}
294
295
	/**
296
	 * Prints the image when called as $image().
297
	 */
298
	public function __invoke() {
299
		return $this->show();
300
	}
301
302
	/**
303
	 * @return resource Returns the image resource in any.
304
	 */
305
	public function resource() {
306
		return $this->resource;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->resource; of type false|resource adds false to the return on line 306 which is incompatible with the return type declared by the interface OCP\IImage::resource of type resource. It seems like you forgot to handle an error condition.
Loading history...
307
	}
308
309
	/**
310
	 * @return null|string Returns the raw image data.
311
	 */
312
	public function data() {
313
		if (!$this->valid()) {
314
			return null;
315
		}
316
		ob_start();
317
		switch ($this->mimeType) {
318
			case "image/png":
319
				$res = imagepng($this->resource);
320
				break;
321
			case "image/jpeg":
322
				$res = imagejpeg($this->resource);
323
				break;
324
			case "image/gif":
325
				$res = imagegif($this->resource);
326
				break;
327
			default:
328
				$res = imagepng($this->resource);
329
				$this->logger->info('OC_Image->data. Could not guess mime-type, defaulting to png', array('app' => 'core'));
330
				break;
331
		}
332
		if (!$res) {
333
			$this->logger->error('OC_Image->data. Error getting image data.', array('app' => 'core'));
334
		}
335
		return ob_get_clean();
336
	}
337
338
	/**
339
	 * @return string - base64 encoded, which is suitable for embedding in a VCard.
340
	 */
341
	function __toString() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
342
		return base64_encode($this->data());
343
	}
344
345
	/**
346
	 * (I'm open for suggestions on better method name ;)
347
	 * Get the orientation based on EXIF data.
348
	 *
349
	 * @return int The orientation or -1 if no EXIF data is available.
350
	 */
351
	public function getOrientation() {
352
		if ($this->exif !== null) {
353
			return $this->exif['Orientation'];
354
		}
355
356
		if ($this->imageType !== IMAGETYPE_JPEG) {
357
			$this->logger->debug('OC_Image->fixOrientation() Image is not a JPEG.', array('app' => 'core'));
358
			return -1;
359
		}
360
		if (!is_callable('exif_read_data')) {
361
			$this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core'));
362
			return -1;
363
		}
364
		if (!$this->valid()) {
365
			$this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core'));
366
			return -1;
367
		}
368
		if (is_null($this->filePath) || !is_readable($this->filePath)) {
369
			$this->logger->debug('OC_Image->fixOrientation() No readable file path set.', array('app' => 'core'));
370
			return -1;
371
		}
372
		$exif = @exif_read_data($this->filePath, 'IFD0');
373
		if (!$exif) {
374
			return -1;
375
		}
376
		if (!isset($exif['Orientation'])) {
377
			return -1;
378
		}
379
		$this->exif = $exif;
380
		return $exif['Orientation'];
381
	}
382
383
	public function readExif($data) {
384
		if (!is_callable('exif_read_data')) {
385
			$this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core'));
386
			return;
387
		}
388
		if (!$this->valid()) {
389
			$this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core'));
390
			return;
391
		}
392
393
		$exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($data));
394
		if (!$exif) {
395
			return;
396
		}
397
		if (!isset($exif['Orientation'])) {
398
			return;
399
		}
400
		$this->exif = $exif;
401
	}
402
403
	/**
404
	 * (I'm open for suggestions on better method name ;)
405
	 * Fixes orientation based on EXIF data.
406
	 *
407
	 * @return bool.
0 ignored issues
show
Documentation introduced by
The doc-type bool. could not be parsed: Unknown type name "bool." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
408
	 */
409
	public function fixOrientation() {
410
		$o = $this->getOrientation();
411
		$this->logger->debug('OC_Image->fixOrientation() Orientation: ' . $o, array('app' => 'core'));
412
		$rotate = 0;
413
		$flip = false;
414
		switch ($o) {
415
			case -1:
416
				return false; //Nothing to fix
417
			case 1:
418
				$rotate = 0;
419
				break;
420
			case 2:
421
				$rotate = 0;
422
				$flip = true;
423
				break;
424
			case 3:
425
				$rotate = 180;
426
				break;
427
			case 4:
428
				$rotate = 180;
429
				$flip = true;
430
				break;
431
			case 5:
432
				$rotate = 90;
433
				$flip = true;
434
				break;
435
			case 6:
436
				$rotate = 270;
437
				break;
438
			case 7:
439
				$rotate = 270;
440
				$flip = true;
441
				break;
442
			case 8:
443
				$rotate = 90;
444
				break;
445
		}
446
		if($flip && function_exists('imageflip')) {
447
			imageflip($this->resource, IMG_FLIP_HORIZONTAL);
448
		}
449
		if ($rotate) {
450
			$res = imagerotate($this->resource, $rotate, 0);
451
			if ($res) {
452
				if (imagealphablending($res, true)) {
453
					if (imagesavealpha($res, true)) {
454
						imagedestroy($this->resource);
455
						$this->resource = $res;
456
						return true;
457
					} else {
458
						$this->logger->debug('OC_Image->fixOrientation() Error during alpha-saving', array('app' => 'core'));
459
						return false;
460
					}
461
				} else {
462
					$this->logger->debug('OC_Image->fixOrientation() Error during alpha-blending', array('app' => 'core'));
463
					return false;
464
				}
465
			} else {
466
				$this->logger->debug('OC_Image->fixOrientation() Error during orientation fixing', array('app' => 'core'));
467
				return false;
468
			}
469
		}
470
		return false;
471
	}
472
473
	/**
474
	 * Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function.
475
	 *
476
	 * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by an imagecreate* function or a file resource (file handle    ).
477
	 * @return resource|false An image resource or false on error
478
	 */
479
	public function load($imageRef) {
480
		if (is_resource($imageRef)) {
481
			if (get_resource_type($imageRef) == 'gd') {
482
				$this->resource = $imageRef;
483
				return $this->resource;
484
			} elseif (in_array(get_resource_type($imageRef), array('file', 'stream'))) {
485
				return $this->loadFromFileHandle($imageRef);
486
			}
487
		} elseif ($this->loadFromBase64($imageRef) !== false) {
0 ignored issues
show
Bug introduced by
It seems like $imageRef defined by parameter $imageRef on line 479 can also be of type resource; however, OC_Image::loadFromBase64() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
488
			return $this->resource;
489
		} elseif ($this->loadFromFile($imageRef) !== false) {
0 ignored issues
show
Bug introduced by
It seems like $imageRef defined by parameter $imageRef on line 479 can also be of type resource; however, OC_Image::loadFromFile() does only seem to accept boolean|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
490
			return $this->resource;
491
		} elseif ($this->loadFromData($imageRef) !== false) {
0 ignored issues
show
Bug introduced by
It seems like $imageRef defined by parameter $imageRef on line 479 can also be of type resource; however, OC_Image::loadFromData() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
492
			return $this->resource;
493
		}
494
		$this->logger->debug(__METHOD__ . '(): could not load anything. Giving up!', array('app' => 'core'));
495
		return false;
496
	}
497
498
	/**
499
	 * Loads an image from an open file handle.
500
	 * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again.
501
	 *
502
	 * @param resource $handle
503
	 * @return resource|false An image resource or false on error
504
	 */
505
	public function loadFromFileHandle($handle) {
506
		$contents = stream_get_contents($handle);
507
		if ($this->loadFromData($contents)) {
508
			return $this->resource;
509
		}
510
		return false;
511
	}
512
513
	/**
514
	 * Loads an image from a local file.
515
	 *
516
	 * @param bool|string $imagePath The path to a local file.
517
	 * @return bool|resource An image resource or false on error
518
	 */
519
	public function loadFromFile($imagePath = false) {
520
		// exif_imagetype throws "read error!" if file is less than 12 byte
521
		if (!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) {
522
			return false;
523
		}
524
		$iType = exif_imagetype($imagePath);
0 ignored issues
show
Bug introduced by
It seems like $imagePath defined by parameter $imagePath on line 519 can also be of type boolean; however, exif_imagetype() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
525
		switch ($iType) {
526 View Code Duplication
			case IMAGETYPE_GIF:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
527
				if (imagetypes() & IMG_GIF) {
528
					$this->resource = imagecreatefromgif($imagePath);
529
					// Preserve transparency
530
					imagealphablending($this->resource, true);
531
					imagesavealpha($this->resource, true);
532
				} else {
533
					$this->logger->debug('OC_Image->loadFromFile, GIF images not supported: ' . $imagePath, array('app' => 'core'));
534
				}
535
				break;
536
			case IMAGETYPE_JPEG:
537 View Code Duplication
				if (imagetypes() & IMG_JPG) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
538
					$this->resource = imagecreatefromjpeg($imagePath);
539
				} else {
540
					$this->logger->debug('OC_Image->loadFromFile, JPG images not supported: ' . $imagePath, array('app' => 'core'));
541
				}
542
				break;
543 View Code Duplication
			case IMAGETYPE_PNG:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
544
				if (imagetypes() & IMG_PNG) {
545
					$this->resource = imagecreatefrompng($imagePath);
546
					// Preserve transparency
547
					imagealphablending($this->resource, true);
548
					imagesavealpha($this->resource, true);
549
				} else {
550
					$this->logger->debug('OC_Image->loadFromFile, PNG images not supported: ' . $imagePath, array('app' => 'core'));
551
				}
552
				break;
553
			case IMAGETYPE_XBM:
554 View Code Duplication
				if (imagetypes() & IMG_XPM) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
555
					$this->resource = imagecreatefromxbm($imagePath);
556
				} else {
557
					$this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: ' . $imagePath, array('app' => 'core'));
558
				}
559
				break;
560
			case IMAGETYPE_WBMP:
561 View Code Duplication
				if (imagetypes() & IMG_WBMP) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
562
					$this->resource = imagecreatefromwbmp($imagePath);
563
				} else {
564
					$this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: ' . $imagePath, array('app' => 'core'));
565
				}
566
				break;
567
			case IMAGETYPE_BMP:
568
				$this->resource = $this->imagecreatefrombmp($imagePath);
0 ignored issues
show
Bug introduced by
It seems like $imagePath defined by parameter $imagePath on line 519 can also be of type boolean; however, OC_Image::imagecreatefrombmp() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
569
				break;
570
			/*
571
			case IMAGETYPE_TIFF_II: // (intel byte order)
572
				break;
573
			case IMAGETYPE_TIFF_MM: // (motorola byte order)
574
				break;
575
			case IMAGETYPE_JPC:
576
				break;
577
			case IMAGETYPE_JP2:
578
				break;
579
			case IMAGETYPE_JPX:
580
				break;
581
			case IMAGETYPE_JB2:
582
				break;
583
			case IMAGETYPE_SWC:
584
				break;
585
			case IMAGETYPE_IFF:
586
				break;
587
			case IMAGETYPE_ICO:
588
				break;
589
			case IMAGETYPE_SWF:
590
				break;
591
			case IMAGETYPE_PSD:
592
				break;
593
			*/
594
			default:
595
596
				// this is mostly file created from encrypted file
597
				$this->resource = imagecreatefromstring(\OC\Files\Filesystem::file_get_contents(\OC\Files\Filesystem::getLocalPath($imagePath)));
0 ignored issues
show
Bug introduced by
It seems like $imagePath defined by parameter $imagePath on line 519 can also be of type boolean; however, OC\Files\Filesystem::getLocalPath() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
598
				$iType = IMAGETYPE_PNG;
599
				$this->logger->debug('OC_Image->loadFromFile, Default', array('app' => 'core'));
600
				break;
601
		}
602
		if ($this->valid()) {
603
			$this->imageType = $iType;
0 ignored issues
show
Documentation Bug introduced by
It seems like $iType can also be of type string or boolean. However, the property $imageType is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
604
			$this->mimeType = image_type_to_mime_type($iType);
605
			$this->filePath = $imagePath;
0 ignored issues
show
Documentation Bug introduced by
It seems like $imagePath can also be of type boolean. However, the property $filePath is declared as type null|string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
606
		}
607
		return $this->resource;
608
	}
609
610
	/**
611
	 * Loads an image from a string of data.
612
	 *
613
	 * @param string $str A string of image data as read from a file.
614
	 * @return bool|resource An image resource or false on error
615
	 */
616
	public function loadFromData($str) {
617
		if (is_resource($str)) {
618
			return false;
619
		}
620
		$this->resource = @imagecreatefromstring($str);
621
		if ($this->fileInfo) {
622
			$this->mimeType = $this->fileInfo->buffer($str);
623
		}
624
		if (is_resource($this->resource)) {
625
			imagealphablending($this->resource, false);
626
			imagesavealpha($this->resource, true);
627
		}
628
629
		if (!$this->resource) {
630
			$this->logger->debug('OC_Image->loadFromFile, could not load', array('app' => 'core'));
631
			return false;
632
		}
633
		return $this->resource;
634
	}
635
636
	/**
637
	 * Loads an image from a base64 encoded string.
638
	 *
639
	 * @param string $str A string base64 encoded string of image data.
640
	 * @return bool|resource An image resource or false on error
641
	 */
642
	public function loadFromBase64($str) {
643
		if (!is_string($str)) {
644
			return false;
645
		}
646
		$data = base64_decode($str);
647
		if ($data) { // try to load from string data
648
			$this->resource = @imagecreatefromstring($data);
649
			if ($this->fileInfo) {
650
				$this->mimeType = $this->fileInfo->buffer($data);
651
			}
652
			if (!$this->resource) {
653
				$this->logger->debug('OC_Image->loadFromBase64, could not load', array('app' => 'core'));
654
				return false;
655
			}
656
			return $this->resource;
657
		} else {
658
			return false;
659
		}
660
	}
661
662
	/**
663
	 * Create a new image from file or URL
664
	 *
665
	 * @link http://www.programmierer-forum.de/function-imagecreatefrombmp-laeuft-mit-allen-bitraten-t143137.htm
666
	 * @version 1.00
667
	 * @param string $fileName <p>
668
	 * Path to the BMP image.
669
	 * </p>
670
	 * @return bool|resource an image resource identifier on success, <b>FALSE</b> on errors.
671
	 */
672
	private function imagecreatefrombmp($fileName) {
673 View Code Duplication
		if (!($fh = fopen($fileName, 'rb'))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
674
			$this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName, array('app' => 'core'));
675
			return false;
676
		}
677
		// read file header
678
		$meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14));
679
		// check for bitmap
680 View Code Duplication
		if ($meta['type'] != 19778) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
681
			fclose($fh);
682
			$this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
683
			return false;
684
		}
685
		// read image header
686
		$meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40));
687
		// read additional 16bit header
688
		if ($meta['bits'] == 16) {
689
			$meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12));
690
		}
691
		// set bytes and padding
692
		$meta['bytes'] = $meta['bits'] / 8;
693
		$this->bitDepth = $meta['bits']; //remember the bit depth for the imagebmp call
694
		$meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4)));
695
		if ($meta['decal'] == 4) {
696
			$meta['decal'] = 0;
697
		}
698
		// obtain imagesize
699
		if ($meta['imagesize'] < 1) {
700
			$meta['imagesize'] = $meta['filesize'] - $meta['offset'];
701
			// in rare cases filesize is equal to offset so we need to read physical size
702
			if ($meta['imagesize'] < 1) {
703
				$meta['imagesize'] = @filesize($fileName) - $meta['offset'];
704 View Code Duplication
				if ($meta['imagesize'] < 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
705
					fclose($fh);
706
					$this->logger->warning('imagecreatefrombmp: Can not obtain file size of ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
707
					return false;
708
				}
709
			}
710
		}
711
		// calculate colors
712
		$meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
713
		// read color palette
714
		$palette = array();
715
		if ($meta['bits'] < 16) {
716
			$palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
717
			// in rare cases the color value is signed
718
			if ($palette[1] < 0) {
719
				foreach ($palette as $i => $color) {
720
					$palette[$i] = $color + 16777216;
721
				}
722
			}
723
		}
724
		// create gd image
725
		$im = imagecreatetruecolor($meta['width'], $meta['height']);
726
		if ($im == false) {
727
			fclose($fh);
728
			$this->logger->warning(
729
				'imagecreatefrombmp: imagecreatetruecolor failed for file "' . $fileName . '" with dimensions ' . $meta['width'] . 'x' . $meta['height'],
730
				array('app' => 'core'));
731
			return false;
732
		}
733
734
		$data = fread($fh, $meta['imagesize']);
735
		$p = 0;
736
		$vide = chr(0);
737
		$y = $meta['height'] - 1;
738
		$error = 'imagecreatefrombmp: ' . $fileName . ' has not enough data!';
739
		// loop through the image data beginning with the lower left corner
740
		while ($y >= 0) {
741
			$x = 0;
742
			while ($x < $meta['width']) {
743
				switch ($meta['bits']) {
744
					case 32:
745
					case 24:
746 View Code Duplication
						if (!($part = substr($data, $p, 3))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
747
							$this->logger->warning($error, array('app' => 'core'));
748
							return $im;
749
						}
750
						$color = unpack('V', $part . $vide);
751
						break;
752
					case 16:
753 View Code Duplication
						if (!($part = substr($data, $p, 2))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
754
							fclose($fh);
755
							$this->logger->warning($error, array('app' => 'core'));
756
							return $im;
757
						}
758
						$color = unpack('v', $part);
759
						$color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3);
760
						break;
761
					case 8:
762
						$color = unpack('n', $vide . substr($data, $p, 1));
763
						$color[1] = $palette[$color[1] + 1];
764
						break;
765
					case 4:
766
						$color = unpack('n', $vide . substr($data, floor($p), 1));
767
						$color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
768
						$color[1] = $palette[$color[1] + 1];
769
						break;
770
					case 1:
771
						$color = unpack('n', $vide . substr($data, floor($p), 1));
772
						switch (($p * 8) % 8) {
773
							case 0:
774
								$color[1] = $color[1] >> 7;
775
								break;
776
							case 1:
777
								$color[1] = ($color[1] & 0x40) >> 6;
778
								break;
779
							case 2:
780
								$color[1] = ($color[1] & 0x20) >> 5;
781
								break;
782
							case 3:
783
								$color[1] = ($color[1] & 0x10) >> 4;
784
								break;
785
							case 4:
786
								$color[1] = ($color[1] & 0x8) >> 3;
787
								break;
788
							case 5:
789
								$color[1] = ($color[1] & 0x4) >> 2;
790
								break;
791
							case 6:
792
								$color[1] = ($color[1] & 0x2) >> 1;
793
								break;
794
							case 7:
795
								$color[1] = ($color[1] & 0x1);
796
								break;
797
						}
798
						$color[1] = $palette[$color[1] + 1];
799
						break;
800
					default:
801
						fclose($fh);
802
						$this->logger->warning('imagecreatefrombmp: ' . $fileName . ' has ' . $meta['bits'] . ' bits and this is not supported!', array('app' => 'core'));
803
						return false;
804
				}
805
				imagesetpixel($im, $x, $y, $color[1]);
806
				$x++;
807
				$p += $meta['bytes'];
808
			}
809
			$y--;
810
			$p += $meta['decal'];
811
		}
812
		fclose($fh);
813
		return $im;
814
	}
815
816
	/**
817
	 * Resizes the image preserving ratio.
818
	 *
819
	 * @param integer $maxSize The maximum size of either the width or height.
820
	 * @return bool
821
	 */
822
	public function resize($maxSize) {
823 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
824
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
825
			return false;
826
		}
827
		$widthOrig = imagesx($this->resource);
828
		$heightOrig = imagesy($this->resource);
829
		$ratioOrig = $widthOrig / $heightOrig;
830
831
		if ($ratioOrig > 1) {
832
			$newHeight = round($maxSize / $ratioOrig);
833
			$newWidth = $maxSize;
834
		} else {
835
			$newWidth = round($maxSize * $ratioOrig);
836
			$newHeight = $maxSize;
837
		}
838
839
		$this->preciseResize(round($newWidth), round($newHeight));
840
		return true;
841
	}
842
843
	/**
844
	 * @param int $width
845
	 * @param int $height
846
	 * @return bool
847
	 */
848
	public function preciseResize($width, $height) {
849 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
850
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
851
			return false;
852
		}
853
		$widthOrig = imagesx($this->resource);
854
		$heightOrig = imagesy($this->resource);
855
		$process = imagecreatetruecolor($width, $height);
856
857
		if ($process == false) {
858
			$this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
859
			imagedestroy($process);
860
			return false;
861
		}
862
863
		// preserve transparency
864 View Code Duplication
		if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
865
			imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
866
			imagealphablending($process, false);
867
			imagesavealpha($process, true);
868
		}
869
870
		imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
871
		if ($process == false) {
872
			$this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core'));
873
			imagedestroy($process);
874
			return false;
875
		}
876
		imagedestroy($this->resource);
877
		$this->resource = $process;
878
		return true;
879
	}
880
881
	/**
882
	 * Crops the image to the middle square. If the image is already square it just returns.
883
	 *
884
	 * @param int $size maximum size for the result (optional)
885
	 * @return bool for success or failure
886
	 */
887
	public function centerCrop($size = 0) {
888
		if (!$this->valid()) {
889
			$this->logger->error('OC_Image->centerCrop, No image loaded', array('app' => 'core'));
890
			return false;
891
		}
892
		$widthOrig = imagesx($this->resource);
893
		$heightOrig = imagesy($this->resource);
894
		if ($widthOrig === $heightOrig and $size == 0) {
895
			return true;
896
		}
897
		$ratioOrig = $widthOrig / $heightOrig;
898
		$width = $height = min($widthOrig, $heightOrig);
899
900
		if ($ratioOrig > 1) {
901
			$x = ($widthOrig / 2) - ($width / 2);
902
			$y = 0;
903
		} else {
904
			$y = ($heightOrig / 2) - ($height / 2);
905
			$x = 0;
906
		}
907
		if ($size > 0) {
908
			$targetWidth = $size;
909
			$targetHeight = $size;
910
		} else {
911
			$targetWidth = $width;
912
			$targetHeight = $height;
913
		}
914
		$process = imagecreatetruecolor($targetWidth, $targetHeight);
915
		if ($process == false) {
916
			$this->logger->error('OC_Image->centerCrop, Error creating true color image', array('app' => 'core'));
917
			imagedestroy($process);
918
			return false;
919
		}
920
921
		// preserve transparency
922 View Code Duplication
		if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
923
			imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
924
			imagealphablending($process, false);
925
			imagesavealpha($process, true);
926
		}
927
928
		imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
929 View Code Duplication
		if ($process == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
930
			$this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, array('app' => 'core'));
931
			imagedestroy($process);
932
			return false;
933
		}
934
		imagedestroy($this->resource);
935
		$this->resource = $process;
936
		return true;
937
	}
938
939
	/**
940
	 * Crops the image from point $x$y with dimension $wx$h.
941
	 *
942
	 * @param int $x Horizontal position
943
	 * @param int $y Vertical position
944
	 * @param int $w Width
945
	 * @param int $h Height
946
	 * @return bool for success or failure
947
	 */
948
	public function crop($x, $y, $w, $h) {
949 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
950
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
951
			return false;
952
		}
953
		$process = imagecreatetruecolor($w, $h);
954
		if ($process == false) {
955
			$this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
956
			imagedestroy($process);
957
			return false;
958
		}
959
960
		// preserve transparency
961 View Code Duplication
		if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
962
			imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
963
			imagealphablending($process, false);
964
			imagesavealpha($process, true);
965
		}
966
967
		imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
968 View Code Duplication
		if ($process == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
969
			$this->logger->error(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, array('app' => 'core'));
970
			imagedestroy($process);
971
			return false;
972
		}
973
		imagedestroy($this->resource);
974
		$this->resource = $process;
975
		return true;
976
	}
977
978
	/**
979
	 * Resizes the image to fit within a boundary while preserving ratio.
980
	 *
981
	 * Warning: Images smaller than $maxWidth x $maxHeight will end up being scaled up
982
	 *
983
	 * @param integer $maxWidth
984
	 * @param integer $maxHeight
985
	 * @return bool
986
	 */
987
	public function fitIn($maxWidth, $maxHeight) {
988 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
989
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
990
			return false;
991
		}
992
		$widthOrig = imagesx($this->resource);
993
		$heightOrig = imagesy($this->resource);
994
		$ratio = $widthOrig / $heightOrig;
995
996
		$newWidth = min($maxWidth, $ratio * $maxHeight);
997
		$newHeight = min($maxHeight, $maxWidth / $ratio);
998
999
		$this->preciseResize(round($newWidth), round($newHeight));
1000
		return true;
1001
	}
1002
1003
	/**
1004
	 * Shrinks larger images to fit within specified boundaries while preserving ratio.
1005
	 *
1006
	 * @param integer $maxWidth
1007
	 * @param integer $maxHeight
1008
	 * @return bool
1009
	 */
1010
	public function scaleDownToFit($maxWidth, $maxHeight) {
1011 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1012
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
1013
			return false;
1014
		}
1015
		$widthOrig = imagesx($this->resource);
1016
		$heightOrig = imagesy($this->resource);
1017
1018
		if ($widthOrig > $maxWidth || $heightOrig > $maxHeight) {
1019
			return $this->fitIn($maxWidth, $maxHeight);
1020
		}
1021
1022
		return false;
1023
	}
1024
1025
	/**
1026
	 * Destroys the current image and resets the object
1027
	 */
1028
	public function destroy() {
1029
		if ($this->valid()) {
1030
			imagedestroy($this->resource);
1031
		}
1032
		$this->resource = null;
1033
	}
1034
1035
	public function __destruct() {
1036
		$this->destroy();
1037
	}
1038
}
1039
1040
if (!function_exists('imagebmp')) {
1041
	/**
1042
	 * Output a BMP image to either the browser or a file
1043
	 *
1044
	 * @link http://www.ugia.cn/wp-data/imagebmp.php
1045
	 * @author legend <[email protected]>
1046
	 * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm
1047
	 * @author mgutt <[email protected]>
1048
	 * @version 1.00
1049
	 * @param resource $im
1050
	 * @param string $fileName [optional] <p>The path to save the file to.</p>
1051
	 * @param int $bit [optional] <p>Bit depth, (default is 24).</p>
1052
	 * @param int $compression [optional]
1053
	 * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
1054
	 */
1055
	function imagebmp($im, $fileName = '', $bit = 24, $compression = 0) {
1056
		if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) {
1057
			$bit = 24;
1058
		} else if ($bit == 32) {
1059
			$bit = 24;
1060
		}
1061
		$bits = pow(2, $bit);
1062
		imagetruecolortopalette($im, true, $bits);
1063
		$width = imagesx($im);
1064
		$height = imagesy($im);
1065
		$colorsNum = imagecolorstotal($im);
1066
		$rgbQuad = '';
1067
		if ($bit <= 8) {
1068
			for ($i = 0; $i < $colorsNum; $i++) {
1069
				$colors = imagecolorsforindex($im, $i);
1070
				$rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0";
1071
			}
1072
			$bmpData = '';
1073
			if ($compression == 0 || $bit < 8) {
1074
				$compression = 0;
1075
				$extra = '';
1076
				$padding = 4 - ceil($width / (8 / $bit)) % 4;
1077
				if ($padding % 4 != 0) {
1078
					$extra = str_repeat("\0", $padding);
1079
				}
1080
				for ($j = $height - 1; $j >= 0; $j--) {
1081
					$i = 0;
1082
					while ($i < $width) {
1083
						$bin = 0;
1084
						$limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0;
1085
						for ($k = 8 - $bit; $k >= $limit; $k -= $bit) {
1086
							$index = imagecolorat($im, $i, $j);
1087
							$bin |= $index << $k;
1088
							$i++;
1089
						}
1090
						$bmpData .= chr($bin);
1091
					}
1092
					$bmpData .= $extra;
1093
				}
1094
			} // RLE8
1095
			else if ($compression == 1 && $bit == 8) {
1096
				for ($j = $height - 1; $j >= 0; $j--) {
1097
					$lastIndex = "\0";
1098
					$sameNum = 0;
1099
					for ($i = 0; $i <= $width; $i++) {
1100
						$index = imagecolorat($im, $i, $j);
1101
						if ($index !== $lastIndex || $sameNum > 255) {
1102
							if ($sameNum != 0) {
1103
								$bmpData .= chr($sameNum) . chr($lastIndex);
1104
							}
1105
							$lastIndex = $index;
1106
							$sameNum = 1;
1107
						} else {
1108
							$sameNum++;
1109
						}
1110
					}
1111
					$bmpData .= "\0\0";
1112
				}
1113
				$bmpData .= "\0\1";
1114
			}
1115
			$sizeQuad = strlen($rgbQuad);
1116
			$sizeData = strlen($bmpData);
1117
		} else {
1118
			$extra = '';
1119
			$padding = 4 - ($width * ($bit / 8)) % 4;
1120
			if ($padding % 4 != 0) {
1121
				$extra = str_repeat("\0", $padding);
1122
			}
1123
			$bmpData = '';
1124
			for ($j = $height - 1; $j >= 0; $j--) {
1125
				for ($i = 0; $i < $width; $i++) {
1126
					$index = imagecolorat($im, $i, $j);
1127
					$colors = imagecolorsforindex($im, $index);
1128
					if ($bit == 16) {
1129
						$bin = 0 << $bit;
1130
						$bin |= ($colors['red'] >> 3) << 10;
1131
						$bin |= ($colors['green'] >> 3) << 5;
1132
						$bin |= $colors['blue'] >> 3;
1133
						$bmpData .= pack("v", $bin);
1134
					} else {
1135
						$bmpData .= pack("c*", $colors['blue'], $colors['green'], $colors['red']);
1136
					}
1137
				}
1138
				$bmpData .= $extra;
1139
			}
1140
			$sizeQuad = 0;
1141
			$sizeData = strlen($bmpData);
1142
			$colorsNum = 0;
1143
		}
1144
		$fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad);
1145
		$infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0);
1146
		if ($fileName != '') {
1147
			$fp = fopen($fileName, 'wb');
1148
			fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData);
1149
			fclose($fp);
1150
			return true;
1151
		}
1152
		echo $fileHeader . $infoHeader . $rgbQuad . $bmpData;
1153
		return true;
1154
	}
1155
}
1156
1157
if (!function_exists('exif_imagetype')) {
1158
	/**
1159
	 * Workaround if exif_imagetype does not exist
1160
	 *
1161
	 * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383
1162
	 * @param string $fileName
1163
	 * @return string|boolean
1164
	 */
1165
	function exif_imagetype($fileName) {
1166
		if (($info = getimagesize($fileName)) !== false) {
1167
			return $info[2];
1168
		}
1169
		return false;
1170
	}
1171
}
1172