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