* Based on plugin by Michael Arlt */ class syntax_plugin_columns extends DokuWiki_Syntax_Plugin { private $mode; private $lexerSyntax; private $syntax; private $xhtmlRenderer; private $odtRenderer; /** * Constructor */ public function __construct() { $this->mode = substr(get_class($this), 7); $columns = $this->getColumnsTagName(); $newColumn = $this->getNewColumnTagName(); if ($this->getConf('wrapnewcol') == 1) { $newColumnLexer = '<' . $newColumn . '(?:>|\s.*?>)'; $newColumnHandler = '<' . $newColumn . '(.*?)>'; } else { $newColumnLexer = $newColumn; $newColumnHandler = $newColumn; } $enterLexer = '<' . $columns . '(?:>|\s.*?>)'; $enterHandler = '<' . $columns . '(.*?)>'; $exit = '<\/' . $columns . '>'; $this->lexerSyntax['enter'] = $enterLexer; $this->lexerSyntax['newcol'] = $newColumnLexer; $this->lexerSyntax['exit'] = $exit; $this->syntax[DOKU_LEXER_ENTER] = '/' . $enterHandler . '/'; $this->syntax[DOKU_LEXER_MATCHED] = '/' . $newColumnHandler . '/'; $this->syntax[DOKU_LEXER_EXIT] = '/' . $exit . '/'; } /** * What kind of syntax are we? */ public function getType() { return 'substition'; } public function getPType() { return 'block'; } /** * Where to sort in? */ public function getSort() { return 65; } public function connectTo($mode) { $this->Lexer->addSpecialPattern($this->lexerSyntax['enter'], $mode, $this->mode); $this->Lexer->addSpecialPattern($this->lexerSyntax['newcol'], $mode, $this->mode); $this->Lexer->addSpecialPattern($this->lexerSyntax['exit'], $mode, $this->mode); } /** * Handle the match */ public function handle($match, $state, $pos, Doku_Handler $handler) { foreach ($this->syntax as $state => $pattern) { if (preg_match($pattern, $match, $data) == 1) { break; } } switch ($state) { case DOKU_LEXER_ENTER: case DOKU_LEXER_MATCHED: return array($state, preg_split('/\s+/', $data[1], -1, PREG_SPLIT_NO_EMPTY)); case DOKU_LEXER_EXIT: return array($state, array()); } return false; } /** * Create output */ public function render($mode, Doku_Renderer $renderer, $data) { $columnsRenderer = $this->getRenderer($mode, $renderer); if ($columnsRenderer != NULL) { $columnsRenderer->render($data[0], $renderer, $data[1]); return true; } return false; } /** * */ private function getRenderer($mode, Doku_Renderer $renderer) { switch ($mode) { case 'xhtml': if ($this->xhtmlRenderer == NULL) { $this->xhtmlRenderer = new columns_renderer_xhtml(); } return $this->xhtmlRenderer; case 'odt': if ($this->odtRenderer == NULL) { if (method_exists($renderer, 'getODTPropertiesFromElement')) { $this->odtRenderer = new columns_renderer_odt_v2(); } else { $this->odtRenderer = new columns_renderer_odt_v1(); } } return $this->odtRenderer; } return NULL; } /** * Returns columns tag */ private function getColumnsTagName() { $tag = $this->getConf('kwcolumns'); if ($tag == '') { $tag = $this->getLang('kwcolumns'); } return $tag; } /** * Returns new column tag */ private function getNewColumnTagName() { $tag = $this->getConf('kwnewcol'); if ($tag == '') { $tag = $this->getLang('kwnewcol'); } return $tag; } } /** * Base class for columns rendering. */ abstract class columns_renderer { /** * */ public function render($state, Doku_Renderer $renderer, $attribute) { switch ($state) { case DOKU_LEXER_ENTER: $this->render_enter($renderer, $attribute); break; case DOKU_LEXER_MATCHED: $this->render_matched($renderer, $attribute); break; case DOKU_LEXER_EXIT: $this->render_exit($renderer, $attribute); break; } } abstract protected function render_enter(Doku_Renderer $renderer, $attribute); abstract protected function render_matched(Doku_Renderer $renderer, $attribute); abstract protected function render_exit(Doku_Renderer $renderer, $attribute); /** * */ protected function getAttribute($attribute, $name) { $result = ''; if (array_key_exists($name, $attribute)) { $result = $attribute[$name]; } return $result; } /** * */ protected function getStyle($attribute, $attributeName, $styleName = '') { $result = $this->getAttribute($attribute, $attributeName); if ($result != '') { if ($styleName == '') { $styleName = $attributeName; } $result = $styleName . ':' . $result . ';'; } return $result; } } /** * Class columns_renderer_xhtml * @author LarsDW223 */ class columns_renderer_xhtml extends columns_renderer { /** * */ public function render($state, Doku_Renderer $renderer, $attribute) { parent::render($state, $renderer, $attribute); if ($state == 987 && method_exists($renderer, 'finishSectionEdit')) { $renderer->finishSectionEdit($attribute); } } /** * */ protected function render_enter(Doku_Renderer $renderer, $attribute) { $renderer->doc .= $this->renderTable($attribute) . DOKU_LF; $renderer->doc .= '' . $this->renderTd($attribute) . DOKU_LF; } /** * */ protected function render_matched(Doku_Renderer $renderer, $attribute) { $renderer->doc .= '' . $this->renderTd($attribute) . DOKU_LF; } /** * */ protected function render_exit(Doku_Renderer $renderer, $attribute) { $renderer->doc .= '' . DOKU_LF; } /** * */ private function renderTable($attribute) { $width = $this->getAttribute($attribute, 'table-width'); if ($width != '') { return ''; } else { return '
'; } } /** * */ private function renderTd($attribute) { $class[] = 'columns-plugin'; $class[] = $this->getAttribute($attribute, 'class'); $class[] = $this->getAttribute($attribute, 'text-align'); $html = '
getStyle($attribute, 'column-width', 'width'); $style .= $this->getStyle($attribute, 'vertical-align'); if ($style != '') { $html .= ' style="' . $style . '"'; } return $html . '>'; } } /** * Class columns_renderer_odt_v1 */ class columns_renderer_odt_v1 extends columns_renderer { /** * */ protected function render_enter(Doku_Renderer $renderer, $attribute) { $this->addOdtTableStyle($renderer, $attribute); $this->addOdtColumnStyles($renderer, $attribute); $this->renderOdtTableEnter($renderer, $attribute); $this->renderOdtColumnEnter($renderer, $attribute); } /** * */ protected function render_matched(Doku_Renderer $renderer, $attribute) { $this->addOdtColumnStyles($renderer, $attribute); $this->renderOdtColumnExit($renderer); $this->renderOdtColumnEnter($renderer, $attribute); } /** * */ protected function render_exit(Doku_Renderer $renderer, $attribute) { $this->renderOdtColumnExit($renderer); $this->renderOdtTableExit($renderer); } /** * */ private function addOdtTableStyle(Doku_Renderer $renderer, $attribute) { $styleName = $this->getOdtTableStyleName($this->getAttribute($attribute, 'block-id')); $style = ''; $style .= 'getAttribute($attribute, 'table-width'); if (($width != '') && ($width != '100%')) { $metrics = $this->getOdtMetrics($renderer->autostyles); $style .= ' style:width="' . $this->getOdtAbsoluteWidth($metrics, $width) . '"'; } $align = ($width == '100%') ? 'margins' : 'left'; $style .= ' table:align="' . $align . '"/>'; $style .= ''; $renderer->autostyles[$styleName] = $style; } /** * */ private function addOdtColumnStyles(Doku_Renderer $renderer, $attribute) { $blockId = $this->getAttribute($attribute, 'block-id'); $columnId = $this->getAttribute($attribute, 'column-id'); $styleName = $this->getOdtTableStyleName($blockId, $columnId); $style = ''; $style .= 'getAttribute($attribute, 'column-width'); if ($width != '') { $metrics = $this->getOdtMetrics($renderer->autostyles); $style .= ' style:column-width="' . $this->getOdtAbsoluteWidth($metrics, $width) . '"'; } $style .= '/>'; $style .= ''; $renderer->autostyles[$styleName] = $style; $styleName = $this->getOdtTableStyleName($blockId, $columnId, 1); $style = ''; $style .= 'getAttribute($attribute, 'class')) { case 'first': $style .= ' fo:padding-left="0cm"'; $style .= ' fo:padding-right="0.4cm"'; break; case 'last': $style .= ' fo:padding-left="0.4cm"'; $style .= ' fo:padding-right="0cm"'; break; } /* There seems to be no easy way to control horizontal alignment of text within the column as fo:text-align aplies to individual paragraphs. */ //TODO: $this->getAttribute($attribute, 'text-align'); $align = $this->getAttribute($attribute, 'vertical-align'); if ($align != '') { $style .= ' style:vertical-align="' . $align . '"'; } else { $style .= ' style:vertical-align="top"'; } $style .= '/>'; $style .= ''; $renderer->autostyles[$styleName] = $style; } /** * */ private function renderOdtTableEnter(Doku_Renderer $renderer, $attribute) { $columns = $this->getAttribute($attribute, 'columns'); $blockId = $this->getAttribute($attribute, 'block-id'); $styleName = $this->getOdtTableStyleName($blockId); $renderer->doc .= ''; for ($c = 0; $c < $columns; $c++) { $styleName = $this->getOdtTableStyleName($blockId, $c + 1); $renderer->doc .= ''; } $renderer->doc .= ''; } /** * */ private function renderOdtColumnEnter(Doku_Renderer $renderer, $attribute) { $blockId = $this->getAttribute($attribute, 'block-id'); $columnId = $this->getAttribute($attribute, 'column-id'); $styleName = $this->getOdtTableStyleName($blockId, $columnId, 1); $renderer->doc .= ''; } /** * */ private function renderOdtColumnExit(Doku_Renderer $renderer) { $renderer->doc .= ''; } /** * */ private function renderOdtTableExit(Doku_Renderer $renderer) { $renderer->doc .= ''; $renderer->doc .= ''; } /** * Convert relative units to absolute */ private function getOdtAbsoluteWidth($metrics, $width) { if (preg_match('/([\d\.]+)(.+)/', $width, $match) == 1) { switch ($match[2]) { case '%': /* Won't work for nested column blocks */ $width = ($match[1] / 100 * $metrics['page-width']) . $metrics['page-width-units']; break; case 'em': /* Rough estimate */ $width = ($match[1] * 0.8 * $metrics['font-size']) . $metrics['font-size-units']; break; } } return $width; } /** * */ private function getOdtTableStyleName($blockId, $columnId = 0, $cell = 0) { $result = 'ColumnsBlock' . $blockId; if ($columnId != 0) { if ($columnId <= 26) { $result .= '.' . chr(ord('A') + $columnId - 1); } else { /* To unlikey to handle it properly */ $result .= '.a'; } if ($cell != 0) { $result .= $cell; } } return $result; } /** * */ private function getOdtMetrics($autoStyle) { $result = array(); if (array_key_exists('pm1', $autoStyle)) { $style = $autoStyle['pm1']; if (preg_match('/fo:page-width="([\d\.]+)(.+?)"/', $style, $match) == 1) { $result['page-width'] = floatval($match[1]); $result['page-width-units'] = $match[2]; $units = $match[2]; if (preg_match('/fo:margin-left="([\d\.]+)(.+?)"/', $style, $match) == 1) { // TODO: Unit conversion if ($match[2] == $units) { $result['page-width'] -= floatval($match[1]); } } if (preg_match('/fo:margin-right="([\d\.]+)(.+?)"/', $style, $match) == 1) { if ($match[2] == $units) { $result['page-width'] -= floatval($match[1]); } } } } if (!array_key_exists('page-width', $result)) { $result['page-width'] = 17; $result['page-width-units'] = 'cm'; } /* There seems to be no easy way to get default font size apart from loading styles.xml. */ $styles = io_readFile(DOKU_PLUGIN . 'odt/styles.xml'); if (preg_match('/(.+?)<\/style:default-style>/s', $styles, $match) == 1) { if (preg_match('//', $match[1], $match) == 1) { if (preg_match('/fo:font-size="([\d\.]+)(.+?)"/', $match[1], $match) == 1) { $result['font-size'] = floatval($match[1]); $result['font-size-units'] = $match[2]; } } } if (!array_key_exists('font-size', $result)) { $result['font-size'] = 12; $result['font-size-units'] = 'pt'; } return $result; } } /** * Class columns_renderer_odt_v2 * @author LarsDW223 */ class columns_renderer_odt_v2 extends columns_renderer { /** * */ protected function render_enter(Doku_Renderer $renderer, $attribute) { $this->renderOdtTableEnter($renderer, $attribute); $this->renderOdtColumnEnter($renderer, $attribute); } /** * */ protected function render_matched(Doku_Renderer $renderer, $attribute) { $this->renderOdtColumnExit($renderer); $this->renderOdtColumnEnter($renderer, $attribute); } /** * */ protected function render_exit(Doku_Renderer $renderer, $attribute) { $this->renderOdtColumnExit($renderer); $this->renderOdtTableExit($renderer); } /** * */ private function renderOdtTableEnter(Doku_Renderer $renderer, $attribute) { $properties = array(); $properties ['width'] = $this->getAttribute($attribute, 'table-width'); $properties ['align'] = 'left'; $renderer->_odtTableOpenUseProperties ($properties); $renderer->tablerow_open(); } /** * */ private function renderOdtColumnEnter(Doku_Renderer $renderer, $attribute) { $properties = array(); $properties ['width'] = $this->getAttribute($attribute, 'column-width'); $properties ['border'] = 'none'; $properties ['padding-top'] = '0cm'; $properties ['padding-bottom'] = '0cm'; switch ($this->getAttribute($attribute, 'class')) { case 'first': $properties ['padding-left'] = '0cm'; $properties ['padding-right'] = '0.4cm'; break; case 'last': $properties ['padding-left'] = '0.4cm'; $properties ['padding-right'] = '0cm'; break; } $align = $this->getAttribute($attribute, 'vertical-align'); if ($align != '') { $properties ['vertical-align'] = $align; } else { $properties ['vertical-align'] = 'top'; } $align = $this->getAttribute($attribute, 'text-align'); if ($align != '') { $properties ['text-align'] = $align; } else { $properties ['text-align'] = 'left'; } $renderer->_odtTableCellOpenUseProperties($properties); } /** * */ private function renderOdtColumnExit(Doku_Renderer $renderer) { $renderer->tablecell_close(); } /** * */ private function renderOdtTableExit(Doku_Renderer $renderer) { $renderer->tablerow_close(); $renderer->table_close(); } }