1<?php
2
3namespace dokuwiki\plugin\gallery\classes;
4
5/**
6 * Formats the gallery
7 *
8 * This is the most basic implementation. It simply adds linked thumbnails to the page. It will not look
9 * good, but will work with any renderer. Specialized formatters can be created for each renderer to make
10 * use of their special features.
11 */
12class BasicFormatter
13{
14    /** @var Options */
15    protected $options;
16    /** @var \Doku_Renderer */
17    protected $renderer;
18
19    /**
20     * Create a new Gallery formatter
21     *
22     * @param \Doku_Renderer $renderer
23     * @param Options $options
24     */
25    public function __construct(\Doku_Renderer $renderer, Options $options)
26    {
27        $this->options = $options;
28        $this->renderer = $renderer;
29    }
30
31    /**
32     * Render the whole Gallery
33     *
34     * @param AbstractGallery $gallery
35     * @return void
36     */
37    public function render(AbstractGallery $gallery)
38    {
39        $images = $gallery->getImages();
40        foreach ($images as $image) {
41            $this->renderImage($image);
42        }
43    }
44
45    /**
46     * Render a single thumbnail image in the gallery
47     *
48     * @param Image $image
49     * @return void
50     */
51    protected function renderImage(Image $image)
52    {
53        [$w, $h] = $this->getThumbnailSize($image);
54        $link = $image->getDetaillink() ?: $image->getSrc();
55
56        $imgdata = [
57            'src' => $image->getSrc(),
58            'title' => $image->getTitle(),
59            'align' => '',
60            'width' => $w,
61            'height' => $h,
62            'cache' => ''
63        ];
64
65        if ($image->isExternal()) {
66            $this->renderer->externallink($link, $imgdata);
67        } else {
68            $this->renderer->internalmedia(":$link", $imgdata); // prefix with : to ensure absolute src
69        }
70    }
71
72
73    // region Utilities
74
75    /**
76     * Calculate the thumbnail size
77     *
78     * @param Image $image
79     * @param int|float $retina The retina scaling factor
80     * @return array
81     */
82    protected function getThumbnailSize(Image $image, $retina = 1)
83    {
84        $thumbW = $this->options->thumbnailWidth * $retina;
85        $thumbH = $this->options->thumbnailHeight * $retina;
86
87        // if image size is unknown, use the configured thumbnail size
88        if (!$image->getWidth() || !$image->getHeight()) {
89            return [$thumbW, $thumbH];
90        }
91
92        // avoid upscaling
93        if (
94            $image->getWidth() < $thumbW &&
95            $image->getHeight() < $thumbH
96        ) {
97            return [$image->getWidth(), $image->getHeight()];
98        }
99
100        if (!$this->options->crop) {
101            [$thumbWidth, $thumbHeight] = $this->fitBoundingBox(
102                $image->getWidth(),
103                $image->getHeight(),
104                $thumbW,
105                $thumbH
106            );
107        } else {
108            $thumbWidth = $thumbW;
109            $thumbHeight = $thumbH;
110        }
111        return [$thumbWidth, $thumbHeight];
112    }
113
114
115    /**
116     * Calculate the size of a thumbnail to fit into a bounding box
117     *
118     * @param int $imgWidth
119     * @param int $imgHeight
120     * @param int $bBoxWidth
121     * @param int $bBoxHeight
122     * @return int[]
123     */
124    protected function fitBoundingBox($imgWidth, $imgHeight, $bBoxWidth, $bBoxHeight)
125    {
126        $scale = min($bBoxWidth / $imgWidth, $bBoxHeight / $imgHeight);
127
128        $width = round($imgWidth * $scale);
129        $height = round($imgHeight * $scale);
130
131        return [$width, $height];
132    }
133
134    // endregion
135}
136