Files
dokuwiki-plugins/lib/plugins/odt/ODT/ODTTable.php
Trevor Batley bce7dd054a add contents
2025-10-09 15:04:29 +11:00

607 lines
23 KiB
PHP

<?php
require_once DOKU_PLUGIN . 'odt/ODT/ODTUnits.php';
require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
/**
* ODTTable:
* Class containing static code for handling tables.
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
*/
class ODTTable
{
/**
* Open/start a table
*
* @param int $maxcols maximum number of columns
* @param int $numrows NOT IMPLEMENTED
*/
public static function tableOpen(ODTInternalParams $params, $maxcols = NULL, $numrows = NULL, $tableStyleName=NULL, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'table';
}
$elementObj = $params->elementObj;
// Close any open paragraph.
$params->document->paragraphClose();
// Do additional actions if the parent element is a list.
// In this case we need to finish the list and re-open it later
// after the table has been closed! --> tables may not be part of a list item in ODT!
$interrupted = false;
if ($tableStyleName == NULL) {
$tableStyleName = $params->document->getStyleName('table');
}
$list_item = $params->document->state->getCurrentListItem();
if ($list_item != NULL) {
// We are in a list item. Query indentation settings.
$list = $list_item->getList();
if ($list != NULL) {
$list_style_name = $list->getStyleName();
$list_style = $params->document->getStyle($list_style_name);
if ($list_style != NULL) {
// The list level stored in the list item/from the parser
// might not be correct. Count 'list' states to get level.
$level = $params->document->state->countClass('list');
// Create a table style for indenting the table.
// We try to achieve this by substracting the list indentation
// from the width of the table and right align it!
// (if not done yet, the name must be unique!)
$count = $params->document->state->getElementCount('table')+1;
$style_name = 'Table'.$count.'_Indentation_Level'.$level;
if (!$params->document->styleExists($style_name)) {
$style_obj = clone $params->document->getStyle($tableStyleName);
$style_obj->setProperty('style-name', $style_name);
if ($style_obj != NULL) {
$max = $params->document->getAbsWidthMindMargins();
$indent = 0 + ODTUnits::getDigits($list_style->getPropertyFromLevel($level, 'margin-left'));
$style_obj->setProperty('margin-left', ($indent).'cm');
if ($style_obj->getProperty('width') == NULL && $style_obj->getProperty('rel-width')) {
$style_obj->setProperty('width', ($max-$indent).'cm');
}
$style_obj->setProperty('align', 'left');
$params->document->addAutomaticStyle($style_obj);
}
}
$tableStyleName = $style_name;
}
}
// Close all open lists and remember their style (may be nested!)
$lists = array();
$first = true;
$iterations = 0;
$list = $params->document->state->getCurrentList();
while ($list != NULL)
{
// Close list items
if ($first == true) {
$first = false;
$params->document->listContentClose();
}
$params->document->listItemClose();
// Now we are in the list state!
// Get the lists style name before closing it.
$lists [] = $list->getStyleName();
// Reset saved last paragraph position to -1 to prevent change of the paragraph style
$list->setListLastParagraphPosition(-1);
$params->document->listClose();
if ($params->document->state == NULL ||
$params->document->state->getCurrent()->getElementName() == 'root') {
break;
}
// List has been closed (and removed from stack). Get next.
$list = $params->document->state->getCurrentList();
// Just to prevent endless loops in case of an error!
$iterations++;
if ($iterations == 50) {
$params->content .= 'Error: ENDLESS LOOP!';
break;
}
}
$interrupted = true;
}
if ($elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
$table = new ODTElementTable($tableStyleName, $maxcols, $numrows);
$params->document->state->enter($table);
if ($interrupted == true) {
// Set marker that list has been interrupted
$table->setListInterrupted(true);
// Save the lists array as temporary data
// in THIS state because this is the state that we get back
// to in table_close!!!
// (we closed the ODT list, we can't access its state info anymore!
// So we use the table state to save the style name!)
$table->setTemp($lists);
}
$table->setHTMLElement ($element);
$params->content .= $table->getOpeningTag();
}
/**
* Close/finish a table
*/
public static function tableClose(ODTInternalParams $params){
$table = $params->document->state->getCurrentTable();
if ($table == NULL) {
// ??? Error. Not table found.
return;
}
if ($params->document->state->getInTableRow()) {
// If we are still inside a table row then close it first,
// to prevent an error or broken document.
$params->document->tableRowClose();
}
$interrupted = $table->getListInterrupted();
$lists = NULL;
if ($interrupted) {
$lists = $table->getTemp();
}
// Eventually adjust table width.
$table->adjustWidth ($params);
// Close the table.
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->content .= $table->getClosingTag($params->content);
$params->document->state->leave();
// Do additional actions required if we interrupted a list,
// see table_open()
if ($interrupted) {
// Re-open list(s) with original style!
// (in revers order of lists array)
$max = count($lists);
for ($index = $max ; $index > 0 ; $index--) {
$params->document->listOpen(true, $lists [$index-1]);
// If this is not the most inner list then we need to open
// a list item too!
if ($index > 0) {
$params->document->listItemOpen($max-$index);
}
}
// DO NOT set marker that list is not interrupted anymore, yet!
// The renderer will still call listcontent_close and listitem_close!
// The marker will only be reset on the next call from the renderer to listitem_open!!!
//$table->setListInterrupted(false);
}
}
/**
* @param array $properties
*/
public static function tableAddColumn (ODTInternalParams $params, $styleNameSet=NULL, &$styleNameGet=NULL){
// Create new column
$column = new ODTElementTableColumn();
$params->document->state->enter($column);
if ($styleNameSet != NULL) {
// Change automatically assigned style name.
$column->setStyleName($styleNameSet);
}
// Return style name to caller.
$styleNameGet = $column->getStyleName();
// Never create any new document content here!!!
// Columns have already been added on table open or are
// re-written on table close.
$params->document->state->leave();
}
/**
* Open a table row
*/
public static function tableRowOpen(ODTInternalParams $params, $styleName=NULL, $element=NULL, $attributes=NULL){
if ($params->document->state->getInTableRow()) {
// If we are still inside a table row then close it first,
// to prevent an error or broken document.
$params->document->tableRowClose();
}
if ($element == NULL) {
$element = 'tr';
}
if ($params->elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
$row = new ODTElementTableRow($styleName);
$params->document->state->enter($row);
$params->content .= $row->getOpeningTag();
$row->setHTMLElement ($element);
}
/**
* Close a table row
*/
public static function tableRowClose(ODTInternalParams $params){
if ($params->document->state->getInTableCell()) {
// If we are still inside a table cell then close it first,
// to prevent an error or broken document.
$params->document->tableCellClose();
}
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
}
/**
* Open a table header cell
*
* @param int $colspan
* @param int $rowspan
* @param string $align left|center|right
*/
public static function tableHeaderOpen(ODTInternalParams $params, $colspan = 1, $rowspan = 1, $align = "left", $cellStyle=NULL, $paragraphStyle=NULL, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'th';
}
// Are style names given? If not, use defaults.
if (empty($cellStyle)) {
$cellStyle = $params->document->getStyleName('table header');
}
if (empty($paragraphStyle)) {
$paragraphStyle = $params->document->getStyleName('table heading');
}
if ($params->elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
// ODT has no element for the table header.
// We mark the state with a differnt class to be able
// to differ between a normal cell and a header cell.
$header_cell = new ODTElementTableHeaderCell
($cellStyle, $colspan, $rowspan);
$params->document->state->enter($header_cell);
$header_cell->setHTMLElement ($element);
// Encode table (header) cell.
$params->content .= $header_cell->getOpeningTag();
// Open new paragraph with table heading style.
$params->document->paragraphOpen($paragraphStyle);
}
/**
* Close a table header cell
*/
public static function tableHeaderClose(ODTInternalParams $params){
$params->document->paragraphClose();
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
}
/**
* Open a table cell
*
* @param int $colspan
* @param int $rowspan
* @param string $align left|center|right
*/
public static function tableCellOpen(ODTInternalParams $params, $colspan = 1, $rowspan = 1, $align = "left", $cellStyle=NULL, $paragraphStyle=NULL, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'td';
}
if ($params->document->state->getInTableCell()) {
// If we are still inside a table cell then close it first,
// to prevent an error or broken document.
$params->document->tableCellClose();
}
// Are style names given? If not, use defaults.
if (empty($cellStyle)) {
$cellStyle = $params->document->getStyleName('table cell');
}
if (empty($paragraphStyle)) {
// Open paragraph with required alignment.
if (!$align) $align = "left";
$paragraphStyle = $params->document->getStyleName('tablealign '.$align);
}
if ($params->elementObj == NULL) {
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
}
$cell = new ODTElementTableCell
($cellStyle, $colspan, $rowspan);
$params->document->state->enter($cell);
$cell->setHTMLElement ($element);
// Encode table cell.
$params->content .= $cell->getOpeningTag();
// Open paragraph.
$params->document->paragraphOpen($paragraphStyle);
}
/**
* Close a table cell
*/
public static function tableCellClose(ODTInternalParams $params){
$params->document->paragraphClose();
ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
$params->document->closeCurrentElement();
}
/**
* This function opens a new table using the style as set in the imported CSS $import.
* So, the function requires the helper class 'helper_plugin_odt_cssimport'.
* The CSS style is selected by the element type 'td' and the specified classes in $classes.
*
* This function calls _odtTableOpenUseProperties. See the function description for supported properties.
*
* The table should be closed by calling 'table_close()'.
*
* @author LarsDW223
*
* @param cssimportnew $import
* @param $classes
* @param null $baseURL
* @param null $element
* @param null $maxcols
* @param null $numrows
*/
public static function tableOpenUseCSS(ODTInternalParams $params, $maxcols=NULL, $numrows=NULL, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'table';
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::tableOpenUseProperties($params, $properties, $maxcols, $numrows);
}
/**
* This function opens a new table using the style as set in the assoziative array $properties.
* The parameters in the array should be named as the CSS property names e.g. 'width'.
*
* The currently supported properties are:
* width, border-collapse, background-color
*
* The table must be closed by calling 'table_close'.
*
* @author LarsDW223
*
* @param array $properties
* @param null $maxcols
* @param null $numrows
*/
public static function tableOpenUseProperties (ODTInternalParams $params, $properties, $maxcols = 0, $numrows = 0){
$elementObj = $params->elementObj;
// Eventually adjust table width.
if ( !empty ($properties ['width']) ) {
if ( $properties ['width'] [strlen($properties ['width'])-1] != '%' ) {
// Width has got an absolute value.
// Some units are not supported by ODT for table width (e.g. 'px').
// So we better convert it to points.
$properties ['width'] = $params->document->toPoints($properties ['width'], 'x');
}
}
// Create style.
// FIXME: fix disabled_props, ask state for current max width...
$style_obj = ODTTableStyle::createTableTableStyle($properties, NULL, 17);
$params->document->addAutomaticStyle($style_obj);
$style_name = $style_obj->getProperty('style-name');
// Open the table referencing our style.
$params->elementObj = $elementObj;
self::tableOpen($params, $maxcols, $numrows, $style_name);
}
/**
* @param array $properties
*/
public static function tableAddColumnUseProperties (ODTInternalParams $params, array $properties = NULL){
// Add column and set/query assigned style name
$styleName = $properties ['style-name'];
$styleNameGet = '';
self::tableAddColumn ($params, $styleName, $styleNameGet);
// Overwrite/Create column style for actual column
$properties ['style-name'] = $styleNameGet;
$style_obj = ODTTableColumnStyle::createTableColumnStyle ($properties);
$params->document->addAutomaticStyle($style_obj);
}
/**
* @param helper_plugin_odt_cssimport $import
* @param $classes
* @param null $baseURL
* @param null $element
* @param int $colspan
* @param int $rowspan
*/
public static function tableHeaderOpenUseCSS(ODTInternalParams $params, $colspan = 1, $rowspan = 1, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'th';
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::tableHeaderOpenUseProperties($params, $properties, $colspan, $rowspan);
}
/**
* @param null $properties
* @param int $colspan
* @param int $rowspan
*/
public static function tableHeaderOpenUseProperties (ODTInternalParams $params, $properties = NULL, $colspan = 1, $rowspan = 1){
// Open cell, second parameter MUST BE true to indicate we are in the header.
self::tableCellOpenUsePropertiesInternal ($params, $properties, true, $colspan, $rowspan);
}
/**
* This function opens a new table row using the style as set in the imported CSS $import.
* So, the function requires the helper class 'helper_plugin_odt_cssimport'.
* The CSS style is selected by the element type 'td' and the specified classes in $classes.
*
* This function calls _odtTableRowOpenUseProperties. See the function description for supported properties.
*
* The row should be closed by calling 'tablerow_close()'.
*
* @author LarsDW223
* @param helper_plugin_odt_cssimport $import
* @param $classes
* @param null $baseURL
* @param null $element
*/
public static function tableRowOpenUseCSS(ODTInternalParams $params, $element=NULL, $attributes=NULL){
if ($element == NULL) {
$element = 'tr';
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::tableRowOpenUseProperties($params, $properties);
}
/**
* @param array $properties
*/
public static function tableRowOpenUseProperties (ODTInternalParams $params, $properties){
// Create style.
$style_obj = ODTTableRowStyle::createTableRowStyle ($properties);
$params->document->addAutomaticStyle($style_obj);
$style_name = $style_obj->getProperty('style-name');
// Open table row with our new style.
self::tableRowOpen($params, $style_name);
}
/**
* This function opens a new table cell using the style as set in the imported CSS $import.
* So, the function requires the helper class 'helper_plugin_odt_cssimport'.
* The CSS style is selected by the element type 'td' and the specified classes in $classes.
*
* This function calls _odtTableCellOpenUseProperties. See the function description for supported properties.
*
* The cell should be closed by calling 'tablecell_close()'.
*
* @author LarsDW223
*
* @param helper_plugin_odt_cssimport $import
* @param $classes
* @param null $baseURL
* @param null $element
*/
public static function tableCellOpenUseCSS(ODTInternalParams $params, $element=NULL, $attributes=NULL, $colspan = 1, $rowspan = 1){
if ($element == NULL) {
$element = 'td';
}
$properties = array();
ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
$params->elementObj = $params->htmlStack->getCurrentElement();
self::tableCellOpenUseProperties($params, $properties, $colspan, $rowspan);
}
/**
* @param $properties
*/
public static function tableCellOpenUseProperties (ODTInternalParams $params, $properties = NULL, $colspan = 1, $rowspan = 1){
self::tableCellOpenUsePropertiesInternal ($params, $properties, false, $colspan, $rowspan);
}
/**
* @param $properties
* @param bool $inHeader
* @param int $colspan
* @param int $rowspan
*/
static protected function tableCellOpenUsePropertiesInternal (ODTInternalParams $params, $properties, $inHeader = false, $colspan = 1, $rowspan = 1){
$disabled = array ();
// Create style name. (Re-enable background-color!)
$style_obj = ODTTableCellStyle::createTableCellStyle ($properties);
$params->document->addAutomaticStyle($style_obj);
$style_name = $style_obj->getProperty('style-name');
// Create a paragraph style for the paragraph within the cell.
// Disable properties that belong to the table cell style.
$disabled ['border'] = 1;
$disabled ['border-left'] = 1;
$disabled ['border-right'] = 1;
$disabled ['border-top'] = 1;
$disabled ['border-bottom'] = 1;
$disabled ['background-color'] = 1;
$disabled ['background-image'] = 1;
$disabled ['vertical-align'] = 1;
$style_obj = ODTParagraphStyle::createParagraphStyle ($properties, $disabled);
$params->document->addAutomaticStyle($style_obj);
$style_name_paragraph = $style_obj->getProperty('style-name');
// Open header or normal cell.
if ($inHeader) {
self::tableHeaderOpen($params, $colspan, $rowspan, NULL, $style_name, $style_name_paragraph);
} else {
self::tableCellOpen($params, $colspan, $rowspan, NULL, $style_name, $style_name_paragraph);
}
// There might be properties in the table header cell/normal cell which in ODT belong to the
// column, e.g. 'width'. So eventually adjust column style.
self::adjustColumnStyle($params, $properties);
}
static protected function adjustColumnStyle(ODTInternalParams $params, array $properties) {
$table = $params->document->state->getCurrentTable();
if ($table == NULL) {
// ??? Error. Not table found.
return;
}
$curr_column = $table->getTableCurrentColumn();
$table_column_styles = $table->getTableColumnStyles();
$style_name = $table_column_styles [$curr_column-1];
$style_obj = $params->document->getStyle($style_name);
if ($style_obj != NULL) {
if (!empty($properties ['width'])) {
$width = $properties ['width'];
$length = strlen ($width);
$width = $params->document->toPoints($width, 'x');
$style_obj->setProperty('column-width', $width);
}
} else {
self::tableAddColumnUseProperties ($params, $properties);
}
}
}