]> git.mxchange.org Git - friendica.git/blob - src/Util/Images.php
spelling: associative
[friendica.git] / src / Util / Images.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Util;
23
24 use Friendica\Core\Logger;
25 use Friendica\DI;
26 use Friendica\Model\Photo;
27 use Friendica\Network\HTTPClient\Client\HttpClientAccept;
28 use Friendica\Object\Image;
29
30 /**
31  * Image utilities
32  */
33 class Images
34 {
35         /**
36          * Maps Mime types to Imagick formats
37          *
38          * @return array Format map
39          */
40         public static function getFormatsMap()
41         {
42                 return [
43                         'image/jpeg' => 'JPG',
44                         'image/jpg' => 'JPG',
45                         'image/png' => 'PNG',
46                         'image/gif' => 'GIF',
47                 ];
48         }
49
50         /**
51          * Return file extension for MIME type
52          *
53          * @param string $mimetype MIME type
54          * @return string File extension for MIME type
55          */
56         public static function getExtensionByMimeType(string $mimetype): string
57         {
58                 switch ($mimetype) {
59                         case 'image/png':
60                                 $imagetype = IMAGETYPE_PNG;
61                                 break;
62
63                         case 'image/gif':
64                                 $imagetype = IMAGETYPE_GIF;
65                                 break;
66
67                         case 'image/jpeg':
68                         case 'image/jpg':
69                                 $imagetype = IMAGETYPE_JPEG;
70                                 break;
71
72                         default: // Unknown type must be a blob then
73                                 return 'blob';
74                                 break;
75                 }
76
77                 return image_type_to_extension($imagetype);
78         }
79
80         /**
81          * Returns supported image mimetypes and corresponding file extensions
82          *
83          * @return array
84          */
85         public static function supportedTypes(): array
86         {
87                 $types = [
88                         'image/jpeg' => 'jpg',
89                         'image/jpg' => 'jpg',
90                 ];
91
92                 if (class_exists('Imagick')) {
93                         // Imagick::queryFormats won't help us a lot there...
94                         // At least, not yet, other parts of friendica uses this array
95                         $types += [
96                                 'image/png' => 'png',
97                                 'image/gif' => 'gif'
98                         ];
99                 } elseif (imagetypes() & IMG_PNG) {
100                         $types += [
101                                 'image/png' => 'png'
102                         ];
103                 }
104
105                 return $types;
106         }
107
108         /**
109          * Fetch image mimetype from the image data or guessing from the file name
110          *
111          * @param string $image_data Image data
112          * @param string $filename   File name (for guessing the type via the extension)
113          * @param string $default    Default MIME type
114          * @return string MIME type
115          * @throws \Exception
116          */
117         public static function getMimeTypeByData(string $image_data, string $filename = '', string $default = ''): string
118         {
119                 if (substr($default, 0, 6) == 'image/') {
120                         Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
121                         return $default;
122                 }
123
124                 $image = @getimagesizefromstring($image_data);
125                 if (!empty($image['mime'])) {
126                         Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $default, 'mime' => $image['mime']]);
127                         return $image['mime'];
128                 }
129
130                 return self::guessTypeByExtension($filename);
131         }
132
133         /**
134          * Fetch image mimetype from the image data or guessing from the file name
135          *
136          * @param string $sourcefile Source file of the image
137          * @param string $filename   File name (for guessing the type via the extension)
138          * @param string $default    default MIME type
139          * @return string MIME type
140          * @throws \Exception
141          */
142         public static function getMimeTypeBySource(string $sourcefile, string $filename = '', string $default = ''): string
143         {
144                 if (substr($default, 0, 6) == 'image/') {
145                         Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
146                         return $default;
147                 }
148
149                 $image = @getimagesize($sourcefile);
150                 if (!empty($image['mime'])) {
151                         Logger::info('Mime type detected via file', ['filename' => $filename, 'default' => $default, 'image' => $image]);
152                         return $image['mime'];
153                 }
154
155                 return self::guessTypeByExtension($filename);
156         }
157
158         /**
159          * Guess image MIME type from the filename's extension
160          *
161          * @param string $filename Image filename
162          * @return string Guessed MIME type by extension
163          * @throws \Exception
164          */
165         public static function guessTypeByExtension(string $filename): string
166         {
167                 $ext = pathinfo(parse_url($filename, PHP_URL_PATH), PATHINFO_EXTENSION);
168                 $types = self::supportedTypes();
169                 $type = 'image/jpeg';
170                 foreach ($types as $m => $e) {
171                         if ($ext == $e) {
172                                 $type = $m;
173                         }
174                 }
175
176                 Logger::info('Mime type guessed via extension', ['filename' => $filename, 'type' => $type]);
177                 return $type;
178         }
179
180         /**
181          * Gets info array from given URL, cached data has priority
182          *
183          * @param string $url
184          * @return array Info
185          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
186          */
187         public static function getInfoFromURLCached(string $url): array
188         {
189                 $data = [];
190
191                 if (empty($url)) {
192                         return $data;
193                 }
194
195                 $cacheKey = 'getInfoFromURL:' . sha1($url);
196
197                 $data = DI::cache()->get($cacheKey);
198
199                 if (empty($data) || !is_array($data)) {
200                         $data = self::getInfoFromURL($url);
201
202                         DI::cache()->set($cacheKey, $data);
203                 }
204
205                 return $data ?? [];
206         }
207
208         /**
209          * Gets info from URL uncached
210          *
211          * @param string $url
212          * @return array Info array
213          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
214          */
215         public static function getInfoFromURL(string $url): array
216         {
217                 $data = [];
218
219                 if (empty($url)) {
220                         return $data;
221                 }
222
223                 if (Network::isLocalLink($url) && ($data = Photo::getResourceData($url))) {
224                         $photo = Photo::selectFirst([], ['resource-id' => $data['guid'], 'scale' => $data['scale']]);
225                         if (!empty($photo)) {
226                                 $img_str = Photo::getImageDataForPhoto($photo);
227                         }
228                         // @todo Possibly add a check for locally stored files
229                 }
230
231                 if (empty($img_str)) {
232                         try {
233                                 $img_str = DI::httpClient()->fetch($url, HttpClientAccept::IMAGE, 4);
234                         } catch (\Exception $exception) {
235                                 Logger::notice('Image is invalid', ['url' => $url, 'exception' => $exception]);
236                                 return [];
237                         }
238                 }
239
240                 if (!$img_str) {
241                         return [];
242                 }
243
244                 $filesize = strlen($img_str);
245
246                 try {
247                         $data = @getimagesizefromstring($img_str);
248                 } catch (\Exception $e) {
249                         return [];
250                 }
251
252                 if ($data) {
253                         $image = new Image($img_str);
254
255                         if ($image->isValid()) {
256                                 $data['blurhash'] = $image->getBlurHash();
257                         }
258
259                         $data['size'] = $filesize;
260                 }
261
262                 return is_array($data) ? $data : [];
263         }
264
265         /**
266          * Returns scaling information
267          *
268          * @param integer $width Width
269          * @param integer $height Height
270          * @param integer $max Max width/height
271          * @return array Scaling dimensions
272          */
273         public static function getScalingDimensions(int $width, int $height, int $max): array
274         {
275                 if ((!$width) || (!$height)) {
276                         return ['width' => 0, 'height' => 0];
277                 }
278
279                 if ($width > $max && $height > $max) {
280                         // very tall image (greater than 16:9)
281                         // constrain the width - let the height float.
282
283                         if ((($height * 9) / 16) > $width) {
284                                 $dest_width = $max;
285                                 $dest_height = intval(($height * $max) / $width);
286                         } elseif ($width > $height) {
287                                 // else constrain both dimensions
288                                 $dest_width = $max;
289                                 $dest_height = intval(($height * $max) / $width);
290                         } else {
291                                 $dest_width = intval(($width * $max) / $height);
292                                 $dest_height = $max;
293                         }
294                 } else {
295                         if ($width > $max) {
296                                 $dest_width = $max;
297                                 $dest_height = intval(($height * $max) / $width);
298                         } else {
299                                 if ($height > $max) {
300                                         // very tall image (greater than 16:9)
301                                         // but width is OK - don't do anything
302
303                                         if ((($height * 9) / 16) > $width) {
304                                                 $dest_width = $width;
305                                                 $dest_height = $height;
306                                         } else {
307                                                 $dest_width = intval(($width * $max) / $height);
308                                                 $dest_height = $max;
309                                         }
310                                 } else {
311                                         $dest_width = $width;
312                                         $dest_height = $height;
313                                 }
314                         }
315                 }
316
317                 return ['width' => $dest_width, 'height' => $dest_height];
318         }
319 }