3 * @copyright Copyright (C) 2010-2023, the Friendica project
5 * @license GNU AGPL version 3 or any later version
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.
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.
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/>.
22 namespace Friendica\Util;
24 use Friendica\Core\Logger;
26 use Friendica\Model\Photo;
27 use Friendica\Network\HTTPClient\Client\HttpClientAccept;
28 use Friendica\Object\Image;
36 * Maps Mime types to Imagick formats
38 * @return array Format map
40 public static function getFormatsMap()
43 'image/jpeg' => 'JPG',
51 * Return file extension for MIME type
53 * @param string $mimetype MIME type
54 * @return string File extension for MIME type
56 public static function getExtensionByMimeType(string $mimetype): string
60 $imagetype = IMAGETYPE_PNG;
64 $imagetype = IMAGETYPE_GIF;
69 $imagetype = IMAGETYPE_JPEG;
72 default: // Unknown type must be a blob then
77 return image_type_to_extension($imagetype);
81 * Returns supported image mimetypes and corresponding file extensions
85 public static function supportedTypes(): array
88 'image/jpeg' => 'jpg',
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
99 } elseif (imagetypes() & IMG_PNG) {
109 * Fetch image mimetype from the image data or guessing from the file name
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
117 public static function getMimeTypeByData(string $image_data, string $filename = '', string $default = ''): string
119 if (substr($default, 0, 6) == 'image/') {
120 Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
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'];
130 return self::guessTypeByExtension($filename);
134 * Fetch image mimetype from the image data or guessing from the file name
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
142 public static function getMimeTypeBySource(string $sourcefile, string $filename = '', string $default = ''): string
144 if (substr($default, 0, 6) == 'image/') {
145 Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
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'];
155 return self::guessTypeByExtension($filename);
159 * Guess image MIME type from the filename's extension
161 * @param string $filename Image filename
162 * @return string Guessed MIME type by extension
165 public static function guessTypeByExtension(string $filename): string
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) {
176 Logger::info('Mime type guessed via extension', ['filename' => $filename, 'type' => $type]);
181 * Gets info array from given URL, cached data has priority
185 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
187 public static function getInfoFromURLCached(string $url): array
195 $cacheKey = 'getInfoFromURL:' . sha1($url);
197 $data = DI::cache()->get($cacheKey);
199 if (empty($data) || !is_array($data)) {
200 $data = self::getInfoFromURL($url);
202 DI::cache()->set($cacheKey, $data);
209 * Gets info from URL uncached
212 * @return array Info array
213 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
215 public static function getInfoFromURL(string $url): array
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);
228 // @todo Possibly add a check for locally stored files
231 if (empty($img_str)) {
233 $img_str = DI::httpClient()->fetch($url, HttpClientAccept::IMAGE, 4);
234 } catch (\Exception $exception) {
235 Logger::notice('Image is invalid', ['url' => $url, 'exception' => $exception]);
244 $filesize = strlen($img_str);
247 $data = @getimagesizefromstring($img_str);
248 } catch (\Exception $e) {
253 $image = new Image($img_str);
255 if ($image->isValid()) {
256 $data['blurhash'] = $image->getBlurHash();
259 $data['size'] = $filesize;
262 return is_array($data) ? $data : [];
266 * Returns scaling information
268 * @param integer $width Width
269 * @param integer $height Height
270 * @param integer $max Max width/height
271 * @return array Scaling dimensions
273 public static function getScalingDimensions(int $width, int $height, int $max): array
275 if ((!$width) || (!$height)) {
276 return ['width' => 0, 'height' => 0];
279 if ($width > $max && $height > $max) {
280 // very tall image (greater than 16:9)
281 // constrain the width - let the height float.
283 if ((($height * 9) / 16) > $width) {
285 $dest_height = intval(($height * $max) / $width);
286 } elseif ($width > $height) {
287 // else constrain both dimensions
289 $dest_height = intval(($height * $max) / $width);
291 $dest_width = intval(($width * $max) / $height);
297 $dest_height = intval(($height * $max) / $width);
299 if ($height > $max) {
300 // very tall image (greater than 16:9)
301 // but width is OK - don't do anything
303 if ((($height * 9) / 16) > $width) {
304 $dest_width = $width;
305 $dest_height = $height;
307 $dest_width = intval(($width * $max) / $height);
311 $dest_width = $width;
312 $dest_height = $height;
317 return ['width' => $dest_width, 'height' => $dest_height];