1<?php 2 3require_once DOKU_PLUGIN . 'odt/ODT/ODTUnits.php'; 4require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php'; 5 6/** 7 * ODTTable: 8 * Class containing static code for handling tables. 9 * 10 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 11 */ 12class ODTTable 13{ 14 /** 15 * Open/start a table 16 * 17 * @param int $maxcols maximum number of columns 18 * @param int $numrows NOT IMPLEMENTED 19 */ 20 public static function tableOpen(ODTInternalParams $params, $maxcols = NULL, $numrows = NULL, $tableStyleName=NULL, $element=NULL, $attributes=NULL){ 21 if (!isset($element)) { 22 $element = 'table'; 23 } 24 $elementObj = $params->elementObj; 25 26 // Close any open paragraph. 27 $params->document->paragraphClose(); 28 29 // Do additional actions if the parent element is a list. 30 // In this case we need to finish the list and re-open it later 31 // after the table has been closed! --> tables may not be part of a list item in ODT! 32 33 $interrupted = false; 34 if (!isset($tableStyleName)) { 35 $tableStyleName = $params->document->getStyleName('table'); 36 } 37 38 $list_item = $params->document->state->getCurrentListItem(); 39 if (isset($list_item)) { 40 // We are in a list item. Query indentation settings. 41 $list = $list_item->getList(); 42 if (isset($list)) { 43 $list_style_name = $list->getStyleName(); 44 $list_style = $params->document->getStyle($list_style_name); 45 if (isset($list_style)) { 46 // The list level stored in the list item/from the parser 47 // might not be correct. Count 'list' states to get level. 48 $level = $params->document->state->countClass('list'); 49 50 // Create a table style for indenting the table. 51 // We try to achieve this by substracting the list indentation 52 // from the width of the table and right align it! 53 // (if not done yet, the name must be unique!) 54 $count = $params->document->state->getElementCount('table')+1; 55 $style_name = 'Table'.$count.'_Indentation_Level'.$level; 56 if (!$params->document->styleExists($style_name)) { 57 $style_obj = clone $params->document->getStyle($tableStyleName); 58 $style_obj->setProperty('style-name', $style_name); 59 if (isset($style_obj)) { 60 $max = $params->document->getAbsWidthMindMargins(); 61 $indent = 0 + ODTUnits::getDigits($list_style->getPropertyFromLevel($level, 'margin-left')); 62 $style_obj->setProperty('margin-left', ($indent).'cm'); 63 if ($style_obj->getProperty('width') == NULL && $style_obj->getProperty('rel-width')) { 64 $style_obj->setProperty('width', ($max-$indent).'cm'); 65 } 66 $style_obj->setProperty('align', 'left'); 67 $params->document->addAutomaticStyle($style_obj); 68 } 69 } 70 $tableStyleName = $style_name; 71 } 72 } 73 74 // Close all open lists and remember their style (may be nested!) 75 $lists = array(); 76 $first = true; 77 $iterations = 0; 78 $list = $params->document->state->getCurrentList(); 79 while (isset($list)) 80 { 81 // Close list items 82 if ($first == true) { 83 $first = false; 84 $params->document->listContentClose(); 85 } 86 $params->document->listItemClose(); 87 88 // Now we are in the list state! 89 // Get the lists style name before closing it. 90 $lists [] = $list->getStyleName(); 91 // Reset saved last paragraph position to -1 to prevent change of the paragraph style 92 $list->setListLastParagraphPosition(-1); 93 $params->document->listClose(); 94 95 if (!isset($params->document->state) || 96 $params->document->state->getCurrent()->getElementName() == 'root') { 97 break; 98 } 99 100 // List has been closed (and removed from stack). Get next. 101 $list = $params->document->state->getCurrentList(); 102 103 // Just to prevent endless loops in case of an error! 104 $iterations++; 105 if ($iterations == 50) { 106 $params->content .= 'Error: ENDLESS LOOP!'; 107 break; 108 } 109 } 110 111 $interrupted = true; 112 } 113 114 if (!isset($elementObj)) { 115 $properties = array(); 116 ODTUtility::openHTMLElement ($params, $properties, $element, $attributes); 117 } 118 119 $table = new ODTElementTable($tableStyleName, $maxcols, $numrows); 120 $params->document->state->enter($table); 121 if ($interrupted == true) { 122 // Set marker that list has been interrupted 123 $table->setListInterrupted(true); 124 125 // Save the lists array as temporary data 126 // in THIS state because this is the state that we get back 127 // to in table_close!!! 128 // (we closed the ODT list, we can't access its state info anymore! 129 // So we use the table state to save the style name!) 130 $table->setTemp($lists); 131 } 132 $table->setHTMLElement ($element); 133 134 $params->content .= $table->getOpeningTag(); 135 } 136 137 /** 138 * Close/finish a table 139 */ 140 public static function tableClose(ODTInternalParams $params){ 141 $table = $params->document->state->getCurrentTable(); 142 if (!isset($table)) { 143 // ??? Error. Not table found. 144 return; 145 } 146 147 if ($params->document->state->getInTableRow()) { 148 // If we are still inside a table row then close it first, 149 // to prevent an error or broken document. 150 $params->document->tableRowClose(); 151 } 152 153 $interrupted = $table->getListInterrupted(); 154 $lists = NULL; 155 if ($interrupted) { 156 $lists = $table->getTemp(); 157 } 158 159 // Eventually adjust table width. 160 $table->adjustWidth ($params); 161 162 // Close the table. 163 ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement()); 164 $params->content .= $table->getClosingTag($params->content); 165 $params->document->state->leave(); 166 167 // Do additional actions required if we interrupted a list, 168 // see table_open() 169 if ($interrupted) { 170 // Re-open list(s) with original style! 171 // (in revers order of lists array) 172 $max = count($lists); 173 for ($index = $max ; $index > 0 ; $index--) { 174 $params->document->listOpen(true, $lists [$index-1]); 175 176 // If this is not the most inner list then we need to open 177 // a list item too! 178 if ($index > 0) { 179 $params->document->listItemOpen($max-$index); 180 } 181 } 182 183 // DO NOT set marker that list is not interrupted anymore, yet! 184 // The renderer will still call listcontent_close and listitem_close! 185 // The marker will only be reset on the next call from the renderer to listitem_open!!! 186 //$table->setListInterrupted(false); 187 } 188 } 189 190 /** 191 * @param array $properties 192 */ 193 public static function tableAddColumn (ODTInternalParams $params, $styleNameSet=NULL, &$styleNameGet=NULL){ 194 // Create new column 195 $column = new ODTElementTableColumn(); 196 $params->document->state->enter($column); 197 198 if (isset($styleNameSet)) { 199 // Change automatically assigned style name. 200 $column->setStyleName($styleNameSet); 201 } 202 203 // Return style name to caller. 204 $styleNameGet = $column->getStyleName(); 205 206 // Never create any new document content here!!! 207 // Columns have already been added on table open or are 208 // re-written on table close. 209 $params->document->state->leave(); 210 } 211 212 /** 213 * Open a table row 214 */ 215 public static function tableRowOpen(ODTInternalParams $params, $styleName=NULL, $element=NULL, $attributes=NULL){ 216 if ($params->document->state->getInTableRow()) { 217 // If we are still inside a table row then close it first, 218 // to prevent an error or broken document. 219 $params->document->tableRowClose(); 220 } 221 222 if (!isset($element)) { 223 $element = 'tr'; 224 } 225 226 if (!isset($params->elementObj)) { 227 $properties = array(); 228 ODTUtility::openHTMLElement ($params, $properties, $element, $attributes); 229 } 230 231 $row = new ODTElementTableRow($styleName); 232 $params->document->state->enter($row); 233 $params->content .= $row->getOpeningTag(); 234 $row->setHTMLElement ($element); 235 } 236 237 /** 238 * Close a table row 239 */ 240 public static function tableRowClose(ODTInternalParams $params){ 241 if ($params->document->state->getInTableCell()) { 242 // If we are still inside a table cell then close it first, 243 // to prevent an error or broken document. 244 $params->document->tableCellClose(); 245 } 246 247 ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement()); 248 $params->document->closeCurrentElement(); 249 } 250 251 /** 252 * Open a table header cell 253 * 254 * @param int $colspan 255 * @param int $rowspan 256 * @param string $align left|center|right 257 */ 258 public static function tableHeaderOpen(ODTInternalParams $params, $colspan = 1, $rowspan = 1, $align = "left", $cellStyle=NULL, $paragraphStyle=NULL, $element=NULL, $attributes=NULL){ 259 if (!isset($element)) { 260 $element = 'th'; 261 } 262 // Are style names given? If not, use defaults. 263 if (empty($cellStyle)) { 264 $cellStyle = $params->document->getStyleName('table header'); 265 } 266 if (empty($paragraphStyle)) { 267 $paragraphStyle = $params->document->getStyleName('table heading'); 268 } 269 270 if (!isset($params->elementObj)) { 271 $properties = array(); 272 ODTUtility::openHTMLElement ($params, $properties, $element, $attributes); 273 } 274 275 // ODT has no element for the table header. 276 // We mark the state with a differnt class to be able 277 // to differ between a normal cell and a header cell. 278 $header_cell = new ODTElementTableHeaderCell 279 ($cellStyle, $colspan, $rowspan); 280 $params->document->state->enter($header_cell); 281 $header_cell->setHTMLElement ($element); 282 283 // Encode table (header) cell. 284 $params->content .= $header_cell->getOpeningTag(); 285 286 // Open new paragraph with table heading style. 287 $params->document->paragraphOpen($paragraphStyle); 288 } 289 290 /** 291 * Close a table header cell 292 */ 293 public static function tableHeaderClose(ODTInternalParams $params){ 294 $params->document->paragraphClose(); 295 296 ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement()); 297 $params->document->closeCurrentElement(); 298 } 299 300 /** 301 * Open a table cell 302 * 303 * @param int $colspan 304 * @param int $rowspan 305 * @param string $align left|center|right 306 */ 307 public static function tableCellOpen(ODTInternalParams $params, $colspan = 1, $rowspan = 1, $align = "left", $cellStyle=NULL, $paragraphStyle=NULL, $element=NULL, $attributes=NULL){ 308 if (!isset($element)) { 309 $element = 'td'; 310 } 311 312 if ($params->document->state->getInTableCell()) { 313 // If we are still inside a table cell then close it first, 314 // to prevent an error or broken document. 315 $params->document->tableCellClose(); 316 } 317 318 // Are style names given? If not, use defaults. 319 if (empty($cellStyle)) { 320 $cellStyle = $params->document->getStyleName('table cell'); 321 } 322 if (empty($paragraphStyle)) { 323 // Open paragraph with required alignment. 324 if (!$align) $align = "left"; 325 $paragraphStyle = $params->document->getStyleName('tablealign '.$align); 326 } 327 328 if (!isset($params->elementObj)) { 329 $properties = array(); 330 ODTUtility::openHTMLElement ($params, $properties, $element, $attributes); 331 } 332 333 $cell = new ODTElementTableCell 334 ($cellStyle, $colspan, $rowspan); 335 $params->document->state->enter($cell); 336 $cell->setHTMLElement ($element); 337 338 // Encode table cell. 339 $params->content .= $cell->getOpeningTag(); 340 341 // Open paragraph. 342 $params->document->paragraphOpen($paragraphStyle); 343 } 344 345 /** 346 * Close a table cell 347 */ 348 public static function tableCellClose(ODTInternalParams $params){ 349 $params->document->paragraphClose(); 350 351 ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement()); 352 $params->document->closeCurrentElement(); 353 } 354 355 /** 356 * This function opens a new table using the style as set in the imported CSS $import. 357 * So, the function requires the helper class 'helper_plugin_odt_cssimport'. 358 * The CSS style is selected by the element type 'td' and the specified classes in $classes. 359 * 360 * This function calls _odtTableOpenUseProperties. See the function description for supported properties. 361 * 362 * The table should be closed by calling 'table_close()'. 363 * 364 * @author LarsDW223 365 * 366 * @param cssimportnew $import 367 * @param $classes 368 * @param null $baseURL 369 * @param null $element 370 * @param null $maxcols 371 * @param null $numrows 372 */ 373 public static function tableOpenUseCSS(ODTInternalParams $params, $maxcols=NULL, $numrows=NULL, $element=NULL, $attributes=NULL){ 374 if (!isset($element)) { 375 $element = 'table'; 376 } 377 378 $properties = array(); 379 ODTUtility::openHTMLElement ($params, $properties, $element, $attributes); 380 $params->elementObj = $params->htmlStack->getCurrentElement(); 381 382 self::tableOpenUseProperties($params, $properties, $maxcols, $numrows); 383 } 384 385 /** 386 * This function opens a new table using the style as set in the assoziative array $properties. 387 * The parameters in the array should be named as the CSS property names e.g. 'width'. 388 * 389 * The currently supported properties are: 390 * width, border-collapse, background-color 391 * 392 * The table must be closed by calling 'table_close'. 393 * 394 * @author LarsDW223 395 * 396 * @param array $properties 397 * @param null $maxcols 398 * @param null $numrows 399 */ 400 public static function tableOpenUseProperties (ODTInternalParams $params, $properties, $maxcols = 0, $numrows = 0){ 401 $elementObj = $params->elementObj; 402 403 // Eventually adjust table width. 404 if ( !empty ($properties ['width']) ) { 405 if ( $properties ['width'] [strlen($properties ['width'])-1] != '%' ) { 406 // Width has got an absolute value. 407 // Some units are not supported by ODT for table width (e.g. 'px'). 408 // So we better convert it to points. 409 $properties ['width'] = $params->document->toPoints($properties ['width'], 'x'); 410 } 411 } 412 413 // Create style. 414 // FIXME: fix disabled_props, ask state for current max width... 415 $style_obj = ODTTableStyle::createTableTableStyle($properties, NULL, 17); 416 $params->document->addAutomaticStyle($style_obj); 417 $style_name = $style_obj->getProperty('style-name'); 418 419 // Open the table referencing our style. 420 $params->elementObj = $elementObj; 421 self::tableOpen($params, $maxcols, $numrows, $style_name); 422 } 423 424 /** 425 * @param array $properties 426 */ 427 public static function tableAddColumnUseProperties (ODTInternalParams $params, array $properties = NULL){ 428 // Add column and set/query assigned style name 429 $styleName = $properties ['style-name']; 430 $styleNameGet = ''; 431 self::tableAddColumn ($params, $styleName, $styleNameGet); 432 433 // Overwrite/Create column style for actual column 434 $properties ['style-name'] = $styleNameGet; 435 $style_obj = ODTTableColumnStyle::createTableColumnStyle ($properties); 436 $params->document->addAutomaticStyle($style_obj); 437 } 438 439 /** 440 * @param helper_plugin_odt_cssimport $import 441 * @param $classes 442 * @param null $baseURL 443 * @param null $element 444 * @param int $colspan 445 * @param int $rowspan 446 */ 447 public static function tableHeaderOpenUseCSS(ODTInternalParams $params, $colspan = 1, $rowspan = 1, $element=NULL, $attributes=NULL){ 448 if (!isset($element)) { 449 $element = 'th'; 450 } 451 452 $properties = array(); 453 ODTUtility::openHTMLElement ($params, $properties, $element, $attributes); 454 $params->elementObj = $params->htmlStack->getCurrentElement(); 455 456 self::tableHeaderOpenUseProperties($params, $properties, $colspan, $rowspan); 457 } 458 459 /** 460 * @param null $properties 461 * @param int $colspan 462 * @param int $rowspan 463 */ 464 public static function tableHeaderOpenUseProperties (ODTInternalParams $params, $properties = NULL, $colspan = 1, $rowspan = 1){ 465 // Open cell, second parameter MUST BE true to indicate we are in the header. 466 self::tableCellOpenUsePropertiesInternal ($params, $properties, true, $colspan, $rowspan); 467 } 468 469 /** 470 * This function opens a new table row using the style as set in the imported CSS $import. 471 * So, the function requires the helper class 'helper_plugin_odt_cssimport'. 472 * The CSS style is selected by the element type 'td' and the specified classes in $classes. 473 * 474 * This function calls _odtTableRowOpenUseProperties. See the function description for supported properties. 475 * 476 * The row should be closed by calling 'tablerow_close()'. 477 * 478 * @author LarsDW223 479 * @param helper_plugin_odt_cssimport $import 480 * @param $classes 481 * @param null $baseURL 482 * @param null $element 483 */ 484 public static function tableRowOpenUseCSS(ODTInternalParams $params, $element=NULL, $attributes=NULL){ 485 if (!isset($element)) { 486 $element = 'tr'; 487 } 488 489 $properties = array(); 490 ODTUtility::openHTMLElement ($params, $properties, $element, $attributes); 491 $params->elementObj = $params->htmlStack->getCurrentElement(); 492 493 self::tableRowOpenUseProperties($params, $properties); 494 } 495 496 /** 497 * @param array $properties 498 */ 499 public static function tableRowOpenUseProperties (ODTInternalParams $params, $properties){ 500 // Create style. 501 $style_obj = ODTTableRowStyle::createTableRowStyle ($properties); 502 $params->document->addAutomaticStyle($style_obj); 503 $style_name = $style_obj->getProperty('style-name'); 504 505 // Open table row with our new style. 506 self::tableRowOpen($params, $style_name); 507 } 508 509 /** 510 * This function opens a new table cell using the style as set in the imported CSS $import. 511 * So, the function requires the helper class 'helper_plugin_odt_cssimport'. 512 * The CSS style is selected by the element type 'td' and the specified classes in $classes. 513 * 514 * This function calls _odtTableCellOpenUseProperties. See the function description for supported properties. 515 * 516 * The cell should be closed by calling 'tablecell_close()'. 517 * 518 * @author LarsDW223 519 * 520 * @param helper_plugin_odt_cssimport $import 521 * @param $classes 522 * @param null $baseURL 523 * @param null $element 524 */ 525 public static function tableCellOpenUseCSS(ODTInternalParams $params, $element=NULL, $attributes=NULL, $colspan = 1, $rowspan = 1){ 526 if (!isset($element)) { 527 $element = 'td'; 528 } 529 530 $properties = array(); 531 ODTUtility::openHTMLElement ($params, $properties, $element, $attributes); 532 $params->elementObj = $params->htmlStack->getCurrentElement(); 533 534 self::tableCellOpenUseProperties($params, $properties, $colspan, $rowspan); 535 } 536 537 /** 538 * @param $properties 539 */ 540 public static function tableCellOpenUseProperties (ODTInternalParams $params, $properties = NULL, $colspan = 1, $rowspan = 1){ 541 self::tableCellOpenUsePropertiesInternal ($params, $properties, false, $colspan, $rowspan); 542 } 543 544 /** 545 * @param $properties 546 * @param bool $inHeader 547 * @param int $colspan 548 * @param int $rowspan 549 */ 550 static protected function tableCellOpenUsePropertiesInternal (ODTInternalParams $params, $properties, $inHeader = false, $colspan = 1, $rowspan = 1){ 551 $disabled = array (); 552 553 // Create style name. (Re-enable background-color!) 554 $style_obj = ODTTableCellStyle::createTableCellStyle ($properties); 555 $params->document->addAutomaticStyle($style_obj); 556 $style_name = $style_obj->getProperty('style-name'); 557 558 // Create a paragraph style for the paragraph within the cell. 559 // Disable properties that belong to the table cell style. 560 $disabled ['border'] = 1; 561 $disabled ['border-left'] = 1; 562 $disabled ['border-right'] = 1; 563 $disabled ['border-top'] = 1; 564 $disabled ['border-bottom'] = 1; 565 $disabled ['background-color'] = 1; 566 $disabled ['background-image'] = 1; 567 $disabled ['vertical-align'] = 1; 568 $style_obj = ODTParagraphStyle::createParagraphStyle ($properties, $disabled); 569 $params->document->addAutomaticStyle($style_obj); 570 $style_name_paragraph = $style_obj->getProperty('style-name'); 571 572 // Open header or normal cell. 573 if ($inHeader) { 574 self::tableHeaderOpen($params, $colspan, $rowspan, NULL, $style_name, $style_name_paragraph); 575 } else { 576 self::tableCellOpen($params, $colspan, $rowspan, NULL, $style_name, $style_name_paragraph); 577 } 578 579 // There might be properties in the table header cell/normal cell which in ODT belong to the 580 // column, e.g. 'width'. So eventually adjust column style. 581 self::adjustColumnStyle($params, $properties); 582 } 583 584 static protected function adjustColumnStyle(ODTInternalParams $params, array $properties) { 585 $table = $params->document->state->getCurrentTable(); 586 if (!isset($table)) { 587 // ??? Error. Not table found. 588 return; 589 } 590 $curr_column = $table->getTableCurrentColumn(); 591 $table_column_styles = $table->getTableColumnStyles(); 592 $style_name = $table_column_styles [$curr_column-1]; 593 $style_obj = $params->document->getStyle($style_name); 594 595 if (isset($style_obj)) { 596 if (!empty($properties ['width'])) { 597 $width = $properties ['width']; 598 $length = strlen ($width); 599 $width = $params->document->toPoints($width, 'x'); 600 $style_obj->setProperty('column-width', $width); 601 } 602 } else { 603 self::tableAddColumnUseProperties ($params, $properties); 604 } 605 } 606} 607