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