Merge branch '3.1' into 5.2
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / util / ImageUtil.class.php
1 <?php
2 namespace wcf\util;
3 use wcf\system\exception\SystemException;
4 use wcf\system\image\ImageHandler;
5
6 /**
7 * Contains image-related functions.
8 *
9 * @author Marcel Werk
10 * @copyright 2001-2019 WoltLab GmbH
11 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
12 * @package WoltLabSuite\Core\Util
13 */
14 final class ImageUtil {
15 /**
16 * image extensions
17 * @var array
18 */
19 protected static $imageExtensions = ['jpeg', 'jpg', 'png', 'gif'];
20
21 /**
22 * Checks the content of an image for bad sections, e.g. the use of javascript
23 * and returns false if any bad stuff was found.
24 *
25 * @param string $file
26 * @return boolean
27 */
28 public static function checkImageContent($file) {
29 // get file content
30 $content = file_get_contents($file);
31
32 // remove some characters
33 $content = strtolower(preg_replace('/[^a-z0-9<\(]+/i', '', $content));
34 $content = str_replace('description', '', $content);
35
36 // search for javascript
37 if (strpos($content, 'script') !== false || strpos($content, 'javascript') !== false || strpos($content, 'expression(') !== false) return false;
38
39 return true;
40 }
41
42 /**
43 * Checks whether a given file is a valid image.
44 *
45 * @param string $location
46 * @param string|null $filename
47 * @param bool $handleSvgAsValidImage flag, whether a svg file is handled as image
48 * @return bool
49 */
50 public static function isImage($location, $filename = null, $handleSvgAsValidImage = false) {
51 if ($filename === null) {
52 $filename = basename($location);
53 }
54
55 if (@getimagesize($location) !== false) {
56 $extension = pathinfo($filename, PATHINFO_EXTENSION);
57
58 if (in_array(mb_strtolower($extension), self::$imageExtensions)) {
59 return true;
60 }
61 }
62 else if ($handleSvgAsValidImage) {
63 if (in_array(FileUtil::getMimeType($location), ['image/svg', 'image/svg+xml']) && mb_strtolower(pathinfo($filename, PATHINFO_EXTENSION)) === 'svg') {
64 return true;
65 }
66 }
67
68 return false;
69 }
70
71 /**
72 * Return the file extension for an image with the given mime type.
73 *
74 * @param string $mimeType
75 * @return string
76 * @see http://www.php.net/manual/en/function.image-type-to-mime-type.php
77 */
78 public static function getExtensionByMimeType($mimeType) {
79 switch ($mimeType) {
80 case 'image/gif':
81 return 'gif';
82 case 'image/jpeg':
83 return 'jpg';
84 case 'image/png':
85 return 'png';
86 case 'application/x-shockwave-flash':
87 return 'swf';
88 case 'image/psd':
89 return 'psd';
90 case 'image/bmp':
91 case 'image/x-ms-bmp':
92 return 'bmp';
93 case 'image/tiff':
94 return 'tiff';
95 default:
96 return '';
97 }
98 }
99
100 /**
101 * Enforces dimensions for given image.
102 *
103 * @param string $filename
104 * @param integer $maxWidth
105 * @param integer $maxHeight
106 * @param boolean $obtainDimensions
107 * @return string new filename if file was changed, otherwise old filename
108 * @since 5.2
109 */
110 public static function enforceDimensions($filename, $maxWidth, $maxHeight, $obtainDimensions = true) {
111 $imageData = getimagesize($filename);
112 if ($imageData[0] > $maxWidth || $imageData[1] > $maxHeight) {
113 $adapter = ImageHandler::getInstance()->getAdapter();
114 $adapter->loadFile($filename);
115 $filename = FileUtil::getTemporaryFilename();
116 $thumbnail = $adapter->createThumbnail($maxWidth, $maxHeight, $obtainDimensions);
117 $adapter->writeImage($thumbnail, $filename);
118 }
119
120 return $filename;
121 }
122
123 /**
124 * Rotates the given image based on the orientation stored in the exif data.
125 *
126 * @param string $filename
127 * @return string new filename if file was changed, otherwise old filename
128 * @since 5.2
129 */
130 public static function fixOrientation($filename) {
131 try {
132 $exifData = ExifUtil::getExifData($filename);
133 if (!empty($exifData)) {
134 $orientation = ExifUtil::getOrientation($exifData);
135 if ($orientation != ExifUtil::ORIENTATION_ORIGINAL) {
136 $adapter = ImageHandler::getInstance()->getAdapter();
137 $adapter->loadFile($filename);
138
139 $newImage = null;
140 switch ($orientation) {
141 case ExifUtil::ORIENTATION_180_ROTATE:
142 $newImage = $adapter->rotate(180);
143 break;
144
145 case ExifUtil::ORIENTATION_90_ROTATE:
146 $newImage = $adapter->rotate(90);
147 break;
148
149 case ExifUtil::ORIENTATION_270_ROTATE:
150 $newImage = $adapter->rotate(270);
151 break;
152
153 case ExifUtil::ORIENTATION_HORIZONTAL_FLIP:
154 case ExifUtil::ORIENTATION_VERTICAL_FLIP:
155 case ExifUtil::ORIENTATION_VERTICAL_FLIP_270_ROTATE:
156 case ExifUtil::ORIENTATION_HORIZONTAL_FLIP_270_ROTATE:
157 // unsupported
158 break;
159 }
160
161 if ($newImage !== null) {
162 if ($newImage instanceof \Imagick) {
163 $newImage->setImageOrientation(\Imagick::ORIENTATION_TOPLEFT);
164 }
165
166 $adapter->load($newImage, $adapter->getType());
167 }
168
169 $newFilename = FileUtil::getTemporaryFilename();
170 $adapter->writeImage($newFilename);
171 $filename = $newFilename;
172 }
173 }
174 }
175 catch (SystemException $e) {}
176
177 return $filename;
178 }
179
180 /**
181 * Forbid creation of ImageUtil objects.
182 */
183 private function __construct() {
184 // does nothing
185 }
186 }