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