Compare commits
12 commits
5eb9bb9a31
...
551c366e6c
Author | SHA1 | Date | |
---|---|---|---|
551c366e6c | |||
e4087c61c1 | |||
29684043ab | |||
7f6e84e593 | |||
7cffa284d9 | |||
2382c22169 | |||
8ba6bf8272 | |||
36406a9770 | |||
63ae8f338f | |||
cd8d93ad05 | |||
a1aefc0d6b | |||
a9c98a61ed |
14 changed files with 322 additions and 80 deletions
|
@ -6,7 +6,8 @@
|
|||
"twig/twig": "^1.44.2",
|
||||
"lifo/ip": "^1.0",
|
||||
"gettext/gettext": "^1.0",
|
||||
"mrclay/minify": "^2.1.6"
|
||||
"mrclay/minify": "^2.1.6",
|
||||
"xantios/mimey": "^2.2"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["inc/"],
|
||||
|
@ -25,7 +26,9 @@
|
|||
"inc/polyfill.php",
|
||||
"inc/error.php",
|
||||
"inc/functions.php",
|
||||
"inc/functions/net.php"
|
||||
"inc/functions/net.php",
|
||||
"inc/functions/fs.php"
|
||||
|
||||
]
|
||||
},
|
||||
"license": "Tinyboard + vichan",
|
||||
|
|
56
composer.lock
generated
56
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "346d80deda89b0298a414b565213f312",
|
||||
"content-hash": "1339c8c595b0cde0968d91be0edc1908",
|
||||
"packages": [
|
||||
{
|
||||
"name": "gettext/gettext",
|
||||
|
@ -316,15 +316,63 @@
|
|||
}
|
||||
],
|
||||
"time": "2021-11-25T13:31:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "xantios/mimey",
|
||||
"version": "v2.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Xantios/mimey.git",
|
||||
"reference": "8cb6f0c29b8eadde38777ed947847f4253c00b60"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Xantios/mimey/zipball/8cb6f0c29b8eadde38777ed947847f4253c00b60",
|
||||
"reference": "8cb6f0c29b8eadde38777ed947847f4253c00b60",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.4",
|
||||
"phpunit/phpunit": "^9.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mimey\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Xantios Krugor",
|
||||
"email": "git@xantios.nl"
|
||||
},
|
||||
{
|
||||
"name": "Ralph Khattar",
|
||||
"email": "ralph.khattar@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP package for converting file extensions to MIME types and vice versa.",
|
||||
"support": {
|
||||
"issues": "https://github.com/Xantios/mimey/issues",
|
||||
"source": "https://github.com/Xantios/mimey/tree/v2.2.0"
|
||||
},
|
||||
"time": "2021-06-12T14:33:14+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"platform": {},
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
|
15
inc/Data/ImageMetadataResult.php
Normal file
15
inc/Data/ImageMetadataResult.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
namespace Vichan\Data;
|
||||
|
||||
|
||||
class ImageMetadataResult {
|
||||
public int $width;
|
||||
public int $height;
|
||||
public string $mime;
|
||||
|
||||
public function __construct(int $width, int $height, string $mime) {
|
||||
$this->width = $$width;
|
||||
$this->height = $$height;
|
||||
$this->mime = $$mime;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ namespace Vichan\Data;
|
|||
|
||||
class ThumbGenerationResult {
|
||||
public string $thumb_file_path;
|
||||
public string $thumb_mime;
|
||||
public bool $is_thumb_file_temporary;
|
||||
public int $width;
|
||||
public int $height;
|
||||
|
|
29
inc/Service/Media/DefaultImageMetadataReader.php
Normal file
29
inc/Service/Media/DefaultImageMetadataReader.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
namespace Vichan\Service\Media;
|
||||
|
||||
use Vichan\Data\ImageMetadataResult;
|
||||
use Vichan\Data\MagickMetadataReader;
|
||||
|
||||
|
||||
/**
|
||||
* Do not use this if you can.
|
||||
*
|
||||
* Some formats may contain no image or may contain multiple images. In these cases, getimagesize() might not be
|
||||
* able to properly determine the image size. getimagesize() will return zero for width and height in these cases.
|
||||
*
|
||||
* getimagesize() is agnostic of any image metadata.
|
||||
* If e.g. the Exif Orientation flag is set to a value which rotates the image by 90 or 270 degress, index 0 and 1
|
||||
* are swapped, i.e. the contain the height and width, respectively.
|
||||
*/
|
||||
class DefaultImageMetadataReader implements ImageMetadataReader {
|
||||
public function getMetadata(string $file_path): ImageMetadataResult {
|
||||
$ret = \getimagesize($file_path, $info);
|
||||
if ($ret === false) {
|
||||
throw new \RuntimeException("Could not read image sizes of '$file_path'");
|
||||
}
|
||||
if ($ret[2] == \IMAGETYPE_UNKNOWN) {
|
||||
throw new \RuntimeException("Error '$file_path' is not an image");
|
||||
}
|
||||
return new ImageMetadataResult($ret[0], $ret[1], $ret['mime']);
|
||||
}
|
||||
}
|
|
@ -5,16 +5,18 @@ use Vichan\Data\ThumbGenerationResult;
|
|||
|
||||
|
||||
class FallbackThumbGenerator implements ThumbGenerator {
|
||||
private string $thumb_path;
|
||||
private string $thumb_width;
|
||||
private string $thumb_height;
|
||||
private string $path;
|
||||
private int $width;
|
||||
private int $height;
|
||||
private string $mime;
|
||||
|
||||
|
||||
public function __construct(ImageFormatReader $image_format_reader, string $default_thumb_path) {
|
||||
list($width, $height) = $image_format_reader->getSizes($default_thumb_path);
|
||||
$this->thumb_path = $default_thumb_path;
|
||||
$this->thumb_width = $width;
|
||||
$this->thumb_height = $height;
|
||||
public function __construct(ImageMetadataReader $image_metadate_reader, string $default_thumb_path) {
|
||||
$res = $image_metadate_reader->getMetadata($default_thumb_path);
|
||||
$this->path = $default_thumb_path;
|
||||
$this->width = $res->width;
|
||||
$this->height = $res->height;
|
||||
$this->mime = $res->mime;
|
||||
}
|
||||
|
||||
public function supportsMime(string $mime): bool {
|
||||
|
@ -23,15 +25,18 @@ class FallbackThumbGenerator implements ThumbGenerator {
|
|||
|
||||
public function generateThumb(
|
||||
string $source_file_path,
|
||||
string $source_file_mime,
|
||||
string $preferred_out_file_path,
|
||||
string $preferred_out_mime,
|
||||
int $max_width,
|
||||
int $max_height
|
||||
): ThumbGenerationResult {
|
||||
$res = new ThumbGenerationResult();
|
||||
$res->thumb_file_path = $this->thumb_path;
|
||||
$res->thumb_file_path = $this->path;
|
||||
$res->thumb_mime = $this->mime;
|
||||
$res->is_thumb_file_temporary = false;
|
||||
$res->width = \min($this->thumb_width, $max_width);
|
||||
$res->height = \min($this->thumb_height, $max_height);
|
||||
$res->width = \min($this->width, $max_width);
|
||||
$res->height = \min($this->height, $max_height);
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
namespace Vichan\Service\Media;
|
||||
|
||||
|
||||
class GdImageFormatReader implements ImageFormatReader {
|
||||
/**
|
||||
* getimagesize() is agnostic of any image metadata.
|
||||
* If e.g. the Exif Orientation flag is set to a value which rotates the image by 90 or 270 degress, index 0 and 1
|
||||
* are swapped, i.e. the contain the height and width, respectively.
|
||||
*/
|
||||
public function getSizes(string $file_path): array {
|
||||
$ret = \getimagesize($file_path);
|
||||
if ($ret === false) {
|
||||
throw new \RuntimeException("Could not read image sizes of '$file_path'");
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
132
inc/Service/Media/GdThumbGenerator.php
Normal file
132
inc/Service/Media/GdThumbGenerator.php
Normal file
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
namespace Vichan\Service\Media;
|
||||
|
||||
use Vichan\Data\ThumbGenerationResult;
|
||||
|
||||
use function Vichan\Functions\Fs\link_or_copy;
|
||||
|
||||
class GdThumbGenerator implements ThumbGenerator {
|
||||
private const PHP81 = PHP_MAJOR_VERSION >= 8 && PHP_MINOR_VERSION >= 1;
|
||||
|
||||
private const MIME_TO_EXT = [
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/png' => 'png',
|
||||
'image/gif' => 'gif',
|
||||
'image/webp' => 'webp',
|
||||
'image/bmp' => 'bmp',
|
||||
'image/avif' => 'avif'
|
||||
];
|
||||
|
||||
|
||||
private static function imageCreateFrom(string $file, string $mime): mixed {
|
||||
switch ($mime) {
|
||||
case 'image/jpeg':
|
||||
return imagecreatefromjpeg($file);
|
||||
case 'image/png':
|
||||
return imagecreatefrompng($file);
|
||||
case 'image/gif':
|
||||
return imagecreatefromgif($file);
|
||||
case 'image/webp':
|
||||
return imagecreatefromwbmp($file);
|
||||
case 'image/bmp':
|
||||
return imagecreatefrombmp($file);
|
||||
case 'image/avif':
|
||||
return self::PHP81 ? imagecreatefromavif($file) : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function imageSaveTo(mixed $gd, string $file, string $mime) {
|
||||
// Somebody should tune the quality and speed values...
|
||||
switch ($mime) {
|
||||
case 'image/jpeg':
|
||||
return imagejpeg($gd, $file, 50);
|
||||
case 'image/png':
|
||||
return imagepng($gd, $file, 7);
|
||||
case 'image/gif':
|
||||
return imagegif($gd, $file);
|
||||
case 'image/webp':
|
||||
return imagewbmp($gd, $file, 50);
|
||||
case 'image/bmp':
|
||||
return imagebmp($gd, $file, true);
|
||||
case 'image/avif':
|
||||
return self::PHP81 ? imageavif($gd, $file, 30, 6) : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function createCanvas(string $mime, int $width, int $height) {
|
||||
$gd = \imagecreatetruecolor($width, $height);
|
||||
switch ($mime) {
|
||||
case 'image/png':
|
||||
imagecolortransparent($gd, imagecolorallocatealpha($gd, 0, 0, 0, 0));
|
||||
imagesavealpha($gd, true);
|
||||
imagealphablending($gd, false);
|
||||
break;
|
||||
case 'image/gif':
|
||||
\imagecolortransparent($gd, \imagecolorallocatealpha($gd, 0, 0, 0, 0));
|
||||
\imagesavealpha($gd, true);
|
||||
break;
|
||||
}
|
||||
return $gd;
|
||||
}
|
||||
|
||||
public function supportsMime(string $mime): bool {
|
||||
$info = \gd_info();
|
||||
return ($mime === 'image/jpeg' && $info['JPEG Support'])
|
||||
|| ($mime === 'image/png' && $info['PNG Support'])
|
||||
|| ($mime === 'image/gif' && $info['GIF Read Support'] && $info['GIF Create Support'])
|
||||
|| ($mime === 'image/webp' && $info['WebP Support'])
|
||||
|| $mime === 'image/bmp'
|
||||
|| ($mime === 'image/avif' && self::PHP81 && $info['AVIF Support']);
|
||||
}
|
||||
|
||||
public function generateThumb(
|
||||
string $source_file_path,
|
||||
string $source_file_mime,
|
||||
string $preferred_out_file_path,
|
||||
string $preferred_out_mime,
|
||||
int $max_width,
|
||||
int $max_height
|
||||
): ThumbGenerationResult {
|
||||
$gd = self::imageCreateFrom($source_file_path, $source_file_mime);
|
||||
if ($gd === false) {
|
||||
throw new \RuntimeException("Could not open '$source_file_path'");
|
||||
}
|
||||
|
||||
$width = \imagesx($gd);
|
||||
$height = \imagesy($gd);
|
||||
|
||||
if ($width <= $max_width && $height <= $max_height) {
|
||||
$out_path = $preferred_out_file_path . '.' . self::MIME_TO_EXT[$source_file_mime];
|
||||
|
||||
if (!link_or_copy($source_file_path, $out_path)) {
|
||||
throw new \RuntimeException("Could not link or copy '$source_file_path' to '$out_path'");
|
||||
}
|
||||
$res = new ThumbGenerationResult();
|
||||
$res->thumb_file_path = $out_path;
|
||||
$res->thumb_mime = $source_file_mime;
|
||||
$res->is_thumb_file_temporary = false;
|
||||
$res->width = $width;
|
||||
$res->height = $height;
|
||||
return $res;
|
||||
} else {
|
||||
$out_path = $preferred_out_file_path . '.' . self::MIME_TO_EXT[$preferred_out_mime];
|
||||
|
||||
$gd_other = self::createCanvas($preferred_out_mime, $max_width, $max_height);
|
||||
\imagecopyresampled($gd_other, $gd, 0, 0, 0, 0, $max_width, $max_height, $width, $height);
|
||||
|
||||
if (!self::imageSaveTo($gd_other, $out_path, $preferred_out_mime)) {
|
||||
throw new \RuntimeException("Could not create thumbnail file at '$out_path'");
|
||||
}
|
||||
|
||||
$res = new ThumbGenerationResult();
|
||||
$res->thumb_file_path = $out_path;
|
||||
$res->thumb_mime = $preferred_out_mime;
|
||||
$res->is_thumb_file_temporary = false;
|
||||
$res->width = $max_width;
|
||||
$res->height = $max_height;
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
namespace Vichan\Service\Media;
|
||||
|
||||
|
||||
interface ImageFormatReader {
|
||||
/**
|
||||
* @param string $file_path Image file path.
|
||||
* @return array An array with width and height.
|
||||
*/
|
||||
public function getSizes(string $file_path): array;
|
||||
}
|
12
inc/Service/Media/ImageMetadataReader.php
Normal file
12
inc/Service/Media/ImageMetadataReader.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
namespace Vichan\Service\Media;
|
||||
|
||||
use Vichan\Data\ImageMetadataResult;
|
||||
|
||||
|
||||
interface ImageMetadataReader {
|
||||
/**
|
||||
* @param string $file_path Image file path.
|
||||
*/
|
||||
public function getMetadata(string $file_path): ImageMetadataResult;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
namespace Vichan\Service\Media;
|
||||
|
||||
|
||||
class MagickImageFormatReader implements ImageFormatReader {
|
||||
private string $prefix;
|
||||
|
||||
public static function createImageMagickReader(): MagickImageFormatReader {
|
||||
return new self('');
|
||||
}
|
||||
|
||||
public static function createGraphicsMagickReader(): MagickImageFormatReader {
|
||||
return new self('gm ');
|
||||
}
|
||||
|
||||
private function __construct(string $prefix) {
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
public function getSizes(string $file_path): array {
|
||||
$arg = escapeshellarg("$file_path[0]");
|
||||
$ret_exec = shell_exec_error("{$this->prefix}identify -format \"%w %h\" $arg");
|
||||
|
||||
if (!\is_string($ret_exec)) {
|
||||
throw new \RuntimeException("Error while executing identify");
|
||||
}
|
||||
$ret_match = \preg_match('/^(\d+) (\d+)$/', $ret_exec, $m);
|
||||
if (!$ret_match) {
|
||||
throw new \RuntimeException("Could not parse identify output");
|
||||
}
|
||||
return [ $m[1], $m[2] ];
|
||||
}
|
||||
}
|
40
inc/Service/Media/MagickImageMetadataReader.php
Normal file
40
inc/Service/Media/MagickImageMetadataReader.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
namespace Vichan\Service\Media;
|
||||
|
||||
use Mimey\MimeTypes;
|
||||
use Vichan\Data\ImageMetadataResult;
|
||||
|
||||
|
||||
class MagickImageMetadataReader implements ImageMetadataReader {
|
||||
private string $prefix;
|
||||
private MimeTypes $mime_types;
|
||||
|
||||
public static function createImageMagickReader(MimeTypes $mime_types): MagickImageMetadataReader {
|
||||
return new self('', $mime_types);
|
||||
}
|
||||
|
||||
public static function createGraphicsMagickReader(MimeTypes $mime_types): MagickImageMetadataReader {
|
||||
return new self('gm ', $mime_types);
|
||||
}
|
||||
|
||||
private function __construct(string $prefix, MimeTypes $mime_types) {
|
||||
$this->prefix = $prefix;
|
||||
$this->mime_types = $mime_types;
|
||||
}
|
||||
|
||||
public function getMetadata(string $file_path): ImageMetadataResult {
|
||||
$arg = escapeshellarg("$file_path[0]");
|
||||
$ret_exec = shell_exec_error("{$this->prefix}identify -format \"%w %h %m\" $arg");
|
||||
|
||||
if (!\is_string($ret_exec)) {
|
||||
throw new \RuntimeException("Error while executing identify");
|
||||
}
|
||||
$ret_match = \preg_match('/^(\d+) (\d+) ([\w\d]+)$/', $ret_exec, $m);
|
||||
if (!$ret_match) {
|
||||
throw new \RuntimeException("Could not parse identify output");
|
||||
}
|
||||
|
||||
$mime = $this->mime_types->getMimeType($m[3]) ?? 'application/octet-stream';
|
||||
return new ImageMetadataResult($m[1], $m[2], $mime);
|
||||
}
|
||||
}
|
|
@ -11,10 +11,19 @@ interface ThumbGenerator {
|
|||
* Generates a thumbnail from the given file.
|
||||
*
|
||||
* @param string $source_file_path
|
||||
* @param string $source_file_mime
|
||||
* @param string $preferred_out_file_path
|
||||
* @param string $preferred_out_mime
|
||||
* @param int $max_width
|
||||
* @param int $max_height
|
||||
* @return ThumbGenerationResult
|
||||
*/
|
||||
public function generateThumb(string $source_file_path, string $preferred_out_file_path, int $max_width, int $max_height): ThumbGenerationResult;
|
||||
public function generateThumb(
|
||||
string $source_file_path,
|
||||
string $source_file_mime,
|
||||
string $preferred_out_file_path,
|
||||
string $preferred_out_mime,
|
||||
int $max_width,
|
||||
int $max_height
|
||||
): ThumbGenerationResult;
|
||||
}
|
||||
|
|
10
inc/functions/fs.php
Normal file
10
inc/functions/fs.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
namespace Vichan\Functions\Fs;
|
||||
|
||||
|
||||
function link_or_copy(string $from, string $to) {
|
||||
if (!\link($from, $to)) {
|
||||
return \copy($from, $to);
|
||||
}
|
||||
return true;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue