1<?php
2/**
3 * ODTFrame: Frame handling.
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     LarsDW223
7 */
8
9/** Include ODTDocument.php */
10require_once DOKU_PLUGIN . 'odt/ODT/ODTDocument.php';
11
12/**
13 * ODTFrame:
14 * Class containing static code for handling frames.
15 *
16 * @package    ODT\Frame
17 */
18class ODTFrame
19{
20    static $frameCount = 0;
21    static $fields = array('background-color'           => 'fo:background-color',
22                           'fill-color'                 => 'draw:fill-color',
23                           'fill'                       => 'draw:fill',
24                           'stroke-color'               => 'svg:stroke-color',
25                           'stroke'                     => 'draw:stroke',
26                           'stroke-width'               => 'svg:stroke-width',
27                           'border'                     => 'fo:border',
28                           'border-left'                => 'fo:border-left',
29                           'border-right'               => 'fo:border-right',
30                           'border-top'                 => 'fo:border-top',
31                           'border-bottom'              => 'fo:border-bottom',
32                           'padding-left'               => 'fo:padding-left',
33                           'padding-right'              => 'fo:padding-right',
34                           'padding-top'                => 'fo:padding-top',
35                           'padding-bottom'             => 'fo:padding-bottom',
36                           'margin-left'                => 'fo:margin-left',
37                           'margin-right'               => 'fo:margin-right',
38                           'margin-top'                 => 'fo:margin-top',
39                           'margin-bottom'              => 'fo:margin-bottom',
40                           'vertical-align'             => 'draw:textarea-vertical-align',
41                           'horizontal-align'           => 'draw:textarea-horizontal-align',
42                           'min-height'                 => 'fo:min-height',
43                           'background-transparency'    => 'style:background-transparency',
44                           'textarea-horizontal-align'  => 'draw:textarea-horizontal-align',
45                           'run-through'                => 'style:run-through',
46                           'vertical-pos'               => 'style:vertical-pos',
47                           'vertical-rel'               => 'style:vertical-rel',
48                           'horizontal-pos'             => 'style:horizontal-pos',
49                           'horizontal-rel'             => 'style:horizontal-rel',
50                           'wrap'                       => 'style:wrap',
51                           'number-wrapped-paragraphs'  => 'style:number-wrapped-paragraphs',
52                           'wrap-influence-on-position' => 'draw:wrap-influence-on-position'
53    );
54
55    /**
56     * This function opens a textbox in a frame using CSS.
57     *
58     * The currently supported CSS properties are:
59     * background-color, color, padding, margin, display, border-radius, min-height.
60     * The background-image is simulated using a picture frame.
61     * FIXME: Find a way to successfuly use the background-image in the graphic style (see comments).
62     *
63     * The text box should be closed by calling 'closeTextBox()'.
64     *
65     * @param     ODTInternalParams $params     Commom params.
66     * @param     string            $element    The element name, e.g. "div"
67     * @param     string            $attributes The attributes belonging o the element, e.g. 'class="example"'
68     */
69    public static function openTextBoxUseCSS (ODTInternalParams $params, $element=NULL, $attributes=NULL) {
70        $frame = $params->document->state->getCurrentFrame();
71        if (isset($frame)) {
72            // Do not open a nested frame as this will make the content ofthe nested frame disappear.
73            //return;
74        }
75
76        $properties = array();
77        ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
78        $params->elementObj = $params->htmlStack->getCurrentElement();
79
80        self::openTextBoxUseProperties ($params, $properties);
81    }
82
83    /**
84     * This function opens a textbox in a frame.
85     *
86     * The currently supported CSS properties are:
87     * background-color, color, padding, margin, display, border-radius, min-height.
88     * The background-image is simulated using a picture frame.
89     * FIXME: Find a way to successfuly use the background-image in the graphic style (see comments).
90     *
91     * The text box should be closed by calling 'closeTextBox()'.
92     *
93     * @param     ODTInternalParams $params     Commom params.
94     * @param     array             $properties Properties to use for creating the text box
95     * @param     string            $element    The element name, e.g. "div"
96     * @param     string            $attributes The attributes belonging o the element, e.g. 'class="example"'
97     */
98    public static function openTextBoxUseProperties (ODTInternalParams $params, $properties, $element=NULL, $attributes=NULL) {
99        // Encode frame
100        self::openFrameUseProperties ($params, $properties, $element, $attributes);
101
102        // Create text box
103        $box = new ODTElementTextBox();
104        $box_attrs = '';
105        // If required use round corners.
106        if ( !empty($properties ['border-radius']) )
107            $box_attrs .= 'draw:corner-radius="'.$properties ['border-radius'].'"';
108        $box->setAttributes($box_attrs);
109        $params->document->state->enter($box);
110
111        // Encode box
112        $params->content .= $box->getOpeningTag($params);
113    }
114
115    /**
116     * This function closes a textbox (previously opened with openTextBoxUseProperties()).
117     *
118     * @param     ODTInternalParams $params     Commom params.
119     */
120    public static function closeTextBox (ODTInternalParams $params) {
121        // Close paragraph (if open)
122        $params->document->paragraphClose();
123        // Close text box
124        $params->document->closeCurrentElement();
125        // Close frame
126        self::closeFrame($params);
127    }
128
129    /**
130     * This function opens a multi column frame/text box according to the
131     * parameters in $properties. Call 'closeMultiColumnTextBox()' to
132     * close the text box.
133     *
134     * @param     ODTInternalParams $params     Commom params.
135     * @param     array             $properties Properties to use
136     * @see ODTUnknownStyle::createMultiColumnFrameStyle for information
137     *      about supported $properties.
138     */
139    public static function openMultiColumnTextBoxUseProperties (ODTInternalParams $params, $properties) {
140        if (!isset($element)) {
141            $element = 'div';
142        }
143
144        // Create style name.
145        $style_obj = ODTUnknownStyle::createMultiColumnFrameStyle ($properties);
146        $params->document->addAutomaticStyle($style_obj);
147        $style_name = $style_obj->getProperty('style-name');
148
149        $width_abs = $params->document->getAbsWidthMindMargins (100);
150
151        // Group the frame so that they are stacked one on each other.
152        $params->document->paragraphClose();
153        $params->document->paragraphOpen();
154
155        // Draw a frame with a text box in it. the text box will be left opened
156        // to grow with the content (requires fo:min-height in $style_name).
157
158        if (!isset($params->elementObj)) {
159            $properties = array();
160            ODTUtility::openHTMLElement ($params, $properties, $element, $attributes);
161        }
162
163        // Create frame
164        $frame = new ODTElementFrame($style_name);
165        self::$frameCount++;
166        $frame_attrs = 'draw:name="Frame'.self::$frameCount.'" text:anchor-type="paragraph" svg:width="'.$width_abs.'cm" draw:z-index="0">';
167        $frame->setAttributes($frame_attrs);
168        $params->document->state->enter($frame);
169        $frame->setHTMLElement ($element);
170
171        // Encode frame
172        $params->content .= $frame->getOpeningTag($params);
173
174        // Create text box
175        $box = new ODTElementTextBox();
176        $box_attrs = 'fo:min-height="1pt"';
177        $box->setAttributes($box_attrs);
178        $params->document->state->enter($box);
179
180        // Encode box
181        $params->content .= $box->getOpeningTag($params);
182    }
183
184    /**
185     * This function closes a multi column frame (previously opened with _odtOpenMultiColumnFrame).
186     *
187     * @param     ODTInternalParams $params     Commom params.
188     */
189    public static function closeMultiColumnTextBox (ODTInternalParams $params) {
190        // Close paragraph (if open)
191        $params->document->paragraphClose();
192        // Close text box
193        $params->document->closeCurrentElement();
194        // Close frame
195        ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
196        $params->document->closeCurrentElement();
197
198        $params->document->paragraphClose();
199
200        $params->document->div_z_index -= 5;
201    }
202
203    /**
204     * This function opens a textbox in a frame.
205     *
206     * The currently supported CSS properties are:
207     * background-color, color, padding, margin, display, border-radius, min-height.
208     * The background-image is simulated using a picture frame.
209     * FIXME: Find a way to successfuly use the background-image in the graphic style (see comments).
210     *
211     * The text box should be closed by calling 'closeTextBox()'.
212     *
213     * @param     ODTInternalParams $params     Commom params.
214     * @param     array             $properties Properties to use for creating the frame
215     * @param     string            $element    The element name, e.g. "div"
216     * @param     string            $attributes The attributes belonging o the element, e.g. 'class="example"'
217     */
218    public static function openFrameUseProperties (ODTInternalParams $params, $properties, $element=NULL, $attributes=NULL) {
219        $frame = $params->document->state->getCurrentFrame();
220        if (isset($frame)) {
221            // Do not open a nested frame as this will make the content ofthe nested frame disappear.
222            //return;
223        }
224        if (!isset($element)) {
225            $element = 'div';
226        }
227        $elementObj = $params->elementObj;
228
229        // If we are not in a paragraph then open one.
230        $inParagraph = $params->document->state->getInParagraph();
231        if (!$inParagraph) {
232            $params->document->paragraphOpen();
233        }
234
235        $position = $properties ['position'];
236        $picture = $properties ['background-image'];
237        $pic_positions = preg_split ('/\s/', $properties ['background-position']);
238        //$min_height = $properties ['min-height'];
239        $width = $properties ['width'];
240
241        $pic_link = '';
242        $pic_width = '';
243        $pic_height = '';
244        if ( !empty ($picture) ) {
245            // If a picture/background-image is set in the CSS, than we insert it manually here.
246            // This is a workaround because ODT does not support the background-image attribute in a span.
247            $pic_link = $params->document->addFileAsPicture($picture);
248            list($pic_width, $pic_height) = ODTUtility::getImageSizeString($picture, NULL, NULL, true, $params->units);
249        }
250
251        if ( empty ($width) ) {
252            $width = '100%';
253        }
254        if ( !empty($pic_positions [0]) ) {
255            $pic_positions [0] = $params->document->toPoints($pic_positions [0], 'x');
256        }
257        //if ( empty($min_height) ) {
258        //    $min_height = '1pt';
259        //}
260
261        // Get anchor type
262        $anchor_type = 'paragraph';
263        if (!empty($properties ['anchor-type'])) {
264            $anchor_type = $properties ['anchor-type'];
265        }
266
267        // Get X and Y position.
268        // X and Y position can be set using 'x' or 'left' and 'y' or 'top'.
269        $svgX = null;
270        $svgY = null;
271        if (!empty($properties ['x'])) {
272            $svgX = $properties ['x'];
273        }
274        if (!empty($properties ['left'])) {
275            $svgX = $properties ['left'];
276        }
277        if (!empty($properties ['y'])) {
278            $svgY = $properties ['y'];
279        }
280        if (!empty($properties ['top'])) {
281            $svgY = $properties ['top'];
282        }
283
284        // Adjust properties for CSS property 'position' if given
285        switch ($position) {
286            case 'absolute':
287                $anchor_type = 'page';
288                break;
289            case 'relative':
290                $anchor_type = 'paragraph';
291                break;
292            case 'static':
293                $anchor_type = 'paragraph';
294                $svgX = '0cm';
295                $svgY = '0cm';
296                break;
297        }
298
299        // Add our styles.
300        $style_name = ODTStyle::getNewStylename('Frame');
301        $style  = '<style:style style:name="'.$style_name.'_text_frame" style:family="graphic" style:parent-style-name="Frame">';
302        $style .= '<style:graphic-properties ';
303
304        foreach (self::$fields as $name => $odtName) {
305            if (!empty($properties [$name])) {
306                $style .= $odtName.'="'.$properties [$name].'" ';
307            }
308        }
309        $style .= '>';
310        $style .= '</style:graphic-properties>';
311        $style .= '</style:style>';
312
313        // Add style to our document
314        // (as unknown style because style-family graphic is not supported)
315        $style_obj = ODTUnknownStyle::importODTStyle($style);
316        $params->document->addAutomaticStyle($style_obj);
317
318        // Draw a frame with a text box in it. the text box will be left opened
319        // to grow with the content (requires fo:min-height in $style_name).
320        if (!isset($elementObj)) {
321            $throwAway = array();
322            ODTUtility::openHTMLElement ($params, $throwAway, $element, $attributes);
323        }
324
325        // Create frame
326        $frame = new ODTElementFrame($style_name.'_text_frame');
327        self::$frameCount++;
328        /*$frame_attrs .= 'draw:name="Frame'.self::$frameCount.'"
329                         text:anchor-type="'.$anchor_type.'"
330                         svg:width="'.$width.'" svg:min-height="'.$min_height.'"
331                         draw:z-index="'.($params->document->div_z_index + 0).'"';*/
332        $frame_attrs .= 'draw:name="Frame'.self::$frameCount.'"
333                         text:anchor-type="'.$anchor_type.'"
334                         svg:width="'.$width.'"
335                         draw:z-index="'.($params->document->div_z_index + 0).'"';
336        if (isset($svgX)) {
337            $frame_attrs .= ' svg:x="'.$svgX.'"';
338        }
339        if (isset($svgY)) {
340            $frame_attrs .= ' svg:y="'.$svgY.'"';
341        }
342        if (!empty($properties ['min-height'])) {
343            $frame_attrs .= ' svg:min-height="'.$properties ['min-height'].'"';
344        }
345        if (!empty($properties ['height'])) {
346            $frame_attrs .= ' svg:height="'.$properties ['height'].'"';
347        }
348
349        $frame->setAttributes($frame_attrs);
350        $params->document->state->enter($frame);
351        $frame->setHTMLElement ($element);
352
353        // Encode frame
354        $params->content .= $frame->getOpeningTag($params);
355
356        $params->document->div_z_index += 1;
357    }
358
359    /**
360     * This function closes a textbox (previously opened with openTextBoxUseProperties()).
361     *
362     * @param     ODTInternalParams $params     Commom params.
363     */
364    public static function closeFrame (ODTInternalParams $params) {
365        $frame = $params->document->state->getCurrentFrame();
366        if (!isset($frame)) {
367            // ??? Error. Not table found.
368            return;
369        }
370
371        // Close paragraph (if open)
372        $params->document->paragraphClose();
373
374        // Eventually adjust frame width.
375        $frame->adjustWidth ($params);
376
377        // Close frame
378        $element = $params->document->state->getHTMLElement();
379        ODTUtility::closeHTMLElement ($params, $params->document->state->getHTMLElement());
380        $params->document->closeCurrentElement();
381
382        // Do not close the open paragraph here as it may lead to extra empty lines.
383
384        $params->document->div_z_index -= 1;
385    }
386}
387