xref: /template/strap/ComboStrap/FetcherRaster.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1*04fd306cSNickeau<?php
2*04fd306cSNickeau
3*04fd306cSNickeau
4*04fd306cSNickeaunamespace ComboStrap;
5*04fd306cSNickeau
6*04fd306cSNickeauuse ComboStrap\Web\Url;
7*04fd306cSNickeau
8*04fd306cSNickeau/**
9*04fd306cSNickeau *
10*04fd306cSNickeau * A Image Raster processing class that:
11*04fd306cSNickeau *   * takes as input:
12*04fd306cSNickeau *      * a {@link FetcherRaster::buildFromUrl() fetch URL}:
13*04fd306cSNickeau *          * from an HTTP request
14*04fd306cSNickeau *          * or {@link MediaMarkup::getFetchUrl() markup})
15*04fd306cSNickeau *      * or data via setter
16*04fd306cSNickeau *   * outputs:
17*04fd306cSNickeau *      * a {@link FetcherRaster::getFetchPath() raster image file} for:
18*04fd306cSNickeau *         * an HTTP response
19*04fd306cSNickeau *         * or further local processing
20*04fd306cSNickeau *      * or a {@link FetcherRaster::getFetchUrl() fetch url} to use in a {@link RasterImageLink img html tag}
21*04fd306cSNickeau *
22*04fd306cSNickeau */
23*04fd306cSNickeauclass FetcherRaster extends IFetcherLocalImage
24*04fd306cSNickeau{
25*04fd306cSNickeau
26*04fd306cSNickeau    use FetcherTraitWikiPath {
27*04fd306cSNickeau        setSourcePath as protected setOriginalPathTrait;
28*04fd306cSNickeau    }
29*04fd306cSNickeau
30*04fd306cSNickeau    const CANONICAL = "raster";
31*04fd306cSNickeau    const FAKE_LENGTH_FOR_BROKEN_IMAGES = 10;
32*04fd306cSNickeau
33*04fd306cSNickeau
34*04fd306cSNickeau    private int $imageWidth;
35*04fd306cSNickeau    private int $imageWeight;
36*04fd306cSNickeau
37*04fd306cSNickeau
38*04fd306cSNickeau    /**
39*04fd306cSNickeau     * @param string $imageId
40*04fd306cSNickeau     * @param null $rev
41*04fd306cSNickeau     * @return FetcherRaster
42*04fd306cSNickeau     * @throws ExceptionBadArgument
43*04fd306cSNickeau     * @throws ExceptionBadSyntax
44*04fd306cSNickeau     * @throws ExceptionNotExists
45*04fd306cSNickeau     */
46*04fd306cSNickeau    public static function createImageRasterFetchFromId(string $imageId, $rev = null): FetcherRaster
47*04fd306cSNickeau    {
48*04fd306cSNickeau        return FetcherRaster::createImageRasterFetchFromPath(WikiPath::createMediaPathFromId($imageId, $rev));
49*04fd306cSNickeau    }
50*04fd306cSNickeau
51*04fd306cSNickeau    /**
52*04fd306cSNickeau     * @param Path $path
53*04fd306cSNickeau     * @return FetcherRaster
54*04fd306cSNickeau     * @throws ExceptionBadArgument
55*04fd306cSNickeau     * @throws ExceptionBadSyntax
56*04fd306cSNickeau     * @throws ExceptionNotExists
57*04fd306cSNickeau     */
58*04fd306cSNickeau    public static function createImageRasterFetchFromPath(Path $path): FetcherRaster
59*04fd306cSNickeau    {
60*04fd306cSNickeau        $path = WikiPath::createFromPathObject($path);
61*04fd306cSNickeau        return self::createEmptyRaster()
62*04fd306cSNickeau            ->setSourcePath($path);
63*04fd306cSNickeau    }
64*04fd306cSNickeau
65*04fd306cSNickeau    public static function createEmptyRaster(): FetcherRaster
66*04fd306cSNickeau    {
67*04fd306cSNickeau        return new FetcherRaster();
68*04fd306cSNickeau    }
69*04fd306cSNickeau
70*04fd306cSNickeau    /**
71*04fd306cSNickeau     * @throws ExceptionBadArgument
72*04fd306cSNickeau     */
73*04fd306cSNickeau    public static function createRasterFromFetchUrl(Url $fetchUrl): FetcherRaster
74*04fd306cSNickeau    {
75*04fd306cSNickeau        $fetchImageRaster = self::createEmptyRaster();
76*04fd306cSNickeau        $fetchImageRaster->buildFromUrl($fetchUrl);
77*04fd306cSNickeau        return $fetchImageRaster;
78*04fd306cSNickeau    }
79*04fd306cSNickeau
80*04fd306cSNickeau
81*04fd306cSNickeau    /**
82*04fd306cSNickeau     * @return int - the width of the image from the file
83*04fd306cSNickeau     */
84*04fd306cSNickeau    public function getIntrinsicWidth(): int
85*04fd306cSNickeau    {
86*04fd306cSNickeau        return $this->imageWidth;
87*04fd306cSNickeau    }
88*04fd306cSNickeau
89*04fd306cSNickeau    public function getFetchUrl(Url $url = null): Url
90*04fd306cSNickeau    {
91*04fd306cSNickeau        /**
92*04fd306cSNickeau         *
93*04fd306cSNickeau         * Because {@link FetcherRaster} does not create the image itself
94*04fd306cSNickeau         * but dokuwiki does, we need to add the with and height dimension
95*04fd306cSNickeau         * if the ratio is asked
96*04fd306cSNickeau         *
97*04fd306cSNickeau         * Before all other parent requirement such as
98*04fd306cSNickeau         * ({@link FetcherImage::getTok()} uses them
99*04fd306cSNickeau         *
100*04fd306cSNickeau         * Note that we takes the target value
101*04fd306cSNickeau         * before setting them otherwise it will affect the calculcation
102*04fd306cSNickeau         * ie if we set the height and then calculatiing the target width, we will get
103*04fd306cSNickeau         * a mini difference
104*04fd306cSNickeau         *
105*04fd306cSNickeau         */
106*04fd306cSNickeau        try {
107*04fd306cSNickeau            $this->getRequestedAspectRatio();
108*04fd306cSNickeau            $targetHeight = $this->getTargetHeight();
109*04fd306cSNickeau            $targetWidth = $this->getTargetWidth();
110*04fd306cSNickeau            $this->setRequestedWidth($targetWidth);
111*04fd306cSNickeau            $this->setRequestedHeight($targetHeight);
112*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
113*04fd306cSNickeau            //
114*04fd306cSNickeau        }
115*04fd306cSNickeau
116*04fd306cSNickeau        $url = parent::getFetchUrl($url);
117*04fd306cSNickeau
118*04fd306cSNickeau        /**
119*04fd306cSNickeau         * Trait
120*04fd306cSNickeau         */
121*04fd306cSNickeau        $this->addLocalPathParametersToFetchUrl($url, self::$MEDIA_QUERY_PARAMETER);
122*04fd306cSNickeau
123*04fd306cSNickeau        return $url;
124*04fd306cSNickeau    }
125*04fd306cSNickeau
126*04fd306cSNickeau
127*04fd306cSNickeau    /**
128*04fd306cSNickeau     * @return int - the height of the image from the file
129*04fd306cSNickeau     */
130*04fd306cSNickeau    public function getIntrinsicHeight(): int
131*04fd306cSNickeau    {
132*04fd306cSNickeau        return $this->imageWeight;
133*04fd306cSNickeau    }
134*04fd306cSNickeau
135*04fd306cSNickeau    /**
136*04fd306cSNickeau     * We check the existence of the file at build time
137*04fd306cSNickeau     * because when we build the url, we make it at several breakpoints.
138*04fd306cSNickeau     * We therefore needs the intrinsic dimension (height and weight)
139*04fd306cSNickeau     *
140*04fd306cSNickeau     * @throws ExceptionBadSyntax - if the path is not valid image format
141*04fd306cSNickeau     */
142*04fd306cSNickeau    private
143*04fd306cSNickeau    function analyzeImageIfNeeded()
144*04fd306cSNickeau    {
145*04fd306cSNickeau
146*04fd306cSNickeau        if (!FileSystems::exists($this->getSourcePath())) {
147*04fd306cSNickeau            // The user may type a bad path
148*04fd306cSNickeau            // We don't throw as we want to be able to build
149*04fd306cSNickeau            LogUtility::warning("The path ({$this->getSourcePath()}) does not exists");
150*04fd306cSNickeau            // broken image in the browser does not have any dimension
151*04fd306cSNickeau            // todo: https://bitsofco.de/styling-broken-images/
152*04fd306cSNickeau            $this->imageWidth = self::FAKE_LENGTH_FOR_BROKEN_IMAGES;
153*04fd306cSNickeau            $this->imageWeight = self::FAKE_LENGTH_FOR_BROKEN_IMAGES;
154*04fd306cSNickeau            return;
155*04fd306cSNickeau        }
156*04fd306cSNickeau
157*04fd306cSNickeau        /**
158*04fd306cSNickeau         * Based on {@link media_image_preview_size()}
159*04fd306cSNickeau         * $dimensions = media_image_preview_size($this->id, '', false);
160*04fd306cSNickeau         */
161*04fd306cSNickeau        $path = $this->getSourcePath()->toLocalPath();
162*04fd306cSNickeau        $imageSize = getimagesize($path->toAbsolutePath()->toAbsoluteId());
163*04fd306cSNickeau        if ($imageSize === false) {
164*04fd306cSNickeau            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);
165*04fd306cSNickeau        }
166*04fd306cSNickeau        $this->imageWidth = (int)$imageSize[0];
167*04fd306cSNickeau        if (empty($this->imageWidth)) {
168*04fd306cSNickeau            throw new ExceptionBadSyntax("We couldn't retrieve the width of the image ($this)", self::CANONICAL);
169*04fd306cSNickeau        }
170*04fd306cSNickeau        $this->imageWeight = (int)$imageSize[1];
171*04fd306cSNickeau        if (empty($this->imageWeight)) {
172*04fd306cSNickeau            throw new ExceptionBadSyntax("We couldn't retrieve the height of the image ($this)", self::CANONICAL);
173*04fd306cSNickeau        }
174*04fd306cSNickeau
175*04fd306cSNickeau    }
176*04fd306cSNickeau
177*04fd306cSNickeau
178*04fd306cSNickeau    /**
179*04fd306cSNickeau     * We overwrite the {@link FetcherTraitImage::getRequestedWidth()}
180*04fd306cSNickeau     * because we don't scale up for raster image
181*04fd306cSNickeau     * to not lose quality.
182*04fd306cSNickeau     *
183*04fd306cSNickeau     * @return int
184*04fd306cSNickeau     * @throws ExceptionNotFound
185*04fd306cSNickeau     */
186*04fd306cSNickeau    public
187*04fd306cSNickeau    function getRequestedWidth(): int
188*04fd306cSNickeau    {
189*04fd306cSNickeau
190*04fd306cSNickeau        /**
191*04fd306cSNickeau         * Test, requested width should not be bigger than the media Height
192*04fd306cSNickeau         * If this is the case, we return the media width
193*04fd306cSNickeau         */
194*04fd306cSNickeau        $requestedWidth = parent::getRequestedWidth();
195*04fd306cSNickeau
196*04fd306cSNickeau        /**
197*04fd306cSNickeau         * A width was requested
198*04fd306cSNickeau         */
199*04fd306cSNickeau        $mediaWidth = $this->getIntrinsicWidth();
200*04fd306cSNickeau        if ($requestedWidth > $mediaWidth) {
201*04fd306cSNickeau            global $ID;
202*04fd306cSNickeau            if ($ID !== "wiki:syntax") {
203*04fd306cSNickeau                // There is a bug in the wiki syntax page
204*04fd306cSNickeau                // {{wiki:dokuwiki-128.png?200x50}}
205*04fd306cSNickeau                // https://forum.dokuwiki.org/d/19313-bugtypo-how-to-make-a-request-to-change-the-syntax-page-on-dokuwikii
206*04fd306cSNickeau                LogUtility::warning("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);
207*04fd306cSNickeau            }
208*04fd306cSNickeau            return $mediaWidth;
209*04fd306cSNickeau        }
210*04fd306cSNickeau
211*04fd306cSNickeau        return $requestedWidth;
212*04fd306cSNickeau
213*04fd306cSNickeau    }
214*04fd306cSNickeau
215*04fd306cSNickeau
216*04fd306cSNickeau    /**
217*04fd306cSNickeau     *
218*04fd306cSNickeau     */
219*04fd306cSNickeau    public
220*04fd306cSNickeau    function getTargetHeight(): int
221*04fd306cSNickeau    {
222*04fd306cSNickeau
223*04fd306cSNickeau        try {
224*04fd306cSNickeau            $requestedHeight = $this->getRequestedHeight();
225*04fd306cSNickeau
226*04fd306cSNickeau            // it should not be bigger than the media Height
227*04fd306cSNickeau            $mediaHeight = $this->getIntrinsicHeight();
228*04fd306cSNickeau            if ($requestedHeight > $mediaHeight) {
229*04fd306cSNickeau                LogUtility::warning("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);
230*04fd306cSNickeau                return $mediaHeight;
231*04fd306cSNickeau            }
232*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
233*04fd306cSNickeau            // no request height
234*04fd306cSNickeau        }
235*04fd306cSNickeau        return parent::getTargetHeight();
236*04fd306cSNickeau
237*04fd306cSNickeau
238*04fd306cSNickeau    }
239*04fd306cSNickeau
240*04fd306cSNickeau
241*04fd306cSNickeau    function getFetchPath(): LocalPath
242*04fd306cSNickeau    {
243*04fd306cSNickeau        /**
244*04fd306cSNickeau         * In fetch.php
245*04fd306cSNickeau         * if($HEIGHT && $WIDTH) {
246*04fd306cSNickeau         *    $data['file'] = $FILE = media_crop_image($data['file'], $EXT, $WIDTH, $HEIGHT);
247*04fd306cSNickeau         * } else {
248*04fd306cSNickeau         *    $data['file'] = $FILE = media_resize_image($data['file'], $EXT, $WIDTH, $HEIGHT);
249*04fd306cSNickeau         * }
250*04fd306cSNickeau         */
251*04fd306cSNickeau        throw new ExceptionRuntime("Fetch Raster image is not yet implemented");
252*04fd306cSNickeau    }
253*04fd306cSNickeau
254*04fd306cSNickeau
255*04fd306cSNickeau    /**
256*04fd306cSNickeau     * @param TagAttributes $tagAttributes
257*04fd306cSNickeau     * @return FetcherRaster
258*04fd306cSNickeau     * @throws ExceptionBadArgument - if the path is not an image
259*04fd306cSNickeau     * @throws ExceptionBadSyntax - if the image is badly encoded
260*04fd306cSNickeau     * @throws ExceptionNotExists - if the image does not exists
261*04fd306cSNickeau     */
262*04fd306cSNickeau
263*04fd306cSNickeau    public function buildFromTagAttributes(TagAttributes $tagAttributes): FetcherImage
264*04fd306cSNickeau    {
265*04fd306cSNickeau
266*04fd306cSNickeau        parent::buildFromTagAttributes($tagAttributes);
267*04fd306cSNickeau        $this->buildOriginalPathFromTagAttributes($tagAttributes);
268*04fd306cSNickeau        $this->analyzeImageIfNeeded();
269*04fd306cSNickeau        return $this;
270*04fd306cSNickeau
271*04fd306cSNickeau    }
272*04fd306cSNickeau
273*04fd306cSNickeau    /**
274*04fd306cSNickeau     * @throws ExceptionBadSyntax - if the file is badly encoded
275*04fd306cSNickeau     * @throws ExceptionNotExists - if the file does not exists
276*04fd306cSNickeau     */
277*04fd306cSNickeau    public function setSourcePath(WikiPath $path): FetcherRaster
278*04fd306cSNickeau    {
279*04fd306cSNickeau        $this->setOriginalPathTrait($path);
280*04fd306cSNickeau        $this->analyzeImageIfNeeded();
281*04fd306cSNickeau        return $this;
282*04fd306cSNickeau    }
283*04fd306cSNickeau
284*04fd306cSNickeau
285*04fd306cSNickeau    public function getFetcherName(): string
286*04fd306cSNickeau    {
287*04fd306cSNickeau        return self::CANONICAL;
288*04fd306cSNickeau    }
289*04fd306cSNickeau
290*04fd306cSNickeau    public function __toString()
291*04fd306cSNickeau    {
292*04fd306cSNickeau        return $this->getSourcePath()->__toString();
293*04fd306cSNickeau    }
294*04fd306cSNickeau
295*04fd306cSNickeau
296*04fd306cSNickeau}
297