xref: /plugin/dw2pdf/renderer.php (revision 852931daed0aa7c73fc4da5d421d2c117decf509)
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') return true;
61        return false;
62    }
63
64    /**
65     * Simplified header printing with PDF bookmarks
66     *
67     * @param string $text
68     * @param int $level from 1 (highest) to 6 (lowest)
69     * @param int $pos
70     */
71    public function header($text, $level, $pos, $returnonly = false)
72    {
73        if (!$text) return; //skip empty headlines
74        global $ID;
75
76        $hid = $this->_headerToLink($text, true);
77
78        //only add items within global configured levels (doesn't check the pdf toc settings)
79        $this->toc_additem($hid, $text, $level);
80
81        $check = false;
82        $pid = sectionID($ID, $check);
83        $hid = $pid . '__' . $hid;
84
85
86        // retrieve numbered headings option
87        $isnumberedheadings = $this->actioninstance->getExportConfig('headernumber');
88
89        $header_prefix = "";
90        if ($isnumberedheadings) {
91            if ($level > 0) {
92                if (self::$previous_level > $level) {
93                    for ($i = $level + 1; $i <= self::$previous_level; $i++) {
94                        self::$header_count[$i] = 0;
95                    }
96                }
97            }
98            self::$header_count[$level]++;
99
100            // $header_prefix = "";
101            for ($i = 1; $i <= $level; $i++) {
102                $header_prefix .= self::$header_count[$i] . ".";
103            }
104        }
105
106        // add PDF bookmark
107        $bookmark = '';
108        $maxbookmarklevel = $this->actioninstance->getExportConfig('maxbookmarks');
109        // 0: off, 1-6: show down to this level
110        if ($maxbookmarklevel && $maxbookmarklevel >= $level) {
111            $bookmarklevel = $this->calculateBookmarklevel($level);
112            $bookmark = sprintf(
113                '<bookmark content="%s %s" level="%d" />',
114                $header_prefix,
115                $this->_xmlEntities($text),
116                $bookmarklevel
117            );
118        }
119
120        // print header
121        $this->doc .= DOKU_LF . "<h$level>$bookmark";
122        $this->doc .= $header_prefix . "<a name=\"$hid\">";
123        $this->doc .= $this->_xmlEntities($text);
124        $this->doc .= "</a>";
125        $this->doc .= "</h$level>" . DOKU_LF;
126        self::$previous_level = $level;
127    }
128
129    /**
130     * Bookmark levels might increase maximal +1 per level.
131     * (note: levels start at 1, bookmarklevels at 0)
132     *
133     * @param int $level 1 (highest) to 6 (lowest)
134     * @return int
135     */
136    protected function calculateBookmarklevel($level)
137    {
138        if ($this->lastHeaderLevel == -1) {
139            $this->lastHeaderLevel = $level;
140        }
141        $step = $level - $this->lastHeaderLevel;
142        if ($step > 1) {
143            $this->difference += $step - 1;
144        }
145        if ($step < 0) {
146            $this->difference = min($this->difference, $level - $this->originalHeaderLevel);
147            $this->difference = max($this->difference, 0);
148        }
149
150        $bookmarklevel = $level - $this->difference;
151
152        if ($step > 1) {
153            $this->originalHeaderLevel = $bookmarklevel;
154        }
155
156        $this->lastHeaderLevel = $level;
157        return $bookmarklevel - 1; //zero indexed
158    }
159
160    /**
161     * Render a page local link
162     *
163     * // modified copy of parent function
164     *
165     * @param string $hash hash link identifier
166     * @param string $name name for the link
167     * @param bool $returnonly
168     * @return string|void
169     *
170     * @see Doku_Renderer_xhtml::locallink
171     */
172    public function locallink($hash, $name = null, $returnonly = false)
173    {
174        global $ID;
175        $name = $this->_getLinkTitle($name, $hash, $isImage);
176        $hash = $this->_headerToLink($hash);
177        $title = $ID . ' ↵';
178
179        $check = false;
180        $pid = sectionID($ID, $check);
181
182        $this->doc .= '<a href="#' . $pid . '__' . $hash . '" title="' . $title . '" class="wikilink1">';
183        $this->doc .= $name;
184        $this->doc .= '</a>';
185    }
186
187    /**
188     * Wrap centered media in a div to center it
189     *
190     * @param string $src media ID
191     * @param string $title descriptive text
192     * @param string $align left|center|right
193     * @param int $width width of media in pixel
194     * @param int $height height of media in pixel
195     * @param string $cache cache|recache|nocache
196     * @param bool $render should the media be embedded inline or just linked
197     * @return string
198     */
199    public function _media(
200        $src,
201        $title = null,
202        $align = null,
203        $width = null,
204        $height = null,
205        $cache = null,
206        $render = true
207    ) {
208
209        $out = '';
210        if ($align == 'center') {
211            $out .= '<div align="center" style="text-align: center">';
212        }
213
214        $out .= parent::_media($src, $title, $align, $width, $height, $cache, $render);
215
216        if ($align == 'center') {
217            $out .= '</div>';
218        }
219
220        return $out;
221    }
222
223    /**
224     * hover info makes no sense in PDFs, so drop acronyms
225     *
226     * @param string $acronym
227     */
228    public function acronym($acronym)
229    {
230        $this->doc .= $this->_xmlEntities($acronym);
231    }
232
233    /**
234     * reformat links if needed
235     *
236     * @param array $link
237     * @return string
238     */
239    public function _formatLink($link)
240    {
241
242        // for internal links contains the title the pageid
243        if (in_array($link['title'], $this->actioninstance->getExportedPages())) {
244            [/* url */, $hash] = sexplode('#', $link['url'], 2, '');
245
246            $check = false;
247            $pid = sectionID($link['title'], $check);
248            $link['url'] = "#" . $pid . '__' . $hash;
249        }
250
251        // prefix interwiki links with interwiki icon
252        if ($link['name'][0] != '<' && preg_match('/\binterwiki iw_(.\w+)\b/', $link['class'], $m)) {
253            if (file_exists(DOKU_INC . 'lib/images/interwiki/' . $m[1] . '.png')) {
254                $img = DOKU_BASE . 'lib/images/interwiki/' . $m[1] . '.png';
255            } elseif (file_exists(DOKU_INC . 'lib/images/interwiki/' . $m[1] . '.gif')) {
256                $img = DOKU_BASE . 'lib/images/interwiki/' . $m[1] . '.gif';
257            } else {
258                $img = DOKU_BASE . 'lib/images/interwiki.png';
259            }
260
261            $link['name'] = sprintf(
262                '<img src="%s" width="16" height="16" style="vertical-align: middle" class="%s" />%s',
263                $img,
264                $link['class'],
265                $link['name']
266            );
267        }
268        return parent::_formatLink($link);
269    }
270
271    /**
272     * no obfuscation for email addresses
273     *
274     * @param string $address
275     * @param null $name
276     * @param bool $returnonly
277     * @return string|void
278     */
279    public function emaillink($address, $name = null, $returnonly = false)
280    {
281        global $conf;
282        $old = $conf['mailguard'];
283        $conf['mailguard'] = 'none';
284        parent::emaillink($address, $name, $returnonly);
285        $conf['mailguard'] = $old;
286    }
287}
288