xref: /plugin/dw2pdf/renderer.php (revision c4c95814504bb32ebf3f9b6a0e189c19779325fb)
1<?php
2
3// phpcs:disable: PSR1.Methods.CamelCapsMethodName.NotCamelCaps
4// phpcs:disable: PSR2.Methods.MethodDeclaration.Underscore
5
6/**
7 * DokuWiki Plugin dw2pdf (Renderer Component)
8 * Render xhtml suitable as input for mpdf library
9 *
10 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
11 * @author  Andreas Gohr <gohr@cosmocode.de>
12 */
13class renderer_plugin_dw2pdf extends Doku_Renderer_xhtml
14{
15    private $lastHeaderLevel = -1;
16    private $originalHeaderLevel = 0;
17    private $difference = 0;
18    private static $header_count = [];
19    private static $previous_level = 0;
20
21    /**
22     * Stores action instance
23     *
24     * @var action_plugin_dw2pdf
25     */
26    private $actioninstance;
27
28    /**
29     * load action plugin instance
30     */
31    public function __construct()
32    {
33        $this->actioninstance = plugin_load('action', 'dw2pdf');
34    }
35
36    public function document_start()
37    {
38        global $ID;
39
40        parent::document_start();
41
42        //ancher for rewritten links to included pages
43        $check = false;
44        $pid = sectionID($ID, $check);
45
46        $this->doc .= "<a name=\"{$pid}__\">";
47        $this->doc .= "</a>";
48
49        self::$header_count[1] = $this->actioninstance->getCurrentBookChapter();
50    }
51
52    /**
53     * Make available as XHTML replacement renderer
54     *
55     * @param $format
56     * @return bool
57     */
58    public function canRender($format)
59    {
60        if ($format == 'xhtml') {
61            return true;
62        }
63        return false;
64    }
65
66    /**
67     * Simplified header printing with PDF bookmarks
68     *
69     * @param string $text
70     * @param int $level from 1 (highest) to 6 (lowest)
71     * @param int $pos
72     */
73    public function header($text, $level, $pos, $returnonly = false)
74    {
75        if (!$text) {
76            return;
77        } //skip empty headlines
78        global $ID;
79
80        $hid = $this->_headerToLink($text, true);
81
82        //only add items within global configured levels (doesn't check the pdf toc settings)
83        $this->toc_additem($hid, $text, $level);
84
85        $check = false;
86        $pid = sectionID($ID, $check);
87        $hid = $pid . '__' . $hid;
88
89
90        // retrieve numbered headings option
91        $isnumberedheadings = $this->actioninstance->getExportConfig('headernumber');
92
93        $header_prefix = "";
94        if ($isnumberedheadings) {
95            if ($level > 0) {
96                if (self::$previous_level > $level) {
97                    for ($i = $level + 1; $i <= self::$previous_level; $i++) {
98                        self::$header_count[$i] = 0;
99                    }
100                }
101            }
102            self::$header_count[$level]++;
103
104            // $header_prefix = "";
105            for ($i = 1; $i <= $level; $i++) {
106                $header_prefix .= self::$header_count[$i] . ".";
107            }
108        }
109
110        // add PDF bookmark
111        $bookmark = '';
112        $maxbookmarklevel = $this->actioninstance->getExportConfig('maxbookmarks');
113        // 0: off, 1-6: show down to this level
114        if ($maxbookmarklevel && $maxbookmarklevel >= $level) {
115            $bookmarklevel = $this->calculateBookmarklevel($level);
116            $bookmark = sprintf(
117                '<bookmark content="%s %s" level="%d" />',
118                $header_prefix,
119                $this->_xmlEntities($text),
120                $bookmarklevel
121            );
122        }
123
124        // print header
125        $this->doc .= DOKU_LF . "<h$level>$bookmark";
126        $this->doc .= $header_prefix . "<a name=\"$hid\">";
127        $this->doc .= $this->_xmlEntities($text);
128        $this->doc .= "</a>";
129        $this->doc .= "</h$level>" . DOKU_LF;
130        self::$previous_level = $level;
131    }
132
133    /**
134     * Bookmark levels might increase maximal +1 per level.
135     * (note: levels start at 1, bookmarklevels at 0)
136     *
137     * @param int $level 1 (highest) to 6 (lowest)
138     * @return int
139     */
140    protected function calculateBookmarklevel($level)
141    {
142        if ($this->lastHeaderLevel == -1) {
143            $this->lastHeaderLevel = $level;
144        }
145        $step = $level - $this->lastHeaderLevel;
146        if ($step > 1) {
147            $this->difference += $step - 1;
148        }
149        if ($step < 0) {
150            $this->difference = min($this->difference, $level - $this->originalHeaderLevel);
151            $this->difference = max($this->difference, 0);
152        }
153
154        $bookmarklevel = $level - $this->difference;
155
156        if ($step > 1) {
157            $this->originalHeaderLevel = $bookmarklevel;
158        }
159
160        $this->lastHeaderLevel = $level;
161        return $bookmarklevel - 1; //zero indexed
162    }
163
164    /**
165     * Render a page local link
166     *
167     * // modified copy of parent function
168     *
169     * @param string $hash hash link identifier
170     * @param string $name name for the link
171     * @param bool $returnonly
172     * @return string|void
173     *
174     * @see Doku_Renderer_xhtml::locallink
175     */
176    public function locallink($hash, $name = null, $returnonly = false)
177    {
178        global $ID;
179        $name = $this->_getLinkTitle($name, $hash, $isImage);
180        $hash = $this->_headerToLink($hash);
181        $title = $ID . ' ↵';
182
183        $check = false;
184        $pid = sectionID($ID, $check);
185
186        $this->doc .= '<a href="#' . $pid . '__' . $hash . '" title="' . $title . '" class="wikilink1">';
187        $this->doc .= $name;
188        $this->doc .= '</a>';
189    }
190
191    /**
192     * Wrap centered media in a div to center it
193     *
194     * @param string $src media ID
195     * @param string $title descriptive text
196     * @param string $align left|center|right
197     * @param int $width width of media in pixel
198     * @param int $height height of media in pixel
199     * @param string $cache cache|recache|nocache
200     * @param bool $render should the media be embedded inline or just linked
201     * @return string
202     */
203    public function _media(
204        $src,
205        $title = null,
206        $align = null,
207        $width = null,
208        $height = null,
209        $cache = null,
210        $render = true
211    ) {
212
213        $out = '';
214        if ($align == 'center') {
215            $out .= '<div align="center" style="text-align: center">';
216        }
217
218        $out .= parent::_media($src, $title, $align, $width, $height, $cache, $render);
219
220        if ($align == 'center') {
221            $out .= '</div>';
222        }
223
224        return $out;
225    }
226
227    /**
228     * hover info makes no sense in PDFs, so drop acronyms
229     *
230     * @param string $acronym
231     */
232    public function acronym($acronym)
233    {
234        $this->doc .= $this->_xmlEntities($acronym);
235    }
236
237    /**
238     * reformat links if needed
239     *
240     * @param array $link
241     * @return string
242     */
243    public function _formatLink($link)
244    {
245
246        // for internal links contains the title the pageid
247        if (in_array($link['title'], $this->actioninstance->getExportedPages())) {
248            [/* url */, $hash] = sexplode('#', $link['url'], 2, '');
249
250            $check = false;
251            $pid = sectionID($link['title'], $check);
252            $link['url'] = "#" . $pid . '__' . $hash;
253        }
254
255        // prefix interwiki links with interwiki icon
256        if ($link['name'][0] != '<' && preg_match('/\binterwiki iw_(.\w+)\b/', $link['class'], $m)) {
257            if (file_exists(DOKU_INC . 'lib/images/interwiki/' . $m[1] . '.png')) {
258                $img = DOKU_BASE . 'lib/images/interwiki/' . $m[1] . '.png';
259            } elseif (file_exists(DOKU_INC . 'lib/images/interwiki/' . $m[1] . '.gif')) {
260                $img = DOKU_BASE . 'lib/images/interwiki/' . $m[1] . '.gif';
261            } else {
262                $img = DOKU_BASE . 'lib/images/interwiki.png';
263            }
264
265            $link['name'] = sprintf(
266                '<img src="%s" width="16" height="16" style="vertical-align: middle" class="%s" />%s',
267                $img,
268                $link['class'],
269                $link['name']
270            );
271        }
272        return parent::_formatLink($link);
273    }
274
275    /**
276     * no obfuscation for email addresses
277     *
278     * @param string $address
279     * @param null $name
280     * @param bool $returnonly
281     * @return string|void
282     */
283    public function emaillink($address, $name = null, $returnonly = false)
284    {
285        global $conf;
286        $old = $conf['mailguard'];
287        $conf['mailguard'] = 'none';
288        parent::emaillink($address, $name, $returnonly);
289        $conf['mailguard'] = $old;
290    }
291}
292