]> git.mxchange.org Git - friendica.git/blob - src/Util/Images.php
Merge pull request #8271 from MrPetovan/bug/8229-frio-mobile-back-to-top
[friendica.git] / src / Util / Images.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
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\Core\System;
26 use Friendica\DI;
27 use Imagick;
28
29 /**
30  * Image utilities
31  */
32 class Images
33 {
34         /**
35          * Maps Mime types to Imagick formats
36          *
37          * @return array
38          */
39         public static function getFormatsMap()
40         {
41                 $m = [
42                         'image/jpeg' => 'JPG',
43                         'image/png' => 'PNG',
44                         'image/gif' => 'GIF'
45                 ];
46
47                 return $m;
48         }
49
50         /**
51          * Returns supported image mimetypes and corresponding file extensions
52          *
53          * @return array
54          */
55         public static function supportedTypes()
56         {
57                 $types = [
58                         'image/jpeg' => 'jpg'
59                 ];
60                 if (class_exists('Imagick')) {
61                         // Imagick::queryFormats won't help us a lot there...
62                         // At least, not yet, other parts of friendica uses this array
63                         $types += [
64                                 'image/png' => 'png',
65                                 'image/gif' => 'gif'
66                         ];
67                 } elseif (imagetypes() & IMG_PNG) {
68                         $types += [
69                                 'image/png' => 'png'
70                         ];
71                 }
72
73                 return $types;
74         }
75
76         /**
77          * Guess image mimetype from filename or from Content-Type header
78          *
79          * @param string  $filename Image filename
80          * @param boolean $fromcurl Check Content-Type header from curl request
81          * @param string  $header   passed headers to take into account
82          *
83          * @return string|null
84          * @throws \Exception
85          */
86         public static function guessType($filename, $fromcurl = false, $header = '')
87         {
88                 Logger::info('Image: guessType: ' . $filename . ($fromcurl ? ' from curl headers' : ''));
89                 $type = null;
90                 if ($fromcurl) {
91                         $headers = [];
92                         $h = explode("\n", $header);
93                         foreach ($h as $l) {
94                                 $data = array_map("trim", explode(":", trim($l), 2));
95                                 if (count($data) > 1) {
96                                         list($k, $v) = $data;
97                                         $headers[$k] = $v;
98                                 }
99                         }
100
101                         if (array_key_exists('Content-Type', $headers)) {
102                                 $type = $headers['Content-Type'];
103                         }
104                 }
105
106                 if (is_null($type)) {
107                         // Guessing from extension? Isn't that... dangerous?
108                         if (class_exists('Imagick') && file_exists($filename) && is_readable($filename)) {
109                                 /**
110                                  * Well, this not much better,
111                                  * but at least it comes from the data inside the image,
112                                  * we won't be tricked by a manipulated extension
113                                  */
114                                 $image = new Imagick($filename);
115                                 $type = $image->getImageMimeType();
116                         } else {
117                                 $ext = pathinfo($filename, PATHINFO_EXTENSION);
118                                 $types = self::supportedTypes();
119                                 $type = 'image/jpeg';
120                                 foreach ($types as $m => $e) {
121                                         if ($ext == $e) {
122                                                 $type = $m;
123                                         }
124                                 }
125                         }
126                 }
127
128                 Logger::info('Image: guessType: type=' . $type);
129                 return $type;
130         }
131
132
133         /**
134          * @param string $url
135          * @return array
136          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
137          */
138         public static function getInfoFromURLCached($url)
139         {
140                 $data = [];
141
142                 if (empty($url)) {
143                         return $data;
144                 }
145
146                 $data = DI::cache()->get($url);
147
148                 if (empty($data) || !is_array($data)) {
149                         $data = self::getInfoFromURL($url);
150
151                         DI::cache()->set($url, $data);
152                 }
153
154                 return $data;
155         }
156
157         /**
158          * @param string $url
159          * @return array
160          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
161          */
162         public static function getInfoFromURL($url)
163         {
164                 $data = [];
165
166                 if (empty($url)) {
167                         return $data;
168                 }
169
170                 $img_str = Network::fetchUrl($url, true, 4);
171
172                 if (!$img_str) {
173                         return [];
174                 }
175
176                 $filesize = strlen($img_str);
177
178                 try {
179                         if (function_exists("getimagesizefromstring")) {
180                                 $data = @getimagesizefromstring($img_str);
181                         } else {
182                                 $tempfile = tempnam(get_temppath(), "cache");
183
184                                 $stamp1 = microtime(true);
185                                 file_put_contents($tempfile, $img_str);
186                                 DI::profiler()->saveTimestamp($stamp1, "file", System::callstack());
187
188                                 $data = getimagesize($tempfile);
189                                 unlink($tempfile);
190                         }
191                 } catch (\Exception $e) {
192                         return [];
193                 }
194
195                 if ($data) {
196                         $data['size'] = $filesize;
197                 }
198
199                 return $data;
200         }
201
202         /**
203          * @param integer $width
204          * @param integer $height
205          * @param integer $max
206          * @return array
207          */
208         public static function getScalingDimensions($width, $height, $max)
209         {
210                 if ((!$width) || (!$height)) {
211                         return ['width' => 0, 'height' => 0];
212                 }
213
214                 if ($width > $max && $height > $max) {
215                         // very tall image (greater than 16:9)
216                         // constrain the width - let the height float.
217
218                         if ((($height * 9) / 16) > $width) {
219                                 $dest_width = $max;
220                                 $dest_height = intval(($height * $max) / $width);
221                         } elseif ($width > $height) {
222                                 // else constrain both dimensions
223                                 $dest_width = $max;
224                                 $dest_height = intval(($height * $max) / $width);
225                         } else {
226                                 $dest_width = intval(($width * $max) / $height);
227                                 $dest_height = $max;
228                         }
229                 } else {
230                         if ($width > $max) {
231                                 $dest_width = $max;
232                                 $dest_height = intval(($height * $max) / $width);
233                         } else {
234                                 if ($height > $max) {
235                                         // very tall image (greater than 16:9)
236                                         // but width is OK - don't do anything
237
238                                         if ((($height * 9) / 16) > $width) {
239                                                 $dest_width = $width;
240                                                 $dest_height = $height;
241                                         } else {
242                                                 $dest_width = intval(($width * $max) / $height);
243                                                 $dest_height = $max;
244                                         }
245                                 } else {
246                                         $dest_width = $width;
247                                         $dest_height = $height;
248                                 }
249                         }
250                 }
251
252                 return ['width' => $dest_width, 'height' => $dest_height];
253         }
254 }