1<?php
2
3
4use ComboStrap\AnalyticsDocument;
5use ComboStrap\CallStack;
6use ComboStrap\Dimension;
7use ComboStrap\DokuPath;
8use ComboStrap\ExceptionCombo;
9use ComboStrap\ExceptionComboRuntime;
10use ComboStrap\Image;
11use ComboStrap\LogUtility;
12use ComboStrap\MediaLink;
13use ComboStrap\Mime;
14use ComboStrap\Page;
15use ComboStrap\PagePath;
16use ComboStrap\Path;
17use ComboStrap\PluginUtility;
18use ComboStrap\SvgDocument;
19use ComboStrap\TagAttributes;
20
21
22require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
23
24
25/**
26 * Set the cache of the bar
27 * Ie add the possibility to add a time
28 * over {@link \dokuwiki\Parsing\ParserMode\Nocache}
29 */
30class syntax_plugin_combo_pageimage extends DokuWiki_Syntax_Plugin
31{
32
33
34    const TAG = "pageimage";
35
36    const MARKUP = "page-image";
37
38
39    const CANONICAL = self::TAG;
40
41
42    function getType(): string
43    {
44        return 'formatting';
45    }
46
47    /**
48     * How Dokuwiki will add P element
49     *
50     *  * 'normal' - The plugin can be used inside paragraphs (inline)
51     *  * 'block'  - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs
52     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
53     *
54     * @see DokuWiki_Syntax_Plugin::getPType()
55     */
56    function getPType()
57    {
58        return 'normal';
59    }
60
61    function getAllowedTypes()
62    {
63        return array();
64    }
65
66    function getSort()
67    {
68        return 201;
69    }
70
71
72    function connectTo($mode)
73    {
74
75        $this->Lexer->addSpecialPattern(PluginUtility::getVoidElementTagPattern(self::MARKUP), $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
76
77    }
78
79
80    function handle($match, $state, $pos, Doku_Handler $handler): array
81    {
82
83        switch ($state) {
84
85
86            case DOKU_LEXER_SPECIAL :
87
88                /**
89                 * Because the pageimage can also be used
90                 * in a template
91                 *
92                 * The calculation are done in the {@link syntax_plugin_combo_pageimage::render render function}
93                 *
94                 */
95                $tagAttributes = TagAttributes::createFromTagMatch($match);
96                $callStack = CallStack::createFromHandler($handler);
97                $context = self::TAG;
98                $parent = $callStack->moveToParent();
99                if ($parent !== false) {
100                    $context = $parent->getTagName();
101                }
102
103
104                return array(
105                    PluginUtility::STATE => $state,
106                    PluginUtility::ATTRIBUTES => $tagAttributes->toCallStackArray(),
107                    PluginUtility::CONTEXT => $context
108                );
109
110
111        }
112        return array();
113
114    }
115
116    /**
117     * Render the output
118     * @param string $format
119     * @param Doku_Renderer $renderer
120     * @param array $data - what the function handle() return'ed
121     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
122     * @see DokuWiki_Syntax_Plugin::render()
123     *
124     *
125     */
126    function render($format, Doku_Renderer $renderer, $data): bool
127    {
128
129        switch ($format) {
130
131            case 'xhtml':
132
133                $tagAttributes = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]);
134                if (!$tagAttributes->hasAttribute(PagePath::PROPERTY_NAME)) {
135
136                    LogUtility::msg("The path is mandatory and was not found", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
137                    return false;
138                }
139
140                $path = $tagAttributes->getValueAndRemove(PagePath::PROPERTY_NAME);
141                DokuPath::addRootSeparatorIfNotPresent($path);
142
143                /**
144                 * Image selection
145                 */
146                $page = Page::createPageFromQualifiedPath($path);
147                $selectedPageImage = $page->getImage();
148                if ($selectedPageImage === null) {
149                    LogUtility::msg("No page image defined for the page ($path)", LogUtility::LVL_MSG_INFO, self::CANONICAL);
150                    return false;
151                }
152
153                /**
154                 * We select the best image for the ratio
155                 *
156                 */
157                $targetRatio = null;
158                if ($tagAttributes->hasComponentAttribute(Dimension::RATIO_ATTRIBUTE)) {
159                    $stringRatio = $tagAttributes->getValue(Dimension::RATIO_ATTRIBUTE);
160                    if (empty($stringRatio)) {
161
162                        LogUtility::msg("The ratio value is empty and was therefore not taken into account", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
163
164                    } else {
165
166                        $bestRatioDistance = 9999;
167
168                        $targetRatio = Dimension::convertTextualRatioToNumber($stringRatio);
169
170                        foreach ($page->getPageImagesOrDefault() as $pageImage) {
171                            $image = $pageImage->getImage();
172                            $ratioDistance = $targetRatio - $image->getIntrinsicAspectRatio();
173                            if ($ratioDistance < $bestRatioDistance) {
174                                $bestRatioDistance = $ratioDistance;
175                                $selectedPageImage = $image;
176                            }
177                        }
178
179
180                    }
181                }
182
183                /**
184                 * {@link Dimension::RATIO_ATTRIBUTE Ratio} is part of the request
185                 * because in svg it is the definition of the viewBox
186                 * The rendering function takes care of it
187                 * and it's also passed in the fetch url
188                 */
189
190
191                /**
192                 * Used as an illustration in a card
193                 * If the image is too small, we allow that it will stretch
194                 * to take the whole space
195                 */
196                if ($data[PluginUtility::CONTEXT] === syntax_plugin_combo_card::TAG) {
197                    $tagAttributes->addStyleDeclarationIfNotSet("max-width", "100%");
198                    $tagAttributes->addStyleDeclarationIfNotSet("max-height", "unset");
199                }
200
201                $tagAttributes->setComponentAttributeValue(TagAttributes::TYPE_KEY, SvgDocument::ILLUSTRATION_TYPE);
202
203
204                $mediaLink = MediaLink::createMediaLinkFromPath(
205                    $selectedPageImage->getPath(),
206                    $tagAttributes
207                );
208                $renderer->doc .= $mediaLink->renderMediaTag();
209
210                break;
211
212
213        }
214        // unsupported $mode
215        return false;
216    }
217
218
219}
220
221