refactor: susun semula struktur folder — Laravel source ke src/
This commit is contained in:
219
vendor/intervention/image/src/Drivers/Gd/Analyzers/ResolutionAnalyzer.php
vendored
Normal file
219
vendor/intervention/image/src/Drivers/Gd/Analyzers/ResolutionAnalyzer.php
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Image\Drivers\Gd\Analyzers;
|
||||
|
||||
use Intervention\Image\Analyzers\ResolutionAnalyzer as GenericResolutionAnalyzer;
|
||||
use Intervention\Image\Exceptions\AnalyzerException;
|
||||
use Intervention\Image\Exceptions\StreamException;
|
||||
use Intervention\Image\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Image\Exceptions\MissingDependencyException;
|
||||
use Intervention\Image\Interfaces\ImageInterface;
|
||||
use Intervention\Image\Interfaces\OriginInterface;
|
||||
use Intervention\Image\Interfaces\SpecializedInterface;
|
||||
use Intervention\Image\Resolution;
|
||||
use Intervention\Image\Traits\CanBuildStream;
|
||||
use Throwable;
|
||||
|
||||
class ResolutionAnalyzer extends GenericResolutionAnalyzer implements SpecializedInterface
|
||||
{
|
||||
use CanBuildStream;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see AnalyzerInterface::analyze()
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws AnalyzerException
|
||||
*/
|
||||
public function analyze(ImageInterface $image): mixed
|
||||
{
|
||||
$result = imageresolution($image->core()->native());
|
||||
|
||||
if (!is_array($result)) {
|
||||
throw new AnalyzerException('Failed to read image resolution');
|
||||
}
|
||||
|
||||
// GD returns 96x96 as resolution by default even if the image has no resolution.
|
||||
// This is problematic because it is impossible to tell whether the image
|
||||
// really has this resolution or whether it just corresponds to the default value.
|
||||
//
|
||||
// If GD's default resolution is returned here and the resolution is still unchanged
|
||||
// we will make an attempt to find the resolution from origin.
|
||||
if ($this->isGdDefaultResolution($result) && $image->core()->meta()->get('resolutionChanged') !== true) {
|
||||
try {
|
||||
$alternativeResoltion = $this->readResolutionFromOrigin($image->origin());
|
||||
} catch (Throwable) {
|
||||
$alternativeResoltion = [96, 96];
|
||||
}
|
||||
|
||||
$result = $alternativeResoltion !== $result ? $alternativeResoltion : $result;
|
||||
}
|
||||
|
||||
return new Resolution(...$result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|float> $resolution
|
||||
*/
|
||||
private function isGdDefaultResolution(array $resolution): bool
|
||||
{
|
||||
return intval($resolution[0] ?? 0) === 96 && intval($resolution[1] ?? 0) === 96;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AnalyzerException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StreamException
|
||||
* @return array<float>
|
||||
*/
|
||||
private function readResolutionFromOrigin(OriginInterface $origin): array
|
||||
{
|
||||
$handle = self::buildStreamOrFail(file_get_contents($origin->filePath()));
|
||||
|
||||
try {
|
||||
try {
|
||||
return $this->resolutionFromJfifHeader($handle);
|
||||
} catch (Throwable) {
|
||||
# code ...
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->resolutionFromExifHeader($handle);
|
||||
} catch (Throwable) {
|
||||
# code ...
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->resolutionFromPngPhys($handle);
|
||||
} catch (Throwable) {
|
||||
# code ...
|
||||
}
|
||||
|
||||
throw new AnalyzerException('Unable to read resolution from path');
|
||||
} finally {
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @throws AnalyzerException
|
||||
* @return array<float>
|
||||
*/
|
||||
private function resolutionFromJfifHeader($handle): array
|
||||
{
|
||||
// read first 20 bytes
|
||||
rewind($handle);
|
||||
$header = fread($handle, 20);
|
||||
|
||||
// find the JFIF segment
|
||||
$offset = strpos($header, 'JFIF');
|
||||
if ($offset === false) {
|
||||
throw new AnalyzerException('Unable to read JFIF header');
|
||||
}
|
||||
|
||||
// read bytes at known offsets relative to JFIF
|
||||
$units = ord($header[$offset + 7]);
|
||||
$x = unpack('n', substr($header, $offset + 8, 2))[1];
|
||||
$y = unpack('n', substr($header, $offset + 10, 2))[1];
|
||||
|
||||
if ($units === 2) { // unit is dots per cm → convert to DPI
|
||||
return [round($x * 2.54), round($y * 2.54)];
|
||||
}
|
||||
|
||||
return [$x, $y]; // unit is DPI or no unit
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @throws MissingDependencyException
|
||||
* @throws AnalyzerException
|
||||
* @return array<float>
|
||||
*/
|
||||
private function resolutionFromExifHeader($handle): array
|
||||
{
|
||||
if (!function_exists('exif_read_data')) {
|
||||
throw new MissingDependencyException('Unable to read exif data');
|
||||
}
|
||||
|
||||
rewind($handle);
|
||||
$data = @exif_read_data($handle, null, true);
|
||||
|
||||
if ($data === false) {
|
||||
throw new AnalyzerException('Unable to read exif data');
|
||||
}
|
||||
|
||||
if (isset($data['XResolution']) && isset($data['YResolution'])) {
|
||||
$resolution = [$data['XResolution'], $data['YResolution']];
|
||||
}
|
||||
|
||||
if (isset($data['IFD0']) && isset($data['IFD0']['XResolution']) && isset($data['IFD0']['YResolution'])) {
|
||||
$resolution = [$data['IFD0']['XResolution'], $data['IFD0']['YResolution']];
|
||||
}
|
||||
|
||||
if (!isset($resolution)) {
|
||||
throw new AnalyzerException('Unable to read exif data');
|
||||
}
|
||||
|
||||
return array_map(function (mixed $value): int|float {
|
||||
if (strpos($value, '/') === false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$values = array_map(fn(string $value): int => intval($value), explode('/', $value));
|
||||
|
||||
if ($values[1] === 0) {
|
||||
throw new AnalyzerException('Unable to read exif data, division by zero');
|
||||
}
|
||||
|
||||
return $values[0] / $values[1];
|
||||
}, $resolution);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $handle
|
||||
* @throws AnalyzerException
|
||||
* @return array<float>
|
||||
*/
|
||||
private function resolutionFromPngPhys($handle): array
|
||||
{
|
||||
rewind($handle);
|
||||
$signature = fread($handle, 8);
|
||||
|
||||
// no PNG content
|
||||
if ($signature !== "\x89PNG\x0D\x0A\x1A\x0A") {
|
||||
throw new AnalyzerException('Input must be PNG format');
|
||||
}
|
||||
|
||||
$marker = '';
|
||||
|
||||
while (!feof($handle)) {
|
||||
$marker = strlen($marker) < 4 ? $marker . fread($handle, 1) : substr($marker, 1) . fread($handle, 1);
|
||||
|
||||
// find pHYs chunk
|
||||
if ($marker === 'pHYs') {
|
||||
// find length
|
||||
fseek($handle, -8, SEEK_CUR);
|
||||
$length = fread($handle, 4);
|
||||
$length = unpack('N', $length)[1];
|
||||
fseek($handle, 4, SEEK_CUR);
|
||||
|
||||
// read data
|
||||
$data = fread($handle, $length);
|
||||
|
||||
$x = unpack('N', substr($data, 0, 4))[1];
|
||||
$y = unpack('N', substr($data, 4, 4))[1];
|
||||
|
||||
return [
|
||||
round($x * .0254),
|
||||
round($y * .0254),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [0, 0];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user