⇐ ODT Plugin homepage
In the following I give a short introduction on how other plugins can make use of the ODT export plugin to export their content to ODT. As this is only a short introduction it is not meant to be a complete guide to the ODT export plugin functions or the ODT xml format itself.
The plugins that have added ODT support are listed at the ODT render support page.
In the following I assume that the plugin to be extended is a DokuWiki_Syntax_Plugin. The procedure to extend other kinds of plugins with ODT export might be different.
Each plugin which is of the type DokuWiki_Syntax_Plugin needs to implement the public function render() which is doing the main work of delivering the content for the different output formats. In a plugin which only supports XHTML (Extensible HyperText Markup Language) output yet, the obvious first step is to differ between the output formats and branch to the corresponding function for a certain output format.
Basically an existing render function looks like this:
public function render($mode, Doku_Renderer $renderer, $data) { if ($mode == 'xhtml') { /* @var Doku_Renderer_xhtml $renderer */ // Generate XHTML content... $renderer->doc .= 'My XHTML code and content'; return true; } // We do not support any other output formats yet. return false; }
So we have to extend the function to catch the mode 'odt' and add our ODT content generating code. The most beautiful solution is to use the function render only to branch to the corresponding function for content generation and pass through the parameters.
So the adopted code might look like this:
public function render($mode, Doku_Renderer $renderer, $data) { switch ($mode) { case "xhtml": /* @var Doku_Renderer_xhtml $renderer */ // Calling the XHTML render function. $this->render_for_xhtml($renderer, $data); break; case "odt": /* @var renderer_plugin_odt_page $renderer */ // Calling the ODT render function. $this->render_for_odt($renderer, $data); break; default: // Some other format that we do not support. return false; } // Success. We did support the format. return true; } protected function render_for_xhtml ($renderer, $data) { // Generate XHTML content... $renderer->doc .= 'My XHTML code and content'; } protected function render_for_odt ($renderer, $data) { // Generate ODT content... $renderer->doc .= 'My ODT xml code and content'; }
As you might have already noticed, the main work or effort is of course to generate a valid ODT file with valid ODT xml content. An ODT file is usually a zip archive consisting of the following file structure:
If you like to have a look yourself, just rename an ODT document an change the file extension from .odt to .zip. Then un-zip the archive and have a look.
Even the generation of an empty file would mean some work for us. Thanks to the ODT export plugin from Andreas Gohr, Aurélien Bompard and Florian Lamml, we do not need to do this work ourselves. The only thing our plugin needs to do is to insert valid ODT xml code for 'our' content. The creation and packetization of the file is all done by the ODT export plugin.
Let's create some first simple text output. Make sure your ODT render function looks like this:
protected function render_for_odt ($renderer, $data) { // Generate ODT content... $renderer->doc. = 'My ODT xml code and content'; }
Open your wiki page, export it to ODT and have a look. The text was inserted at the current position directly between the other content.
Now, we enclose the text with a paragraph. We call the functions p_open() and p_close() (see ODT export plugin, file renderer/page.php if you like). So we do not need to write the ODT xml code ourselves. Make sure your ODT render function looks like this:
protected function render_for_odt ($renderer, $data) { // Generate ODT content... $renderer->p_open(); $renderer->doc .= 'My ODT xml code and content'; $renderer->p_close(); }
Again, test it and see (Open your wiki page, export it to ODT and have a look), no change.
If you want to insert a new line simply call the linebreak() function. Make sure your ODT render function looks like this:
protected function render_for_odt ($renderer, $data) { // Generate ODT content... $renderer->p_open(); $renderer->doc .= 'My ODT xml code and content'; $renderer->linebreak(); $renderer->doc .= 'This is another line'; $renderer->p_close(); }
The ODT export plugin also includes functions for the various text styles like bold or italic. Make sure your ODT render function looks like this:
protected function render_for_odt ($renderer, $data) { // Generate ODT content... $renderer->p_open(); $renderer->doc .= 'My ODT xml code and content'; $renderer->linebreak(); $renderer->doc .= 'This is another line'; $renderer->linebreak(); $renderer->linebreak(); $renderer->strong_open(); $renderer->doc .= 'This is strong text.'; $renderer->strong_close(); $renderer->linebreak(); $renderer->linebreak(); $renderer->emphasis_open(); $renderer->doc .= 'This is emphasis text.'; $renderer->emphasis_close(); $renderer->linebreak(); $renderer->linebreak(); $renderer->underline_open(); $renderer->doc .= 'This is underlined text.'; $renderer->underline_close(); $renderer->linebreak(); $renderer->linebreak(); $renderer->monospace_open(); $renderer->doc .= 'This is monospace text.'; $renderer->monospace_close(); $renderer->linebreak(); $renderer->linebreak(); $renderer->subscript_open(); $renderer->doc .= 'This is subscript text.'; $renderer->subscript_close(); $renderer->linebreak(); $renderer->linebreak(); $renderer->superscript_open(); $renderer->doc .= 'This is superscript text.'; $renderer->superscript_close(); $renderer->linebreak(); $renderer->linebreak(); $renderer->deleted_open(); $renderer->doc .= 'This is deleted text.'; $renderer->deleted_close(); $renderer->linebreak(); $renderer->linebreak(); $renderer->p_close(); }
If you export the page, you will see all the various text styles. If you want to use different styles at the same time, just combine the functions. The following code shows an example which will create text which is bold and underlined.
protected function render_for_odt ($renderer, $data) { // Generate ODT content... $renderer->p_open(); $renderer->strong_open(); $renderer->underline_open(); $renderer->doc .= 'This is strong AND underlined text.'; $renderer->underline_close(); $renderer->strong_close(); $renderer->linebreak(); $renderer->p_close(); }
The following code creates a 3×3 table with a table header spanned over all columns:
protected function render_for_odt ($renderer, $data) { // Generate ODT content... // This line is important!!! $renderer->p_close(); $renderer->table_open(3,3); $renderer->tablerow_open(); $renderer->tableheader_open(3,1); $renderer->doc .= 'Tableheader.'; $renderer->tableheader_close(); $renderer->tablerow_close(); $renderer->tablerow_open(); $renderer->tablecell_open(); $renderer->p_open(); $renderer->doc .= 'Cell 1/1'; $renderer->p_close(); $renderer->tablecell_close(); $renderer->tablecell_open(); $renderer->p_open(); $renderer->doc .= 'Cell 2/1'; $renderer->p_close(); $renderer->tablecell_close(); $renderer->tablecell_open(); $renderer->p_open(); $renderer->doc .= 'Cell 3/1'; $renderer->p_close(); $renderer->tablecell_close(); $renderer->tablerow_close(); $renderer->tablerow_open(); $renderer->tablecell_open(); $renderer->p_open(); $renderer->doc .= 'Cell 1/2'; $renderer->p_close(); $renderer->tablecell_close(); $renderer->tablecell_open(); $renderer->p_open(); $renderer->doc .= 'Cell 2/2'; $renderer->p_close(); $renderer->tablecell_close(); $renderer->tablecell_open(); $renderer->p_open(); $renderer->doc .= 'Cell 3/2'; $renderer->p_close(); $renderer->tablecell_close(); $renderer->tablerow_close(); $renderer->table_close(); }
The initial call to p_close() is important to make sure the paragraph is closed before creating the table. You can even call p_close if no paragraph is open because it keeps count of the open paragraphs. If you forget to close the paragraph, then the resulting ODT file format will be corrupted because you inserted a table into a paragraph.
An image can simply be included by a call to the function _odtAddImage() passing the file name as the parameter. Make sure that the file includes the full path, otherwise it might not be found. The following code shows an example in which the image “example.png” included in the plugins subdirectory “images” will be loaded (this assumes that DOKU_INC and DOKU_PLUGIN_IMAGES carry the standard values).
protected function render_for_odt ($renderer, $data) { // Generate ODT content... $renderer->_odtAddImage (DOKU_INC . DOKU_PLUGIN_IMAGES . 'example.png'); }
In the ODT format elements like spans, paragraphs, frames and tables use styles to specify the look of the element. These styles have properties which are often similar to CSS properties. But some facts make writing ODT styles an exhaustive and annoying work:
To take the burden of writing/defining ODT styles from the plugin developers, ODT functions have been written which use CSS to define the style of an element. This makes it much simpler for plugin developers to export to ODT for the following reasons:
style='color:red;'
In the following paragraphs the CSS based functions of the ODT renderer are described. CSS support in the ODT renderer exists since release 2015-03-20 and are still in development.
Some of the functions reuse CSS code from other files like e.g. 'style.css'. The imported file needs to be passed as an argument to these functions. Here is an example code showing the import:
// Import Wrap-CSS. if ( self::$import == NULL ) { self::$import = plugin_load('helper', 'odt_cssimport'); self::$import->importFrom(DOKU_PLUGIN.'myplugin/style.css'); self::$import->loadReplacements(DOKU_INC.DOKU_TPL.'style.ini'); } // Now, $import can be used in later functions calls...
The code saves the imported file in the static class variable $import. This prevents importing the file every time the handle or render functions are called. In the following examples it is assumed that a static class variable $import carrying the information of a imported CSS file does exist.
The function opens a new text span. The style is specified by a class string which represents the CSS selector. An imported CSS file needs to be passed to the function also. Here is an example call:
$renderer->_odtSpanOpenUseCSS (self::$import, 'dokuwiki myplugin', DOKU_PLUGIN.'myplugin/');
This will open a span which uses the CSS properties of the classes dokuwiki
and myplugin
. The third argument represents a path which can be used to convert the URL of a background-image into a local path. In this example a URL like from background-image: url(images/example.png);
would become the local path DOKU_PLUGIN.'myplugin/images/example.png'
.
The function makes use of the following CSS properties:
The function _odtSpanClose() needs to be called to close the text span.
The function opens a new text span. The style is specified as a string including the CSS properties. Here is an example call:
$renderer->_odtSpanOpenUseCSSStyle('color:red;background-color:black');
This will open a span with a red text color on a black background.
As a second parameter the function also optionally accepts a URL replacement path, see function _odtSpanOpenUseCSS(). For supported properties, please see function _odtSpanOpenUseCSS(). The function _odtSpanClose() needs to be called to close the text span.
The function opens a new text span. The style is specified by passing an associative array carrying the CSS properties. Here is an example call:
$properties ['color'] = 'red'; $properties ['background-color'] = 'black'; $renderer->_odtSpanOpenUseProperties($properties);
This will open a span with a red text color on a black background.
For supported properties, please see function _odtSpanOpenUseCSS(). The function _odtSpanClose() needs to be called to close the text span.
The function opens a new paragraph. The style is specified by a class string which represents the CSS selector. An imported CSS file needs to be passed to the function also. Here is an example call:
$renderer->_odtParagraphOpenUseCSS (self::$import, 'dokuwiki myplugin', DOKU_PLUGIN.'myplugin/');
This will open a paragraph which uses the CSS properties of the classes dokuwiki and myplugin. The third argument represents a path which can be used to convert the URL of a background-image into a local path. In this example a URL like from background-image: url(images/example.png);
would become the local path DOKU_PLUGIN.'myplugin/images/example.png'
.
The function makes use of the following CSS properties:
The function p_close() needs to be called to close the paragraph.
The function opens a new paragraph. The style is specified as a string including the CSS properties. Here is an example call:
$renderer->_odtParagraphOpenUseCSSStyle('color:red;background-color:black');
This will open a paragraph with a red text color on a black background.
As a second parameter the function also optionally accepts a URL replacement path, see function _odtParagraphOpenUseCSS(). For supported properties, please see function _odtParagraphOpenUseCSS(). The function p_close() needs to be called to close the paragraph.
The function opens a new paragraph. The style is specified by passing an associative array carrying the CSS properties. Here is an example call:
$properties ['color'] = 'red'; $properties ['background-color'] = 'black'; $renderer->_odtParagraphOpenUseCSSStyle($properties);
This will open a paragraph with a red text color on a black background.
For supported properties, please see function _odtParagraphOpenUseCSS. The function p_close() needs to be called to close the paragraph.
The function opens a new frame(s) to represent a div element. The style is specified by a class string which represents the CSS selector. An imported CSS file needs to be passed to the function also. Here is an example call:
$renderer->_odtDivOpenAsFrameUseCSS (self::$import, 'dokuwiki myplugin', DOKU_PLUGIN.'myplugin/');
This will open a frame which uses the CSS properties of the classes dokuwiki
and myplugin
. The third argument represents a path which can be used to convert the URL of a background-image into a local path. In this example a URL like from background-image: url(images/example.png);
would become the local path DOKU_PLUGIN.'myplugin/images/example.png'
.
The function makes use of the following CSS properties:
The function _odtDivCloseAsFrame() needs to be called to close the paragraph.
Documents which might be useful.