1<?php
2
3require_once DOKU_PLUGIN . 'odt/ODT/ODTState.php';
4require_once DOKU_PLUGIN . 'odt/ODT/ODTUtility.php';
5require_once DOKU_PLUGIN . 'odt/ODT/ODTList.php';
6require_once DOKU_PLUGIN . 'odt/ODT/ODTFootnote.php';
7require_once DOKU_PLUGIN . 'odt/ODT/ODTHeading.php';
8require_once DOKU_PLUGIN . 'odt/ODT/ODTParagraph.php';
9require_once DOKU_PLUGIN . 'odt/ODT/ODTTable.php';
10require_once DOKU_PLUGIN . 'odt/ODT/ODTFrame.php';
11require_once DOKU_PLUGIN . 'odt/ODT/ODTImage.php';
12require_once DOKU_PLUGIN . 'odt/ODT/ODTSpan.php';
13require_once DOKU_PLUGIN . 'odt/ODT/ODTIndex.php';
14require_once DOKU_PLUGIN . 'odt/ODT/ODTUnits.php';
15require_once DOKU_PLUGIN . 'odt/ODT/ODTmeta.php';
16require_once DOKU_PLUGIN . 'odt/ODT/ODTmanifest.php';
17require_once DOKU_PLUGIN . 'odt/ODT/css/cssimportnew.php';
18require_once DOKU_PLUGIN . 'odt/ODT/ODTImport.php';
19require_once DOKU_PLUGIN . 'odt/ODT/ODTExport.php';
20
21// Siple class as storage for internal parameters passed to other
22// classes to prevent to long parameter lines.
23class ODTInternalParams
24{
25    public $document = NULL;
26    public $htmlStack = NULL;
27    public $import = NULL;
28    public $units = NULL;
29    public $content = NULL;
30    public $elementObj = NULL;
31    public $ZIP = NULL;
32    public $manifest = NULL;
33    public $styleset = NULL;
34}
35
36/**
37 * Main class/API for creating an ODTDocument.
38 *
39 * Work in progress!!! Goals:
40 *
41 * - Move all pure ODT specific code away from the ODT-DokuWiki
42 *   renderer class in page.php/book.php
43 *
44 * - Make the ODT DokuWiki renderer classes only call functions in this
45 *   class directly to have a single class only which is seen/used by
46 *   the renderer classes
47 *
48 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
49 * @author  LarsDW223
50 */
51class ODTDocument
52{
53    // Public for now.
54    // Will become protected as soon as all stuff using state
55    // has been moved.
56    public $state;
57    public $div_z_index = 0;
58    /** @var Debug string */
59    public $trace_dump = '';
60
61    /** @var  has any text content been added yet (excluding whitespace)? */
62    protected $text_empty = true;
63    /** @var array store the table of contents */
64    protected $toc = array();
65    /** @var ODTMeta */
66    protected $meta;
67    /** @var helper_plugin_odt_units */
68    protected $units = null;
69    /** @var Current pageFormat */
70    protected $page = null;
71    /** @var changePageFormat */
72    protected $changePageFormat = NULL;
73    /** @var indexesData */
74    protected $indexesData = array();
75    /** @var Array of used page styles. Will stay empty if only A4-portrait is used */
76    protected $pageStyles = array ();
77    /** @var Array of paragraph style names that prevent an empty paragraph from being deleted */
78    protected $preventDeletetionStyles = array ();
79    /** @var pagebreak */
80    protected $pagebreak = false;
81    /** @var headers */
82    protected $headers = array();
83    /** @var refIDCount */
84    protected $refIDCount = 0;
85    /** @var pageBookmark */
86    protected $pageBookmark = NULL;
87    /** @var array store the bookmarks */
88    protected $bookmarks = array();
89    /** @var string temporary storage of xml-content */
90    public $store = '';
91    /** @var array */
92    public $footnotes = array();
93    protected $quote_depth = 0;
94    protected $linksEnabled = true;
95    // Used by Fields Plugin
96    protected $fields = array();
97    // The document content
98    protected $content = '';
99    /** @var cssimportnew */
100    protected $importnew = null;
101    /** @var cssdocument */
102    protected $htmlStack = null;
103    protected $CSSUsage = 'off';
104    protected $params = NULL;
105    protected $manifest = NULL;
106    protected $ZIP = NULL;
107    protected $styleset = NULL;
108    protected $registrations = array();
109
110    /**
111     * Constructor:
112     * - initializes the state
113     */
114    public function __construct() {
115        // Initialize state
116        $this->state = new ODTState();
117
118        // Initialize HTML state
119        $this->htmlStack = new cssdocument();
120
121        // Create default styles/styles storage.
122        $this->styleset = new ODTDefaultStyles();
123        $this->styleset->import();
124
125        // Set standard page format: A4, portrait, 2cm margins
126        $this->page = new pageFormat();
127        $this->setStartPageFormat ('A4', 'portrait', 2, 2, 2, 2);
128
129        // Create units object and set default values
130        $this->units = new ODTUnits();
131        $this->units->setPixelPerEm(16);
132        $this->units->setTwipsPerPixelX(16);
133        $this->units->setTwipsPerPixelY(20);
134
135        // Setup meta data store/handler
136        $this->meta = new ODTMeta();
137
138        // Setup manifest data
139        $this->manifest = new ODTManifest();
140
141        // Prepare the zipper
142        // (This instance is only for our to-be-created ODT-ZIP-Archive
143        //  - NOT to be used for extracting any ODT-Templates!)
144        if (class_exists('\splitbrain\PHPArchive\Zip'))
145        {
146            $this->ZIP = new \splitbrain\PHPArchive\Zip();
147            $this->ZIP->create();
148        }
149
150        $this->params = new ODTInternalParams();
151    }
152
153    /**
154     * Initialize the document.
155     */
156    public function initialize () {
157        $this->state->setDocument($this);
158
159        $this->params->document  = $this;
160        $this->params->htmlStack = $this->htmlStack;
161        $this->params->units     = $this->units;
162        $this->params->content   = &$this->content;
163        $this->params->ZIP       = $this->ZIP;
164        $this->params->manifest  = $this->manifest;
165        $this->params->styleset  = $this->styleset;
166
167        if ($this->ZIP === NULL) {
168            return false;
169        }
170        return true;
171    }
172
173    /**
174     * Set CSS usage.
175     *
176     * @param string $usage
177     */
178    public function setCSSUsage ($usage) {
179        switch (strtolower($usage)) {
180            case 'basic':
181            case 'full':
182                $this->CSSUsage = $usage;
183                break;
184            default:
185                $this->CSSUsage = 'off';
186                break;
187        }
188    }
189
190    protected function setupImport() {
191        if ($this->importnew == NULL) {
192            // No CSS imported yet. Create object.
193            $this->importnew = new cssimportnew();
194            if ($this->importnew == NULL) {
195                return;
196            }
197        }
198        $this->params->import = $this->importnew;
199    }
200
201    /**
202     * Set commom CSS media selector.
203     *
204     * @param string $mediaSel
205     */
206    public function setMediaSelector ($mediaSel) {
207        if ($this->importnew == NULL) {
208            $this->setupImport();
209        }
210        $this->importnew->setMedia ($mediaSel);
211    }
212
213    /**
214     * Callback function which adjusts all CSS length values to point.
215     *
216     * @param $property The name of the current CSS property, e.g. 'border-left'
217     * @param $value The current value from the original CSS code
218     * @param $type There are 3 possible values:
219     *              - LengthValueXAxis: the property represents a value on the X axis
220     *              - LengthValueYAxis: the property represents a value on the Y axis
221     *              - CSSValueType::StrokeOrBorderWidth: the property represents a stroke
222     *                or border width
223     * @return string The new, adjusted value for the property
224     */
225    public function adjustLengthCallback ($property, $value, $type, $rule) {
226        // Get the digits and unit
227        $digits = ODTUnits::getDigits($value);
228        $unit = ODTUnits::stripDigits($value);
229
230        if ( $unit == 'px' ) {
231            // Replace px with pt (px does not seem to be supported by ODT)
232            switch ($type) {
233                case CSSValueType::LengthValueXAxis:
234                    $adjusted = $this->toPoints($value, 'x');
235                break;
236
237                case CSSValueType::StrokeOrBorderWidth:
238                    switch ($property) {
239                        case 'border':
240                        case 'border-left':
241                        case 'border-right':
242                        case 'border-top':
243                        case 'border-bottom':
244                            // border in ODT spans does not support 'px' units, so we convert it.
245                            $adjusted = $this->toPoints($value, 'y');
246                        break;
247
248                        default:
249                            $adjusted = $value;
250                        break;
251                    }
252                break;
253
254                case CSSValueType::LengthValueYAxis:
255                default:
256                    switch ($property) {
257                        case 'line-height':
258                            $adjusted = $this->toPoints($value, 'y');
259                        break;
260                        default:
261                            $adjusted = $this->toPoints($value, 'y');
262                        break;
263                    }
264                break;
265            }
266            return $adjusted;
267        } else {
268            if ($property == 'line-height' && $value != 'normal') {
269                if ($unit == '%' || empty($unit)) {
270                    // Relative values must be handled later
271                    return $value;
272                }
273                $adjusted = $this->toPoints($value, 'y');
274                return $adjusted;
275            }
276            return $value;
277        }
278
279        return $value;
280    }
281
282    public function replaceURLPrefixes ($callback) {
283        if ($this->importnew != NULL) {
284            $this->importnew->replaceURLPrefixes ($callback);
285        }
286    }
287
288    public function enableLinks () {
289        $this->linksEnabled = true;
290    }
291
292    public function disableLinks () {
293        $this->linksEnabled = false;
294    }
295
296    // Functions generating content for now will have to be passed
297    // $renderer->doc. Later this will be removed and an internal doc
298    // variable will be maintained. This will break backwards compatibility
299    // with plugins writing to $renderer->doc directly (instead of calling cdata).
300
301    /**
302     * Render plain text data
303     *
304     * @param string $text
305     */
306    function addPlainText($text) {
307        // Check if there is some content in the text.
308        // Only insert bookmark/pagebreak/format change if text is not empty.
309        // Otherwise a empty paragraph/line would be created!
310        if ( !empty($text) && !ctype_space($text) ) {
311            // Insert page bookmark if requested and not done yet.
312            $this->insertPendingPageBookmark();
313
314            // Insert pagebreak or page format change if still pending.
315            // Attention: NOT if $text is empty. This would lead to empty lines before headings
316            //            right after a pagebreak!
317            $in_paragraph = $this->state->getInParagraph();
318            if ( ($this->pagebreakIsPending() || $this->pageFormatChangeIsPending()) ||
319                  !$in_paragraph ) {
320                $this->paragraphOpen();
321            }
322        }
323        $this->content .= $this->replaceXMLEntities($text);
324        if ($this->text_empty && !ctype_space($text)) {
325            $this->text_empty = false;
326        }
327    }
328
329    /**
330     * Open a text span.
331     *
332     * @param string $styleName The style to use.
333     * @see ODTSpan::spanOpen for detailed documentation
334     */
335    function spanOpen($styleName, $element=NULL, $attributes=NULL){
336        if ( !$in_paragraph ) {
337            $this->paragraphOpen();
338        }
339        unset($this->params->elementObj);
340        ODTSpan::spanOpen($this->params, $styleName, $element, $attributes);
341    }
342
343    /**
344     * Open a text span using CSS.
345     *
346     * @see ODTSpan::spanOpenUseCSS for detailed documentation
347     */
348    function spanOpenUseCSS($element=NULL, $attributes=NULL, cssimportnew $import=NULL){
349        if ( !$in_paragraph ) {
350            $this->paragraphOpen();
351        }
352        if ($import == NULL) {
353            $import = $this->importnew;
354        }
355        if ($element == NULL) {
356            $element = 'span';
357        }
358        unset($this->params->elementObj);
359        $this->params->import = $import;
360        ODTSpan::spanOpenUseCSS($this->params, $element, $attributes);
361        $this->params->import = $this->importnew;
362    }
363
364    /**
365     * Open a text span using properties.
366     *
367     * @see ODTSpan::spanOpenUseProperties for detailed documentation
368     */
369    function spanOpenUseProperties($properties){
370        if ( !$in_paragraph ) {
371            $this->paragraphOpen();
372        }
373        ODTUtility::adjustValuesForODT($properties, $this->units);
374        unset($this->params->elementObj);
375        ODTSpan::spanOpenUseProperties($this->params, $properties);
376    }
377
378    /**
379     * Close a text span.
380     *
381     * @param string $style_name The style to use.
382     * @see ODTSpan::spanClose for detailed documentation
383     */
384    function spanClose() {
385        unset($this->params->elementObj);
386        ODTSpan::spanClose($this->params);
387    }
388
389    /**
390     * Automatically generate ODT format for $HTMLCode
391     * including text spans.
392     *
393     * @param string $style_name The style to use.
394     * @see ODTSpan::generateSpansfromHTMLCode for detailed documentation
395     */
396    function generateSpansfromHTMLCode($HTMLCode){
397        ODTSpan::generateSpansfromHTMLCode($this->params, $HTMLCode);
398    }
399
400    /**
401     * Open a paragraph
402     *
403     * @param string $styleName The style to use.
404     * @see ODTParagraph::paragraphOpen for detailed documentation
405     */
406    function paragraphOpen($styleName=NULL, $element=NULL, $attributes=NULL){
407        unset($this->params->elementObj);
408        ODTParagraph::paragraphOpen($this->params, $styleName, $element, $attributes);
409    }
410
411    /**
412     * Close a paragraph
413     *
414     * @see ODTParagraph::paragraphClose for detailed documentation
415     */
416    function paragraphClose(){
417        unset($this->params->elementObj);
418        ODTParagraph::paragraphClose($this->params);
419    }
420
421    /**
422     * Open a paragraph using CSS.
423     *
424     * @see ODTParagraph::paragraphOpenUseCSS for detailed documentation
425     */
426    function paragraphOpenUseCSS($element=NULL, $attributes=NULL, cssimportnew $import=NULL){
427        if ($import == NULL) {
428            $import = $this->importnew;
429        }
430        if ($element == NULL) {
431            $element = 'p';
432        }
433        unset($this->params->elementObj);
434        $this->params->import = $import;
435        ODTParagraph::paragraphOpenUseCSS($this->params, $element, $attributes);
436        $this->params->import = $this->importnew;
437    }
438
439    /**
440     * Open a paragraph using properties.
441     *
442     * @see ODTParagraph::paragraphOpenUseProperties for detailed documentation
443     */
444    function paragraphOpenUseProperties($properties){
445        ODTUtility::adjustValuesForODT($properties, $this->units);
446        unset($this->params->elementObj);
447        ODTParagraph::paragraphOpenUseProperties($this->params, $properties);
448    }
449
450    /**
451     * Insert a horizontal rule
452     */
453    function horizontalRule() {
454        $this->paragraphClose();
455        $styleName = $this->getStyleName('horizontal line');
456        $this->paragraphOpen($styleName);
457        $this->paragraphClose();
458
459        // Save paragraph style name in 'Do not delete array'!
460        $this->preventDeletetionStyles [] = $styleName;
461    }
462
463    /**
464     * static call back to replace spaces
465     *
466     * @param array $matches
467     * @return string
468     */
469    function _preserveSpace($matches){
470        $spaces = $matches[1];
471        $len    = strlen($spaces);
472        return '<text:s text:c="'.$len.'"/>';
473    }
474
475    /**
476     * @param string $text
477     * @param string $style
478     * @param bool $notescaped
479     */
480    function addPreformattedText($text, $style=null, $notescaped=true) {
481        if (empty($style)) {
482            $style = $this->getStyleName('preformatted');
483        }
484        if ($notescaped) {
485            $text = $this->replaceXMLEntities($text);
486        }
487
488        // Check newline at start
489        $first_newline = strpos($text, "\n");
490        if ($first_newline !== FALSE and $first_newline == 0) {
491            // text starts with a newline, remove it
492            $text = substr($text,1);
493        }
494
495        // Check newline at end
496        $length = strlen($text);
497        if ($text[$length-1] == "\n") {
498            $text = substr($text, 0, $length-1);
499        }
500
501        $text = str_replace("\n",'<text:line-break/>',$text);
502        $text = str_replace("\t",'<text:tab/>',$text);
503        $text = preg_replace_callback('/(  +)/',array($this,'_preserveSpace'),$text);
504
505        $list_item = $this->state->getCurrentListItem();
506        if ($list_item != NULL) {
507            // if we're in a list item, we must close the <text:p> tag
508            $this->paragraphClose();
509            $this->paragraphOpen($style);
510            $this->content .= $text;
511            $this->paragraphClose();
512            // FIXME: query previous style before preformatted text was opened and re-use it here
513            $this->paragraphOpen();
514        } else {
515            $this->paragraphClose();
516            $this->paragraphOpen($style);
517            $this->content .= $text;
518            $this->paragraphClose();
519        }
520    }
521
522    /**
523     * Add a linebreak
524     */
525    function linebreak() {
526        $this->content .= '<text:line-break/>';
527    }
528
529    /**
530     * Add a pagebreak
531     */
532    function pagebreak() {
533        // Only set marker to insert a pagebreak on "next occasion".
534        // The pagebreak will then be inserted in the next call to p_open() or header().
535        // The style will be a "pagebreak" style with the paragraph or header style as the parent.
536        // This prevents extra empty lines after the pagebreak.
537        $this->paragraphClose();
538        $this->pagebreak = true;
539    }
540
541    /**
542     * Check if a pagebreak is pending
543     *
544     * @return bool
545     */
546    function pagebreakIsPending() {
547        return $this->pagebreak;
548    }
549
550    /**
551     * Check if a page format change is pending
552     *
553     * @return bool
554     */
555    function pageFormatChangeIsPending() {
556        if ($this->changePageFormat != NULL) {
557            return true;
558        }
559        return false;
560    }
561
562    /**
563     * Set pagebreak pending.
564     *
565     * @return bool
566     */
567    function setPagebreakPending($value) {
568        return $this->pagebreak = $value;
569    }
570
571    /**
572     * Check if a header with $title exists.
573     *
574     * @return bool
575     */
576    function headerExists($title) {
577        return in_array($title, $this->headers);
578    }
579
580    /**
581     * Add $title to headers.
582     *
583     * @return bool
584     */
585    function addHeader($title) {
586        $this->headers[] = $title;
587    }
588
589    /**
590     * Insert an index into the document using the parameters in $settings.
591     *
592     * @see ODTIndex::insertIndex for detailed documentation
593     */
594    function insertIndex($type='toc', array $settings=NULL) {
595        ODTIndex::insertIndex($this->params, $this->indexesData, $type, $settings);
596    }
597
598    /**
599     * Creates a reference ID for the TOC
600     *
601     * @param string $title The headline/item title
602     * @return string
603     *
604     * @author LarsDW223
605     */
606    public function buildTOCReferenceID($title) {
607        // FIXME: not DokuWiki dependant function woud be nicer...
608        $title = str_replace(':','',cleanID($title));
609        $title = ltrim($title,'0123456789._-');
610        if(empty($title)) {
611            $title='NoTitle';
612        }
613
614        $this->refIDCount++;
615
616        // The reference ID needs to start with '__RefHeading___'.
617        // Otherwise LibreOffice will display $ref instead of the heading
618        // name when moving the mouse over the link in the TOC.
619        $ref = '__RefHeading___'.$title.'_'.$this->refIDCount;
620        return $ref;
621    }
622
623    /**
624     * Add an item to the TOC
625     *
626     * @param string $refID    the reference ID
627     * @param string $hid      the hash link
628     * @param string $text     the text to display
629     * @param int    $level    the nesting level
630     */
631    function tocAddItemInternal($refID, $hid, $text, $level) {
632        $item = $refID.','.$hid.','.$text.','. $level;
633        $this->toc[] = $item;
634    }
635
636    /**
637     * Set bookmark for the start of the page. This just saves the title temporarily.
638     * It is then to be inserted in the first header or paragraph.
639     *
640     * @param string $id    ID of the bookmark
641     */
642    function setPageBookmark($id){
643        $inParagraph = $this->state->getInParagraph();
644        if ($inParagraph) {
645            $this->insertBookmarkInternal($id, true);
646        } else {
647            $this->pageBookmark = $id;
648        }
649    }
650
651    /**
652     * Insert a bookmark. If $now is true then the bookmark will be created
653     * immediately. This eventually causes a paragraph to be opened.
654     * If $now is false then the bookmark will be inserted with the next
655     * cdata text.
656     *
657     * @param string $id    ID of the bookmark
658     * @param string $now   Insert bookmark immediately?
659     */
660    public function insertBookmark($id, $now) {
661        if ($now) {
662            $this->insertBookmarkInternal($id);
663        } else {
664            $this->pageBookmark = $id;
665        }
666    }
667
668    /**
669     * Insert a bookmark.
670     *
671     * @param string $id    ID of the bookmark
672     */
673    protected function insertBookmarkInternal($id, $openParagraph=true){
674        if ($openParagraph) {
675            $this->paragraphOpen();
676        }
677        $this->content .= '<text:bookmark text:name="'.$id.'"/>';
678        $this->bookmarks [] = $id;
679    }
680
681    /**
682     * Insert a pending page bookmark
683     *
684     * @param string $text  the text to display
685     * @param int    $level header level
686     * @param int    $pos   byte position in the original source
687     */
688    function insertPendingPageBookmark(){
689        // Insert page bookmark if requested and not done yet.
690        if ( !empty($this->pageBookmark) ) {
691            $this->insertBookmarkInternal($this->pageBookmark, false);
692            $this->pageBookmark = NULL;
693        }
694    }
695
696    /**
697     * Render a heading.
698     *
699     * @param string $text  the text to display
700     * @param int    $level header level
701     * @param int    $pos   byte position in the original source
702     * @see ODTHeading::heading for detailed documentation
703     */
704    function heading($text, $level, $element=NULL, $attributes=NULL){
705        ODTHeading::heading($this->params, $text, $level, $element, $attributes);
706    }
707
708    /**
709     * Make sure that a user field name only contains valid sings.
710     * (Code has been adopted from the fields plugin)
711     *
712     * @param string $name The name of the field
713     * @return string The cleaned up $name
714     * @author Aurelien Bompard <aurelien@bompard.org>
715     */
716    protected function cleanupUserFieldName($name) {
717        // Keep only allowed chars in the name
718        return preg_replace('/[^a-zA-Z0-9_.]/', '', $name);
719    }
720
721    /**
722     * Add a user field.
723     * (Code has been adopted from the fields plugin)
724     *
725     * @param string $name The name of the field
726     * @param string $value The value of the field
727     * @author Aurelien Bompard <aurelien@bompard.org>
728     */
729    public function addUserField($name, $value) {
730        $name = $this->cleanupUserFieldName($name);
731        $this->fields [$name] = $value;
732    }
733
734    /**
735     * Insert a user field reference.
736     * (Code has been adopted from the fields plugin)
737     *
738     * @param string $name The name of the field
739     * @author Aurelien Bompard <aurelien@bompard.org>
740     */
741    public function insertUserField($name) {
742        $name = $this->cleanupUserFieldName($name);
743        if (array_key_exists($name, $this->fields)) {
744            $this->content .= '<text:user-field-get text:name="'.$name.'">'.$this->fields [$name].'</text:user-field-get>';
745        }
746    }
747
748    /**
749     * This function encodes the <text:user-field-decls> section of a
750     * ODT document.
751     *
752     * @return string
753     */
754    protected function getUserFieldDecls() {
755        $value = '<text:user-field-decls>';
756        foreach ($this->fields as $fname => $fvalue) {
757            $value .= '<text:user-field-decl office:value-type="string" text:name="'.$fname.'" office:string-value="'.$fvalue.'"/>';
758        }
759        $value .= '</text:user-field-decls>';
760        return $value;
761    }
762
763    /**
764     * Get ODT file as string (ZIP archive).
765     *
766     * @param string $content The content
767     * @return string String containing ODT ZIP stream
768     */
769    public function getODTFileAsString($ODTtemplate=NULL, $tempDir=NULL) {
770        // Close any open paragraph if not done yet.
771        $this->paragraphClose();
772
773        // Replace local link placeholders with links to headings or bookmarks
774        $styleName = $this->getStyleName('local link');
775        $visitedStyleName = $this->getStyleName('visited local link');
776        ODTUtility::replaceLocalLinkPlaceholders($this->content, $this->toc, $this->bookmarks, $styleName, $visitedStyleName);
777
778        // Build indexes
779        ODTIndex::replaceIndexesPlaceholders($this->params, $this->indexesData, $this->toc);
780
781        // Delete paragraphs which only contain whitespace (but keep pagebreaks!)
782        ODTUtility::deleteUselessElements($this->content, $this->preventDeletetionStyles);
783
784        //$this->trace_dump .= $this->htmlStack->getDump();
785        //$this->trace_dump .= $this->importnew->rulesToString();
786
787        if (!empty($this->trace_dump)) {
788            $this->paragraphOpen();
789            $this->linebreak();
790            $this->content .= 'Tracedump: ';
791            $this->addPreformattedText($this->trace_dump);
792            $this->paragraphClose();
793        }
794
795        // Get meta content
796        $metaContent = $this->meta->getContent();
797
798        // Get user field declarations
799        $userFieldDecls = $this->getUserFieldDecls();
800
801        // Build the document
802        ODTExport::buildZIPFile($this->params,
803                                $metaContent,
804                                $userFieldDecls,
805                                $this->pageStyles,
806                                $ODTtemplate,
807                                $tempDir);
808
809        // Return document
810        return $this->ZIP->getArchive();
811    }
812
813    /**
814     * Import CSS code for styles from a string.
815     *
816     * @param string $cssCode The CSS code to import
817     * @param string $mediaSel The media selector to use e.g. 'print'
818     * @param string $mediaPath Local path to media files
819     */
820    public function importCSSFromString($cssCode, $mediaSel=NULL, $URLCallback=NULL, $forceStyles=false, $listAlign='right') {
821        // Import CSS as styles?
822        $importStyles = false;
823        if ($this->CSSUsage == 'basic' || $this->CSSUsage == 'full' || $forceStyles) {
824            $importStyles = true;
825        }
826        ODTImport::importCSSFromString ($this->params, $cssCode, $mediaSel, array($this, 'adjustLengthCallback'), $URLCallback, $this->registrations, $importStyles, $listAlign);
827    }
828
829    /**
830     * Import CSS code for styles from a file.
831     *
832     * @param string $CSSTemplate String containing the path and file name of the CSS file to import
833     * @param string $mediaSel The media selector to use e.g. 'print'
834     * @param string $mediaPath Local path to media files
835     */
836    public function importCSSFromFile($CSSTemplate, $mediaSel=NULL, $URLCallback=NULL, $listAlign='right') {
837        // Import CSS as styles?
838        $importStyles = false;
839        if ($this->CSSUsage == 'basic' || $this->CSSUsage == 'full') {
840            $importStyles = true;
841        }
842        ODTImport::importCSSFromFile ($this->params, $CSSTemplate, $mediaSel, array($this, 'adjustLengthCallback'), $URLCallback, $this->registrations, $importStyles, $listAlign);
843    }
844
845    public function importODTStyles($template=NULL, $tempDir=NULL) {
846        ODTImport::importODTStyles($this->params, $template, $tempDir);
847    }
848
849    /**
850     * General internal function for closing an element.
851     * Can always be used to close any open element if no more actions
852     * are required apart from generating the closing tag and
853     * removing the element from the state stack.
854     */
855    public function closeCurrentElement() {
856        $current = $this->state->getCurrent();
857        if ($current != NULL) {
858            $this->content .= $current->getClosingTag($this->content);
859            $this->state->leave();
860        }
861    }
862
863    /**
864     * This function creates a style for changing the page format if required.
865     * It returns NULL if no page format change is pending or if the current
866     * page format is equal to the required page format.
867     *
868     * @param string  $parent Parent style name.
869     * @return string Name of the style to be used for changing page format
870     *
871     * FIXME: make protected as soon as function header is moved here also!
872     */
873    public function doPageFormatChange ($parent = NULL) {
874        if ( $this->changePageFormat == NULL ) {
875            // Error.
876            return NULL;
877        }
878        $data = $this->changePageFormat;
879        $this->changePageFormat = NULL;
880
881        if ( empty($parent) ) {
882            $parent = 'Standard';
883        }
884
885        // Create page layout style
886        $format_string = $this->page->formatToString ($data['format'], $data['orientation'], $data['margin-top'], $data['margin-right'], $data['margin-bottom'], $data['margin-left']);
887        $properties ['style-name']    = 'Style-Page-'.$format_string;
888        $properties ['width']         = $data ['width'];
889        $properties ['height']        = $data ['height'];
890        $properties ['margin-top']    = $data ['margin-top'];
891        $properties ['margin-bottom'] = $data ['margin-bottom'];
892        $properties ['margin-left']   = $data ['margin-left'];
893        $properties ['margin-right']  = $data ['margin-right'];
894        $style_obj = ODTPageLayoutStyle::createPageLayoutStyle($properties);
895        $style_name = $style_obj->getProperty('style-name');
896
897        // It is iassumed the proper media selector has been set by calling setMediaSelector()
898        if (($this->CSSUsage == 'basic' || $this->CSSUsage == 'full') && $this->importnew != NULL) {
899            ODTImport::set_page_properties ($this->params, $style_obj);
900        }
901
902        // Save style data in page style array, in common styles and set current page format
903        $master_page_style_name = $format_string;
904        $this->pageStyles [$master_page_style_name] = $style_name;
905        $this->addAutomaticStyle($style_obj);
906        $this->page->setFormat($data ['format'], $data ['orientation'], $data['margin-top'], $data['margin-right'], $data['margin-bottom'], $data['margin-left']);
907
908        // Create paragraph style.
909        $properties = array();
910        $properties ['style-name']             = 'Style-'.$format_string;
911        $properties ['style-parent']           = $parent;
912        $properties ['style-master-page-name'] = $master_page_style_name;
913        $properties ['page-number']            = 'auto';
914        $style_obj = ODTParagraphStyle::createParagraphStyle($properties);
915        $style_name = $style_obj->getProperty('style-name');
916        $this->addAutomaticStyle($style_obj);
917
918        // Save paragraph style name in 'Do not delete array'!
919        $this->preventDeletetionStyles [] = $style_name;
920
921        return $style_name;
922    }
923
924    public function createPagebreakStyle($parent=NULL,$before=true) {
925        $style_name = 'pagebreak';
926        if ( !$before ) {
927            $style_name .= '_after';
928        }
929        if ( !empty($parent) ) {
930            $style_name .= '_'.$parent;
931        }
932        if ( !$this->styleExists($style_name) ) {
933            $style_obj = ODTParagraphStyle::createPagebreakStyle($style_name, $parent, $before);
934            $this->addAutomaticStyle($style_obj);
935
936            // Save paragraph style name in 'Do not delete array'!
937            $this->preventDeletetionStyles [] = $style_name;
938        }
939
940        return $style_name;
941    }
942
943    /**
944     * Replace XML entities
945     *
946     * @param string $value
947     * @return string
948     */
949    function replaceXMLEntities($value) {
950        return str_replace( array('&','"',"'",'<','>'), array('&#38;','&#34;','&#39;','&#60;','&#62;'), $value);
951    }
952
953    /**
954     * Open/start a footnote.
955     *
956     * @author Andreas Gohr <andi@splitbrain.org>
957     * @see ODTFootnote::footnoteOpen for detailed documentation
958     */
959    function footnoteOpen() {
960        ODTFootnote::footnoteOpen($this->params);
961    }
962
963    /**
964     * Close/end a footnote.
965     *
966     * @author Andreas Gohr
967     * @see ODTFootnote::footnoteClose for detailed documentation
968     */
969    function footnoteClose() {
970        ODTFootnote::footnoteClose($this->params);
971    }
972
973    function quoteOpen() {
974        // Do not go higher than 5 because only 5 quotation styles are defined.
975        if ( $this->quote_depth < 5 ) {
976            $this->quote_depth++;
977        }
978        unset($this->params->elementObj);
979        ODTTable::tableOpen($this->params, 1, 1, 'Table_Quotation'.$this->quote_depth, 'blockquote', NULL);
980        $this->tableRowOpen();
981        unset($this->params->elementObj);
982        ODTTable::tableCellOpen($this->params, 1, 1, 'left', 'Cell_Quotation'.$this->quote_depth, NULL, NULL, NULL);
983    }
984
985    function quoteClose() {
986        $this->paragraphClose();
987        $this->tableCellClose();
988        $this->tableRowClose();
989        $this->tableClose();
990        if ( $this->quote_depth > 0 ) {
991            $this->quote_depth--;
992        }
993    }
994
995    /**
996     * Opens a list.
997     * The list style specifies if the list is an ordered or unordered list.
998     *
999     * @param bool $continue Continue numbering?
1000     * @param string $styleName Name of style to use for the list
1001     * @see ODTList::listOpen for detailed documentation
1002     */
1003    function listOpen($continue=false, $styleName, $element=NULL, $attributes=NULL) {
1004        if ($element == NULL) {
1005            if ($styleName == $this->getStyleName('list')) {
1006                $element = 'ul';
1007            }
1008            if ($styleName == $this->getStyleName('numbering')) {
1009                $element = 'ol';
1010            }
1011        }
1012        ODTList::listOpen($this->params, $continue, $styleName, $element, $attributes);
1013    }
1014
1015    /**
1016     * Close a list.
1017     *
1018     * @see ODTList::listClose for detailed documentation
1019     */
1020    function listClose() {
1021        ODTList::listClose($this->params);
1022    }
1023
1024    /**
1025     * Open a list item.
1026     *
1027     * @param int $level The nesting level
1028     * @see ODTList::listItemOpen for detailed documentation
1029     */
1030    function listItemOpen($level, $element=NULL, $attributes=NULL) {
1031        ODTList::listItemOpen($this->params, $level, $element, $attributes);
1032    }
1033
1034    /**
1035     * Close a list item.
1036     *
1037     * @see ODTList::listItemClose for detailed documentation
1038     */
1039    function listItemClose() {
1040        ODTList::listItemClose($this->params);
1041    }
1042
1043    /**
1044     * Open a list header.
1045     *
1046     * @param int $level The nesting level
1047     * @see ODTList::listHeaderOpen for detailed documentation
1048     */
1049    function listHeaderOpen($level, $element=NULL, $attributes=NULL) {
1050        ODTList::listHeaderOpen($this->params, $level, $element, $attributes);
1051    }
1052
1053    /**
1054     * Close a list header.
1055     *
1056     * @see ODTList::listHeaderClose for detailed documentation
1057     */
1058    function listHeaderClose() {
1059        ODTList::listHeaderClose($this->params);
1060    }
1061
1062    /**
1063     * Open list content/a paragraph in a list item.
1064     *
1065     * @see ODTList::listContentOpen for detailed documentation
1066     */
1067    function listContentOpen($element=NULL, $attributes=NULL) {
1068        ODTList::listContentOpen($this->params, $element, $attributes);
1069    }
1070
1071    /**
1072     * Close list content/a paragraph in a list item.
1073     *
1074     * @see ODTList::listContentClose for detailed documentation
1075     */
1076    function listContentClose() {
1077        ODTList::listContentClose($this->params);
1078    }
1079
1080    /**
1081     * Open/start a table.
1082     *
1083     * @param int $maxcols maximum number of columns
1084     * @param int $numrows NOT IMPLEMENTED
1085     * @see ODTTable::tableOpen for detailed documentation
1086     */
1087    function tableOpen($maxcols = NULL, $numrows = NULL, $element=NULL, $attributes=NULL){
1088        unset($this->params->elementObj);
1089        ODTTable::tableOpen($this->params, $maxcols, $numrows, NULL, $element, $attributes);
1090    }
1091
1092    /**
1093     * Close/finish a table.
1094     *
1095     * @param int $maxcols maximum number of columns
1096     * @param int $numrows NOT IMPLEMENTED
1097     * @see ODTTable::tableClose for detailed documentation
1098     */
1099    function tableClose(){
1100        unset($this->params->elementObj);
1101        ODTTable::tableClose($this->params);
1102    }
1103
1104    /**
1105     * Add a column to a table.
1106     *
1107     * @see ODTTable::tableAddColumn for detailed documentation
1108     */
1109    function tableAddColumn (){
1110        unset($this->params->elementObj);
1111        ODTTable::tableAddColumn ($this->params);
1112    }
1113
1114    /**
1115     * Open a table row.
1116     *
1117     * @see ODTTable::tableRowOpen for detailed documentation
1118     */
1119    function tableRowOpen($element=NULL, $attributes=NULL){
1120        unset($this->params->elementObj);
1121        ODTTable::tableRowOpen($this->params, NULL, $element, $attributes);
1122    }
1123
1124    /**
1125     * Close a table row.
1126     *
1127     * @see ODTTable::tableRowClose for detailed documentation
1128     */
1129    function tableRowClose(){
1130        unset($this->params->elementObj);
1131        ODTTable::tableRowClose($this->params);
1132    }
1133
1134    /**
1135     * Open a table header cell.
1136     *
1137     * @see ODTTable::tableHeaderOpen for detailed documentation
1138     */
1139    function tableHeaderOpen($colspan = 1, $rowspan = 1, $align, $element=NULL, $attributes=NULL){
1140        unset($this->params->elementObj);
1141        ODTTable::tableHeaderOpen($this->params, $colspan, $rowspan, $align, NULL, NULL, $element, $attributes);
1142    }
1143
1144    /**
1145     * Close a table header cell.
1146     *
1147     * @see ODTTable::tableHeaderClose for detailed documentation
1148     */
1149    function tableHeaderClose(){
1150        unset($this->params->elementObj);
1151        ODTTable::tableHeaderClose($this->params);
1152    }
1153
1154    /**
1155     * Open a table cell.
1156     *
1157     * @see ODTTable::tableCellOpen for detailed documentation
1158     */
1159    function tableCellOpen($colspan, $rowspan, $align, $element=NULL, $attributes=NULL){
1160        unset($this->params->elementObj);
1161        ODTTable::tableCellOpen($this->params, $colspan, $rowspan, $align, NULL, NULL, $element, $attributes);
1162    }
1163
1164    /**
1165     * Close a table cell.
1166     *
1167     * @see ODTTable::tableCellClose for detailed documentation
1168     */
1169    function tableCellClose(){
1170        unset($this->params->elementObj);
1171        ODTTable::tableCellClose($this->params);
1172    }
1173
1174    /**
1175     * Open a table using CSS.
1176     *
1177     * @see ODTTable::tableOpenUseCSS for detailed documentation
1178     */
1179    function tableOpenUseCSS($maxcols=NULL, $numrows=NULL, $element=NULL, $attributes=NULL, cssimportnew $import=NULL){
1180        if ($import == NULL) {
1181            $import = $this->importnew;
1182        }
1183        if ($element == NULL) {
1184            $element = 'table';
1185        }
1186
1187        unset($this->params->elementObj);
1188        $this->params->import = $import;
1189        ODTTable::tableOpenUseCSS($this->params, $maxcols, $numrows, $element, $attributes);
1190        $this->params->import = $this->importnew;
1191    }
1192
1193    /**
1194     * Open a table using properties.
1195     *
1196     * @see ODTTable::tableOpenUseProperties for detailed documentation
1197     */
1198    function tableOpenUseProperties ($properties, $maxcols = 0, $numrows = 0){
1199        unset($this->params->elementObj);
1200        ODTTable::tableOpenUseProperties($this->params, $properties, $maxcols, $numrows);
1201    }
1202
1203    /**
1204     * Add a table column using properties.
1205     *
1206     * @see ODTTable::tableAddColumnUseProperties for detailed documentation
1207     */
1208    function tableAddColumnUseProperties ($properties){
1209        unset($this->params->elementObj);
1210        ODTTable::tableAddColumnUseProperties($this->params, $properties);
1211    }
1212
1213    /**
1214     * Open a table header using CSS.
1215     *
1216     * @see ODTTable::tableHeaderOpenUseCSS for detailed documentation
1217     */
1218    function tableHeaderOpenUseCSS($colspan = 1, $rowspan = 1, $element=NULL, $attributes=NULL, cssimportnew $import=NULL){
1219        if ($import == NULL) {
1220            $import = $this->importnew;
1221        }
1222        if ($element == NULL) {
1223            $element = 'th';
1224        }
1225
1226        unset($this->params->elementObj);
1227        $this->params->import = $import;
1228        ODTTable::tableHeaderOpenUseCSS($this->params, $colspan, $rowspan, $element, $attributes);
1229        $this->params->import = $this->importnew;
1230    }
1231
1232    /**
1233     * Open a table header using properties.
1234     *
1235     * @see ODTTable::tableHeaderOpenUseProperties for detailed documentation
1236     */
1237    function tableHeaderOpenUseProperties($properties, $colspan = 1, $rowspan = 1){
1238        unset($this->params->elementObj);
1239        ODTTable::tableHeaderOpenUseProperties($this->params, $properties, $colspan, $rowspan);
1240    }
1241
1242    /**
1243     * Open a table row using CSS.
1244     *
1245     * @see ODTTable::tableRowOpenUseCSS for detailed documentation
1246     */
1247    function tableRowOpenUseCSS($element=NULL, $attributes=NULL, cssimportnew $import=NULL){
1248        if ($import == NULL) {
1249            $import = $this->importnew;
1250        }
1251        if ($element == NULL) {
1252            $element = 'tr';
1253        }
1254
1255        unset($this->params->elementObj);
1256        $this->params->import = $import;
1257        ODTTable::tableRowOpenUseCSS($this->params, $element, $attributes);
1258        $this->params->import = $this->importnew;
1259    }
1260
1261    /**
1262     * Open a table row using properties.
1263     *
1264     * @see ODTTable::tableRowOpenUseProperties for detailed documentation
1265     */
1266    function tableRowOpenUseProperties($properties){
1267        unset($this->params->elementObj);
1268        ODTTable::tableRowOpenUseProperties($this->params, $properties);
1269    }
1270
1271    /**
1272     * Open a table cell using CSS.
1273     *
1274     * @see ODTTable::tableCellOpenUseCSS for detailed documentation
1275     */
1276    function tableCellOpenUseCSS($colspan = 1, $rowspan = 1, $element=NULL, $attributes=NULL, cssimportnew $import=NULL){
1277        if ($import == NULL) {
1278            $import = $this->importnew;
1279        }
1280        if ($element == NULL) {
1281            $element = 'td';
1282        }
1283
1284        unset($this->params->elementObj);
1285        $this->params->import = $import;
1286        ODTTable::tableCellOpenUseCSS($this->params, $element, $attributes, $colspan, $rowspan);
1287        $this->params->import = $this->importnew;
1288    }
1289
1290    /**
1291     * Open a table cell using properties.
1292     *
1293     * @see ODTTable::tableCellOpenUseProperties for detailed documentation
1294     */
1295    function tableCellOpenUseProperties($properties, $colspan = 1, $rowspan = 1){
1296        unset($this->params->elementObj);
1297        ODTTable::tableCellOpenUseProperties($this->params, $properties, $colspan, $rowspan);
1298    }
1299
1300    /**
1301     * Open a text box in a frame using CSS.
1302     *
1303     * @see ODTFrame::openTextBoxUseCSS for detailed documentation
1304     */
1305    function openTextBoxUseCSS ($element=NULL, $attributes=NULL, cssimportnew $import=NULL) {
1306        if ($import == NULL) {
1307            $import = $this->importnew;
1308        }
1309        if ($element == NULL) {
1310            $element = 'div';
1311        }
1312
1313        unset($this->params->elementObj);
1314        $this->params->import = $import;
1315        ODTFrame::openTextBoxUseCSS($this->params, $element, $attributes);
1316        $this->params->import = $this->importnew;
1317    }
1318
1319    /**
1320     * Open a text box in a frame using properties.
1321     *
1322     * @see ODTFrame::openTextBoxUseProperties for detailed documentation
1323     */
1324    function openTextBoxUseProperties ($properties) {
1325        unset($this->params->elementObj);
1326        ODTFrame::openTextBoxUseProperties($this->params, $properties);
1327    }
1328
1329    /**
1330     * This function closes a textbox.
1331     *
1332     * @see ODTFrame::closeTextBox for detailed documentation
1333     */
1334    function closeTextBox () {
1335        unset($this->params->elementObj);
1336        ODTFrame::closeTextBox($this->params);
1337    }
1338
1339    /**
1340     * Open a frame using properties.
1341     *
1342     * @see ODTFrame::openFrameUseProperties for detailed documentation
1343     */
1344    function openFrameUseProperties ($properties) {
1345        unset($this->params->elementObj);
1346        ODTFrame::openFrameUseProperties($this->params, $properties);
1347    }
1348
1349    /**
1350     * This function closes a textbox.
1351     *
1352     * @see ODTFrame::closeTextBox for detailed documentation
1353     */
1354    function closeFrame () {
1355        unset($this->params->elementObj);
1356        ODTFrame::closeFrame($this->params);
1357    }
1358
1359    /**
1360     * Open a multi column text box in a frame using properties.
1361     *
1362     * @see ODTFrame::openMultiColumnTextBoxUseProperties for detailed documentation
1363     */
1364    function openMultiColumnTextBoxUseProperties ($properties) {
1365        unset($this->params->elementObj);
1366        ODTFrame::openMultiColumnTextBoxUseProperties($this->params, $properties);
1367    }
1368
1369    /**
1370     * This function closes a multi column textbox.
1371     *
1372     * @see ODTFrame::closeTextBox for detailed documentation
1373     */
1374    function closeMultiColumnTextBox () {
1375        unset($this->params->elementObj);
1376        ODTFrame::closeMultiColumnTextBox($this->params);
1377    }
1378
1379    /**
1380     * Change outline style to given value.
1381     * Currently only 'Numbers' is supported. Any other value will
1382     * not change anything.
1383     *
1384     * @param string $type Type of outline style to set
1385     */
1386    public function setOutlineStyle ($type) {
1387        $outline_style = $this->getStyle('Outline');
1388        if ($outline_style == NULL) {
1389            // Outline style not found!
1390            return;
1391        }
1392        switch ($type) {
1393            case 'Numbers':
1394                for ($level = 1 ; $level < 11 ; $level++) {
1395                    $outline_style->setPropertyForLevel($level, 'num-format', '1');
1396                    $outline_style->setPropertyForLevel($level, 'num-suffix', NULL);
1397                    $outline_style->setPropertyForLevel($level, 'num-prefix', NULL);
1398                    $outline_style->setPropertyForLevel($level, 'display-levels', $level);
1399                }
1400                break;
1401        }
1402    }
1403
1404    /**
1405     * This function creates a text style for spans with the given properties.
1406     * If $common is true it will be added to the common styles otherwise it
1407     * will be dadded to the automatic styles.
1408     *
1409     * Common styles are visible for the user after export e.g. in LibreOffice
1410     * 'Styles and Formatting' view. Therefore they should have
1411     * $properties ['style-display-name'] set to a meaningfull name.
1412     *
1413     * @param $properties The properties to use
1414     * @param $common Add style to common or automatic styles?
1415     * @see ODTTextStyle::createTextStyle for more documentation
1416     */
1417    public function createTextStyle ($properties, $common=true) {
1418        $style_obj = ODTTextStyle::createTextStyle($properties, NULL, $this);
1419        if ($common == true) {
1420            $this->addStyle($style_obj);
1421        } else {
1422            $this->addAutomaticStyle($style_obj);
1423        }
1424    }
1425
1426    /**
1427     * This function creates a paragraph style for paragraphs with the given properties.
1428     * If $common is true it will be added to the common styles otherwise it
1429     * will be dadded to the automatic styles.
1430     *
1431     * Common styles are visible for the user after export e.g. in LibreOffice
1432     * 'Styles and Formatting' view. Therefore they should have
1433     * $properties ['style-display-name'] set to a meaningfull name.
1434     *
1435     * @param $properties The properties to use
1436     * @param $common Add style to common or automatic styles?
1437     * @see ODTParagraphStyle::createParagraphStyle for more documentation
1438     */
1439    public function createParagraphStyle ($properties, $common=true) {
1440        $style_obj = ODTParagraphStyle::createParagraphStyle($properties, NULL, $this);
1441        if ($common == true) {
1442            $this->addStyle($style_obj);
1443        } else {
1444            $this->addAutomaticStyle($style_obj);
1445        }
1446    }
1447
1448    /**
1449     * This function creates a table style for tables with the given properties.
1450     * If $common is true it will be added to the common styles otherwise it
1451     * will be dadded to the automatic styles.
1452     *
1453     * Common styles are visible for the user after export e.g. in LibreOffice
1454     * 'Styles and Formatting' view. Therefore they should have
1455     * $properties ['style-display-name'] set to a meaningfull name.
1456     *
1457     * @param $properties The properties to use
1458     * @param $common Add style to common or automatic styles?
1459     * @see ODTTableStyle::createTableTableStyle for more documentation
1460     */
1461    public function createTableStyle ($properties, $common=true) {
1462        $style_obj = ODTTableStyle::createTableTableStyle($properties);
1463        if ($common == true) {
1464            $this->addStyle($style_obj);
1465        } else {
1466            $this->addAutomaticStyle($style_obj);
1467        }
1468    }
1469
1470    /**
1471     * This function creates a table row style for table rows with the given properties.
1472     * If $common is true it will be added to the common styles otherwise it
1473     * will be dadded to the automatic styles.
1474     *
1475     * Common styles are visible for the user after export e.g. in LibreOffice
1476     * 'Styles and Formatting' view. Therefore they should have
1477     * $properties ['style-display-name'] set to a meaningfull name.
1478     *
1479     * @param $properties The properties to use
1480     * @param $common Add style to common or automatic styles?
1481     * @see ODTTableRowStyle::createTableRowStyle for more documentation
1482     */
1483    public function createTableRowStyle ($properties, $common=true) {
1484        $style_obj = ODTTableRowStyle::createTableRowStyle($properties);
1485        if ($common == true) {
1486            $this->addStyle($style_obj);
1487        } else {
1488            $this->addAutomaticStyle($style_obj);
1489        }
1490    }
1491
1492    /**
1493     * This function creates a table cell style for table cells with the given properties.
1494     * If $common is true it will be added to the common styles otherwise it
1495     * will be dadded to the automatic styles.
1496     *
1497     * Common styles are visible for the user after export e.g. in LibreOffice
1498     * 'Styles and Formatting' view. Therefore they should have
1499     * $properties ['style-display-name'] set to a meaningfull name.
1500     *
1501     * @param $properties The properties to use
1502     * @param $common Add style to common or automatic styles?
1503     * @see ODTTableCellStyle::createTableCellStyle for more documentation
1504     */
1505    public function createTableCellStyle ($properties, $common=true) {
1506        $style_obj = ODTTableCellStyle::createTableCellStyle($properties);
1507        if ($common == true) {
1508            $this->addStyle($style_obj);
1509        } else {
1510            $this->addAutomaticStyle($style_obj);
1511        }
1512    }
1513
1514    /**
1515     * This function creates a table column style for table columns with the given properties.
1516     * If $common is true it will be added to the common styles otherwise it
1517     * will be dadded to the automatic styles.
1518     *
1519     * Common styles are visible for the user after export e.g. in LibreOffice
1520     * 'Styles and Formatting' view. Therefore they should have
1521     * $properties ['style-display-name'] set to a meaningfull name.
1522     *
1523     * @param $properties The properties to use
1524     * @param $common Add style to common or automatic styles?
1525     * @see ODTTableColumnStyle::createTableColumnStyle for more documentation
1526     */
1527    public function createTableColumnStyle ($properties, $common=true) {
1528        $style_obj = ODTTableColumnStyle::createTableColumnStyle($properties);
1529        if ($common == true) {
1530            $this->addStyle($style_obj);
1531        } else {
1532            $this->addAutomaticStyle($style_obj);
1533        }
1534    }
1535
1536    /**
1537     * The function tries to examine the width and height
1538     * of the image stored in file $src.
1539     *
1540     * @see ODTUtility::getImageSize for a detailed description
1541     */
1542    public function getImageSize($src, $maxwidth=NULL, $maxheight=NULL){
1543        return ODTUtility::getImageSize($src, $maxwidth, $maxheight);
1544    }
1545
1546    /**
1547     * @param string $src
1548     * @param  $width
1549     * @param  $height
1550     * @return array
1551     */
1552    public function getImageSizeString($src, $width = NULL, $height = NULL){
1553        return ODTUtility::getImageSizeString($src, $width, $height, false, $this->params->units);
1554    }
1555
1556    /**
1557     * Adds an image $src to the document.
1558     *
1559     * @param string  $src        The path to the image file
1560     * @param string  $width      Width of the picture (NULL=original size)
1561     * @param string  $height     Height of the picture (NULL=original size)
1562     * @param string  $align      Alignment
1563     * @param string  $title      Title
1564     * @param string  $style      Optional "draw:style-name"
1565     * @param boolean $returnonly Only return code
1566     *
1567     * @see ODTImage::addImage for a detailed description
1568     */
1569    public function addImage($src, $width = NULL, $height = NULL, $align = NULL, $title = NULL, $style = NULL, $returnonly = false){
1570        if ($returnonly) {
1571            return ODTImage::addImage($this->params, $src, $width, $height, $align, $title, $style, $returnonly);
1572        } else {
1573            ODTImage::addImage($this->params, $src, $width, $height, $align, $title, $style, $returnonly);
1574        }
1575    }
1576
1577    /**
1578     * Adds an image $src to the document using the parameters set in $properties.
1579     *
1580     * @param string  $src        The path to the image file
1581     * @param array   $properties Properties (width, height... see ODTImage::addImageUseProperties)
1582     * @param boolean $returnonly Only return code
1583     *
1584     * @see ODTImage::addImageUseProperties for a detailed description
1585     */
1586    public function addImageUseProperties($src, $properties, $returnonly = false){
1587        if ($returnonly) {
1588            return ODTImage::addImageUseProperties($this->params, $src, $properties, $returnonly);
1589        } else {
1590            ODTImage::addImageUseProperties($this->params, $src, $properties, $returnonly);
1591        }
1592    }
1593
1594    /**
1595     * The function adds $string as an SVG image file.
1596     * It does NOT insert the image in the document.
1597     *
1598     * @see ODTImage::addStringAsSVGImageFile for a detailed description
1599     */
1600    public function addStringAsSVGImageFile($string) {
1601        return ODTImage::addStringAsSVGImageFile($this, $string);
1602    }
1603
1604    /**
1605     * Adds the content of $string as a SVG picture to the document.
1606     *
1607     * @see ODTImage::addStringAsSVGImage for a detailed description
1608     */
1609    public function addStringAsSVGImage($string, $width = NULL, $height = NULL, $align = NULL, $title = NULL, $style = NULL) {
1610        return ODTImage::addStringAsSVGImage($this->params, $string, $width, $height, $align, $title, $style);
1611    }
1612
1613    /**
1614     * Get properties defined in a CSS style statement.
1615     *
1616     * @see ODTUtility::getCSSStylePropertiesForODT
1617     */
1618    public function getCSSStylePropertiesForODT(&$properties, $style, $baseURL = NULL){
1619        ODTUtility::getCSSStylePropertiesForODT($properties, $style, $baseURL, $this->units);
1620    }
1621
1622    /**
1623     * This function sets the page format for the FIRST page.
1624     * The format, orientation and page margins can be changed.
1625     * See function queryFormat() in ODT/page.php for supported formats.
1626     *
1627     * @param string  $format         e.g. 'A4', 'A3'
1628     * @param string  $orientation    e.g. 'portrait' or 'landscape'
1629     * @param numeric $margin_top     Top-Margin in cm, default 2
1630     * @param numeric $margin_right   Right-Margin in cm, default 2
1631     * @param numeric $margin_bottom  Bottom-Margin in cm, default 2
1632     * @param numeric $margin_left    Left-Margin in cm, default 2
1633     */
1634    public function setStartPageFormat ($format=NULL, $orientation=NULL, $margin_top=NULL, $margin_right=NULL, $margin_bottom=NULL, $margin_left=NULL) {
1635        // Setup page format.
1636        // Set the page format of the current page for calculation ($this->page)
1637        $this->page->setFormat
1638            ($format, $orientation, $margin_top, $margin_right, $margin_bottom, $margin_left);
1639
1640        // Change the standard page layout style
1641        $first_page = $this->getStyleByAlias('first page');
1642        if ($first_page != NULL) {
1643            $first_page->setProperty('width', $this->page->getWidth().'cm');
1644            $first_page->setProperty('height', $this->page->getHeight().'cm');
1645            $first_page->setProperty('margin-top', $this->page->getMarginTop().'cm');
1646            $first_page->setProperty('margin-right', $this->page->getMarginRight().'cm');
1647            $first_page->setProperty('margin-bottom', $this->page->getMarginBottom().'cm');
1648            $first_page->setProperty('margin-left', $this->page->getMarginLeft().'cm');
1649        }
1650    }
1651
1652    /**
1653     * This function sets the page format.
1654     * The format, orientation and page margins can be changed.
1655     * See function queryFormat() in ODT/page.php for supported formats.
1656     *
1657     * @param string  $format         e.g. 'A4', 'A3'
1658     * @param string  $orientation    e.g. 'portrait' or 'landscape'
1659     * @param numeric $margin_top     Top-Margin in cm, default 2
1660     * @param numeric $margin_right   Right-Margin in cm, default 2
1661     * @param numeric $margin_bottom  Bottom-Margin in cm, default 2
1662     * @param numeric $margin_left    Left-Margin in cm, default 2
1663     */
1664    public function setPageFormat ($format=NULL, $orientation=NULL, $margin_top=NULL, $margin_right=NULL, $margin_bottom=NULL, $margin_left=NULL) {
1665        $data = array ();
1666
1667        // Fill missing values with current settings
1668        if ( empty($format) ) {
1669            $format = $this->page->getFormat();
1670        }
1671        if ( empty($orientation) ) {
1672            $orientation = $this->page->getOrientation();
1673        }
1674        if ( empty($margin_top) ) {
1675            $margin_top = $this->page->getMarginTop();
1676        }
1677        if ( empty($margin_right) ) {
1678            $margin_right = $this->page->getMarginRight();
1679        }
1680        if ( empty($margin_bottom) ) {
1681            $margin_bottom = $this->page->getMarginBottom();
1682        }
1683        if ( empty($margin_left) ) {
1684            $margin_left = $this->page->getMarginLeft();
1685        }
1686
1687        // Adjust given parameters, query resulting format data and get format-string
1688        $this->page->queryFormat ($data, $format, $orientation, $margin_top, $margin_right, $margin_bottom, $margin_left);
1689        $format_string = $this->page->formatToString ($data['format'], $data['orientation'], $data['margin-top'], $data['margin-right'], $data['margin-bottom'], $data['margin-left']);
1690
1691        if ( $format_string == $this->page->toString () ) {
1692            // Current page already uses this format, no need to do anything...
1693            return;
1694        }
1695
1696        if ($this->text_empty) {
1697            // If the text is still empty, then we change the start page format now.
1698            $this->page->setFormat($data ['format'], $data ['orientation'], $data['margin-top'], $data['margin-right'], $data['margin-bottom'], $data['margin-left']);
1699            $first_page = $this->getStyleByAlias('first page');
1700            if ($first_page != NULL) {
1701                $first_page->setProperty('width', $this->page->getWidth().'cm');
1702                $first_page->setProperty('height', $this->page->getHeight().'cm');
1703                $first_page->setProperty('margin-top', $this->page->getMarginTop().'cm');
1704                $first_page->setProperty('margin-right', $this->page->getMarginRight().'cm');
1705                $first_page->setProperty('margin-bottom', $this->page->getMarginBottom().'cm');
1706                $first_page->setProperty('margin-left', $this->page->getMarginLeft().'cm');
1707            }
1708        } else {
1709            // Set marker and save data for pending change format.
1710            // The format change istelf will be done on the next call to p_open or header()
1711            // to prevent empty lines after the format change.
1712            $this->changePageFormat = $data;
1713
1714            // Close paragraph if open
1715            $this->paragraphClose();
1716        }
1717    }
1718
1719    /**
1720     * Return total page width in centimeters
1721     * (margins are included)
1722     *
1723     * @author LarsDW223
1724     */
1725    function getPageWidth(){
1726        return $this->page->getWidth();
1727    }
1728
1729    /**
1730     * Return total page height in centimeters
1731     * (margins are included)
1732     *
1733     * @author LarsDW223
1734     */
1735    function getPageHeight(){
1736        return $this->page->getHeight();
1737    }
1738
1739    /**
1740     * Return left margin in centimeters
1741     *
1742     * @author LarsDW223
1743     */
1744    function getLeftMargin(){
1745        return $this->page->getMarginLeft();
1746    }
1747
1748    /**
1749     * Return right margin in centimeters
1750     *
1751     * @author LarsDW223
1752     */
1753    function getRightMargin(){
1754        return $this->page->getMarginRight();
1755    }
1756
1757    /**
1758     * Return top margin in centimeters
1759     *
1760     * @author LarsDW223
1761     */
1762    function _getTopMargin(){
1763        return $this->page->getMarginTop();
1764    }
1765
1766    /**
1767     * Return bottom margin in centimeters
1768     *
1769     * @author LarsDW223
1770     */
1771    function _getBottomMargin(){
1772        return $this->page->getMarginBottom();
1773    }
1774
1775    /**
1776     * Return width percentage value if margins are taken into account.
1777     * Usually "100%" means 21cm in case of A4 format.
1778     * But usually you like to take care of margins. This function
1779     * adjusts the percentage to the value which should be used for margins.
1780     * So 100% == 21cm e.g. becomes 80.9% == 17cm (assuming a margin of 2 cm on both sides).
1781     *
1782     * @author LarsDW223
1783     *
1784     * @param int|string $percentage
1785     * @return int|string
1786     */
1787    function getRelWidthMindMargins ($percentage = '100'){
1788        return $this->page->getRelWidthMindMargins($percentage);
1789    }
1790
1791    /**
1792     * Like _getRelWidthMindMargins but returns the absulute width
1793     * in centimeters.
1794     *
1795     * @author LarsDW223
1796     * @param string|int|float $percentage
1797     * @return float
1798     */
1799    function getAbsWidthMindMargins ($percentage = '100'){
1800        return $this->page->getAbsWidthMindMargins($percentage);
1801    }
1802
1803    /**
1804     * Return height percentage value if margins are taken into account.
1805     * Usually "100%" means 29.7cm in case of A4 format.
1806     * But usually you like to take care of margins. This function
1807     * adjusts the percentage to the value which should be used for margins.
1808     * So 100% == 29.7cm e.g. becomes 86.5% == 25.7cm (assuming a margin of 2 cm on top and bottom).
1809     *
1810     * @author LarsDW223
1811     *
1812     * @param string|float|int $percentage
1813     * @return float|string
1814     */
1815    function getRelHeightMindMargins ($percentage = '100'){
1816        return $this->page->getRelHeightMindMargins($percentage);
1817    }
1818
1819    /**
1820     * Like _getRelHeightMindMargins but returns the absulute width
1821     * in centimeters.
1822     *
1823     * @author LarsDW223
1824     *
1825     * @param string|int|float $percentage
1826     * @return float
1827     */
1828    function getAbsHeightMindMargins ($percentage = '100'){
1829        return $this->page->getAbsHeightMindMargins($percentage);
1830    }
1831
1832    /**
1833     * Sets the twips per pixel (X axis) used for px to pt conversion.
1834     *
1835     * @param int $value The value to be set.
1836     */
1837    function setTwipsPerPixelX ($value) {
1838        $this->units->setTwipsPerPixelX ($value);
1839    }
1840
1841    /**
1842     * Sets the twips per pixel (Y axis) unit used for px to pt conversion.
1843     *
1844     * @param int $value The value to be set.
1845     */
1846    function setTwipsPerPixelY ($value) {
1847        $this->units->setTwipsPerPixelY ($value);
1848    }
1849
1850    /**
1851     * Sets the pixel per em unit used for px to em conversion.
1852     *
1853     * @param int $value The value to be set.
1854     */
1855    public function setPixelPerEm ($value) {
1856        $this->units->setPixelPerEm ($value);
1857    }
1858
1859    /**
1860     * Convert length value with valid XSL unit to points.
1861     *
1862     * @param string $value  String with length value, e.g. '20px', '20cm'...
1863     * @param string $axis   Is the value to be converted a value on the X or Y axis? Default is 'y'.
1864     *        Only relevant for conversion from 'px' or 'em'.
1865     * @return string The current value.
1866     */
1867    public function toPoints ($value, $axis = 'y') {
1868        return $this->units->toPoints ($value, $axis);
1869    }
1870
1871    /**
1872     * Convert length value with valid XSL unit to pixel.
1873     *
1874     * @param string $value  String with length value, e.g. '20pt', '20cm'...
1875     * @param string $axis   Is the value to be converted a value on the X or Y axis? Default is 'y'.
1876     *        Only relevant for conversion from 'px' or 'em'.
1877     * @return string The current value.
1878     */
1879    public function toPixel ($value, $axis = 'y') {
1880        return $this->units->toPixel ($value, $axis);
1881    }
1882
1883    public function setTitle ($title) {
1884        // Set title in meta info.
1885        $this->meta->setTitle($title);
1886    }
1887
1888    /**
1889     * Get closest previous TOC entry with $level.
1890     * The function search backwards (previous) in the TOC entries
1891     * for the next entry with level $level and retunrs it reference ID.
1892     *
1893     * @param int    $level    the nesting level
1894     * @return string The reference ID or NULL
1895     */
1896    public function getPreviousToCItem($level) {
1897        $index = count($this->toc);
1898        for (; $index >= 0 ; $index--) {
1899            $item = $this->toc[$index];
1900            $params = explode (',', $item);
1901            if ($params [3] == $level) {
1902                return $params [0];
1903            }
1904        }
1905        return NULL;
1906    }
1907
1908    /**
1909     * Insert cross reference to a "destination" inside of the ODT document.
1910     * To insert a link to an external destination use insertHyperlink().
1911     *
1912     * The function only inserts a placeholder and resolves
1913     * the reference on calling replaceLocalLinkPlaceholders();
1914     *
1915     * @fixme add image handling
1916     *
1917     * @param string $destination The resource to link to (e.g. heading ID)
1918     * @param string $text Text for the link (text inserted instead of $destination)
1919     */
1920    function insertCrossReference($destination, $text){
1921        $this->content .= '<locallink name="'.$text.'">'.$destination.'</locallink>';
1922    }
1923
1924    function openImageLink ($url, $returnonly = false) {
1925        $encoded = '';
1926        if ($this->linksEnabled) {
1927            $url = ODTUtility::stringToIRI($url);
1928            $encoded = '<draw:a xlink:type="simple" xlink:href="'.$url.'">';
1929        }
1930        if ($returnonly) {
1931            return $encoded;
1932        }
1933        $this->content .= $encoded;
1934    }
1935
1936    function closeImageLink ($returnonly = false) {
1937        $encoded = '';
1938        if ($this->linksEnabled) {
1939            $encoded = '</draw:a>';
1940        }
1941        if ($returnonly) {
1942            return $encoded;
1943        }
1944        $this->content .= $encoded;
1945    }
1946
1947    function openHyperlink ($url, $styleName = NULL, $visitedStyleName = NULL, $returnonly = false) {
1948        $encoded = '';
1949        if ($url && $this->linksEnabled) {
1950            if (empty($styleName)) {
1951                $styleName = $this->getStyleName('internet link');
1952            }
1953            if (empty($visitedStyleName)) {
1954                $visitedStyleName = $this->getStyleName('visited internet link');
1955            }
1956            $url = ODTUtility::stringToIRI($url);
1957            $encoded .= '<text:a xlink:type="simple" xlink:href="'.$url.'"';
1958            $encoded .= ' text:style-name="'.$styleName.'"';
1959            $encoded .= ' text:visited-style-name="'.$visitedStyleName.'"';
1960            $encoded .= '>';
1961        }
1962        if ($returnonly) {
1963            return $encoded;
1964        }
1965        $this->content .= $encoded;
1966    }
1967
1968    function closeHyperlink ($returnonly = false) {
1969        $encoded = '';
1970        if ($this->linksEnabled) {
1971            $encoded .= '</text:a>';
1972        }
1973        if ($returnonly) {
1974            return $encoded;
1975        }
1976        $this->content .= $encoded;
1977    }
1978
1979    function insertHyperlink ($url, $text, $styleName = NULL, $visitedStyleName = NULL, $returnonly = false) {
1980        $encoded = '';
1981        if ($url && $this->linksEnabled) {
1982            if (empty($styleName)) {
1983                $styleName = $this->getStyleName('internet link');
1984            }
1985            if (empty($visitedStyleName)) {
1986                $visitedStyleName = $this->getStyleName('visited internet link');
1987            }
1988            $url = ODTUtility::stringToIRI($url);
1989            $encoded .= '<text:a xlink:type="simple" xlink:href="'.$url.'"';
1990            $encoded .= ' text:style-name="'.$styleName.'"';
1991            $encoded .= ' text:visited-style-name="'.$visitedStyleName.'"';
1992            $encoded .= '>';
1993        }
1994        // We get the text already XML encoded
1995        $encoded .= $text;
1996        if ($url && $this->linksEnabled) {
1997            $encoded .= '</text:a>';
1998        }
1999        if ($returnonly) {
2000            return $encoded;
2001        }
2002        $this->content .= $encoded;
2003    }
2004
2005    /**
2006     * Get CSS properties for a given element and adjust them for ODT.
2007     *
2008     * @param array $dest Properties found will be written in $dest as key value pairs,
2009     *                    e.g. $dest ['color'] = 'black';
2010     * @param iElementCSSMatchable $element The element object for which the properties are queried.
2011     *                                      The class of the element needs to implement the interface
2012     *                                      iElementCSSMatchable.
2013     * @param string $media_sel The media selector to use for the query e.g. 'print'. May be empty.
2014     */
2015    public function getODTProperties (array &$dest, $element, $attributes=NULL, $media_sel=NULL, $inherit=true) {
2016        if ($this->importnew == NULL) {
2017            return;
2018        }
2019
2020        $save = $this->importnew->getMedia();
2021        $this->importnew->setMedia($media_sel);
2022
2023        $maxWidth = $this->getAbsWidthMindMargins().'cm';
2024        ODTUtility::getHTMLElementProperties($this->params, $dest, $element, $attributes, $maxWidth, $inherit);
2025
2026        $this->importnew->setMedia($save);
2027    }
2028
2029    public function getODTPropertiesFromElement (array &$dest, iElementCSSMatchable $element, $media_sel=NULL, $inherit=true) {
2030        if ($this->importnew == NULL) {
2031            return;
2032        }
2033
2034        $save = $this->importnew->getMedia();
2035        $this->importnew->setMedia($media_sel);
2036
2037        // Get properties for our class/element from imported CSS
2038        $this->importnew->getPropertiesForElement($dest, $element, $this->units, $inherit);
2039
2040        // Adjust values for ODT
2041        $maxWidth = $this->getAbsWidthMindMargins().'cm';
2042        ODTUtility::adjustValuesForODT($dest, $this->units, $maxWidth);
2043
2044        $this->importnew->setMedia($save);
2045    }
2046
2047    public function adjustValuesForODT (array &$properties) {
2048        ODTUtility::adjustValuesForODT($properties, $this->units);
2049    }
2050
2051    public function adjustValueForODT ($property, $value) {
2052        return ODTUtility::adjustValueForODT($property, $value, $this->units);
2053    }
2054
2055    /**
2056     * Adds an $element with $attributes to the internal HTML stack for
2057     * CSS matching. HTML elments added from extern using this function
2058     * are supposed to never be closed so only root elements should be
2059     * added like 'html' or 'body' or maybe a 'div' that should always
2060     * be present for proper CSS matching.
2061     *
2062     * @param string $element The element name, e.g. 'body'
2063     * @param string $attributes The elements attributes, e.g. 'lang="en" dir="ltr"'
2064     */
2065    public function addHTMLElement ($element, $attributes = NULL) {
2066        $this->htmlStack->open($element, $attributes);
2067        $this->htmlStack->saveRootIndex ();
2068    }
2069
2070    public function getHTMLStack () {
2071        return $this->htmlStack;
2072    }
2073
2074    public function dumpHTMLStack () {
2075        $this->trace_dump .= $this->htmlStack->getDump();
2076    }
2077
2078    /**
2079     * Check if a file already exists in the document.
2080     *
2081     * @param string $fileName Full file name in the document
2082     *                         e.g. 'Pictures/myimage.png'
2083     * @return bool
2084     */
2085    public function fileExists($name) {
2086        return $this->manifest->exists($name);
2087    }
2088
2089    /**
2090     * Add a file to the document.
2091     *
2092     * @param string $fileName Full file name in the document
2093     *                         e.g. 'Pictures/myimage.png'
2094     * @param string $mime Mime type
2095     * @param string $content The content of the file
2096     */
2097    public function addFile($fileName, $mime, $content) {
2098        if(!$this->manifest->exists($fileName)){
2099            $this->manifest->add($fileName, $mime);
2100            $this->ZIP->addData($fileName, $content);
2101            return true;
2102        }
2103
2104        // File with that name already exists!
2105        return false;
2106    }
2107
2108    /**
2109     * Adds the image $fileName as a picture file without adding it to
2110     * the content of the document. The link name which can be used for
2111     * the ODT draw:image xlink:href is returned.
2112     *
2113     * @param string $fileName
2114     * @return string
2115     */
2116    function addFileAsPicture($fileName){
2117        $name = '';
2118        if (file_exists($fileName)) {
2119            list($ext,$mime) = mimetype($fileName);
2120            $name = 'Pictures/'.md5($fileName).'.'.$ext;
2121            $this->addFile($name, $mime, io_readfile($fileName,false));
2122        }
2123        return $name;
2124    }
2125
2126    /**
2127     * Add style object to the document as a common style.
2128     *
2129     * @param ODTStyle $new Object to add
2130     */
2131    public function addStyle(ODTStyle $new) {
2132        return $this->styleset->addStyle($new);
2133    }
2134
2135    /**
2136     * Add style object to the document as an automatic style.
2137     *
2138     * @param ODTStyle $new Object to add
2139     */
2140    public function addAutomaticStyle(ODTStyle $new) {
2141        return $this->styleset->addAutomaticStyle($new);
2142    }
2143
2144    /**
2145     * Check if a style with $styleName already exists.
2146     *
2147     * @param string $styleName The style name ofthe style style.
2148     * @return bool
2149     */
2150    public function styleExists ($name) {
2151        return $this->styleset->styleExists($name);
2152    }
2153
2154    /**
2155     * Get the style object with style name $styleName.
2156     *
2157     * @param string $styleName The style name ofthe style style.
2158     * @return ODTStyle The style object
2159     */
2160    public function getStyle ($styleName) {
2161        return $this->styleset->getStyle($styleName);
2162    }
2163
2164    public function getDefaultStyle ($family) {
2165        return $this->styleset->getDefaultStyle($family);
2166    }
2167
2168    /**
2169     * Get the style name for a style alias.
2170     *
2171     * @param string $alias The alias for the style.
2172     * @return string The style name used in the ODT document
2173     */
2174    public function getStyleName($alias) {
2175        return $this->styleset->getStyleName($alias);
2176    }
2177
2178    /**
2179     * The function returns the style at the given index
2180     *
2181     * @param $element Element of the style e.g. 'office:styles'
2182     * @return ODTStyle or NULL
2183     */
2184    public function getStyleAtIndex($element, $index) {
2185        return $this->styleset->getStyleAtIndex($element, $index);
2186    }
2187
2188    /**
2189     * Get the style object by $alias.
2190     *
2191     * @param string $alias The alias for the style.
2192     * @return ODTStyle The style object
2193     */
2194    public function getStyleByAlias($alias) {
2195        return $this->styleset->getStyle($this->styleset->getStyleName($alias));
2196    }
2197
2198    public function registerHTMLElementForCSSImport ($style_type, $element, $attributes=NULL) {
2199        $this->registrations [$style_type]['element'] = $element;
2200        $this->registrations [$style_type]['attributes'] = $attributes;
2201    }
2202
2203    public function addToValue ($value, $add) {
2204        $valueInPt = $this->units->toPoints($value, 'y');
2205        $valueInPt = $this->units->getDigits($valueInPt);
2206        $addInPt = $this->units->toPoints($add, 'y');
2207        $addInPt = $this->units->getDigits($addInPt);
2208        return ($valueInPt + $addInPt).'pt';
2209    }
2210
2211    public function subFromValue ($value, $sub) {
2212        $valueInPt = $this->units->toPoints($value, 'y');
2213        $valueInPt = $this->units->getDigits($valueInPt);
2214        $subInPt = $this->units->toPoints($sub, 'y');
2215        $subInPt = $this->units->getDigits($subInPt);
2216        return ($valueInPt - $subInPt).'pt';
2217    }
2218
2219    /**
2220     * Adjust font sizes of all styles to $newBaseSize.
2221     * The $newBaseSize will be the new default font-size and all
2222     * other font-sizes will be re-calculated.
2223     *
2224     * @param string $newBaseSize The new base size e.g. '16pt'
2225     */
2226    public function adjustFontSizes($newBaseSize) {
2227        // First get the old base size
2228        $default = $this->styleset->getDefaultStyle('paragraph');
2229        if ($default == NULL) {
2230            // ???
2231            return;
2232        }
2233        $oldBaseSize = $default->getProperty('font-size');
2234        if ($oldBaseSize === NULL) {
2235            return;
2236        }
2237        $oldBaseSizeInPt = trim($this->units->toPoints($oldBaseSize, 'y'), 'pt');
2238
2239        // Convert new base size to pt
2240        $newBaseSizeInPt = trim($this->units->toPoints($newBaseSize, 'y'), 'pt');
2241
2242        $styles_list = array();
2243        $styles_list [] = $this->styleset->getStyles();
2244        $styles_list [] = $this->styleset->getAutomaticStyles();
2245        $styles_list [] = $this->styleset->getMasterStyles();
2246
2247        // Go through the list of style arrays and adjust each one
2248        // having a 'font-size' property
2249        foreach ($styles_list as $styles) {
2250            foreach ($styles as $style) {
2251                $fontSize = $style->getProperty('font-size');
2252                if ($fontSize !== NULL) {
2253                    $fontSizeInPt = trim($this->units->toPoints($fontSize, 'y'), 'pt');
2254                    $fontSizeInPt = ($fontSizeInPt/$oldBaseSizeInPt) * $newBaseSizeInPt;
2255                    $fontSizeInPt = round($fontSizeInPt, 2);
2256                    $style->setProperty('font-size', $fontSizeInPt.'pt');
2257                }
2258            }
2259        }
2260
2261        $this->trace_dump .= 'newBaseSize: '.$newBaseSize."\n";
2262        $this->trace_dump .= 'newBaseSizeInPt: '.$newBaseSizeInPt."\n";
2263        // Also set default font-size to the new base size!
2264        $default->setProperty('font-size', $newBaseSizeInPt.'pt');
2265    }
2266
2267    /**
2268     * The function sets the alignment and indentation for ordered lists.
2269     * This means the alignment of the numbers if front of each list item.
2270     * For each alignment predefined values for the attributes 'list-tab-stop-position',
2271     * 'text-indent' and 'margin-left' is set.
2272     *
2273     * @param string  $align       Alignment to set ('left'/'start', 'center', 'right'/'end')
2274     * @param integer $paddingLeft Left padding in centimeters, moves the whole list to the right
2275     * @param integer $marginLeft  Left margin in centimeters, specifies the indent per level
2276     */
2277    public function setOrderedListParams($setLevel=NULL, $align, $paddingLeft=0, $marginLeft=1) {
2278        if (empty($align)) {
2279            return;
2280        }
2281        $name = $this->styleset->getStyleName('numbering');
2282        $style = $this->styleset->getStyle($name);
2283        if ($style == NULL ) {
2284            return;
2285        }
2286
2287        if ($setLevel === NULL ) {
2288            for ($level = 1 ; $level < 11 ; $level++) {
2289                switch ($align) {
2290                    case 'left':
2291                    case 'start':
2292                        $dist = 1;
2293                        $style->setPropertyForLevel($level, 'text-align', 'left');
2294                        break;
2295                    case 'center':
2296                        $dist = 0.5;
2297                        $style->setPropertyForLevel($level, 'text-align', 'center');
2298                        break;
2299                    case 'right':
2300                    case 'end':
2301                    default:
2302                        $dist = 0.25;
2303                        $style->setPropertyForLevel($level, 'text-align', 'end');
2304                        break;
2305                }
2306                $position = $paddingLeft + ($marginLeft * $level) + $dist;
2307                $style->setPropertyForLevel($level, 'list-level-position-and-space-mode', 'label-alignment');
2308                $style->setPropertyForLevel($level, 'label-followed-by', 'listtab');
2309                $style->setPropertyForLevel($level, 'list-tab-stop-position', $position.'cm');
2310                $style->setPropertyForLevel($level, 'text-indent', ($dist*-1).'cm');
2311                $style->setPropertyForLevel($level, 'margin-left', $position.'cm');
2312            }
2313        } else {
2314            switch ($align) {
2315                case 'left':
2316                case 'start':
2317                    $dist = 1;
2318                    $style->setPropertyForLevel($setLevel, 'text-align', 'left');
2319                    break;
2320                case 'center':
2321                    $dist = 0.5;
2322                    $style->setPropertyForLevel($setLevel, 'text-align', 'center');
2323                    break;
2324                case 'right':
2325                case 'end':
2326                default:
2327                    $dist = 0.25;
2328                    $style->setPropertyForLevel($setLevel, 'text-align', 'end');
2329                    break;
2330            }
2331            $position = $paddingLeft + ($marginLeft * $setLevel) + $dist;
2332            $style->setPropertyForLevel($setLevel, 'list-level-position-and-space-mode', 'label-alignment');
2333            $style->setPropertyForLevel($setLevel, 'label-followed-by', 'listtab');
2334            $style->setPropertyForLevel($setLevel, 'list-tab-stop-position', $position.'cm');
2335            $style->setPropertyForLevel($setLevel, 'text-indent', ($dist*-1).'cm');
2336            $style->setPropertyForLevel($setLevel, 'margin-left', $position.'cm');
2337        }
2338    }
2339
2340    /**
2341     * The function sets the alignment and indentation for unordered lists.
2342     * This means the alignment of the icons/buttons if front of each list item.
2343     * For each alignment predefined values for the attributes 'list-tab-stop-position',
2344     * 'text-indent' and 'margin-left' is set.
2345     *
2346     * @param string  $align       Alignment to set ('left'/'start', 'center', 'right'/'end')
2347     * @param integer $paddingLeft Left padding in centimeters, moves the whole list to the right
2348     * @param integer $marginLeft  Left margin in centimeters, specifies the indent per level
2349     */
2350    public function setUnorderedListParams($setLevel=NULL, $align, $paddingLeft=0, $marginLeft=1) {
2351        if (empty($align)) {
2352            return;
2353        }
2354        $name = $this->styleset->getStyleName('list');
2355        $style = $this->styleset->getStyle($name);
2356        if ($style == NULL ) {
2357            return;
2358        }
2359
2360        if ($setLevel === NULL) {
2361            for ($level = 1 ; $level < 11 ; $level++) {
2362                switch ($align) {
2363                    case 'left':
2364                    case 'start':
2365                        $dist = 1;
2366                        $style->setPropertyForLevel($level, 'text-align', 'left');
2367                        break;
2368                    case 'center':
2369                        $dist = 0.5;
2370                        $style->setPropertyForLevel($level, 'text-align', 'center');
2371                        break;
2372                    case 'right':
2373                    case 'end':
2374                    default:
2375                        $dist = 0.25;
2376                        $style->setPropertyForLevel($level, 'text-align', 'end');
2377                        break;
2378                }
2379                $position = $paddingLeft + ($marginLeft * $level) + $dist;
2380                $style->setPropertyForLevel($level, 'list-level-position-and-space-mode', 'label-alignment');
2381                $style->setPropertyForLevel($level, 'label-followed-by', 'listtab');
2382                $style->setPropertyForLevel($level, 'list-tab-stop-position', $position.'cm');
2383                $style->setPropertyForLevel($level, 'text-indent', ($dist*-1).'cm');
2384                $style->setPropertyForLevel($level, 'margin-left', $position.'cm');
2385            }
2386        } else {
2387            switch ($align) {
2388                case 'left':
2389                case 'start':
2390                    $dist = 1;
2391                    $style->setPropertyForLevel($setLevel, 'text-align', 'left');
2392                    break;
2393                case 'center':
2394                    $dist = 0.5;
2395                    $style->setPropertyForLevel($setLevel, 'text-align', 'center');
2396                    break;
2397                case 'right':
2398                case 'end':
2399                default:
2400                    $dist = 0.25;
2401                    $style->setPropertyForLevel($setLevel, 'text-align', 'end');
2402                    break;
2403            }
2404            $position = $paddingLeft + ($marginLeft * $setLevel) + $dist;
2405            $style->setPropertyForLevel($setLevel, 'list-level-position-and-space-mode', 'label-alignment');
2406            $style->setPropertyForLevel($setLevel, 'label-followed-by', 'listtab');
2407            $style->setPropertyForLevel($setLevel, 'list-tab-stop-position', $position.'cm');
2408            $style->setPropertyForLevel($setLevel, 'text-indent', ($dist*-1).'cm');
2409            $style->setPropertyForLevel($setLevel, 'margin-left', $position.'cm');
2410        }
2411    }
2412
2413    /**
2414     * Automatically generate ODT format for given $HTMLCode.
2415     *
2416     * @param string $HTMLCode
2417     * @see ODTUtility::generateODTfromHTMLCode for detailed documentation
2418     */
2419    public function generateODTfromHTMLCode($HTMLCode, array $options){
2420        ODTUtility::generateODTfromHTMLCode($this->params, $HTMLCode, $options);
2421    }
2422}
2423