xref: /template/strap/ComboStrap/FetcherRaster.php (revision 66a68f7f5dd4cdeb896f012747a85766b740e408)
104fd306cSNickeau<?php
204fd306cSNickeau
304fd306cSNickeau
404fd306cSNickeaunamespace ComboStrap;
504fd306cSNickeau
604fd306cSNickeauuse ComboStrap\Web\Url;
704fd306cSNickeau
804fd306cSNickeau/**
904fd306cSNickeau *
1004fd306cSNickeau * A Image Raster processing class that:
1104fd306cSNickeau *   * takes as input:
1204fd306cSNickeau *      * a {@link FetcherRaster::buildFromUrl() fetch URL}:
1304fd306cSNickeau *          * from an HTTP request
1404fd306cSNickeau *          * or {@link MediaMarkup::getFetchUrl() markup})
1504fd306cSNickeau *      * or data via setter
1604fd306cSNickeau *   * outputs:
1704fd306cSNickeau *      * a {@link FetcherRaster::getFetchPath() raster image file} for:
1804fd306cSNickeau *         * an HTTP response
1904fd306cSNickeau *         * or further local processing
2004fd306cSNickeau *      * or a {@link FetcherRaster::getFetchUrl() fetch url} to use in a {@link RasterImageLink img html tag}
2104fd306cSNickeau *
2204fd306cSNickeau */
2304fd306cSNickeauclass FetcherRaster extends IFetcherLocalImage
2404fd306cSNickeau{
2504fd306cSNickeau
2604fd306cSNickeau    use FetcherTraitWikiPath {
2704fd306cSNickeau        setSourcePath as protected setOriginalPathTrait;
2804fd306cSNickeau    }
2904fd306cSNickeau
3004fd306cSNickeau    const CANONICAL = "raster";
3104fd306cSNickeau    const FAKE_LENGTH_FOR_BROKEN_IMAGES = 10;
3204fd306cSNickeau
3304fd306cSNickeau
3404fd306cSNickeau    private int $imageWidth;
3504fd306cSNickeau    private int $imageWeight;
3604fd306cSNickeau
3704fd306cSNickeau
3804fd306cSNickeau    /**
3904fd306cSNickeau     * @param string $imageId
4004fd306cSNickeau     * @param null $rev
4104fd306cSNickeau     * @return FetcherRaster
4204fd306cSNickeau     * @throws ExceptionBadArgument
4304fd306cSNickeau     * @throws ExceptionBadSyntax
4404fd306cSNickeau     * @throws ExceptionNotExists
4504fd306cSNickeau     */
4604fd306cSNickeau    public static function createImageRasterFetchFromId(string $imageId, $rev = null): FetcherRaster
4704fd306cSNickeau    {
4804fd306cSNickeau        return FetcherRaster::createImageRasterFetchFromPath(WikiPath::createMediaPathFromId($imageId, $rev));
4904fd306cSNickeau    }
5004fd306cSNickeau
5104fd306cSNickeau    /**
5204fd306cSNickeau     * @param Path $path
5304fd306cSNickeau     * @return FetcherRaster
5404fd306cSNickeau     * @throws ExceptionBadArgument
5504fd306cSNickeau     * @throws ExceptionBadSyntax
5604fd306cSNickeau     * @throws ExceptionNotExists
5704fd306cSNickeau     */
5804fd306cSNickeau    public static function createImageRasterFetchFromPath(Path $path): FetcherRaster
5904fd306cSNickeau    {
6004fd306cSNickeau        $path = WikiPath::createFromPathObject($path);
6104fd306cSNickeau        return self::createEmptyRaster()
6204fd306cSNickeau            ->setSourcePath($path);
6304fd306cSNickeau    }
6404fd306cSNickeau
6504fd306cSNickeau    public static function createEmptyRaster(): FetcherRaster
6604fd306cSNickeau    {
6704fd306cSNickeau        return new FetcherRaster();
6804fd306cSNickeau    }
6904fd306cSNickeau
7004fd306cSNickeau    /**
7104fd306cSNickeau     * @throws ExceptionBadArgument
7204fd306cSNickeau     */
7304fd306cSNickeau    public static function createRasterFromFetchUrl(Url $fetchUrl): FetcherRaster
7404fd306cSNickeau    {
7504fd306cSNickeau        $fetchImageRaster = self::createEmptyRaster();
7604fd306cSNickeau        $fetchImageRaster->buildFromUrl($fetchUrl);
7704fd306cSNickeau        return $fetchImageRaster;
7804fd306cSNickeau    }
7904fd306cSNickeau
8004fd306cSNickeau
8104fd306cSNickeau    /**
8204fd306cSNickeau     * @return int - the width of the image from the file
8304fd306cSNickeau     */
8404fd306cSNickeau    public function getIntrinsicWidth(): int
8504fd306cSNickeau    {
8604fd306cSNickeau        return $this->imageWidth;
8704fd306cSNickeau    }
8804fd306cSNickeau
8904fd306cSNickeau    public function getFetchUrl(Url $url = null): Url
9004fd306cSNickeau    {
9104fd306cSNickeau        /**
9204fd306cSNickeau         *
9304fd306cSNickeau         * Because {@link FetcherRaster} does not create the image itself
9404fd306cSNickeau         * but dokuwiki does, we need to add the with and height dimension
9504fd306cSNickeau         * if the ratio is asked
9604fd306cSNickeau         *
9704fd306cSNickeau         * Before all other parent requirement such as
9804fd306cSNickeau         * ({@link FetcherImage::getTok()} uses them
9904fd306cSNickeau         *
10004fd306cSNickeau         * Note that we takes the target value
10104fd306cSNickeau         * before setting them otherwise it will affect the calculcation
10204fd306cSNickeau         * ie if we set the height and then calculatiing the target width, we will get
10304fd306cSNickeau         * a mini difference
10404fd306cSNickeau         *
10504fd306cSNickeau         */
10604fd306cSNickeau        try {
10704fd306cSNickeau            $this->getRequestedAspectRatio();
10804fd306cSNickeau            $targetHeight = $this->getTargetHeight();
10904fd306cSNickeau            $targetWidth = $this->getTargetWidth();
11004fd306cSNickeau            $this->setRequestedWidth($targetWidth);
11104fd306cSNickeau            $this->setRequestedHeight($targetHeight);
11204fd306cSNickeau        } catch (ExceptionNotFound $e) {
11304fd306cSNickeau            //
11404fd306cSNickeau        }
11504fd306cSNickeau
11604fd306cSNickeau        $url = parent::getFetchUrl($url);
11704fd306cSNickeau
11804fd306cSNickeau        /**
11904fd306cSNickeau         * Trait
12004fd306cSNickeau         */
12104fd306cSNickeau        $this->addLocalPathParametersToFetchUrl($url, self::$MEDIA_QUERY_PARAMETER);
12204fd306cSNickeau
12304fd306cSNickeau        return $url;
12404fd306cSNickeau    }
12504fd306cSNickeau
12604fd306cSNickeau
12704fd306cSNickeau    /**
12804fd306cSNickeau     * @return int - the height of the image from the file
12904fd306cSNickeau     */
13004fd306cSNickeau    public function getIntrinsicHeight(): int
13104fd306cSNickeau    {
13204fd306cSNickeau        return $this->imageWeight;
13304fd306cSNickeau    }
13404fd306cSNickeau
13504fd306cSNickeau    /**
13604fd306cSNickeau     * We check the existence of the file at build time
13704fd306cSNickeau     * because when we build the url, we make it at several breakpoints.
13804fd306cSNickeau     * We therefore needs the intrinsic dimension (height and weight)
13904fd306cSNickeau     *
14004fd306cSNickeau     * @throws ExceptionBadSyntax - if the path is not valid image format
14104fd306cSNickeau     */
14204fd306cSNickeau    private
14304fd306cSNickeau    function analyzeImageIfNeeded()
14404fd306cSNickeau    {
14504fd306cSNickeau
14604fd306cSNickeau        if (!FileSystems::exists($this->getSourcePath())) {
14704fd306cSNickeau            // The user may type a bad path
14804fd306cSNickeau            // We don't throw as we want to be able to build
14904fd306cSNickeau            LogUtility::warning("The path ({$this->getSourcePath()}) does not exists");
15004fd306cSNickeau            // broken image in the browser does not have any dimension
15104fd306cSNickeau            // todo: https://bitsofco.de/styling-broken-images/
15204fd306cSNickeau            $this->imageWidth = self::FAKE_LENGTH_FOR_BROKEN_IMAGES;
15304fd306cSNickeau            $this->imageWeight = self::FAKE_LENGTH_FOR_BROKEN_IMAGES;
15404fd306cSNickeau            return;
15504fd306cSNickeau        }
15604fd306cSNickeau
15704fd306cSNickeau        /**
15804fd306cSNickeau         * Based on {@link media_image_preview_size()}
15904fd306cSNickeau         * $dimensions = media_image_preview_size($this->id, '', false);
16004fd306cSNickeau         */
16104fd306cSNickeau        $path = $this->getSourcePath()->toLocalPath();
16204fd306cSNickeau        $imageSize = getimagesize($path->toAbsolutePath()->toAbsoluteId());
16304fd306cSNickeau        if ($imageSize === false) {
16404fd306cSNickeau            throw new ExceptionBadSyntax("We couldn't retrieve the type and dimensions of the image ($this). The image format seems to be not supported.", self::CANONICAL);
16504fd306cSNickeau        }
16604fd306cSNickeau        $this->imageWidth = (int)$imageSize[0];
16704fd306cSNickeau        if (empty($this->imageWidth)) {
16804fd306cSNickeau            throw new ExceptionBadSyntax("We couldn't retrieve the width of the image ($this)", self::CANONICAL);
16904fd306cSNickeau        }
17004fd306cSNickeau        $this->imageWeight = (int)$imageSize[1];
17104fd306cSNickeau        if (empty($this->imageWeight)) {
17204fd306cSNickeau            throw new ExceptionBadSyntax("We couldn't retrieve the height of the image ($this)", self::CANONICAL);
17304fd306cSNickeau        }
17404fd306cSNickeau
17504fd306cSNickeau    }
17604fd306cSNickeau
17704fd306cSNickeau
17804fd306cSNickeau    /**
17904fd306cSNickeau     * We overwrite the {@link FetcherTraitImage::getRequestedWidth()}
18004fd306cSNickeau     * because we don't scale up for raster image
18104fd306cSNickeau     * to not lose quality.
18204fd306cSNickeau     *
18304fd306cSNickeau     * @return int
18404fd306cSNickeau     * @throws ExceptionNotFound
18504fd306cSNickeau     */
18604fd306cSNickeau    public
18704fd306cSNickeau    function getRequestedWidth(): int
18804fd306cSNickeau    {
18904fd306cSNickeau
19004fd306cSNickeau        /**
19104fd306cSNickeau         * Test, requested width should not be bigger than the media Height
19204fd306cSNickeau         * If this is the case, we return the media width
19304fd306cSNickeau         */
19404fd306cSNickeau        $requestedWidth = parent::getRequestedWidth();
19504fd306cSNickeau
19604fd306cSNickeau        /**
19704fd306cSNickeau         * A width was requested
19804fd306cSNickeau         */
19904fd306cSNickeau        $mediaWidth = $this->getIntrinsicWidth();
20004fd306cSNickeau        if ($requestedWidth > $mediaWidth) {
20104fd306cSNickeau            global $ID;
20204fd306cSNickeau            if ($ID !== "wiki:syntax") {
203*66a68f7fSgerardnico                /**
204*66a68f7fSgerardnico                 * Info and not warning level because they fill the error log
205*66a68f7fSgerardnico                 * They don't really break anything and it's difficult
206*66a68f7fSgerardnico                 * to see when it's intended (ie there is no better image or not)
207*66a68f7fSgerardnico                 */
20804fd306cSNickeau                // There is a bug in the wiki syntax page
20904fd306cSNickeau                // {{wiki:dokuwiki-128.png?200x50}}
21004fd306cSNickeau                // https://forum.dokuwiki.org/d/19313-bugtypo-how-to-make-a-request-to-change-the-syntax-page-on-dokuwikii
211*66a68f7fSgerardnico                LogUtility::info("For the image ($this), the requested width of ($requestedWidth) can not be bigger than the intrinsic width of ($mediaWidth). The width was then set to its natural width ($mediaWidth)", self::CANONICAL);
21204fd306cSNickeau            }
21304fd306cSNickeau            return $mediaWidth;
21404fd306cSNickeau        }
21504fd306cSNickeau
21604fd306cSNickeau        return $requestedWidth;
21704fd306cSNickeau
21804fd306cSNickeau    }
21904fd306cSNickeau
22004fd306cSNickeau
22104fd306cSNickeau    /**
22204fd306cSNickeau     *
22304fd306cSNickeau     */
22404fd306cSNickeau    public
22504fd306cSNickeau    function getTargetHeight(): int
22604fd306cSNickeau    {
22704fd306cSNickeau
22804fd306cSNickeau        try {
22904fd306cSNickeau            $requestedHeight = $this->getRequestedHeight();
23004fd306cSNickeau
23104fd306cSNickeau            // it should not be bigger than the media Height
23204fd306cSNickeau            $mediaHeight = $this->getIntrinsicHeight();
23304fd306cSNickeau            if ($requestedHeight > $mediaHeight) {
234*66a68f7fSgerardnico                /**
235*66a68f7fSgerardnico                 * Info and not warning level because they fill the error log
236*66a68f7fSgerardnico                 * They don't really break anything and it's difficult
237*66a68f7fSgerardnico                 * to see when it's intended (ie there is no better image or not)
238*66a68f7fSgerardnico                 */
239*66a68f7fSgerardnico                LogUtility::info("For the image ($this), the requested height of ($requestedHeight) can not be bigger than the intrinsic height of ($mediaHeight). The height was then set to its natural height ($mediaHeight)", self::CANONICAL);
24004fd306cSNickeau                return $mediaHeight;
24104fd306cSNickeau            }
24204fd306cSNickeau        } catch (ExceptionNotFound $e) {
24304fd306cSNickeau            // no request height
24404fd306cSNickeau        }
24504fd306cSNickeau        return parent::getTargetHeight();
24604fd306cSNickeau
24704fd306cSNickeau
24804fd306cSNickeau    }
24904fd306cSNickeau
2503e9dc6bfSgerardnico    public function getTargetWidth(): int
2513e9dc6bfSgerardnico    {
2523e9dc6bfSgerardnico        $targetWidth = parent::getTargetWidth();
2533e9dc6bfSgerardnico        $intrinsicWidth = $this->getIntrinsicWidth();
2543e9dc6bfSgerardnico        if ($targetWidth > $intrinsicWidth) {
2553e9dc6bfSgerardnico            LogUtility::warning("For the image ($this), the calculated width of ($targetWidth) cannot be bigger than the intrinsic width of ($targetWidth). The requested width was then set to its natural width ($intrinsicWidth).", self::CANONICAL);
2563e9dc6bfSgerardnico            return $intrinsicWidth;
2573e9dc6bfSgerardnico        }
2583e9dc6bfSgerardnico        return $targetWidth;
2593e9dc6bfSgerardnico    }
2603e9dc6bfSgerardnico
26104fd306cSNickeau
26204fd306cSNickeau    function getFetchPath(): LocalPath
26304fd306cSNickeau    {
26404fd306cSNickeau        /**
26504fd306cSNickeau         * In fetch.php
26604fd306cSNickeau         * if($HEIGHT && $WIDTH) {
26704fd306cSNickeau         *    $data['file'] = $FILE = media_crop_image($data['file'], $EXT, $WIDTH, $HEIGHT);
26804fd306cSNickeau         * } else {
26904fd306cSNickeau         *    $data['file'] = $FILE = media_resize_image($data['file'], $EXT, $WIDTH, $HEIGHT);
27004fd306cSNickeau         * }
27104fd306cSNickeau         */
27204fd306cSNickeau        throw new ExceptionRuntime("Fetch Raster image is not yet implemented");
27304fd306cSNickeau    }
27404fd306cSNickeau
27504fd306cSNickeau
27604fd306cSNickeau    /**
27704fd306cSNickeau     * @param TagAttributes $tagAttributes
27804fd306cSNickeau     * @return FetcherRaster
27904fd306cSNickeau     * @throws ExceptionBadArgument - if the path is not an image
28004fd306cSNickeau     * @throws ExceptionBadSyntax - if the image is badly encoded
28104fd306cSNickeau     * @throws ExceptionNotExists - if the image does not exists
28204fd306cSNickeau     */
28304fd306cSNickeau
28404fd306cSNickeau    public function buildFromTagAttributes(TagAttributes $tagAttributes): FetcherImage
28504fd306cSNickeau    {
28604fd306cSNickeau
28704fd306cSNickeau        parent::buildFromTagAttributes($tagAttributes);
28804fd306cSNickeau        $this->buildOriginalPathFromTagAttributes($tagAttributes);
28904fd306cSNickeau        $this->analyzeImageIfNeeded();
29004fd306cSNickeau        return $this;
29104fd306cSNickeau
29204fd306cSNickeau    }
29304fd306cSNickeau
29404fd306cSNickeau    /**
29504fd306cSNickeau     * @throws ExceptionBadSyntax - if the file is badly encoded
29604fd306cSNickeau     * @throws ExceptionNotExists - if the file does not exists
29704fd306cSNickeau     */
29804fd306cSNickeau    public function setSourcePath(WikiPath $path): FetcherRaster
29904fd306cSNickeau    {
30004fd306cSNickeau        $this->setOriginalPathTrait($path);
30104fd306cSNickeau        $this->analyzeImageIfNeeded();
30204fd306cSNickeau        return $this;
30304fd306cSNickeau    }
30404fd306cSNickeau
30504fd306cSNickeau
30604fd306cSNickeau    public function getFetcherName(): string
30704fd306cSNickeau    {
30804fd306cSNickeau        return self::CANONICAL;
30904fd306cSNickeau    }
31004fd306cSNickeau
31104fd306cSNickeau    public function __toString()
31204fd306cSNickeau    {
31304fd306cSNickeau        return $this->getSourcePath()->__toString();
31404fd306cSNickeau    }
31504fd306cSNickeau
3163e9dc6bfSgerardnico    /**
3173e9dc6bfSgerardnico     * @return int
3183e9dc6bfSgerardnico     * @throws ExceptionNotFound
3193e9dc6bfSgerardnico     * We can upscale, we limit then the requested height to the internal size
3203e9dc6bfSgerardnico     */
3213e9dc6bfSgerardnico    public function getRequestedHeight(): int
3223e9dc6bfSgerardnico    {
3233e9dc6bfSgerardnico        $requestedHeight = parent::getRequestedHeight();
3243e9dc6bfSgerardnico        $intrinsicHeight = $this->getIntrinsicHeight();
3253e9dc6bfSgerardnico        if ($requestedHeight > $intrinsicHeight) {
3263e9dc6bfSgerardnico            LogUtility::warning("For the image ($this), the requested height of ($requestedHeight) can not be bigger than the intrinsic height of ($intrinsicHeight). The height was then set to its natural height ($intrinsicHeight)", self::CANONICAL);
3273e9dc6bfSgerardnico            return $intrinsicHeight;
3283e9dc6bfSgerardnico        }
3293e9dc6bfSgerardnico        return $requestedHeight;
3303e9dc6bfSgerardnico    }
3313e9dc6bfSgerardnico
33204fd306cSNickeau
33304fd306cSNickeau}
334