1<?php 2/** 3 * DokuWiki Plugin latexcaption (Main Syntax Component) 4 * 5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6 * @author Ben van Magill <ben.vanmagill16@gmail.com> 7 * @author Till Biskup <till@till-biskup> 8 */ 9 10 11class syntax_plugin_latexcaption_caption extends \dokuwiki\Extension\SyntaxPlugin 12{ 13 14 /** 15 * Static variables set to keep track when scope is left. 16 */ 17 private static $_types = array('figure', 'table','codeblock','fileblock'); 18 private $_type = ''; 19 private $_incaption = false; 20 private $_label = ''; 21 private $_opts = array(); 22 private $_parOpts = array(); 23 private $_nested = false; 24 25 /** @var $helper helper_plugin_latexcaption */ 26 var $helper = null; 27 28 29 function getInfo(){ 30 return confToHash(dirname(__FILE__).'/../plugin.info.txt'); 31 } 32 33 public function getType() { 34 return 'container'; 35 } 36 37 public function getAllowedTypes() { 38 return array('formatting', 'substition', 'disabled', 'container', 'protected'); 39 } 40 41 public function getPType() { 42 return 'block'; 43 } 44 45 public function getSort() { 46 return 310; 47 } 48 49 50 public function connectTo($mode) { 51 $this->Lexer->addSpecialPattern('{{setcounter>[a-z0-9=]+?}}',$mode,'plugin_latexcaption_caption'); 52 $this->Lexer->addEntryPattern('<figure.*?>(?=.*</figure>)',$mode,'plugin_latexcaption_caption'); 53 $this->Lexer->addEntryPattern('<table.*?>(?=.*</table>)',$mode,'plugin_latexcaption_caption'); 54 $this->Lexer->addEntryPattern('<codeblock.*?>(?=.*</codeblock>)',$mode,'plugin_latexcaption_caption'); 55 $this->Lexer->addEntryPattern('<fileblock.*?>(?=.*</fileblock>)',$mode,'plugin_latexcaption_caption'); 56 } 57 58 public function postConnect() { 59 $this->Lexer->addExitPattern('</figure>','plugin_latexcaption_caption'); 60 $this->Lexer->addExitPattern('</table>','plugin_latexcaption_caption'); 61 $this->Lexer->addExitPattern('</codeblock>','plugin_latexcaption_caption'); 62 $this->Lexer->addExitPattern('</fileblock>','plugin_latexcaption_caption'); 63 64 $captiontag = $this->getConf('captiontag'); 65 $this->Lexer->addPattern('<'.$captiontag.'>(?=.*</'.$captiontag.'>)','plugin_latexcaption_caption'); 66 $this->Lexer->addPattern('</'.$captiontag.'>','plugin_latexcaption_caption'); 67 } 68 69 private function isSubType($type) { 70 return (substr($type, 0, 3) == 'sub'); 71 } 72 73 private function getParType($type) { 74 return substr($type, 3); 75 } 76 77 public function handle($match, $state, $pos, Doku_Handler $handler){ 78 global $caption_count; 79 80 $params = []; 81 if ($state == DOKU_LEXER_ENTER){ 82 // INPUT $match:<type opts|label> 83 // Strip the <> 84 $match = substr($match,1,-1); 85 // Retrieve the label name if any 86 list($matches, $label) = explode('|', $match, 2); 87 // retrieve type and options if any 88 list($type, $opts) = explode(' ', trim($matches), 2); 89 90 // explode again in the case of multiple options; 91 $opts = (!empty($opts) ? explode(' ', $opts) : ['noalign',]); 92 93 // Set dynamic class counter variable 94 $type_counter_def = '_'.$type.'_count'; 95 96 // Increment the counter of relevant type 97 // Store the type in class for caption match to determine what html tag to use 98 // This is ok since we will never have nested figures and more than one caption 99 $this->_type = $type; 100 $this->_label = $label; 101 $this->_opts = $opts; 102 $this->{$type_counter_def} = (!isset($this->{$type_counter_def}) ? 1 : $this->{$type_counter_def}+1); 103 104 // save params to class variables (cached for use in render) 105 $type_counter = $this->{$type_counter_def}; 106 107 // Set global variable if theres a label (used for ref) 108 if ($label) { 109 $label = trim($label); 110 // Check if we are counting a subtype to store parent in array for references 111 if ($this->isSubType($type)) { 112 $partype = $this->getParType($type); 113 $parcount = $this->{'_'.$partype.'_count'}; 114 } 115 $caption_count[$label] = array($type, $type_counter, $parcount); 116 } 117 118 //Save parent options for use later 119 if (!$this->isSubType($type)){ 120 $this->_parOpts = $opts; 121 } else { 122 $this->_nested = true; 123 } 124 125 // Set the params 126 $params['xhtml']['tagtype'] = (in_array($type, ['figure', 'subfigure']) ? 'figure' : 'div'); 127 $params['label'] = $label; 128 $params['opts'] = $opts; 129 $params['type'] = $type; 130 131 return array($state, $match, $pos, $params); 132 } 133 if ($state == DOKU_LEXER_MATCHED){ 134 // Case of caption. 135 // Toggle the incaption flag 136 $this->_incaption = !$this->_incaption; 137 $type = $this->_type; 138 $params['label'] = $this->_label; 139 $params['xhtml']['captagtype'] = (in_array($type, ['figure', 'subfigure']) ? 'figcaption' : 'div'); 140 $params['incaption'] = $this->_incaption; 141 $params['type_counter'] = $this->{'_'.$type.'_count'}; 142 $params['type'] = $type; 143 // Decide what caption options to send to renderer 144 $params['opts'] = ($this->_nested ? $this->_opts : $this->_parOpts); 145 146 147 return array($state, $match, $pos, $params); 148 } 149 if ($state == DOKU_LEXER_UNMATCHED){ 150 return array($state, $match, $pos, $params); 151 } 152 if ($state == DOKU_LEXER_EXIT){ 153 $type = $this->_type; 154 155 if (substr($type, 0, 3) == 'sub') { 156 // Change environment back to non sub type 157 $this->_type = substr($type, 3); 158 $this->_nested = false; 159 } 160 else { 161 // reset subtype counter 162 $this->{'_sub'.$type.'_count'} = 0; 163 } 164 $params['type'] = $type; 165 $params['xhtml']['tagtype'] = (in_array($type, ['figure', 'subfigure']) ? 'figure' : 'div'); 166 return array($state, $match, $pos, $params); 167 } 168 if ($state == DOKU_LEXER_SPECIAL){ 169 if (substr($match,0,13) != '{{setcounter>') { 170 return true; 171 } 172 173 $match = substr($match,13,-2); 174 list($type,$num) = explode('=',trim($match)); 175 176 $type = trim($type); 177 $num = (int) trim($num); 178 179 if (!in_array($type,$this::$_types)) { 180 return false; 181 } 182 183 // Update the counter. offset by 1 since counter is incremented on caption enter 184 $this->{'_'.$type.'_count'} = $num-1; 185 186 return true; 187 } 188 // this should never be reached 189 return true; 190 } 191 192 public function render($mode, Doku_Renderer $renderer, $data) { 193 194 if (empty($data)) { 195 return false; 196 } 197 198 list($state, $match, $pos, $params) = $data; 199 200 $langset = ($this->getConf('abbrev') ? 'abbrev' : 'long'); 201 202 if (!in_array($mode, ['xhtml','odt', 'latex'])) { 203 return true; 204 } 205 206 if ($mode == 'xhtml') { 207 if ($state == DOKU_LEXER_ENTER) { 208 // We know we already have a valid type on entering this 209 $type = $params['type']; 210 $tagtype = $params['xhtml']['tagtype']; 211 $label = $params['label']; 212 $opts = $params['opts']; 213 214 // print alignment/additional classes 215 $classes = implode(' plugin_latexcaption_', $opts); 216 // Rendering 217 $markup = '<'.$tagtype.' class="plugin_latexcaption_'.$type.' plugin_latexcaption_'.$classes.'" '; 218 219 if ($label){ 220 $markup .= 'id="'.$renderer->_xmlEntities($label).'"'; 221 } 222 223 $markup .= '>'; 224 $renderer->doc .= $markup; 225 return true; 226 } 227 228 if ($state == DOKU_LEXER_MATCHED) { 229 $captagtype = $params['xhtml']['captagtype']; 230 $incaption = $params['incaption']; 231 $count = $params['type_counter']; 232 $type = $params['type']; 233 $label = $params['label']; 234 $opts = $params['opts']; 235 236 $separator = (in_array('blank', $opts) ? '' : ': '); 237 238 if (!$this->$helper) 239 $this->$helper = plugin_load('helper', 'latexcaption'); 240 241 // Rendering a caption 242 if ($incaption) { 243 $markup .= '<'.$captagtype.' class="plugin_latexcaption_caption"><span class="plugin_latexcaption_caption_number"'; 244 // Set title to label 245 // if ($label) $markup .= ' title="'.$label.'"'; 246 $markup .= '>'; 247 if (substr($type, 0, 3) == 'sub') { 248 $markup .= '('.$this->$helper->number_to_alphabet($count).') '; 249 } 250 else { 251 $markup .= $this->getLang($type.$langset).' '. $count.$separator; 252 } 253 $markup .= '</span><span class="plugin_latexcaption_caption_text">'; 254 255 $renderer->doc .= $markup; 256 } 257 else { 258 $renderer->doc .= "</span></$captagtype>"; 259 260 } 261 return true; 262 } 263 264 if ($state == DOKU_LEXER_UNMATCHED) { 265 // return the dokuwiki markup within the figure tags 266 $renderer->doc .= $renderer->_xmlEntities($match); 267 } 268 269 if ($state == DOKU_LEXER_EXIT) { 270 $tagtype = $params['xhtml']['tagtype']; 271 $renderer->doc .= "</$tagtype>"; 272 return true; 273 } 274 } 275 276 if ($mode == 'latex') { 277 // All Doku $states get type param 278 // Only figure and table supported. 279 $type = $params['type']; 280 if (!in_array($type, ['figure', 'table', 'subfigure'])){ 281 return true; 282 } 283 284 if ($state == DOKU_LEXER_ENTER) { 285 // Render 286 $renderer->doc .= "\begin{$type}"; 287 return true; 288 } 289 if ($state == DOKU_LEXER_MATCHED) { 290 $incaption = $params['incaption']; 291 $label = $params['label']; 292 if ($incaption) { 293 $out = '\caption{'; 294 } else { 295 $out = '}'; 296 if ($label){ 297 $out .= "\n" . "\label{$label}"; 298 } 299 } 300 $renderer->doc .= $out; 301 return true; 302 } 303 if ($state == DOKU_LEXER_UNMATCHED) { 304 // Pass it through 305 $renderer->doc .= $match; 306 return true; 307 } 308 if ($state == DOKU_LEXER_EXIT) { 309 $renderer->doc .= "\end{$type}"; 310 return true; 311 } 312 } 313 314 /** 315 * WARNING: The odt mode seems to work in general, but strange things happen 316 * with the tables - therefore, within the table tags only a table 317 * is allowed, without any additional markup. 318 */ 319 if ($mode == 'odt') { 320 // All Doku $states get type param 321 // Only figure and table supported. 322 $type = $params['type']; 323 if (!in_array($type, ['figure', 'table'])) { 324 return true; 325 } 326 327 if ($state == DOKU_LEXER_ENTER) { 328 $renderer->p_open(); 329 return true; 330 } 331 if ($state == DOKU_LEXER_MATCHED) { 332 $incaption = $params['incaption']; 333 $count = $params['type_counter']; 334 $type = $params['type']; 335 $label = $params['label']; 336 337 if ($incaption) { 338 $renderer->p_close(); 339 $style_name = ($type == 'figure' ? 'Illustration' : 'Table'); 340 $labelname = ($label ? $label : 'ref'.$style_name.$count); 341 342 $out = '<text:p text:style-name="'.$style_name.'">'; 343 $out .= $this->getLang($type.$langset); 344 $out .= '<text:sequence text:ref-name="'.$labelname.'"'; 345 $out .= 'text:name="'.$style_name.'" text:formula="ooow:'.$style_name.'+1" style:num-format="1">'; 346 $out .= ' ' . $count . '</text:sequence>: '; 347 348 $renderer->doc .= $out; 349 } else { 350 $renderer->doc .= '</text:p>'; 351 $renderer->p_open(); 352 } 353 return true; 354 } 355 if ($state == DOKU_LEXER_UNMATCHED) { 356 // Pass it through 357 $renderer->cdata($match); 358 return true; 359 } 360 if ($state == DOKU_LEXER_EXIT) { 361 $renderer->p_close(); 362 return true; 363 } 364 } 365 } 366} 367 368// vim:ts=4:sw=4:et: 369