1<?php 2/** 3 * Helper Component for the Wrap Plugin 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Anika Henke <anika@selfthinker.org> 7 */ 8 9class helper_plugin_wrap extends DokuWiki_Plugin { 10 static protected $boxes = array ('wrap_box', 'wrap_danger', 'wrap_warning', 'wrap_caution', 'wrap_notice', 'wrap_safety', 11 'wrap_info', 'wrap_important', 'wrap_alert', 'wrap_tip', 'wrap_help', 'wrap_todo', 12 'wrap_download', 'wrap_hi', 'wrap_spoiler'); 13 static protected $paragraphs = array ('wrap_leftalign', 'wrap_rightalign', 'wrap_centeralign', 'wrap_justify'); 14 15 /* list of languages which normally use RTL scripts */ 16 static protected $rtllangs = array('ar','dv','fa','ha','he','ks','ku','ps','ur','yi','arc'); 17 /* list of right-to-left scripts (may override the language defaults to rtl) */ 18 static protected $rtlscripts = array('arab','thaa','hebr','deva','shrd'); 19 /* selection of left-to-right scripts (may override the language defaults to ltr) */ 20 static protected $ltrscripts = array('latn','cyrl','grek','hebr','cyrs','armn'); 21 22 static $box_left_pos = 0; 23 static $box_right_pos = 0; 24 static $box_first = true; 25 static $table_entr = 0; 26 27 protected $column_count = 0; 28 29 /** 30 * get attributes (pull apart the string between '<wrap' and '>') 31 * and identify classes, width, lang and dir 32 * 33 * @author Anika Henke <anika@selfthinker.org> 34 * @author Christopher Smith <chris@jalakai.co.uk> 35 * (parts taken from http://www.dokuwiki.org/plugin:box) 36 */ 37 function getAttributes($data, $useNoPrefix=true) { 38 39 $attr = array( 40 'lang' => null, 41 'class' => null, 42 'width' => null, 43 'id' => null, 44 'dir' => null 45 ); 46 $tokens = preg_split('/\s+/', $data, 9); 47 48 // anonymous function to convert inclusive comma separated items to regex pattern 49 $pattern = function ($csv) { 50 return '/^(?:'. str_replace(['?','*',' ',','], 51 ['.','.*','','|'], $csv) .')$/'; 52 }; 53 54 // noPrefix: comma separated class names that should be excluded from 55 // being prefixed with "wrap_", 56 // each item may contain wildcard (*, ?) 57 $noPrefix = ($this->getConf('noPrefix') && $useNoPrefix) ? $pattern($this->getConf('noPrefix')) : ''; 58 59 // restrictedClasses : comma separated class names that should be checked 60 // based on restriction type (whitelist or blacklist), 61 // each item may contain wildcard (*, ?) 62 $restrictedClasses = ($this->getConf('restrictedClasses')) ? 63 $pattern($this->getConf('restrictedClasses')) : ''; 64 $restrictionType = $this->getConf('restrictionType'); 65 66 foreach ($tokens as $token) { 67 68 //get width 69 if (preg_match('/^\d*\.?\d+(%|px|em|rem|ex|ch|vw|vh|pt|pc|cm|mm|in)$/', $token)) { 70 $attr['width'] = $token; 71 continue; 72 } 73 74 //get lang 75 if (preg_match('/:([a-z\-]+)/', $token)) { 76 $attr['lang'] = trim($token,':'); 77 continue; 78 } 79 80 //get id 81 if (preg_match('/#([A-Za-z0-9_-]+)/', $token)) { 82 $attr['id'] = trim($token,'#'); 83 continue; 84 } 85 86 //get classes 87 //restrict token (class names) characters to prevent any malicious data 88 if (preg_match('/[^A-Za-z0-9_-]/',$token)) continue; 89 if ($restrictedClasses) { 90 $classIsInList = preg_match($restrictedClasses, $token); 91 // either allow only certain classes or disallow certain classes 92 if ($restrictionType xor $classIsInList) continue; 93 } 94 // prefix adjustment of class name 95 $prefix = (!empty($noPrefix) && preg_match($noPrefix, $token)) ? '' : 'wrap_'; 96 $attr['class'] = (isset($attr['class']) ? $attr['class'].' ' : '').$prefix.$token; 97 } 98 if ($this->getConf('darkTpl')) { 99 $attr['class'] = (isset($attr['class']) ? $attr['class'].' ' : '').'wrap__dark'; 100 } 101 if ($this->getConf('emulatedHeadings')) { 102 $attr['class'] = (isset($attr['class']) ? $attr['class'].' ' : '').'wrap__emuhead'; 103 } 104 105 /* improved RTL detection to make sure it covers more cases: */ 106 if($attr['lang'] && $attr['lang'] !== '') { 107 108 // turn the language code into an array of components: 109 $arr = explode('-', $attr['lang']); 110 111 // is the language iso code (first field) in the list of RTL languages? 112 $rtl = in_array($arr[0], self::$rtllangs); 113 114 // is there a Script specified somewhere which overrides the text direction? 115 $rtl = ($rtl xor (bool) array_intersect( $rtl ? self::$ltrscripts : self::$rtlscripts, $arr)); 116 117 $attr['dir'] = ( $rtl ? 'rtl' : 'ltr' ); 118 } 119 120 return $attr; 121 } 122 123 /** 124 * build attributes (write out classes, width, lang and dir) 125 */ 126 function buildAttributes($data, $addClass='', $mode='xhtml') { 127 128 $attr = $this->getAttributes($data); 129 $out = ''; 130 131 if ($mode=='xhtml') { 132 if($attr['class']) $out .= ' class="'.hsc($attr['class']).' '.$addClass.'"'; 133 // if used in other plugins, they might want to add their own class(es) 134 elseif($addClass) $out .= ' class="'.$addClass.'"'; 135 if($attr['id']) $out .= ' id="'.hsc($attr['id']).'"'; 136 // width on spans normally doesn't make much sense, but in the case of floating elements it could be used 137 if($attr['width']) { 138 if (strpos($attr['width'],'%') !== false) { 139 $out .= ' style="width: '.hsc($attr['width']).';"'; 140 } else { 141 // anything but % should be 100% when the screen gets smaller 142 $out .= ' style="width: '.hsc($attr['width']).'; max-width: 100%;"'; 143 } 144 } 145 // only write lang if it's a language in lang2dir.conf 146 if($attr['dir']) $out .= ' lang="'.$attr['lang'].'" xml:lang="'.$attr['lang'].'" dir="'.$attr['dir'].'"'; 147 } 148 149 return $out; 150 } 151 152 /** 153 * render ODT element, Open 154 * (get Attributes, select ODT element that fits, render it, return element name) 155 */ 156 function renderODTElementOpen($renderer, $HTMLelement, $data) { 157 $attr = $this->getAttributes($data, false); 158 $attr_string = $this->buildAttributes($data); 159 $classes = explode (' ', $attr['class']); 160 161 // Get language 162 $language = $attr['lang']; 163 164 $is_indent = in_array ('wrap_indent', $classes); 165 $is_outdent = in_array ('wrap_outdent', $classes); 166 $is_column = in_array ('wrap_column', $classes); 167 $is_group = in_array ('wrap_group', $classes); 168 $is_pagebreak = in_array ('wrap_pagebreak', $classes); 169 170 // Check for multicolumns 171 $columns = 0; 172 preg_match ('/wrap_col\d/', $attr ['class'], $matches); 173 if ( empty ($matches [0]) === false ) { 174 $columns = $matches [0] [strlen($matches [0])-1]; 175 } 176 177 // Check for boxes 178 $is_box = false; 179 foreach (self::$boxes as $box) { 180 if ( strpos ($attr ['class'], $box) !== false ) { 181 $is_box = true; 182 break; 183 } 184 } 185 186 // Check for paragraphs 187 $is_paragraph = false; 188 if ( empty($language) === false ) { 189 $is_paragraph = true; 190 } else { 191 foreach (self::$paragraphs as $paragraph) { 192 if ( strpos ($attr ['class'], $paragraph) !== false ) { 193 $is_paragraph = true; 194 break; 195 } 196 } 197 } 198 199 $style = null; 200 if ( empty($attr['width']) === false ) { 201 $style = 'width: '.$attr['width'].';'; 202 } 203 $attr ['class'] = 'dokuwiki '.$attr ['class']; 204 205 // Call corresponding functions for current wrap class 206 if ( $HTMLelement == 'span' ) { 207 if ( $is_indent === false && $is_outdent === false ) { 208 $this->renderODTOpenSpan ($renderer, $attr ['class'], $style, $language, $attr_string); 209 return 'span'; 210 } else { 211 $this->renderODTOpenParagraph ($renderer, $attr ['class'], $style, $attr ['dir'], $language, $is_indent, $is_outdent, true, $attr_string); 212 return 'paragraph'; 213 } 214 } else if ( $HTMLelement == 'div' ) { 215 if ( $is_box === true ) { 216 $wrap = $this->loadHelper('wrap'); 217 $fullattr = $wrap->buildAttributes($data, 'plugin_wrap'); 218 219 if ( method_exists ($renderer, 'getODTPropertiesFromElement') === false ) { 220 $this->renderODTOpenBox ($renderer, $attr ['class'], $style, $fullattr); 221 } else { 222 $this->renderODTOpenTable ($renderer, $attr, $style, $attr_string); 223 } 224 return 'box'; 225 } else if ( $columns > 0 ) { 226 $this->renderODTOpenColumns ($renderer, $attr ['class'], $style); 227 return 'multicolumn'; 228 } else if ( $is_paragraph === true || $is_indent === true || $is_outdent === true ) { 229 $this->renderODTOpenParagraph ($renderer, $attr ['class'], $style, $attr ['dir'], $language, $is_indent, $is_outdent, false, $attr_string); 230 return 'paragraph'; 231 } else if ( $is_pagebreak === true ) { 232 $renderer->pagebreak (); 233 // Pagebreak hasn't got a closing stack so we return/push 'other' on the stack 234 return 'other'; 235 } else if ( $is_column === true ) { 236 $this->renderODTOpenColumn ($renderer, $attr ['class'], $style, $attr_string); 237 return 'column'; 238 } else if ( $is_group === true ) { 239 $this->renderODTOpenGroup ($renderer, $attr ['class'], $style); 240 return 'group'; 241 } else if (strpos ($attr ['class'], 'wrap_clear') !== false ) { 242 $renderer->linebreak(); 243 $renderer->p_close(); 244 $renderer->p_open(); 245 246 self::$box_left_pos = 0; 247 self::$box_right_pos = 0; 248 self::$box_first = true; 249 } 250 } 251 return 'other'; 252 } 253 254 /** 255 * render ODT element, Close 256 */ 257 function renderODTElementClose($renderer, $element) { 258 switch ($element) { 259 case 'box': 260 if ( method_exists ($renderer, 'getODTPropertiesFromElement') === false ) { 261 $this->renderODTCloseBox ($renderer); 262 } else { 263 $this->renderODTCloseTable ($renderer); 264 } 265 break; 266 case 'multicolumn': 267 $this->renderODTCloseColumns($renderer); 268 break; 269 case 'paragraph': 270 $this->renderODTCloseParagraph($renderer); 271 break; 272 case 'column': 273 $this->renderODTCloseColumn($renderer); 274 break; 275 case 'group': 276 $this->renderODTCloseGroup($renderer); 277 break; 278 case 'span': 279 $this->renderODTCloseSpan($renderer); 280 break; 281 // No default by intention. 282 } 283 } 284 285 function renderODTOpenBox ($renderer, $class, $style, $fullattr) { 286 $properties = array (); 287 288 if ( method_exists ($renderer, 'getODTProperties') === false ) { 289 // Function is not supported by installed ODT plugin version, return. 290 return; 291 } 292 293 // Get CSS properties for ODT export. 294 $renderer->getODTProperties ($properties, 'div', $class, $style); 295 296 if ( empty($properties ['background-image']) === false ) { 297 $properties ['background-image'] = 298 $renderer->replaceURLPrefix ($properties ['background-image'], DOKU_INC); 299 } 300 301 if ( empty($properties ['float']) === true ) { 302 // If the float property is not set, set it to 'left' becuase the ODT plugin 303 // would default to 'center' which is diffeent to the XHTML behaviour. 304 if ( strpos ($class, 'wrap_center') === false ) { 305 $properties ['float'] = 'left'; 306 } else { 307 $properties ['float'] = 'center'; 308 } 309 } 310 311 // The display property has differing usage in CSS. So we better overwrite it. 312 $properties ['display'] = 'always'; 313 if ( stripos ($class, 'wrap_noprint') !== false ) { 314 $properties ['display'] = 'screen'; 315 } 316 if ( stripos ($class, 'wrap_onlyprint') !== false ) { 317 $properties ['display'] = 'printer'; 318 } 319 320 $renderer->_odtDivOpenAsFrameUseProperties ($properties); 321 } 322 323 function renderODTCloseBox ($renderer) { 324 if ( method_exists ($renderer, '_odtDivCloseAsFrame') === false ) { 325 // Function is not supported by installed ODT plugin version, return. 326 return; 327 } 328 $renderer->_odtDivCloseAsFrame (); 329 } 330 331 function renderODTOpenColumns ($renderer, $class, $style) { 332 $properties = array (); 333 334 if ( method_exists ($renderer, 'getODTProperties') === false ) { 335 // Function is not supported by installed ODT plugin version, return. 336 return; 337 } 338 339 // Get CSS properties for ODT export. 340 $renderer->getODTProperties ($properties, 'div', $class, $style); 341 342 $renderer->_odtOpenMultiColumnFrame($properties); 343 } 344 345 function renderODTCloseColumns ($renderer) { 346 if ( method_exists ($renderer, '_odtCloseMultiColumnFrame') === false ) { 347 // Function is not supported by installed ODT plugin version, return. 348 return; 349 } 350 $renderer->_odtCloseMultiColumnFrame(); 351 } 352 353 function renderODTOpenParagraph ($renderer, $class, $style, $dir, $language, $is_indent, $is_outdent, $indent_first, $attr=null) { 354 $properties = array (); 355 356 if ( method_exists ($renderer, 'getODTPropertiesFromElement') === true ) { 357 // Get CSS properties for ODT export. 358 // Set parameter $inherit=false to prevent changiung the font-size and family! 359 $renderer->getODTPropertiesNew ($properties, 'p', $attr, null, false); 360 } else if ( method_exists ($renderer, 'getODTProperties') === true ) { 361 // Get CSS properties for ODT export (deprecated version). 362 $renderer->getODTProperties ($properties, 'p', $class, $style); 363 364 if ( empty($properties ['background-image']) === false ) { 365 $properties ['background-image'] = 366 $renderer->replaceURLPrefix ($properties ['background-image'], DOKU_INC); 367 } 368 } else { 369 // To old ODT plugin version. 370 return; 371 } 372 373 if ( empty($properties ['text-align']) ) 374 { 375 if ($dir == 'ltr') { 376 $properties ['text-align'] = 'left'; 377 $properties ['writing-mode'] = 'lr'; 378 } 379 if ($dir == 'rtl') { 380 $properties ['text-align'] = 'right'; 381 $properties ['writing-mode'] = 'rl'; 382 } 383 } 384 385 $name = ''; 386 if ( empty($language) === false ) { 387 $properties ['lang'] = $language; 388 $name .= 'Language: '.$language; 389 } 390 391 if ( $indent_first === true ) { 392 // Eventually indent or outdent first line only... 393 if ( $is_indent === true ) { 394 // FIXME: Has to be adjusted if test direction will be supported. 395 // See all.css 396 $properties ['text-indent'] = $properties ['padding-left']; 397 $properties ['padding-left'] = 0; 398 $name .= 'Indent first'; 399 } 400 if ( $is_outdent === true ) { 401 // FIXME: Has to be adjusted if text (RTL, LTR) direction will be supported. 402 // See all.css 403 $properties ['text-indent'] = $properties ['margin-left']; 404 $properties ['margin-left'] = 0; 405 $name .= 'Outdent first'; 406 } 407 } else { 408 // Eventually indent or outdent the whole paragraph... 409 if ( $is_indent === true ) { 410 // FIXME: Has to be adjusted if test direction will be supported. 411 // See all.css 412 $properties ['margin-left'] = $properties ['padding-left'] ?? null; 413 $properties ['padding-left'] = 0; 414 $name .= 'Indent'; 415 } 416 if ( $is_outdent === true ) { 417 // Nothing to change: keep left margin property. 418 // FIXME: Has to be adjusted if text (RTL, LTR) direction will be supported. 419 // See all.css 420 $name .= 'Outdent'; 421 } 422 } 423 424 $renderer->p_close(); 425 if ( method_exists ($renderer, 'createParagraphStyle') === false ) { 426 // Older ODT plugin version. 427 $renderer->_odtParagraphOpenUseProperties($properties); 428 } else { 429 // Newer version create our own common styles. 430 431 // Create parent style to group the others beneath it 432 if (!$renderer->styleExists('Plugin_Wrap_Paragraphs')) { 433 $parent_properties = array(); 434 $parent_properties ['style-parent'] = null; 435 $parent_properties ['style-class'] = 'Plugin Wrap Paragraphs'; 436 $parent_properties ['style-name'] = 'Plugin_Wrap_Paragraphs'; 437 $parent_properties ['style-display-name'] = 'Plugin Wrap'; 438 $renderer->createParagraphStyle($parent_properties); 439 } 440 441 $name .= $this->getODTCommonStyleName($class); 442 $style_name = 'Plugin_Wrap_Paragraph_'.$name; 443 if (!$renderer->styleExists($style_name)) { 444 $properties ['style-parent'] = 'Plugin_Wrap_Paragraphs'; 445 $properties ['style-class'] = null; 446 $properties ['style-name'] = $style_name; 447 $properties ['style-display-name'] = $name; 448 $renderer->createParagraphStyle($properties); 449 } 450 451 $renderer->p_open($style_name); 452 } 453 } 454 455 function renderODTCloseParagraph ($renderer) { 456 if ( method_exists ($renderer, 'p_close') === false ) { 457 // Function is not supported by installed ODT plugin version, return. 458 return; 459 } 460 $renderer->p_close(); 461 } 462 463 function renderODTOpenColumn ($renderer, $class, $style, $attr) { 464 $properties = array (); 465 466 if ( method_exists ($renderer, 'getODTPropertiesFromElement') === true ) { 467 // Get CSS properties for ODT export. 468 $renderer->getODTPropertiesNew ($properties, 'div', $attr); 469 } else if ( method_exists ($renderer, 'getODTProperties') === true ) { 470 // Get CSS properties for ODT export (deprecated version). 471 $renderer->getODTProperties ($properties, null, $class, $style); 472 } else { 473 // To old ODT plugin version. 474 return; 475 } 476 477 // Frames/Textboxes still have some issues with formatting (at least in LibreOffice) 478 // So as a workaround we implement columns as a table. 479 // This is why we now use the margin of the div as the padding for the ODT table. 480 $properties ['padding-left'] = $properties ['margin-left'] ?? null; 481 $properties ['padding-right'] = $properties ['margin-right'] ?? null; 482 $properties ['padding-top'] = $properties ['margin-top'] ?? null; 483 $properties ['padding-bottom'] = $properties ['margin-bottom'] ?? null; 484 $properties ['margin-left'] = null; 485 $properties ['margin-right'] = null; 486 $properties ['margin-top'] = null; 487 $properties ['margin-bottom'] = null; 488 489 // Percentage values are not supported for the padding. Convert to absolute values. 490 $length = strlen ($properties ['padding-left']); 491 if ( $length > 0 && $properties ['padding-left'] [$length-1] == '%' ) { 492 $properties ['padding-left'] = trim ($properties ['padding-left'], '%'); 493 $properties ['padding-left'] = $renderer->_getAbsWidthMindMargins ($properties ['padding-left']).'cm'; 494 } 495 $length = strlen ($properties ['padding-right']); 496 if ( $length > 0 && $properties ['padding-right'] [$length-1] == '%' ) { 497 $properties ['padding-right'] = trim ($properties ['padding-right'], '%'); 498 $properties ['padding-right'] = $renderer->_getAbsWidthMindMargins ($properties ['padding-right']).'cm'; 499 } 500 $length = strlen ($properties ['padding-top']); 501 if ( $length > 0 && $properties ['padding-top'] [$length-1] == '%' ) { 502 $properties ['padding-top'] = trim ($properties ['padding-top'], '%'); 503 $properties ['padding-top'] = $renderer->_getAbsWidthMindMargins ($properties ['padding-top']).'cm'; 504 } 505 $length = strlen ($properties ['padding-bottom']); 506 if ( $length > 0 && $properties ['padding-bottom'] [$length-1] == '%' ) { 507 $properties ['padding-bottom'] = trim ($properties ['padding-bottom'], '%'); 508 $properties ['padding-bottom'] = $renderer->_getAbsWidthMindMargins ($properties ['padding-bottom']).'cm'; 509 } 510 511 $this->column_count++; 512 if ( $this->column_count == 1 ) { 513 // If this is the first column opened since the group was opened 514 // then we have to open the table and a (single) row first. 515 $properties ['width'] = '100%'; 516 $renderer->_odtTableOpenUseProperties($properties); 517 $renderer->_odtTableRowOpenUseProperties($properties); 518 } 519 520 // We did not specify any max column value when we opened the table. 521 // So we have to tell the renderer to add a column just now. 522 unset($properties ['width']); 523 $renderer->_odtTableAddColumnUseProperties($properties); 524 525 // Open the cell. 526 $renderer->_odtTableCellOpenUseProperties($properties); 527 } 528 529 function renderODTCloseColumn ($renderer) { 530 if ( method_exists ($renderer, '_odtTableAddColumnUseProperties') === false ) { 531 // Function is not supported by installed ODT plugin version, return. 532 return; 533 } 534 535 $renderer->tablecell_close(); 536 } 537 538 function renderODTOpenGroup ($renderer, $class, $style) { 539 // Nothing to do for now. 540 } 541 542 function renderODTCloseGroup ($renderer) { 543 // If a table has been opened in the group we close it now. 544 if ( $this->column_count > 0 ) { 545 // At last we need to close the row and the table! 546 $renderer->tablerow_close(); 547 //$renderer->table_close(); 548 $renderer->_odtTableClose(); 549 } 550 $this->column_count = 0; 551 } 552 553 function renderODTOpenSpan ($renderer, $class, $style, $language, $attr) { 554 $properties = array (); 555 556 if ( method_exists ($renderer, 'getODTPropertiesFromElement') === true ) { 557 // Get CSS properties for ODT export. 558 // Set parameter $inherit=false to prevent changiung the font-size and family! 559 $renderer->getODTPropertiesNew ($properties, 'span', $attr, null, false); 560 } else if ( method_exists ($renderer, 'getODTProperties') === true ) { 561 // Get CSS properties for ODT export (deprecated version). 562 $renderer->getODTProperties ($properties, 'span', $class, $style); 563 564 if ( empty($properties ['background-image']) === false ) { 565 $properties ['background-image'] = 566 $renderer->replaceURLPrefix ($properties ['background-image'], DOKU_INC); 567 } 568 } else { 569 // To old ODT plugin version. 570 return; 571 } 572 573 $name = ''; 574 if ( empty($language) === false ) { 575 $properties ['lang'] = $language; 576 $name .= 'Language: '.$language; 577 } 578 579 if ( method_exists ($renderer, 'getODTPropertiesFromElement') === false ) { 580 // Older ODT plugin version. 581 $renderer->_odtSpanOpenUseProperties($properties); 582 } else { 583 // Newer version create our own common styles. 584 $properties ['font-size'] = null; 585 586 // Create parent style to group the others beneath it 587 if (!$renderer->styleExists('Plugin_Wrap_Spans')) { 588 $parent_properties = array(); 589 $parent_properties ['style-parent'] = null; 590 $parent_properties ['style-class'] = 'Plugin Wrap Spans'; 591 $parent_properties ['style-name'] = 'Plugin_Wrap_Spans'; 592 $parent_properties ['style-display-name'] = 'Plugin Wrap'; 593 $renderer->createTextStyle($parent_properties); 594 } 595 596 $name .= $this->getODTCommonStyleName($class); 597 $style_name = 'Plugin_Wrap_Span_'.$name; 598 if (!$renderer->styleExists($style_name)) { 599 $properties ['style-parent'] = 'Plugin_Wrap_Spans'; 600 $properties ['style-class'] = null; 601 $properties ['style-name'] = $style_name; 602 $properties ['style-display-name'] = $name; 603 $renderer->createTextStyle($properties); 604 } 605 606 if (!empty($properties ['background-image'])) { 607 if (method_exists ($renderer, '_odtAddImageUseProperties') === true) { 608 $size = null; 609 if (!empty($properties ['font-size'])) { 610 $size = $properties ['font-size']; 611 $size = $renderer->addToValue($size, '2pt'); 612 } 613 $properties ['width'] = $size; 614 $properties ['height'] = $size; 615 $properties ['title'] = null; 616 $renderer->_odtAddImageUseProperties ($properties ['background-image'],$properties); 617 } else { 618 $renderer->_odtAddImage ($properties ['background-image'],null,null,null,null,null); 619 } 620 } 621 $renderer->_odtSpanOpen($style_name); 622 } 623 } 624 625 function renderODTCloseSpan ($renderer) { 626 if ( method_exists ($renderer, '_odtSpanClose') === false ) { 627 // Function is not supported by installed ODT plugin version, return. 628 return; 629 } 630 $renderer->_odtSpanClose(); 631 } 632 633 function renderODTOpenTable ($renderer, $attr, $style, $attr_string) { 634 self::$table_entr += 1; 635 636 $class = $attr ['class']; 637 $css_properties = array (); 638 639 if ( method_exists ($renderer, 'getODTPropertiesFromElement') === false ) { 640 // Function is not supported by installed ODT plugin version, return. 641 return; 642 } 643 644 // Get CSS properties for ODT export. 645 $renderer->getODTPropertiesNew ($css_properties, 'div', $attr_string, null, true); 646 647 if ( empty($css_properties ['float']) === true ) { 648 // If the float property is not set, set it to 'left' becuase the ODT plugin 649 // would default to 'center' which is diffeent to the XHTML behaviour. 650 //$css_properties ['float'] = 'left'; 651 if (strpos ($class, 'wrap_left') !== false ) { 652 $css_properties ['float'] = 'left'; 653 } else if (strpos ($class, 'wrap_center') !== false ) { 654 $css_properties ['float'] = 'center'; 655 } else if (strpos ($class, 'wrap_right') !== false) { 656 $css_properties ['float'] = 'right'; 657 } 658 } 659 660 // The display property has differing usage in CSS. So we better overwrite it. 661 $css_properties ['display'] = 'always'; 662 if ( stripos ($class, 'wrap_noprint') !== false ) { 663 $css_properties ['display'] = 'screen'; 664 } 665 if ( stripos ($class, 'wrap_onlyprint') !== false ) { 666 $css_properties ['display'] = 'printer'; 667 } 668 669 $background_color = $css_properties ['background-color']; 670 $image = $css_properties ['background-image'] ?? null; 671 $margin_top = $css_properties ['margin-top']; 672 $margin_right = $css_properties ['margin-right']; 673 $margin_bottom = $css_properties ['margin-bottom']; 674 $margin_left = $css_properties ['margin-left']; 675 $width = $attr ['width']; 676 677 // Open 2x1 table if image is present 678 // otherwise only a 1x1 table 679 $properties = array(); 680 $properties ['width'] = '100%'; 681 $properties ['align'] = 'center'; 682 $properties ['margin-top'] = $margin_top; 683 $properties ['margin-right'] = $margin_right; 684 $properties ['margin-bottom'] = $margin_bottom; 685 $properties ['margin-left'] = $margin_left; 686 687 $frame_props = array(); 688 if (!empty($css_properties ['border'])) { 689 $frame_props ['border'] = $css_properties ['border']; 690 } else { 691 $frame_props ['border'] = 'none'; 692 } 693 $frame_props ['min-height'] = '1cm'; 694 $frame_props ['width'] = $attr ['width']; 695 $frame_props ['float'] = $css_properties ['float']; 696 if ( self::$table_entr > 1 ) { 697 $frame_props ['anchor-type'] = 'as-char'; 698 } else { 699 $frame_props ['anchor-type'] = 'paragraph'; 700 } 701 $frame_props ['textarea-horizontal-align'] = 'left'; 702 $frame_props ['run-through'] = 'foreground'; 703 $frame_props ['vertical-pos'] = 'from-top'; 704 $frame_props ['vertical-rel'] = 'paragraph'; 705 $frame_props ['horizontal-pos'] = 'from-left'; 706 $frame_props ['horizontal-rel'] = 'paragraph'; 707 $frame_props ['wrap'] = 'parallel'; 708 $frame_props ['number-wrapped-paragraphs'] = 'no-limit'; 709 if (!empty($frame_props ['float']) && 710 $frame_props ['float'] != 'center') { 711 $frame_props ['margin-top'] = '0cm'; 712 $frame_props ['margin-right'] = '0cm'; 713 $frame_props ['margin-bottom'] = '0cm'; 714 $frame_props ['margin-left'] = '0cm'; 715 $frame_props ['padding-top'] = '0cm'; 716 $frame_props ['padding-bottom'] = '0cm'; 717 } else { 718 // No wrapping on not floating divs 719 $frame_props ['wrap'] = 'none'; 720 } 721 722 switch ($frame_props ['float']) { 723 case 'left': 724 if ( self::$table_entr == 1 ) { 725 $frame_props ['y'] = '0cm'; 726 $frame_props ['x'] = self::$box_left_pos.'cm'; 727 self::$box_left_pos += trim($frame_props ['width'], 'cm'); 728 } 729 $frame_props ['padding-left'] = '0cm'; 730 break; 731 case 'right': 732 $frame_props ['horizontal-rel'] = 'paragraph'; 733 $frame_props ['horizontal-pos'] = 'right'; 734 $frame_props ['padding-right'] = '0cm'; 735 break; 736 case 'center': 737 $frame_props ['horizontal-pos'] = 'center'; 738 break; 739 default: 740 $frame_props ['padding-left'] = '0cm'; 741 break; 742 } 743 $renderer->_odtOpenTextBoxUseProperties($frame_props); 744 745 $renderer->_odtTableOpenUseProperties($properties); 746 747 if (!empty($image)) { 748 $properties = array(); 749 $properties ['width'] = '2cm'; 750 $renderer->_odtTableAddColumnUseProperties($properties); 751 } 752 753 $properties = array(); 754 $renderer->_odtTableAddColumnUseProperties($properties); 755 756 $renderer->tablerow_open(); 757 758 if (!empty($image)) { 759 $properties = array(); 760 $properties ['vertical-align'] = 'middle'; 761 $properties ['text-align'] = 'center'; 762 $properties ['padding'] = '0.1cm'; 763 $properties ['background-color'] = $background_color; 764 765 $renderer->_odtTableCellOpenUseProperties($properties); 766 $renderer->_odtAddImage($image); 767 $renderer->tablecell_close(); 768 } 769 770 $properties = array(); 771 $properties ['vertical-align'] = 'middle'; 772 $properties ['padding'] = '0.3cm'; 773 $properties ['background-color'] = $background_color; 774 $properties ['border'] = 'none'; 775 $renderer->_odtTableCellOpenUseProperties($properties); 776 } 777 778 function renderODTCloseTable ($renderer) { 779 $renderer->tablecell_close(); 780 $renderer->tablerow_close(); 781 $renderer->_odtTableClose(); 782 $renderer->_odtCloseTextBox (); 783 $renderer->p_open(); 784 $renderer->p_close(); 785 786 self::$table_entr -= 1; 787 } 788 789 protected function getODTCommonStyleName ($class_string) { 790 static $map = array ( 791 'wrap_box' => 'Box', 'wrap_danger' => 'Danger', 'wrap_warning' => 'Warning', 792 'wrap_caution' => 'Caution', 'wrap_notice' => 'Notice', 'wrap_safety' => 'Safety', 793 'wrap_info' => 'Info', 'wrap_important' => 'Important', 'wrap_alert' => 'Alert', 794 'wrap_tip' => 'Tip', 'wrap_help' => 'Help', 'wrap_todo' => 'To do', 795 'wrap_download' => 'Download', 'wrap_hi' => 'Highlighted', 'wrap_spoiler' => 'Spoiler', 796 'wrap_leftalign' => 'Left aligned', 'wrap_rightalign' => 'Right aligned', 797 'wrap_centeralign' => 'Centered', 'wrap_justify' => 'Justify', 'wrap_em' => 'Emphasised', 798 'wrap_lo' => 'Less significant'); 799 $classes = explode(' ', $class_string); 800 $name = ''; 801 foreach ($classes as $class) { 802 if (array_key_exists($class, $map)) { 803 $name .= $map [$class]; 804 } 805 } 806 return ($name); 807 } 808} 809