Вход Регистрация
Файл: vendor/intervention/image/src/Drivers/Gd/Analyzers/ResolutionAnalyzer.php
Строк: 323
<?php

declare(strict_types=1);

namespace 
InterventionImageDriversGdAnalyzers;

use 
InterventionImageAnalyzersResolutionAnalyzer as GenericResolutionAnalyzer;
use 
InterventionImageExceptionsAnalyzerException;
use 
InterventionImageExceptionsStreamException;
use 
InterventionImageExceptionsInvalidArgumentException;
use 
InterventionImageExceptionsMissingDependencyException;
use 
InterventionImageInterfacesImageInterface;
use 
InterventionImageInterfacesOriginInterface;
use 
InterventionImageInterfacesSpecializedInterface;
use 
InterventionImageResolution;
use 
InterventionImageTraitsCanBuildStream;
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 = [9696];
            }

            
$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($handle20);

        
// 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 82))[1];
        
$y unpack('n'substr($header$offset 102))[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($handlenulltrue);

        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($handle8);

        
// no PNG content
        
if ($signature !== "x89PNGx0Dx0Ax1Ax0A") {
            throw new 
AnalyzerException('Input must be PNG format');
        }

        
$marker '';

        while (!
feof($handle)) {
            
$marker strlen($marker) < $marker fread($handle1) : substr($marker1) . fread($handle1);

            
// find pHYs chunk
            
if ($marker === 'pHYs') {
                
// find length
                
fseek($handle, -8SEEK_CUR);
                
$length fread($handle4);
                
$length unpack('N'$length)[1];
                
fseek($handle4SEEK_CUR);

                
// read data
                
$data fread($handle$length);

                
$x unpack('N'substr($data04))[1];
                
$y unpack('N'substr($data44))[1];
                
$unit ord(substr($data81));

                
// unit=1 means pixels per metre → convert to DPI
                // unit=0 means unknown unit (just a ratio) → return raw values
                
if ($unit === 1) {
                    return [
                        
round($x .0254),
                        
round($y .0254),
                    ];
                }

                return [
$x$y];
            }
        }

        return [
00];
    }
}
Онлайн: 0
Реклама