1<?php
2/**
3 * ODTStyle: base class for ODT styles.
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author LarsDW223
7 */
8
9require_once 'ODTUnknownStyle.php';
10require_once 'ODTStyleStyle.php';
11require_once 'ODTTextOutlineStyle.php';
12require_once 'ODTTextListStyle.php';
13require_once 'ODTMasterPageStyle.php';
14require_once 'ODTPageLayoutStyle.php';
15require_once 'ODTTextStyle.php';
16require_once 'ODTParagraphStyle.php';
17require_once 'ODTTableStyle.php';
18require_once 'ODTTableRowStyle.php';
19require_once 'ODTTableColumnStyle.php';
20require_once 'ODTTableCellStyle.php';
21
22/**
23 * The ODTStyle class
24 */
25abstract class ODTStyle
26{
27    protected static $style_base_name = 'PluginODTAutoStyle_';
28    protected static $style_count = 0;
29    protected $properties = array();
30
31    /**
32     * Get the element name for the ODT XML encoding of the style.
33     *
34     * @param  $properties Properties to be imported
35     * @param  $disabled Properties to be ignored
36     */
37    abstract public function getElementName();
38
39    /**
40     * Set style properties by importing values from a properties array.
41     * Properties might be disabled by setting them in $disabled.
42     * The style must have been previously created.
43     *
44     * @param  $properties Properties to be imported
45     * @param  $disabled Properties to be ignored
46     */
47    abstract public function importProperties($properties, $disabled);
48
49    /**
50     * Check if a style is a common style.
51     *
52     * @return bool Is common style
53     */
54    abstract public function mustBeCommonStyle();
55
56    /**
57     * Encode current style values in a string and return it.
58     *
59     * @return string ODT XML encoded style
60     */
61    abstract public function toString();
62
63    /**
64     * Set a property.
65     *
66     * @param $property The name of the property to set
67     * @param $value New value to set
68     */
69    abstract public function setProperty($property, $value);
70
71    /**
72     * Get the value of a property.
73     *
74     * @param  $property The property name
75     * @return string The current value of the property
76     */
77    public function getProperty($property) {
78        return $this->properties [$property]['value'];
79    }
80
81    /**
82     * Get the value of a property.
83     *
84     * @param  $property   The property name
85     * @param  $properties Properties array to query the value from,
86     *                     or NULL for using ours.
87     * @return string      The current value of the property
88     */
89    public function getPropertyInternal($property, $properties=NULL) {
90        if ( $properties === NULL ) {
91            return $this->properties [$property]['value'];
92        } else {
93            return $properties [$property]['value'];
94        }
95    }
96
97    /**
98     * Get the value of a property.
99     *
100     * @param  $property The property name
101     * @return string The current value of the property
102     */
103    public function getPropertySection($property) {
104        return $this->properties [$property]['section'];
105    }
106
107    /**
108     * Create new style by importing ODT style definition.
109     *
110     * @param  $xmlCode Style definition in ODT XML format
111     * @return ODTStyle New specific style
112     */
113    static public function importODTStyle($xmlCode) {
114        $matches = array();
115        $pattern = '/<(\w)+[^\s\/>]+/';
116        if (preg_match ($pattern, $xmlCode, $matches) !== 1) {
117            return NULL;
118        }
119        $element = trim ($matches [0], '"<>');
120
121        $style = NULL;
122        switch ($element) {
123            case 'style:style':
124            case 'style:default-style':
125                $style = ODTStyleStyle::importODTStyle($xmlCode);
126                break;
127            case 'text:outline-style':
128                $style = ODTTextOutlineStyle::importODTStyle($xmlCode);
129                break;
130            case 'text:list-style':
131                $style = ODTTextListStyle::importODTStyle($xmlCode);
132                break;
133            case 'style:master-page':
134                $style = ODTMasterPageStyle::importODTStyle($xmlCode);
135                break;
136            case 'style:page-layout':
137                $style = ODTPageLayoutStyle::importODTStyle($xmlCode);
138                break;
139            default:
140                break;
141        }
142        if ($style != NULL ) {
143            return $style;
144        }
145
146        // Unknown/not implemented style.
147        // Create generic style which can not be changed.
148        $unknown = ODTUnknownStyle::importODTStyle($xmlCode);
149        $unknown->setElementName($element);
150        return $unknown;
151    }
152
153    /**
154     * Set a property.
155     *
156     * @param $property The name of the property to set
157     * @param $value New value to set
158     */
159    protected function setPropertyInternal($property, $odt_property, $value, $section, &$dest=NULL) {
160        if ($value !== NULL) {
161            if ( $dest === NULL ) {
162                $this->properties [$property] = array ('odt_property' => $odt_property,
163                                                       'value' => $value,
164                                                       'section' => $section);
165            } else {
166                $dest [$property] = array ('odt_property' => $odt_property,
167                                           'value' => $value,
168                                           'section' => $section);
169            }
170        } else {
171            if ( $dest === NULL ) {
172                unset ($this->properties [$property]);
173            } else {
174                unset ($dest [$property]);
175            }
176        }
177    }
178
179    /**
180     * Import ODT style definition according to given fields into given style.
181     *
182     * @param  $style   ODTStyle object for storing the properties
183     * @param  $fields  Properties accepted by the object/class
184     * @param  $xmlCode Style definition in ODT XML format
185     * @return integer  Number of meaningful properties found
186     */
187    protected function importODTStyleInternal(array $fields, $xmlCode, &$properties=NULL) {
188        $attrs = 0;
189        foreach ($fields as $property => $field) {
190            // The pattern is specified in that way that it also reads in empty attributes.
191            // Sometimes an empty attribute is not the same as an not existing one. E.g.
192            // in ODT XML '<text:outline-level-style text:level="3" style:num-format="" >'
193            // has NOT the same meaning as '<text:outline-level-style text:level="3" >'!!!
194            // So DO NOT change the '*' in the pattern to '+'!
195            if (preg_match ('/'.$field[0].'="[^"]*"/', $xmlCode, $matches) === 1) {
196                $value = substr ($matches [0], strlen($field[0].'="'));
197                $value = trim ($value, '"<>');
198                $this->setPropertyInternal($property, $field[0], $value, $field[1], $properties);
199                if ( $field[2] == true ) {
200                    $attrs++;
201                }
202            }
203        }
204        return $attrs;
205    }
206
207    /**
208     * Set style properties by importing values from a properties array.
209     * Properties might be disabled by setting them in $disabled.
210     * The style must have been previously created. Only those properties
211     * will be accepted that are mentioned in the fields array.
212     *
213     * @param  $style      ODTStyle object for storing the properties
214     * @param  $fields     Properties accepted by the object/class
215     * @param  $properties Properties to be imported
216     * @param  $disabled   Properties to be ignored
217     */
218    protected function importPropertiesInternal(array $fields, $properties, $disabled, &$dest=NULL) {
219        foreach ($properties as $property => $value) {
220            if ($disabled [$property] == 0 && array_key_exists ($property, $fields)) {
221                $this->setPropertyInternal($property, $fields[$property][0], $value, $fields[$property][1], $dest);
222            }
223        }
224    }
225
226    /**
227     * Is this style a default style?
228     * Needs to be overwritten if a style could also be a default style.
229     *
230     * @return boolean  Always false.
231     */
232    public function isDefault() {
233        return false;
234    }
235
236    /**
237     * This function creates a new style name. All functions of this class which create a new
238     * style/style name shall use this function to create the style name. By doing so it is
239     * guaranteed that all style names created by this class are unique.
240     *
241     * The function returns the name of the new style or NULL if all relevant properties are empty.
242     */
243    public static function getNewStylename ($type = '') {
244        self::$style_count++;
245        $style_name = self::$style_base_name.$type.'_'.self::$style_count;
246        return $style_name;
247    }
248}
249