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