1<?php
2
3
4namespace ComboStrap;
5
6
7class ImageRaster extends Image
8{
9
10    const CANONICAL = "raster";
11
12
13    public function __construct($path, $attributes = null)
14    {
15        parent::__construct($path, $attributes);
16        $this->getAttributes()->setLogicalTag(self::CANONICAL);
17    }
18
19    private $imageWidth = null;
20    /**
21     * @var int
22     */
23    private $imageWeight = null;
24    /**
25     * See {@link image_type_to_mime_type}
26     * @var int
27     */
28    private $imageType;
29    private $wasAnalyzed = false;
30
31
32    /**
33     * @var mixed - the mime from the {@link RasterImageLink::analyzeImageIfNeeded()}
34     */
35    private $mime;
36
37    /**
38     * @return int - the width of the image from the file
39     * @throws ExceptionCombo
40     */
41    public function getIntrinsicWidth(): int
42    {
43        $this->analyzeImageIfNeeded();
44        return $this->imageWidth;
45    }
46
47    /**
48     * @return int - the height of the image from the file
49     * @throws ExceptionCombo
50     */
51    public function getIntrinsicHeight(): int
52    {
53        $this->analyzeImageIfNeeded();
54        return $this->imageWeight;
55    }
56
57    /**
58     * @throws ExceptionCombo
59     */
60    private
61    function analyzeImageIfNeeded()
62    {
63
64        if (!$this->wasAnalyzed) {
65
66            if ($this->exists()) {
67
68                /**
69                 * Based on {@link media_image_preview_size()}
70                 * $dimensions = media_image_preview_size($this->id, '', false);
71                 */
72                $imageInfo = array();
73                $path = $this->getPath();
74                if ($path instanceof DokuPath) {
75                    $path = $path->toLocalPath();
76                }
77                $imageSize = getimagesize($path->toAbsolutePath()->toString(), $imageInfo);
78                if ($imageSize === false) {
79                    throw new ExceptionCombo("We couldn't retrieve the type and dimensions of the image ($this). The image format seems to be not supported.", self::CANONICAL);
80                }
81                $this->imageWidth = (int)$imageSize[0];
82                if (empty($this->imageWidth)) {
83                    throw new ExceptionCombo("We couldn't retrieve the width of the image ($this)", self::CANONICAL);
84                }
85                $this->imageWeight = (int)$imageSize[1];
86                if (empty($this->imageWeight)) {
87                    throw new ExceptionCombo("We couldn't retrieve the height of the image ($this)", self::CANONICAL);
88                }
89                $this->imageType = (int)$imageSize[2];
90                $this->mime = $imageSize[3];
91
92            }
93        }
94        $this->wasAnalyzed = true;
95    }
96
97
98    public function getUrl(string $ampersand = DokuwikiUrl::AMPERSAND_URL_ENCODED_FOR_HTML)
99    {
100        return $this->getUrlAtBreakpoint($ampersand);
101    }
102
103    /**
104     * In the HTML attribute srcset (not in the img src), if we set,
105     * ```
106     * http://nico.lan/_media/docs/metadata/metadata_manager.png?w=355&amp;h=176&amp;tseed=1636624852&amp;tok=af396a 355w
107     * ```
108     * the request is encoded by the browser one more time and the server gets understand
109     *   * `&amp;&amp;h  =   176`
110     *   * php create therefore the property
111     *      * `&amp;h  =   176`
112     *      * and note `h = 176`
113     *
114     * @param $breakpoint
115     * @return false|string|null
116     */
117    public function getUrlForSrcSetAtBreakpoint($breakpoint)
118    {
119        if (PluginUtility::isTest()) {
120            // The test library does not support ampersand character
121            return $this->getUrlAtBreakpoint(DokuwikiUrl::AMPERSAND_URL_ENCODED_FOR_HTML, $breakpoint);
122        }
123        return $this->getUrlAtBreakpoint(DokuwikiUrl::AMPERSAND_CHARACTER, $breakpoint);
124    }
125
126    /**
127     * @param string $ampersand - do we encode & or not (in css, you do not, in html, you do)
128     * @param int|null $breakpointWidth - the breakpoint width - use for responsive image
129     * @return string|null
130     */
131    public function getUrlAtBreakpoint(string $ampersand = DokuwikiUrl::AMPERSAND_URL_ENCODED_FOR_HTML, int $breakpointWidth = null)
132    {
133
134        /**
135         * Default
136         */
137        if ($breakpointWidth == null) {
138            $breakpointWidth = $this->getTargetWidth();
139        }
140
141        if (!$this->exists()) {
142            LogUtility::msg("The image ($this) does not exist, you can't ask the URL", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
143            return false;
144        }
145
146        /**
147         * Link attribute
148         */
149        $att = array();
150
151        /**
152         * The image ratio is fixed
153         * Width is driving the computation
154         */
155        // Height for the given width
156        $breakpointHeight = $this->getBreakpointHeight($breakpointWidth);
157
158        /**
159         * If the request is not the original image
160         * and not cropped, add the width and height
161         */
162        if ($breakpointWidth != null &&
163            (
164                $breakpointWidth < $this->getIntrinsicWidth()
165                ||
166                $breakpointHeight < $this->getIntrinsicHeight()
167            )) {
168
169            $att['w'] = $breakpointWidth;
170
171            if (!empty($breakpointHeight)) {
172                $att['h'] = $breakpointHeight;
173                $this->checkLogicalRatioAgainstTargetRatio($breakpointWidth, $breakpointHeight);
174            }
175
176        }
177
178        if (!empty($this->getCache())) {
179            $att[CacheMedia::CACHE_KEY] = $this->getCache();
180        }
181
182        /**
183         * Smart Cache
184         */
185        $this->addCacheBusterToQueryParameters($att);
186
187        $direct = true;
188
189        if ($this->getPath() === null) {
190            LogUtility::msg("The Url of a image not in the media library is not yet supported", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
191            return "";
192        }
193        return ml($this->getPath()->getDokuwikiId(), $att, $direct, $ampersand, true);
194
195
196    }
197
198    public
199    function getAbsoluteUrl()
200    {
201
202        return $this->getUrl();
203
204    }
205
206    /**
207     * We overwrite the {@link Image::getTargetWidth()}
208     * because we don't scale up for raster image
209     * to not lose quality.
210     *
211     * @return int
212     * @throws ExceptionCombo
213     */
214    public
215    function getTargetWidth(): int
216    {
217
218        $requestedWidth = $this->getRequestedWidth();
219
220        /**
221         * May be 0 (ie empty)
222         */
223        if (!empty($requestedWidth)) {
224            // it should not be bigger than the media Height
225            $mediaWidth = $this->getIntrinsicWidth();
226            if (!empty($mediaWidth)) {
227                if ($requestedWidth > $mediaWidth) {
228                    global $ID;
229                    if ($ID != "wiki:syntax") {
230                        // There is a bug in the wiki syntax page
231                        // {{wiki:dokuwiki-128.png?200x50}}
232                        // https://forum.dokuwiki.org/d/19313-bugtypo-how-to-make-a-request-to-change-the-syntax-page-on-dokuwikii
233                        LogUtility::msg("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)", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
234                    }
235                    $requestedWidth = $mediaWidth;
236                }
237            }
238            return $requestedWidth;
239        }
240
241        return parent::getTargetWidth();
242    }
243
244    /**
245     * @throws ExceptionCombo
246     */
247    public function getTargetHeight(): int
248    {
249
250        $requestedHeight = $this->getRequestedHeight();
251        if (!empty($requestedHeight)) {
252            // it should not be bigger than the media Height
253            $mediaHeight = $this->getIntrinsicHeight();
254            if (!empty($mediaHeight)) {
255                if ($requestedHeight > $mediaHeight) {
256                    LogUtility::msg("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)", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
257                    return $mediaHeight;
258                }
259            }
260        }
261
262        return parent::getTargetHeight();
263    }
264
265
266}
267