Removed breaking unit test, originally set up to test xhtml validity throughout the...
[moodle-pu.git] / lib / odslib.class.php
blobd4185d103b622741c96bbbdc4cde2da7bfbeaad6
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 // //
5 // NOTICE OF COPYRIGHT //
6 // //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
9 // //
10 // Copyright (C) 2001-3001 Martin Dougiamas http://dougiamas.com //
11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
12 // (C) 2001-3001 Petr Skoda (skodak) //
13 // //
14 // This program is free software; you can redistribute it and/or modify //
15 // it under the terms of the GNU General Public License as published by //
16 // the Free Software Foundation; either version 2 of the License, or //
17 // (at your option) any later version. //
18 // //
19 // This program is distributed in the hope that it will be useful, //
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
22 // GNU General Public License for more details: //
23 // //
24 // http://www.gnu.org/copyleft/gpl.html //
25 // //
26 ///////////////////////////////////////////////////////////////////////////
29 * The xml used here is derived from output of KSpread 1.6.1
31 * Known problems:
32 * - missing formatting
33 * - write_date() works fine in OOo, but it does not work in KOffice - it knows only date or time but not both :-(
36 class MoodleODSWorkbook {
37 var $worksheets = array();
38 var $filename;
40 function MoodleODSWorkbook($filename) {
41 $this->filename = $filename;
44 /* Create one Moodle Worksheet
45 * @param string $name Name of the sheet
47 function &add_worksheet($name = '') {
48 /// Create the Moodle Worksheet. Returns one pointer to it
49 $ws =& new MoodleODSWorksheet($name);
50 $this->worksheets[] =& $ws;
51 return $ws;
54 /* Create one Moodle Format
55 * @param array $properties array of properties [name]=value;
56 * valid names are set_XXXX existing
57 * functions without the set_ part
58 * i.e: [bold]=1 for set_bold(1)...Optional!
60 function &add_format($properties = array()) {
61 $format = new MoodleODSFormat($properties);
62 return $format;;
65 /* Close the Moodle Workbook
67 function close() {
68 global $CFG;
69 require_once($CFG->libdir.'/filelib.php');
71 $dir = 'temp/ods/'.time();
72 make_upload_directory($dir, false);
73 make_upload_directory($dir.'/META-INF', false);
74 $dir = "$CFG->dataroot/$dir";
75 $files = array();
77 $handle = fopen("$dir/mimetype", 'w');
78 fwrite($handle, get_ods_mimetype());
79 $files[] = "$dir/mimetype";
81 $handle = fopen("$dir/content.xml", 'w');
82 fwrite($handle, get_ods_content($this->worksheets));
83 $files[] = "$dir/content.xml";
85 $handle = fopen("$dir/meta.xml", 'w');
86 fwrite($handle, get_ods_meta());
87 $files[] = "$dir/meta.xml";
89 $handle = fopen("$dir/styles.xml", 'w');
90 fwrite($handle, get_ods_styles());
91 $files[] = "$dir/styles.xml";
93 $handle = fopen("$dir/META-INF/manifest.xml", 'w');
94 fwrite($handle, get_ods_manifest());
95 $files[] = "$dir/META-INF";
97 $filename = "$dir/result.ods";
98 zip_files($files, $filename);
100 $handle = fopen($filename, 'rb');
101 $contents = fread($handle, filesize($filename));
102 fclose($handle);
104 remove_dir($dir); // cleanup the temp directory
106 send_file($contents, $this->filename, 0, 0, true, true, 'application/vnd.oasis.opendocument.spreadsheet');
109 /* Not required to use
110 * @param string $name Name of the downloaded file
112 function send($filename) {
113 $this->filename = $filename;
118 class MoodleODSWorksheet {
119 var $data = array();
120 var $columns = array();
121 var $rows = array();
122 var $name;
125 /* Constructs one Moodle Worksheet.
126 * @param string $filename The name of the file
128 function MoodleODSWorksheet($name) {
129 $this->name = $name;
132 /* Write one string somewhere in the worksheet
133 * @param integer $row Zero indexed row
134 * @param integer $col Zero indexed column
135 * @param string $str The string to write
136 * @param mixed $format The XF format for the cell
138 function write_string($row, $col, $str, $format=0) {
139 if (!array_key_exists($row, $this->data)) {
140 $this->data[$row] = array();
142 $this->data[$row][$col] = new object();
143 $this->data[$row][$col]->value = $str;
144 $this->data[$row][$col]->type = 'string';
145 $this->data[$row][$col]->format = $format;
148 /* Write one number somewhere in the worksheet
149 * @param integer $row Zero indexed row
150 * @param integer $col Zero indexed column
151 * @param float $num The number to write
152 * @param mixed $format The XF format for the cell
154 function write_number($row, $col, $num, $format=0) {
155 if (!array_key_exists($row, $this->data)) {
156 $this->data[$row] = array();
158 $this->data[$row][$col] = new object();
159 $this->data[$row][$col]->value = $num;
160 $this->data[$row][$col]->type = 'float';
161 $this->data[$row][$col]->format = $format;
164 /* Write one url somewhere in the worksheet
165 * @param integer $row Zero indexed row
166 * @param integer $col Zero indexed column
167 * @param string $url The url to write
168 * @param mixed $format The XF format for the cell
170 function write_url($row, $col, $url, $format=0) {
171 if (!array_key_exists($row, $this->data)) {
172 $this->data[$row] = array();
174 $this->data[$row][$col] = new object();
175 $this->data[$row][$col]->value = $url;
176 $this->data[$row][$col]->type = 'string';
177 $this->data[$row][$col]->format = $format;
180 /* Write one date somewhere in the worksheet
181 * @param integer $row Zero indexed row
182 * @param integer $col Zero indexed column
183 * @param string $url The url to write
184 * @param mixed $format The XF format for the cell
186 function write_date($row, $col, $date, $format=0) {
187 if (!array_key_exists($row, $this->data)) {
188 $this->data[$row] = array();
190 $this->data[$row][$col] = new object();
191 $this->data[$row][$col]->value = $date;
192 $this->data[$row][$col]->type = 'date';
193 $this->data[$row][$col]->format = $format;
196 /* Write one blanck somewhere in the worksheet
197 * @param integer $row Zero indexed row
198 * @param integer $col Zero indexed column
199 * @param mixed $format The XF format for the cell
201 function write_blank($row, $col, $format=0) {
202 if (array_key_exists($row, $this->data)) {
203 unset($this->data[$row][$col]);
207 /* Write anything somewhere in the worksheet
208 * Type will be automatically detected
209 * @param integer $row Zero indexed row
210 * @param integer $col Zero indexed column
211 * @param mixed $token What we are writing
212 * @param mixed $format The XF format for the cell
214 function write($row, $col, $token, $format=0) {
216 /// Analyse what are we trying to send
217 if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/", $token)) {
218 /// Match number
219 return $this->write_number($row, $col, $token, $format);
220 } elseif (preg_match("/^[fh]tt?p:\/\//", $token)) {
221 /// Match http or ftp URL
222 return $this->write_url($row, $col, $token, '', $format);
223 } elseif (preg_match("/^mailto:/", $token)) {
224 /// Match mailto:
225 return $this->write_url($row, $col, $token, '', $format);
226 } elseif (preg_match("/^(?:in|ex)ternal:/", $token)) {
227 /// Match internal or external sheet link
228 return $this->write_url($row, $col, $token, '', $format);
229 } elseif (preg_match("/^=/", $token)) {
230 /// Match formula
231 return $this->write_formula($row, $col, $token, $format);
232 } elseif (preg_match("/^@/", $token)) {
233 /// Match formula
234 return $this->write_formula($row, $col, $token, $format);
235 } elseif ($token == '') {
236 /// Match blank
237 return $this->write_blank($row, $col, $format);
238 } else {
239 /// Default: match string
240 return $this->write_string($row, $col, $token, $format);
244 /* Sets the height (and other settings) of one row
245 * @param integer $row The row to set
246 * @param integer $height Height we are giving to the row (null to set just format withouth setting the height)
247 * @param mixed $format The optional XF format we are giving to the row
248 * @param bool $hidden The optional hidden attribute
249 * @param integer $level The optional outline level (0-7)
251 function set_row($row, $height, $format = 0, $hidden = false, $level = 0) {
252 $this->rows[$row] = new object();
253 $this->rows[$row]->height = $height;
254 //$this->rows[$row]->format = $format; // TODO: fix and enable
255 $this->rows[$row]->hidden = $hidden;
258 /* Sets the width (and other settings) of one column
259 * @param integer $firstcol first column on the range
260 * @param integer $lastcol last column on the range
261 * @param integer $width width to set
262 * @param mixed $format The optional XF format to apply to the columns
263 * @param integer $hidden The optional hidden atribute
264 * @param integer $level The optional outline level (0-7)
266 function set_column($firstcol, $lastcol, $width, $format = 0, $hidden = false, $level = 0) {
267 for($i=$firstcol; $i<=$lastcol; $i++) {
268 $this->columns[$i] = new object();
269 $this->columns[$i]->width = $width;
270 //$this->columns[$i]->format = $format; // TODO: fix and enable
271 $this->columns[$i]->hidden = $hidden;
279 * Define and operate over one Format.
281 class MoodleODSFormat {
282 var $id;
283 var $properties;
285 /* Constructs one Moodle Format.
286 * @param object $workbook The internal PEAR Workbook onject we are creating
288 function MoodleODSFormat($properties = array()) {
289 static $fid = 1;
291 $this->id = $fid++;
293 foreach($properties as $property => $value) {
294 if(method_exists($this,"set_$property")) {
295 $aux = 'set_'.$property;
296 $this->$aux($value);
301 /* Set weight of the format
302 * @param integer $weight Weight for the text, 0 maps to 400 (normal text),
303 * 1 maps to 700 (bold text). Valid range is: 100-1000.
304 * It's Optional, default is 1 (bold).
306 function set_bold($weight = 1) {
307 $this->properties['bold'] = $weight;
310 /* Set underline of the format
311 * @param integer $underline The value for underline. Possible values are:
312 * 1 => underline, 2 => double underline
314 function set_underline($underline = 1) {
315 $this->properties['underline'] = $underline;
318 /* Set italic of the format
320 function set_italic() {
321 $this->properties['italic'] = true;
324 /* Set strikeout of the format
326 function set_strikeout() {
327 $this->properties['strikeout'] = true;
330 /* Set outlining of the format
332 function set_outline() {
335 /* Set shadow of the format
337 function set_shadow() {
340 /* Set the script of the text
341 * @param integer $script The value for script type. Possible values are:
342 * 1 => superscript, 2 => subscript
344 function set_script($script) {
347 /* Set color of the format
348 * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63])
350 function set_color($color) {
351 $this->properties['color'] = $this->_get_color($color);
354 /* Set foreground color of the format
355 * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63])
357 function set_fg_color($color) {
360 /* Set background color of the format
361 * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63])
363 function set_bg_color($color) {
364 $this->properties['bg_color'] = $this->_get_color($color);
367 /* Set the fill pattern of the format
368 * @param integer Optional. Defaults to 1. Meaningful values are: 0-18
369 * 0 meaning no background.
371 function set_pattern($pattern=1) {
374 /* Set text wrap of the format
376 function set_text_wrap() {
379 /* Set the cell alignment of the format
380 * @param string $location alignment for the cell ('left', 'right', etc...)
382 function set_align($location) {
383 switch ($location) {
384 case 'start':
385 case 'left':
386 $this->properties['align'] = 'start';
387 break;
388 case 'center':
389 $this->properties['align'] = 'center';
390 break;
391 case 'end':
392 case 'right':
393 $this->properties['align'] = 'end';
394 break;
395 default:
396 //ignore the rest == start
400 /* Set the cell horizontal alignment of the format
401 * @param string $location alignment for the cell ('left', 'right', etc...)
403 function set_h_align($location) {
404 set_align($location);
407 /* Set the cell vertical alignment of the format
408 * @param string $location alignment for the cell ('top', 'vleft', etc...)
410 function set_v_align($location) {
411 switch ($location) {
412 case 'top':
413 $this->properties['v_align'] = 'top';
414 break;
415 case 'bottom':
416 $this->properties['v_align'] = 'bottom';
417 break;
418 default:
419 //ignore the rest == middle
423 /* Set the top border of the format
424 * @param integer $style style for the cell. 1 => thin, 2 => thick
426 function set_top($style) {
429 /* Set the bottom border of the format
430 * @param integer $style style for the cell. 1 => thin, 2 => thick
432 function set_bottom($style) {
435 /* Set the left border of the format
436 * @param integer $style style for the cell. 1 => thin, 2 => thick
438 function set_left($style) {
441 /* Set the right border of the format
442 * @param integer $style style for the cell. 1 => thin, 2 => thick
444 function set_right($style) {
448 * Set cells borders to the same style
449 * @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick.
451 function set_border($style) {
454 /* Set the numerical format of the format
455 * It can be date, time, currency, etc...
456 /* Set the numerical format of the format
457 * It can be date, time, currency, etc...
458 * @param integer $num_format The numeric format
460 function set_num_format($num_format) {
463 function _get_color($name_color = '') {
464 if (strpos($name_color, '#') === 0) {
465 return $name_color; // no conversion needed
468 $colors = array('aqua' => '#00FFFF',
469 'cyan' => '#00FFFF',
470 'black' => '#FFFFFF',
471 'blue' => '#0000FF',
472 'brown' => '#A52A2A',
473 'magenta' => '#FF00FF',
474 'fuchsia' => '#FF00FF',
475 'gray' => '#A0A0A0',
476 'grey' => '#A0A0A0',
477 'green' => '#00FF00',
478 'lime' => '#00FF00',
479 'navy' => '#000080',
480 'orange' => '#FF8000',
481 'purple' => '#800080',
482 'red' => '#FF0000',
483 'silver' => '#DCDCDC',
484 'white' => '#FFFFFF',
485 'yellow' => '#FFFF00');
487 if(array_key_exists($name_color, $colors)) {
488 return $colors[$name_color];
489 } else {
490 return false;
496 //=============================
497 // OpenDocument XML functions
498 //=============================
499 function get_ods_content(&$worksheets) {
502 // find out the size of worksheets and used styles
503 $formats = array();
504 $formatstyles = '';
505 $rowstyles = '';
506 $colstyles = '';
508 foreach($worksheets as $wsnum=>$ws) {
509 $worksheets[$wsnum]->maxr = 0;
510 $worksheets[$wsnum]->maxc = 0;
511 foreach($ws->data as $rnum=>$row) {
512 if ($rnum > $worksheets[$wsnum]->maxr) {
513 $worksheets[$wsnum]->maxr = $rnum;
515 foreach($row as $cnum=>$cell) {
516 if ($cnum > $worksheets[$wsnum]->maxc) {
517 $worksheets[$wsnum]->maxc = $cnum;
519 if (!empty($cell->format)) {
520 if (!array_key_exists($cell->format->id, $formats)) {
521 $formats[$cell->format->id] = $cell->format;
527 foreach($ws->rows as $rnum=>$row) {
528 if (!empty($row->format)) {
529 if (!array_key_exists($row->format->id, $formats)) {
530 $formats[$row->format->id] = $row->format;
533 if ($rnum > $worksheets[$wsnum]->maxr) {
534 $worksheets[$wsnum]->maxr = $rnum;
536 //define all column styles
537 if (!empty($ws->rows[$rnum])) {
538 $rowstyles .= '
539 <style:style style:name="ws'.$wsnum.'ro'.$rnum.'" style:family="table-row">
540 <style:table-row-properties style:row-height="'.$row->height.'pt"/>
541 </style:style>';
545 foreach($ws->columns as $cnum=>$col) {
546 if (!empty($col->format)) {
547 if (!array_key_exists($col->format->id, $formats)) {
548 $formats[$col->format->id] = $col->format;
551 if ($cnum > $worksheets[$wsnum]->maxc) {
552 $worksheets[$wsnum]->maxc = $cnum;
554 //define all column styles
555 if (!empty($ws->columns[$cnum])) {
556 $colstyles .= '
557 <style:style style:name="ws'.$wsnum.'co'.$cnum.'" style:family="table-column">
558 <style:table-column-properties style:column-width="'.$col->width.'pt"/>
559 </style:style>';
564 foreach($formats as $format) {
565 $textprop = '';
566 $cellprop = '';
567 $parprop = '';
568 foreach($format->properties as $pname=>$pvalue) {
569 switch ($pname) {
570 case 'bold':
571 if (!empty($pvalue)) {
572 $textprop .= ' fo:font-weight="bold"';
574 break;
575 case 'italic':
576 if (!empty($pvalue)) {
577 $textprop .= ' fo:font-style="italic"';
579 break;
580 case 'underline':
581 if (!empty($pvalue)) {
582 $textprop .= ' style:text-underline-color="font-color" style:text-underline-style="solid" style:text-underline-width="auto"';
584 break;
585 case 'strikeout':
586 if (!empty($pvalue)) {
587 $textprop .= ' style:text-line-through-style="solid"';
589 break;
590 case 'color':
591 if ($pvalue !== false) {
592 $textprop .= ' fo:color="'.$pvalue.'"';
594 break;
595 case 'bg_color':
596 if ($pvalue !== false) {
597 $cellprop .= ' fo:background-color="'.$pvalue.'"';
599 break;
600 case 'align':
601 $parprop .= ' fo:text-align="'.$pvalue.'"';
602 break;
603 case 'v_align':
604 $cellprop .= ' style:vertical-align="'.$pvalue.'"';
605 break;
608 if (!empty($textprop)) {
609 $textprop = '
610 <style:text-properties'.$textprop.'/>';
613 if (!empty($cellprop)) {
614 $cellprop = '
615 <style:table-cell-properties'.$cellprop.'/>';
618 if (!empty($parprop)) {
619 $parprop = '
620 <style:paragraph-properties'.$parprop.'/>';
623 $formatstyles .= '
624 <style:style style:name="format'.$format->id.'" style:family="table-cell">'.$textprop.$cellprop.$parprop.'
625 </style:style>';
628 /// header
629 $buffer =
630 '<?xml version="1.0" encoding="UTF-8"?>
631 <office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink">
632 <office:automatic-styles>
633 <style:style style:name="ta1" style:family="table" style:master-page-name="Standard1">
634 <style:table-properties table:display="true"/>
635 </style:style>
636 <style:style style:name="date0" style:family="table-cell"/>';
638 $buffer .= $formatstyles;
639 $buffer .= $rowstyles;
640 $buffer .= $colstyles;
642 $buffer .= '
643 </office:automatic-styles>
644 <office:body>
645 <office:spreadsheet>
648 foreach($worksheets as $wsnum=>$ws) {
650 /// worksheet header
651 $buffer .= '<table:table table:name="' . htmlspecialchars($ws->name) . '" table:style-name="ta1">'."\n";
653 // define column properties
654 for($c=0; $c<=$ws->maxc; $c++) {
655 if (array_key_exists($c, $ws->columns)) {
656 $extra = '';
657 if (!empty($ws->columns[$c]->format)) {
658 $extra .= ' table:default-cell-style-name="format'.$ws->columns[$c]->format->id.'"';
660 if ($ws->columns[$c]->hidden) {
661 $extra .= ' table:visibility="collapse"';
663 $buffer .= '<table:table-column table:style-name="ws'.$wsnum.'co'.$c.'"'.$extra.'/>'."\n";
664 } else {
665 $buffer .= '<table:table-column/>'."\n";
669 // print all rows
670 for($r=0; $r<=$ws->maxr; $r++) {
671 if (array_key_exists($r, $ws->rows)) {
672 $extra = '';
673 if (!empty($ws->rows[$r]->format)) {
674 $extra .= ' table:default-cell-style-name="format'.$ws->rows[$r]->format->id.'"';
676 if ($ws->rows[$r]->hidden) {
677 $extra .= ' table:visibility="collapse"';
679 $buffer .= '<table:table-row table:style-name="ws'.$wsnum.'ro'.$r.'"'.$extra.'>'."\n";
680 } else {
681 $buffer .= '<table:table-row>'."\n";
683 for($c=0; $c<=$ws->maxc; $c++) {
684 if (isset($ws->data[$r][$c])) {
685 $cell = $ws->data[$r][$c];
686 $extra = ' ';
687 if (!empty($cell->format)) {
688 $extra = ' table:style-name="format'.$cell->format->id.'"';
690 if ($cell->type == 'date') {
691 $buffer .= '<table:table-cell office:value-type="date" table:style-name="date0" office:date-value="' . strftime('%Y-%m-%dT%H:%M:%S', $cell->value) . '"'.$extra.'>'
692 . '<text:p>' . strftime('%Y-%m-%dT%H:%M:%S', $cell->value) . '</text:p>'
693 . '</table:table-cell>'."\n";
694 } else if ($cell->type == 'float') {
695 $buffer .= '<table:table-cell office:value-type="float" office:value="' . htmlspecialchars($cell->value) . '"'.$extra.'>'
696 . '<text:p>' . htmlspecialchars($cell->value) . '</text:p>'
697 . '</table:table-cell>'."\n";
698 } else if ($cell->type == 'string') {
699 $buffer .= '<table:table-cell office:value-type="string" office:string-value="' . htmlspecialchars($cell->value) . '"'.$extra.'>'
700 . '<text:p>' . htmlspecialchars($cell->value) . '</text:p>'
701 . '</table:table-cell>'."\n";
702 } else {
703 $buffer .= '<table:table-cell office:value-type="string"'.$extra.'>'
704 . '<text:p>!!Error - unknown type!!</text:p>'
705 . '</table:table-cell>'."\n";
707 } else {
708 $buffer .= '<table:table-cell/>'."\n";
711 $buffer .= '</table:table-row>'."\n";
713 /// worksheet footer
714 $buffer .= '</table:table>'."\n";
718 /// footer
719 $buffer .=
720 ' </office:spreadsheet>
721 </office:body>
722 </office:document-content>';
724 return $buffer;
727 function get_ods_mimetype() {
728 return 'application/vnd.oasis.opendocument.spreadsheet';
731 function get_ods_meta() {
732 global $CFG, $USER;
734 return
735 '<?xml version="1.0" encoding="UTF-8"?>
736 <office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink">
737 <office:meta>
738 <meta:generator>Moodle '.$CFG->version.'</meta:generator>
739 <meta:initial-creator>'.fullname($USER, true).'</meta:initial-creator>
740 <meta:editing-cycles>1</meta:editing-cycles>
741 <meta:creation-date>'.strftime('%Y-%m-%dT%H:%M:%S').'</meta:creation-date>
742 <dc:date>'.strftime('%Y-%m-%dT%H:%M:%S').'</dc:date>
743 <dc:creator>'.fullname($USER, true).'</dc:creator>
744 </office:meta>
745 </office:document-meta>';
748 function get_ods_styles() {
749 return
750 '<?xml version="1.0" encoding="UTF-8"?>
751 <office:document-styles xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink">
752 <office:styles>
753 <style:default-style style:family="table-column">
754 <style:table-column-properties style:column-width="75pt"/>
755 </style:default-style>
756 <style:default-style style:family="table-row">
757 <style:table-row-properties style:row-height="15pt"/>
758 </style:default-style>
759 <style:default-style style:family="table-cell">
760 <style:table-cell-properties fo:background-color="#ffffff" style:cell-protect="protected" style:vertical-align="middle"/>
761 <style:text-properties fo:color="#000000" fo:font-family="Arial" fo:font-size="12pt"/>
762 </style:default-style>
763 </office:styles>
764 <office:automatic-styles>
765 <style:page-layout style:name="pm1">
766 <style:page-layout-properties fo:margin-bottom="56.6930116pt" fo:margin-left="56.6930116pt" fo:margin-right="56.6930116pt" fo:margin-top="56.6930116pt" fo:page-height="841.89122226pt" fo:page-width="595.2766218pt" style:print="objects charts drawings zero-values" style:print-orientation="portrait"/>
767 </style:page-layout>
768 </office:automatic-styles>
769 <office:master-styles>
770 <style:master-page style:name="Standard1" style:page-layout-name="pm1">
771 <style:header>
772 <text:p>
773 <text:sheet-name>???</text:sheet-name>
774 </text:p>
775 </style:header><style:footer>
776 <text:p>
777 <text:sheet-name>Page </text:sheet-name>
778 <text:page-number>1</text:page-number>
779 </text:p>
780 </style:footer>
781 </style:master-page>
782 </office:master-styles>
783 </office:document-styles>
787 function get_ods_manifest() {
788 return
789 '<?xml version="1.0" encoding="UTF-8"?>
790 <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
791 <manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.spreadsheet" manifest:full-path="/"/>
792 <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
793 <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>
794 <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/>
795 </manifest:manifest>';