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