1<?php
2
3require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
4
5/**
6 * ODTParagraph:
7 * Class containing static code for handling spans.
8 *
9 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
10 */
11class ODTSpan
12{
13    /**
14     * Open a text span.
15     *
16     * @param string $styleName The style to use.
17     */
18    public static function spanOpen(ODTInternalParams $params, $styleName, $element=NULL, $attributes=NULL){
19        if ($element == NULL) {
20            $element = 'span';
21        }
22        if ($params->elementObj == NULL) {
23            $properties = array();
24            ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
25        }
26
27        $span = new ODTElementSpan ($styleName);
28        $params->document->state->enter($span);
29        $params->content .= $span->getOpeningTag();
30        $span->setHTMLElement ($element);
31    }
32
33    /**
34     * This function opens a new span using the style as set in the imported CSS $import.
35     * So, the function requires the helper class 'helper_plugin_odt_cssimport'.
36     * The CSS style is selected by the element type 'span' and the specified classes in $classes.
37     * The property 'background-image' is not supported by an ODT span. This will be emulated
38     * by inserting an image manually in the span. If the url from the CSS should be converted to
39     * a local path, then the caller can specify a $baseURL. The full path will then be $baseURL/background-image.
40     *
41     * This function calls _odtSpanOpenUseProperties. See the function description for supported properties.
42     *
43     * The span should be closed by calling '_odtSpanClose'.
44     *
45     * @author LarsDW223
46     *
47     * @param helper_plugin_odt_cssimport $import
48     * @param $classes
49     * @param $baseURL
50     * @param $element
51     */
52    public static function spanOpenUseCSS(ODTInternalParams $params, $element=NULL, $attributes=NULL){
53        $properties = array();
54        ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
55        $params->elementObj = $params->htmlStack->getCurrentElement();
56
57        self::spanOpenUseProperties($params, $properties);
58    }
59
60    /**
61     * This function opens a new span using the style as set in the assoziative array $properties.
62     * The parameters in the array should be named as the CSS property names e.g. 'color' or 'background-color'.
63     * The property 'background-image' is not supported by an ODT span. This will be emulated
64     * by inserting an image manually in the span.
65     *
66     * background-color, color, font-style, font-weight, font-size, border, font-family, font-variant, letter-spacing,
67     * vertical-align, background-image (emulated)
68     *
69     * The span should be closed by calling '_odtSpanClose'.
70     *
71     * @author LarsDW223
72     *
73     * @param array $properties
74     */
75    public static function spanOpenUseProperties(ODTInternalParams $params, $properties){
76        $disabled = array ();
77
78        $odt_bg = $properties ['background-color'];
79        $picture = $properties ['background-image'];
80
81        if ( !empty ($picture) ) {
82            // If a picture/background-image is set, than we insert it manually here.
83            // This is a workaround because ODT does not support the background-image attribute in a span.
84
85            // Define graphic style for picture
86            $style_name = ODTStyle::getNewStylename('span_graphic');
87            $image_style = '<style:style style:name="'.$style_name.'" style:family="graphic" style:parent-style-name="'.$params->document->getStyleName('graphics').'"><style:graphic-properties style:vertical-pos="middle" style:vertical-rel="text" style:horizontal-pos="from-left" style:horizontal-rel="paragraph" fo:background-color="'.$odt_bg.'" style:flow-with-text="true"></style:graphic-properties></style:style>';
88
89            // Add style and image to our document
90            // (as unknown style because style-family graphic is not supported)
91            $style_obj = ODTUnknownStyle::importODTStyle($image_style);
92            $params->document->addAutomaticStyle($style_obj);
93            ODTImage::addImage ($params, $picture, NULL, NULL, NULL, NULL, $style_name);
94        }
95
96        // Create a text style for our span
97        $disabled ['background-image'] = 1;
98        $style_obj = ODTTextStyle::createTextStyle ($properties, $disabled);
99        $params->document->addAutomaticStyle($style_obj);
100        $style_name = $style_obj->getProperty('style-name');
101
102        // Open span
103        self::spanOpen($params, $style_name);
104    }
105
106    /**
107     * Close a text span.
108     *
109     * @param string $style_name The style to use.
110     */
111    public static function spanClose(ODTInternalParams $params) {
112        ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
113        $params->document->closeCurrentElement($params->content);
114    }
115
116    public static function createSpanInternal (ODTInternalParams $params, $attributes) {
117        // Get properties
118        $properties = array();
119        ODTUtility::getHTMLElementProperties ($params, $properties, 'span', $attributes);
120
121        // Create automatic style
122        $properties ['style-name'] = ODTStyle::getNewStylename ('span');
123        $params->document->createTextStyle($properties, false);
124
125        // Return style name
126        return $properties ['style-name'];
127    }
128
129    public static function generateSpansfromHTMLCode(ODTInternalParams $params, $HTMLCode){
130        $spans = array ('sup' => array ('open' => '<text:span text:style-name="sup">',
131                                        'close' => '</text:span>'),
132                        'sub' => array ('open' => '<text:span text:style-name="sub">',
133                                        'close' => '</text:span>'),
134                        'u' => array ('open' => '<text:span text:style-name="underline">',
135                                      'close' => '</text:span>'),
136                        'em' => array ('open' => '<text:span text:style-name="Emphasis">',
137                                       'close' => '</text:span>'),
138                        'strong' => array ('open' => '<text:span text:style-name="Strong_20_Emphasis">',
139                                             'close' => '</text:span>'),
140                        'del' => array ('open' => '<text:span text:style-name="del">',
141                                        'close' => '</text:span>'),
142                       );
143        $parsed = array();
144
145        // First examine $HTMLCode and differ between normal content,
146        // opening tags and closing tags.
147        $max = strlen ($HTMLCode);
148        $pos = 0;
149        while ($pos < $max) {
150            $found = ODTUtility::getNextTag($HTMLCode, $pos);
151            if ($found !== false) {
152                $entry = array();
153                $entry ['content'] = substr($HTMLCode, $pos, $found [0]-$pos);
154                if ($entry ['content'] === false) {
155                    $entry ['content'] = '';
156                }
157                $parsed [] = $entry;
158
159                $tagged = substr($HTMLCode, $found [0], $found [1]-$found [0]+1);
160                $entry = array();
161
162                if ($HTMLCode [$found[1]-1] == '/') {
163                    // Element without content <abc/>, doesn'T make sense, save as content
164                    $entry ['content'] = $tagged;
165                } else {
166                    if ($HTMLCode [$found[0]+1] != '/') {
167                        $parts = explode(' ', trim($tagged, '<> '), 2);
168                        $entry ['tag-open'] = $parts [0];
169                        if ($parts [1] !== NULL ) {
170                            $entry ['attributes'] = $parts [1];
171                        }
172                        $entry ['tag-orig'] = $tagged;
173                    } else {
174                        $entry ['tag-close'] = trim ($tagged, '<>/ ');
175                        $entry ['tag-orig'] = $tagged;
176                    }
177                }
178                $entry ['matched'] = false;
179                $parsed [] = $entry;
180
181                $pos = $found [1]+1;
182            } else {
183                $entry = array();
184                $entry ['content'] = substr($HTMLCode, $pos);
185                $parsed [] = $entry;
186                break;
187            }
188        }
189
190        // Check each array entry.
191        $checked = array();
192        for ($out = 0 ; $out < count($parsed) ; $out++) {
193            if ($checked [$out] !== NULL) {
194                continue;
195            }
196            $found = &$parsed [$out];
197            if ($found ['content'] !== NULL) {
198                $checked [$out] = $params->document->replaceXMLEntities($found ['content']);
199            } else if ($found ['tag-open'] !== NULL) {
200                $closed = false;
201
202                for ($in = $out+1 ; $in < count($parsed) ; $in++) {
203                    $search = &$parsed [$in];
204                    if ($search ['tag-close'] !== NULL &&
205                        $found ['tag-open'] == $search ['tag-close'] &&
206                        $search ['matched'] === false &&
207                        (array_key_exists($found ['tag-open'], $spans) || $found ['tag-open'] == 'span')) {
208
209                        $closed = true;
210                        $search ['matched'] = true;
211
212                        // Known and closed tag, convert to ODT
213                        if ($found ['tag-open'] != 'span') {
214                            $checked [$out] = $spans [$found ['tag-open']]['open'];
215                            $checked [$in] = $spans [$found ['tag-open']]['close'];
216                        } else {
217                            $style_name = self::createSpanInternal ($params, $found ['attributes']);
218                            $checked [$out] = '<text:span text:style-name="'.$style_name.'">';
219                            $checked [$in] = '</text:span>';
220                        }
221                        break;
222                    }
223                }
224
225                // Known tag? Closing tag found?
226                if (!$closed) {
227                    // No, save as content
228                    $checked [$out] = $params->document->replaceXMLEntities($found ['tag-orig']);
229                }
230            } else if ($found ['tag-close'] !== NULL) {
231                // If we find a closing tag it means it did not match
232                // an opening tag. Convert to content!
233                $checked [$out] = $params->document->replaceXMLEntities($found ['tag-orig']);
234            }
235        }
236
237        // Add checked entries to content
238        for ($index = 0 ; $index < count($checked) ; $index++) {
239            $params->content .= $checked [$index];
240        }
241    }
242}
243