1<?php
2
3require_once DOKU_INC.'lib/plugins/odt/ODT/XMLUtil.php';
4require_once DOKU_INC.'lib/plugins/odt/ODT/styles/ODTStyle.php';
5
6/**
7 * ODTStyleSet: Abstract class defining the interface a style set/template
8 * needs to implement towards the ODT renderer.
9 *
10 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
11 * @author LarsDW223
12 */
13abstract class ODTStyleSet
14{
15    protected $styles = array();
16    protected $styles_by_name = array();
17    protected $auto_styles = array();
18    protected $auto_styles_by_name = array();
19    protected $master_styles = array();
20    protected $master_styles_by_name = array();
21
22    /**
23     * Read/import style source.
24     *
25     * @param $source (optional)
26     */
27    abstract public function import($source);
28
29    /**
30     * Export $element styles (e.g. 'office:styles' or 'office:automatic-styles')
31     *
32     * @param  $element The style element to export
33     * @return string   The ODT XML encoded $element style
34     */
35    abstract public function export($element);
36
37    /**
38     * The function needs to be able to return a style name
39     * for the following basic styles used by the renderer:
40     * - standard
41     * - body
42     * - heading1
43     * - heading2
44     * - heading3
45     * - heading4
46     * - heading5
47     * - heading6
48     * - list
49     * - numbering
50     * - table content
51     * - table heading
52     * - preformatted
53     * - source code
54     * - source file
55     * - horizontal line
56     * - footnote
57     * - emphasis
58     * - strong
59     * - graphics
60     * - monospace
61     * - quotation1
62     * - quotation2
63     * - quotation3
64     * - quotation4
65     * - quotation5
66     *
67     * @param $style
68     * @return mixed
69     */
70    abstract public function getStyleName($style);
71
72    /**
73     * @param null $source
74     */
75    public function importFromODTFile($sourceFile, $root_element, $overwrite=false) {
76        if (empty($sourceFile) || empty($root_element)) {
77            return false;
78        }
79
80        // Get file contents
81        $styles_xml_content = file_get_contents ($sourceFile);
82        if (empty($styles_xml_content)) {
83            return false;
84        }
85
86        return $this->importFromODT($styles_xml_content, $root_element, $overwrite);
87    }
88
89    public function importFromODT($styles_xml_content, $root_element, $overwrite=false) {
90        if (empty($styles_xml_content) || empty($root_element)) {
91            return false;
92        }
93
94        // Only import known style elements
95        switch ($root_element) {
96            case 'office:styles':
97            case 'office:automatic-styles':
98            case 'office:master-styles':
99                $style_elements = XMLUtil::getElementContent($root_element, $styles_xml_content, $end);
100                break;
101
102            default:
103                return false;
104        }
105
106        $pos = 0;
107        $max = strlen($style_elements);
108        while ($pos < $max) {
109            $xml_code = XMLUtil::getNextElement($element, substr($style_elements, $pos), $end);
110            if (!isset($xml_code)) {
111                break;
112            }
113            $pos += $end;
114
115            // Create new ODTStyle
116            $object = ODTStyle::importODTStyle($xml_code);
117            if ( isset($object) ) {
118                // Success, add it
119                switch ($root_element) {
120                    case 'office:styles':
121                        $this->addStyle($object, $overwrite);
122                        break;
123                    case 'office:automatic-styles':
124                        $this->addAutomaticStyle($object, $overwrite);
125                        break;
126                    case 'office:master-styles':
127                        $this->addMasterStyle($object, $overwrite);
128                        break;
129                }
130            }
131        }
132        return true;
133    }
134
135    /**
136     * @param null $destination
137     */
138    public function exportToODT($root_element) {
139        $export = NULL;
140        switch ($root_element) {
141            case 'office:styles':
142                $export = &$this->styles;
143                break;
144            case 'office:automatic-styles':
145                $export = &$this->auto_styles;
146                break;
147            case 'office:master-styles':
148                $export = &$this->master_styles;
149                break;
150        }
151        if (isset($export)) {
152            $office_styles = "<".$root_element.">\n";
153            foreach ($export as $style) {
154                $office_styles .= $style->toString();
155            }
156            $office_styles .= "</".$root_element.">\n";
157            return $office_styles;
158        }
159        return NULL;
160    }
161
162    /**
163     * @param null $source
164     */
165    public function addStyle(ODTStyle $new, $overwrite=false) {
166        return $this->addStyleInternal
167            ($this->styles, $this->styles_by_name, $new, $overwrite);
168    }
169
170    /**
171     * @param null $source
172     */
173    public function addAutomaticStyle(ODTStyle $new, $overwrite=false) {
174        return $this->addStyleInternal
175            ($this->auto_styles, $this->auto_styles_by_name, $new, $overwrite);
176    }
177
178    /**
179     * @param null $source
180     */
181    public function addMasterStyle(ODTStyle $new, $overwrite=false) {
182        return $this->addStyleInternal
183            ($this->master_styles, $this->master_styles_by_name, $new, $overwrite);
184    }
185
186    /**
187     * @param null $source
188     */
189    public function addStyleInternal(&$dest, &$dest_by_name, ODTStyle $new, $overwrite=false) {
190        if ($new->isDefault()) {
191            // The key for a default style is the family.
192            $family = $new->getFamily();
193
194            // Search for default style with same family.
195            for ($index = 0 ; $index < count($dest) ; $index++) {
196                if ($dest [$index]->isDefault() &&
197                    $dest [$index]->getFamily() == $family) {
198                    // Only overwrite it if allowed.
199                    if ($overwrite) {
200                        $dest [$index] = $new;
201                    }
202                    return false;
203                }
204            }
205
206            // Default style for that family does not exist yet, add it.
207            $dest [] = $new;
208        } else {
209            // The key for a normal style is the name.
210            $name = $new->getProperty('style-name');
211
212            if (!isset($dest_by_name [$name])) {
213                $dest [] = $new;
214                if (!empty($name)) {
215                    $dest_by_name [$name] = $new;
216                }
217                return true;
218            } elseif ($overwrite) {
219                for ($index = 0 ; $index < count($dest) ; $index++) {
220                    if ($dest [$index] == $dest_by_name [$name]) {
221                        $dest [$index] = $new;
222                        break;
223                    }
224                }
225                $dest_by_name [$name] = $new;
226                return true;
227            }
228        }
229
230        // Do not overwrite an already existing style.
231        return false;
232    }
233
234    /**
235     * The function style checks if a style with the given $name already exists.
236     *
237     * @param $name Name of the style to check
238     * @return boolean
239     */
240    public function styleExists ($name) {
241        if (isset($this->auto_styles_by_name [$name])) {
242            return true;
243        }
244        if (isset($this->styles_by_name [$name])) {
245            return true;
246        }
247        if (isset($this->master_styles_by_name [$name])) {
248            return true;
249        }
250        return false;
251    }
252
253    /**
254     * The function returns the style with the given name
255     *
256     * @param $name Name of the style
257     * @return ODTStyle or NULL
258     */
259    public function getStyle ($name) {
260        if (isset($this->auto_styles_by_name [$name])) {
261            return $this->auto_styles_by_name [$name];
262        }
263        if (isset($this->styles_by_name [$name])) {
264            return $this->styles_by_name [$name];
265        }
266        if (isset($this->master_styles_by_name [$name])) {
267            return $this->master_styles_by_name [$name];
268        }
269        return NULL;
270    }
271
272    /**
273     * The function returns the style at the given index
274     *
275     * @param $element Element of the style e.g. 'office:styles'
276     * @return ODTStyle or NULL
277     */
278    public function getStyleAtIndex($element, $index) {
279        switch ($element) {
280            case 'office:styles':
281                return $this->styles [$index];
282            case 'office:automatic-styles':
283                return $this->auto_styles [$index];
284            case 'office:master-styles':
285                return $this->master_styles [$index];
286        }
287        return NULL;
288    }
289
290    public function getStyleCount($element) {
291        switch ($element) {
292            case 'office:styles':
293                return count($this->styles);
294            case 'office:automatic-styles':
295                return count($this->auto_styles);
296            case 'office:master-styles':
297                return count($this->master_styles);
298        }
299        return -1;
300    }
301
302    /**
303     * @param null $source
304     */
305    public function getDefaultStyle($family) {
306        // Search for default style with same family.
307        for ($index = 0 ; $index < count($this->styles) ; $index++) {
308            if ($this->styles [$index]->isDefault() &&
309                $this->styles [$index]->getFamily() == $family) {
310                return $this->styles [$index];
311            }
312        }
313        return NULL;
314    }
315
316    /**
317     * Get styles array.
318     */
319    public function getStyles() {
320        return $this->styles;
321    }
322
323    /**
324     * Get automatci/common styles array.
325     */
326    public function getAutomaticStyles() {
327        return $this->auto_styles;
328    }
329
330    /**
331     * Get master styles array.
332     */
333    public function getMasterStyles() {
334        return $this->master_styles;
335    }
336}
337