Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * ODS file writer. 19 * The xml used here is derived from output of LibreOffice 3.6.4 20 * 21 * The design is based on Excel writer abstraction by Eloy Lafuente and others. 22 * 23 * @package core 24 * @copyright 2006 Petr Skoda {@link http://skodak.org} 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 */ 27 28 defined('MOODLE_INTERNAL') || die(); 29 30 31 /** 32 * ODS workbook abstraction. 33 * 34 * @package core 35 * @copyright 2006 Petr Skoda {@link http://skodak.org} 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class MoodleODSWorkbook { 39 protected $worksheets = array(); 40 protected $filename; 41 42 public function __construct($filename) { 43 $this->filename = $filename; 44 } 45 46 /** 47 * Create one Moodle Worksheet. 48 * 49 * @param string $name Name of the sheet 50 * @return MoodleODSWorksheet 51 */ 52 public function add_worksheet($name = '') { 53 $ws = new MoodleODSWorksheet($name, $this->worksheets); 54 $this->worksheets[] = $ws; 55 return $ws; 56 } 57 58 /** 59 * Create one Moodle Format. 60 * 61 * @param array $properties array of properties [name]=value; 62 * valid names are set_XXXX existing 63 * functions without the set_ part 64 * i.e: [bold]=1 for set_bold(1)...Optional! 65 * @return MoodleODSFormat 66 */ 67 public function add_format($properties = array()) { 68 return new MoodleODSFormat($properties); 69 } 70 71 /** 72 * Close the Moodle Workbook. 73 */ 74 public function close() { 75 global $CFG; 76 require_once($CFG->libdir . '/filelib.php'); 77 78 $writer = new MoodleODSWriter($this->worksheets); 79 $contents = $writer->get_file_content(); 80 81 send_file($contents, $this->filename, 0, 0, true, true, $writer->get_ods_mimetype()); 82 } 83 84 /** 85 * Not required to use. 86 * @param string $filename Name of the downloaded file 87 */ 88 public function send($filename) { 89 $this->filename = $filename; 90 } 91 92 } 93 94 95 /** 96 * ODS Cell abstraction. 97 * 98 * @package core 99 * @copyright 2013 Petr Skoda {@link http://skodak.org} 100 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 101 */ 102 class MoodleODSCell { 103 public $value; 104 public $type; 105 public $format; 106 public $formula; 107 } 108 109 110 /** 111 * ODS Worksheet abstraction. 112 * 113 * @package core 114 * @copyright 2006 Petr Skoda {@link http://skodak.org} 115 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 116 */ 117 class MoodleODSWorksheet { 118 public $data = array(); 119 public $columns = array(); 120 public $rows = array(); 121 public $showgrid = true; 122 public $name; 123 /** @var int Max number of rows in the sheet. */ 124 public $maxr = 0; 125 /** @var int Max number of cols in the sheet. */ 126 public $maxc = 0; 127 128 /** 129 * Constructs one Moodle Worksheet. 130 * 131 * @param string $name The name of the file 132 * @param array $worksheets existing worksheets 133 */ 134 public function __construct($name, array $worksheets) { 135 // Replace any characters in the name that Excel cannot cope with. 136 $name = strtr($name, '[]*/\?:', ' '); 137 138 if ($name === '') { 139 // Name is required! 140 $name = 'Sheet'.(count($worksheets)+1); 141 } 142 143 $this->name = $name; 144 } 145 146 /** 147 * Write one string somewhere in the worksheet. 148 * 149 * @param integer $row Zero indexed row 150 * @param integer $col Zero indexed column 151 * @param string $str The string to write 152 * @param mixed $format The XF format for the cell 153 */ 154 public function write_string($row, $col, $str, $format = null) { 155 if (!isset($this->data[$row][$col])) { 156 $this->data[$row][$col] = new MoodleODSCell(); 157 } 158 if (is_array($format)) { 159 $format = new MoodleODSFormat($format); 160 } 161 $this->data[$row][$col]->value = $str; 162 $this->data[$row][$col]->type = 'string'; 163 $this->data[$row][$col]->format = $format; 164 $this->data[$row][$col]->formula = null; 165 } 166 167 /** 168 * Write one number somewhere in the worksheet. 169 * 170 * @param integer $row Zero indexed row 171 * @param integer $col Zero indexed column 172 * @param float $num The number to write 173 * @param mixed $format The XF format for the cell 174 */ 175 public function write_number($row, $col, $num, $format = null) { 176 if (!isset($this->data[$row][$col])) { 177 $this->data[$row][$col] = new MoodleODSCell(); 178 } 179 if (is_array($format)) { 180 $format = new MoodleODSFormat($format); 181 } 182 $this->data[$row][$col]->value = $num; 183 $this->data[$row][$col]->type = 'float'; 184 $this->data[$row][$col]->format = $format; 185 $this->data[$row][$col]->formula = null; 186 } 187 188 /** 189 * Write one url somewhere in the worksheet. 190 * 191 * @param integer $row Zero indexed row 192 * @param integer $col Zero indexed column 193 * @param string $url The url to write 194 * @param mixed $format The XF format for the cell 195 */ 196 public function write_url($row, $col, $url, $format = null) { 197 if (!isset($this->data[$row][$col])) { 198 $this->data[$row][$col] = new MoodleODSCell(); 199 } 200 if (is_array($format)) { 201 $format = new MoodleODSFormat($format); 202 } 203 $this->data[$row][$col]->value = $url; 204 $this->data[$row][$col]->type = 'string'; 205 $this->data[$row][$col]->format = $format; 206 $this->data[$row][$col]->formula = null; 207 } 208 209 /** 210 * Write one date somewhere in the worksheet. 211 * 212 * @param integer $row Zero indexed row 213 * @param integer $col Zero indexed column 214 * @param string $date The url to write 215 * @param mixed $format The XF format for the cell 216 */ 217 public function write_date($row, $col, $date, $format = null) { 218 if (!isset($this->data[$row][$col])) { 219 $this->data[$row][$col] = new MoodleODSCell(); 220 } 221 if (is_array($format)) { 222 $format = new MoodleODSFormat($format); 223 } 224 $this->data[$row][$col]->value = $date; 225 $this->data[$row][$col]->type = 'date'; 226 $this->data[$row][$col]->format = $format; 227 $this->data[$row][$col]->formula = null; 228 } 229 230 /** 231 * Write one formula somewhere in the worksheet. 232 * 233 * @param integer $row Zero indexed row 234 * @param integer $col Zero indexed column 235 * @param string $formula The formula to write 236 * @param mixed $format The XF format for the cell 237 */ 238 public function write_formula($row, $col, $formula, $format = null) { 239 if (!isset($this->data[$row][$col])) { 240 $this->data[$row][$col] = new MoodleODSCell(); 241 } 242 if (is_array($format)) { 243 $format = new MoodleODSFormat($format); 244 } 245 $this->data[$row][$col]->formula = $formula; 246 $this->data[$row][$col]->format = $format; 247 $this->data[$row][$col]->value = null; 248 $this->data[$row][$col]->format = null; 249 } 250 251 /** 252 * Write one blank somewhere in the worksheet. 253 * 254 * @param integer $row Zero indexed row 255 * @param integer $col Zero indexed column 256 * @param mixed $format The XF format for the cell 257 */ 258 public function write_blank($row, $col, $format = null) { 259 if (is_array($format)) { 260 $format = new MoodleODSFormat($format); 261 } 262 $this->write_string($row, $col, '', $format); 263 } 264 265 /** 266 * Write anything somewhere in the worksheet, 267 * type will be automatically detected. 268 * 269 * @param integer $row Zero indexed row 270 * @param integer $col Zero indexed column 271 * @param mixed $token What we are writing 272 * @param mixed $format The XF format for the cell 273 */ 274 public function write($row, $col, $token, $format = null) { 275 // Analyse what are we trying to send. 276 if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/", $token)) { 277 // Match number 278 return $this->write_number($row, $col, $token, $format); 279 } elseif (preg_match("/^[fh]tt?p:\/\//", $token)) { 280 // Match http or ftp URL 281 return $this->write_url($row, $col, $token, '', $format); 282 } elseif (preg_match("/^mailto:/", $token)) { 283 // Match mailto: 284 return $this->write_url($row, $col, $token, '', $format); 285 } elseif (preg_match("/^(?:in|ex)ternal:/", $token)) { 286 // Match internal or external sheet link 287 return $this->write_url($row, $col, $token, '', $format); 288 } elseif (preg_match("/^=/", $token)) { 289 // Match formula 290 return $this->write_formula($row, $col, $token, $format); 291 } elseif (preg_match("/^@/", $token)) { 292 // Match formula 293 return $this->write_formula($row, $col, $token, $format); 294 } elseif ($token == '') { 295 // Match blank 296 return $this->write_blank($row, $col, $format); 297 } else { 298 // Default: match string 299 return $this->write_string($row, $col, $token, $format); 300 } 301 } 302 303 /** 304 * Sets the height (and other settings) of one row. 305 * 306 * @param integer $row The row to set 307 * @param integer $height Height we are giving to the row (null to set just format without setting the height) 308 * @param mixed $format The optional format we are giving to the row 309 * @param bool $hidden The optional hidden attribute 310 * @param integer $level The optional outline level (0-7) 311 */ 312 public function set_row($row, $height, $format = null, $hidden = false, $level = 0) { 313 if (is_array($format)) { 314 $format = new MoodleODSFormat($format); 315 } 316 if ($level < 0) { 317 $level = 0; 318 } else if ($level > 7) { 319 $level = 7; 320 } 321 if (!isset($this->rows[$row])) { 322 $this->rows[$row] = new stdClass(); 323 } 324 if (isset($height)) { 325 $this->rows[$row]->height = $height; 326 } 327 $this->rows[$row]->format = $format; 328 $this->rows[$row]->hidden = $hidden; 329 $this->rows[$row]->level = $level; 330 } 331 332 /** 333 * Sets the width (and other settings) of one column. 334 * 335 * @param integer $firstcol first column on the range 336 * @param integer $lastcol last column on the range 337 * @param integer $width width to set (null to set just format without setting the width) 338 * @param mixed $format The optional format to apply to the columns 339 * @param bool $hidden The optional hidden attribute 340 * @param integer $level The optional outline level (0-7) 341 */ 342 public function set_column($firstcol, $lastcol, $width, $format = null, $hidden = false, $level = 0) { 343 if (is_array($format)) { 344 $format = new MoodleODSFormat($format); 345 } 346 if ($level < 0) { 347 $level = 0; 348 } else if ($level > 7) { 349 $level = 7; 350 } 351 for($i=$firstcol; $i<=$lastcol; $i++) { 352 if (!isset($this->columns[$i])) { 353 $this->columns[$i] = new stdClass(); 354 } 355 if (isset($width)) { 356 $this->columns[$i]->width = $width*6.15; // 6.15 is a magic constant here! 357 } 358 $this->columns[$i]->format = $format; 359 $this->columns[$i]->hidden = $hidden; 360 $this->columns[$i]->level = $level; 361 } 362 } 363 364 /** 365 * Set the option to hide gridlines on the printed page. 366 */ 367 public function hide_gridlines() { 368 // Not implemented - always off. 369 } 370 371 /** 372 * Set the option to hide gridlines on the worksheet (as seen on the screen). 373 */ 374 public function hide_screen_gridlines() { 375 $this->showgrid = false; 376 } 377 378 /** 379 * Insert a 24bit bitmap image in a worksheet. 380 * 381 * @param integer $row The row we are going to insert the bitmap into 382 * @param integer $col The column we are going to insert the bitmap into 383 * @param string $bitmap The bitmap filename 384 * @param integer $x The horizontal position (offset) of the image inside the cell. 385 * @param integer $y The vertical position (offset) of the image inside the cell. 386 * @param integer $scale_x The horizontal scale 387 * @param integer $scale_y The vertical scale 388 */ 389 public function insert_bitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) { 390 // Not implemented. 391 } 392 393 /** 394 * Merges the area given by its arguments. 395 * 396 * @param integer $first_row First row of the area to merge 397 * @param integer $first_col First column of the area to merge 398 * @param integer $last_row Last row of the area to merge 399 * @param integer $last_col Last column of the area to merge 400 */ 401 public function merge_cells($first_row, $first_col, $last_row, $last_col) { 402 if ($first_row > $last_row or $first_col > $last_col) { 403 return; 404 } 405 406 if (!isset($this->data[$first_row][$first_col])) { 407 $this->data[$first_row][$first_col] = new MoodleODSCell(); 408 } 409 410 $this->data[$first_row][$first_col]->merge = array('rows'=>($last_row-$first_row+1), 'columns'=>($last_col-$first_col+1)); 411 } 412 } 413 414 415 /** 416 * ODS cell format abstraction. 417 * 418 * @package core 419 * @copyright 2006 Petr Skoda {@link http://skodak.org} 420 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 421 */ 422 class MoodleODSFormat { 423 public $id; 424 public $properties = array(); 425 426 /** 427 * Constructs one Moodle Format. 428 * 429 * @param array $properties 430 */ 431 public function __construct($properties = array()) { 432 static $fid = 1; 433 434 $this->id = $fid++; 435 436 foreach($properties as $property => $value) { 437 if (method_exists($this, "set_$property")) { 438 $aux = 'set_'.$property; 439 $this->$aux($value); 440 } 441 } 442 } 443 444 /** 445 * Set the size of the text in the format (in pixels). 446 * By default all texts in generated sheets are 10pt. 447 * 448 * @param integer $size Size of the text (in points) 449 */ 450 public function set_size($size) { 451 $this->properties['size'] = $size; 452 } 453 454 /** 455 * Set weight of the format. 456 * 457 * @param integer $weight Weight for the text, 0 maps to 400 (normal text), 458 * 1 maps to 700 (bold text). Valid range is: 100-1000. 459 * It's Optional, default is 1 (bold). 460 */ 461 public function set_bold($weight = 1) { 462 if ($weight == 1) { 463 $weight = 700; 464 } 465 $this->properties['bold'] = ($weight > 400); 466 } 467 468 /** 469 * Set underline of the format. 470 * 471 * @param integer $underline The value for underline. Possible values are: 472 * 1 => underline, 2 => double underline 473 */ 474 public function set_underline($underline = 1) { 475 if ($underline == 1) { 476 $this->properties['underline'] = 1; 477 } else if ($underline == 2) { 478 $this->properties['underline'] = 2; 479 } else { 480 unset($this->properties['underline']); 481 } 482 } 483 484 /** 485 * Set italic of the format. 486 */ 487 public function set_italic() { 488 $this->properties['italic'] = true; 489 } 490 491 /** 492 * Set strikeout of the format 493 */ 494 public function set_strikeout() { 495 $this->properties['strikeout'] = true; 496 } 497 498 /** 499 * Set outlining of the format. 500 */ 501 public function set_outline() { 502 // Not implemented. 503 } 504 505 /** 506 * Set shadow of the format. 507 */ 508 public function set_shadow() { 509 // Not implemented. 510 } 511 512 /** 513 * Set the script of the text. 514 * 515 * @param integer $script The value for script type. Possible values are: 516 * 1 => superscript, 2 => subscript 517 */ 518 public function set_script($script) { 519 if ($script == 1) { 520 $this->properties['super_script'] = true; 521 unset($this->properties['sub_script']); 522 523 } else if ($script == 2) { 524 $this->properties['sub_script'] = true; 525 unset($this->properties['super_script']); 526 527 } else { 528 unset($this->properties['sub_script']); 529 unset($this->properties['super_script']); 530 } 531 } 532 533 /** 534 * Set color of the format. 535 * 536 * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]) 537 */ 538 public function set_color($color) { 539 $this->properties['color'] = $this->parse_color($color); 540 } 541 542 /** 543 * Not used. 544 * 545 * @param mixed $color 546 */ 547 public function set_fg_color($color) { 548 // Not implemented. 549 } 550 551 /** 552 * Set background color of the cell. 553 * 554 * @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]) 555 */ 556 public function set_bg_color($color) { 557 $this->properties['bg_color'] = $this->parse_color($color); 558 } 559 560 /** 561 * Set the cell fill pattern. 562 * 563 * @deprecated use set_bg_color() instead. 564 * @param integer 565 */ 566 public function set_pattern($pattern=1) { 567 if ($pattern > 0) { 568 if (!isset($this->properties['bg_color'])) { 569 $this->properties['bg_color'] = $this->parse_color('black'); 570 } 571 } else { 572 unset($this->properties['bg_color']); 573 } 574 575 } 576 577 /** 578 * Set text wrap of the format 579 */ 580 public function set_text_wrap() { 581 $this->properties['wrap'] = true; 582 } 583 584 /** 585 * Set the cell alignment of the format. 586 * 587 * @param string $location alignment for the cell ('left', 'right', 'justify', etc...) 588 */ 589 public function set_align($location) { 590 if (in_array($location, array('left', 'centre', 'center', 'right', 'fill', 'merge', 'justify', 'equal_space'))) { 591 $this->set_h_align($location); 592 593 } else if (in_array($location, array('top', 'vcentre', 'vcenter', 'bottom', 'vjustify', 'vequal_space'))) { 594 $this->set_v_align($location); 595 } 596 } 597 598 /** 599 * Set the cell horizontal alignment of the format. 600 * 601 * @param string $location alignment for the cell ('left', 'right', 'justify', etc...) 602 */ 603 public function set_h_align($location) { 604 switch ($location) { 605 case 'left': 606 $this->properties['align'] = 'start'; 607 break; 608 case 'center': 609 case 'centre': 610 $this->properties['align'] = 'center'; 611 break; 612 case 'right': 613 $this->properties['align'] = 'end'; 614 break; 615 } 616 } 617 618 /** 619 * Set the cell vertical alignment of the format. 620 * 621 * @param string $location alignment for the cell ('top', 'bottom', 'center', 'justify') 622 */ 623 public function set_v_align($location) { 624 switch ($location) { 625 case 'top': 626 $this->properties['v_align'] = 'top'; 627 break; 628 case 'vcentre': 629 case 'vcenter': 630 case 'centre': 631 case 'center': 632 $this->properties['v_align'] = 'middle'; 633 break; 634 default: 635 $this->properties['v_align'] = 'bottom'; 636 } 637 } 638 639 /** 640 * Set the top border of the format. 641 * 642 * @param integer $style style for the cell. 1 => thin, 2 => thick 643 */ 644 public function set_top($style) { 645 if ($style == 1) { 646 $style = 0.2; 647 } else if ($style == 2) { 648 $style = 0.5; 649 } else { 650 return; 651 } 652 $this->properties['border_top'] = $style; 653 } 654 655 /** 656 * Set the bottom border of the format. 657 * 658 * @param integer $style style for the cell. 1 => thin, 2 => thick 659 */ 660 public function set_bottom($style) { 661 if ($style == 1) { 662 $style = 0.2; 663 } else if ($style == 2) { 664 $style = 0.5; 665 } else { 666 return; 667 } 668 $this->properties['border_bottom'] = $style; 669 } 670 671 /** 672 * Set the left border of the format. 673 * 674 * @param integer $style style for the cell. 1 => thin, 2 => thick 675 */ 676 public function set_left($style) { 677 if ($style == 1) { 678 $style = 0.2; 679 } else if ($style == 2) { 680 $style = 0.5; 681 } else { 682 return; 683 } 684 $this->properties['border_left'] = $style; 685 } 686 687 /** 688 * Set the right border of the format. 689 * 690 * @param integer $style style for the cell. 1 => thin, 2 => thick 691 */ 692 public function set_right($style) { 693 if ($style == 1) { 694 $style = 0.2; 695 } else if ($style == 2) { 696 $style = 0.5; 697 } else { 698 return; 699 } 700 $this->properties['border_right'] = $style; 701 } 702 703 /** 704 * Set cells borders to the same style 705 * @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick. 706 */ 707 public function set_border($style) { 708 $this->set_top($style); 709 $this->set_bottom($style); 710 $this->set_left($style); 711 $this->set_right($style); 712 } 713 714 /** 715 * Set the numerical format of the format. 716 * It can be date, time, currency, etc... 717 * 718 * @param mixed $num_format The numeric format 719 */ 720 public function set_num_format($num_format) { 721 722 $numbers = array(); 723 724 $numbers[1] = '0'; 725 $numbers[2] = '0.00'; 726 $numbers[3] = '#,##0'; 727 $numbers[4] = '#,##0.00'; 728 $numbers[11] = '0.00E+00'; 729 $numbers[12] = '# ?/?'; 730 $numbers[13] = '# ??/??'; 731 $numbers[14] = 'mm-dd-yy'; 732 $numbers[15] = 'd-mmm-yy'; 733 $numbers[16] = 'd-mmm'; 734 $numbers[17] = 'mmm-yy'; 735 $numbers[22] = 'm/d/yy h:mm'; 736 $numbers[49] = '@'; 737 738 if ($num_format !== 0 and in_array($num_format, $numbers)) { 739 $flipped = array_flip($numbers); 740 $this->properties['num_format'] = $flipped[$num_format]; 741 } 742 if (!isset($numbers[$num_format])) { 743 return; 744 } 745 746 $this->properties['num_format'] = $num_format; 747 } 748 749 /** 750 * Standardise colour name. 751 * 752 * @param mixed $color name of the color (i.e.: 'blue', 'red', etc..), or an integer (range is [8...63]). 753 * @return string the RGB color value 754 */ 755 protected function parse_color($color) { 756 if (strpos($color, '#') === 0) { 757 // No conversion should be needed. 758 return $color; 759 } 760 761 if ($color > 7 and $color < 53) { 762 $numbers = array( 763 8 => 'black', 764 12 => 'blue', 765 16 => 'brown', 766 15 => 'cyan', 767 23 => 'gray', 768 17 => 'green', 769 11 => 'lime', 770 14 => 'magenta', 771 18 => 'navy', 772 53 => 'orange', 773 33 => 'pink', 774 20 => 'purple', 775 10 => 'red', 776 22 => 'silver', 777 9 => 'white', 778 13 => 'yellow', 779 ); 780 if (isset($numbers[$color])) { 781 $color = $numbers[$color]; 782 } else { 783 $color = 'black'; 784 } 785 } 786 787 $colors = array( 788 'aqua' => '00FFFF', 789 'black' => '000000', 790 'blue' => '0000FF', 791 'brown' => 'A52A2A', 792 'cyan' => '00FFFF', 793 'fuchsia' => 'FF00FF', 794 'gray' => '808080', 795 'grey' => '808080', 796 'green' => '00FF00', 797 'lime' => '00FF00', 798 'magenta' => 'FF00FF', 799 'maroon' => '800000', 800 'navy' => '000080', 801 'orange' => 'FFA500', 802 'olive' => '808000', 803 'pink' => 'FAAFBE', 804 'purple' => '800080', 805 'red' => 'FF0000', 806 'silver' => 'C0C0C0', 807 'teal' => '008080', 808 'white' => 'FFFFFF', 809 'yellow' => 'FFFF00', 810 ); 811 812 if (isset($colors[$color])) { 813 return('#'.$colors[$color]); 814 } 815 816 return('#'.$colors['black']); 817 } 818 } 819 820 821 /** 822 * ODS file writer. 823 * 824 * @package core 825 * @copyright 2013 Petr Skoda {@link http://skodak.org} 826 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 827 */ 828 class MoodleODSWriter { 829 protected $worksheets; 830 831 public function __construct(array $worksheets) { 832 $this->worksheets = $worksheets; 833 } 834 835 /** 836 * Fetch the file ocntnet for the ODS. 837 * 838 * @return string 839 */ 840 public function get_file_content() { 841 $dir = make_request_directory(); 842 $filename = $dir . '/result.ods'; 843 844 $files = [ 845 'mimetype' => [$this->get_ods_mimetype()], 846 'content.xml' => [$this->get_ods_content($this->worksheets)], 847 'meta.xml' => [$this->get_ods_meta()], 848 'styles.xml' => [$this->get_ods_styles()], 849 'settings.xml' => [$this->get_ods_settings()], 850 'META-INF/manifest.xml' => [$this->get_ods_manifest()], 851 ]; 852 853 $packer = get_file_packer('application/zip'); 854 $packer->archive_to_pathname($files, $filename); 855 856 $contents = file_get_contents($filename); 857 858 remove_dir($dir); 859 return $contents; 860 } 861 862 protected function get_ods_content() { 863 864 // Find out the size of worksheets and used styles. 865 $formats = array(); 866 $formatstyles = ''; 867 $rowstyles = ''; 868 $colstyles = ''; 869 870 foreach($this->worksheets as $wsnum=>$ws) { 871 foreach($ws->data as $rnum=>$row) { 872 if ($rnum > $this->worksheets[$wsnum]->maxr) { 873 $this->worksheets[$wsnum]->maxr = $rnum; 874 } 875 foreach($row as $cnum=>$cell) { 876 if ($cnum > $this->worksheets[$wsnum]->maxc) { 877 $this->worksheets[$wsnum]->maxc = $cnum; 878 } 879 if (!empty($cell->format)) { 880 if (!array_key_exists($cell->format->id, $formats)) { 881 $formats[$cell->format->id] = $cell->format; 882 } 883 } 884 } 885 } 886 887 foreach($ws->rows as $rnum=>$row) { 888 if (!empty($row->format)) { 889 if (!array_key_exists($row->format->id, $formats)) { 890 $formats[$row->format->id] = $row->format; 891 } 892 } 893 if ($rnum > $this->worksheets[$wsnum]->maxr) { 894 $this->worksheets[$wsnum]->maxr = $rnum; 895 } 896 // Define all column styles. 897 if (!empty($ws->rows[$rnum])) { 898 $rowstyles .= '<style:style style:name="ws'.$wsnum.'ro'.$rnum.'" style:family="table-row">'; 899 if (isset($row->height)) { 900 $rowstyles .= '<style:table-row-properties style:row-height="'.$row->height.'pt"/>'; 901 } 902 $rowstyles .= '</style:style>'; 903 } 904 } 905 906 foreach($ws->columns as $cnum=>$col) { 907 if (!empty($col->format)) { 908 if (!array_key_exists($col->format->id, $formats)) { 909 $formats[$col->format->id] = $col->format; 910 } 911 } 912 if ($cnum > $this->worksheets[$wsnum]->maxc) { 913 $this->worksheets[$wsnum]->maxc = $cnum; 914 } 915 // Define all column styles. 916 if (!empty($ws->columns[$cnum])) { 917 $colstyles .= '<style:style style:name="ws'.$wsnum.'co'.$cnum.'" style:family="table-column">'; 918 if (isset($col->width)) { 919 $colstyles .= '<style:table-column-properties style:column-width="'.$col->width.'pt"/>'; 920 } 921 $colstyles .= '</style:style>'; 922 } 923 } 924 } 925 926 foreach($formats as $format) { 927 $textprop = ''; 928 $cellprop = ''; 929 $parprop = ''; 930 $dataformat = ''; 931 foreach($format->properties as $pname=>$pvalue) { 932 switch ($pname) { 933 case 'size': 934 if (!empty($pvalue)) { 935 $textprop .= ' fo:font-size="'.$pvalue.'pt"'; 936 } 937 break; 938 case 'bold': 939 if (!empty($pvalue)) { 940 $textprop .= ' fo:font-weight="bold"'; 941 } 942 break; 943 case 'italic': 944 if (!empty($pvalue)) { 945 $textprop .= ' fo:font-style="italic"'; 946 } 947 break; 948 case 'underline': 949 if (!empty($pvalue)) { 950 $textprop .= ' style:text-underline-color="font-color" style:text-underline-style="solid" style:text-underline-width="auto"'; 951 if ($pvalue == 2) { 952 $textprop .= ' style:text-underline-type="double"'; 953 } 954 } 955 break; 956 case 'strikeout': 957 if (!empty($pvalue)) { 958 $textprop .= ' style:text-line-through-style="solid"'; 959 } 960 break; 961 case 'color': 962 if ($pvalue !== false) { 963 $textprop .= ' fo:color="'.$pvalue.'"'; 964 } 965 break; 966 case 'bg_color': 967 if ($pvalue !== false) { 968 $cellprop .= ' fo:background-color="'.$pvalue.'"'; 969 } 970 break; 971 case 'align': 972 $parprop .= ' fo:text-align="'.$pvalue.'"'; 973 break; 974 case 'v_align': 975 $cellprop .= ' style:vertical-align="'.$pvalue.'"'; 976 break; 977 case 'wrap': 978 if ($pvalue) { 979 $cellprop .= ' fo:wrap-option="wrap"'; 980 } 981 break; 982 case 'border_top': 983 $cellprop .= ' fo:border-top="'.$pvalue.'pt solid #000000"'; 984 break; 985 case 'border_left': 986 $cellprop .= ' fo:border-left="'.$pvalue.'pt solid #000000"'; 987 break; 988 case 'border_bottom': 989 $cellprop .= ' fo:border-bottom="'.$pvalue.'pt solid #000000"'; 990 break; 991 case 'border_right': 992 $cellprop .= ' fo:border-right="'.$pvalue.'pt solid #000000"'; 993 break; 994 case 'num_format': 995 $dataformat = ' style:data-style-name="NUM'.$pvalue.'"'; 996 break; 997 } 998 } 999 if (!empty($textprop)) { 1000 $textprop = ' 1001 <style:text-properties'.$textprop.'/>'; 1002 } 1003 1004 if (!empty($cellprop)) { 1005 $cellprop = ' 1006 <style:table-cell-properties'.$cellprop.'/>'; 1007 } 1008 1009 if (!empty($parprop)) { 1010 $parprop = ' 1011 <style:paragraph-properties'.$parprop.'/>'; 1012 } 1013 1014 $formatstyles .= ' 1015 <style:style style:name="format'.$format->id.'" style:family="table-cell"'.$dataformat.'>'.$textprop.$cellprop.$parprop.' 1016 </style:style>'; 1017 } 1018 1019 // The text styles may be breaking older ODF validators. 1020 $scriptstyles =' 1021 <style:style style:name="T1" style:family="text"> 1022 <style:text-properties style:text-position="33% 58%"/> 1023 </style:style> 1024 <style:style style:name="T2" style:family="text"> 1025 <style:text-properties style:text-position="-33% 58%"/> 1026 </style:style> 1027 '; 1028 // Header. 1029 $buffer = 1030 '<?xml version="1.0" encoding="UTF-8"?> 1031 <office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 1032 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 1033 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 1034 xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 1035 xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 1036 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 1037 xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" 1038 xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" 1039 xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" 1040 xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" 1041 xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 1042 xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" 1043 xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" 1044 xmlns:math="http://www.w3.org/1998/Math/MathML" 1045 xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" 1046 xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" 1047 xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" 1048 xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" 1049 xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 1050 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 1051 xmlns:rpt="http://openoffice.org/2005/report" 1052 xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" 1053 xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" 1054 xmlns:tableooo="http://openoffice.org/2009/table" 1055 xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" 1056 xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" 1057 xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" 1058 xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2"> 1059 <office:scripts/> 1060 <office:font-face-decls> 1061 <style:font-face style:name="Arial" svg:font-family="Arial" style:font-family-generic="swiss" 1062 style:font-pitch="variable"/> 1063 <style:font-face style:name="Arial Unicode MS" svg:font-family="'Arial Unicode MS'" 1064 style:font-family-generic="system" style:font-pitch="variable"/> 1065 <style:font-face style:name="Tahoma" svg:font-family="Tahoma" style:font-family-generic="system" 1066 style:font-pitch="variable"/> 1067 </office:font-face-decls> 1068 <office:automatic-styles>'; 1069 $buffer .= $this->get_num_styles(); 1070 $buffer .= ' 1071 <style:style style:name="ta1" style:family="table" style:master-page-name="Standard1"> 1072 <style:table-properties table:display="true"/> 1073 </style:style>'; 1074 1075 $buffer .= $formatstyles; 1076 $buffer .= $rowstyles; 1077 $buffer .= $colstyles; 1078 $buffer .= $scriptstyles; 1079 1080 $buffer .= ' 1081 </office:automatic-styles> 1082 <office:body> 1083 <office:spreadsheet> 1084 '; 1085 1086 foreach($this->worksheets as $wsnum=>$ws) { 1087 1088 // Worksheet header. 1089 $buffer .= '<table:table table:name="' . htmlspecialchars($ws->name, ENT_QUOTES, 'utf-8') . '" table:style-name="ta1">'."\n"; 1090 1091 // Define column properties. 1092 $level = 0; 1093 for($c=0; $c<=$ws->maxc; $c++) { 1094 if (array_key_exists($c, $ws->columns)) { 1095 $column = $ws->columns[$c]; 1096 if ($column->level > $level) { 1097 while ($column->level > $level) { 1098 $buffer .= '<table:table-column-group>'; 1099 $level++; 1100 } 1101 } else if ($column->level < $level) { 1102 while ($column->level < $level) { 1103 $buffer .= '</table:table-column-group>'; 1104 $level--; 1105 } 1106 } 1107 $extra = ''; 1108 if (!empty($column->format)) { 1109 $extra .= ' table:default-cell-style-name="format'.$column->format->id.'"'; 1110 } 1111 if ($column->hidden) { 1112 $extra .= ' table:visibility="collapse"'; 1113 } 1114 $buffer .= '<table:table-column table:style-name="ws'.$wsnum.'co'.$c.'"'.$extra.'/>'."\n"; 1115 } else { 1116 while ($level > 0) { 1117 $buffer .= '</table:table-column-group>'; 1118 $level--; 1119 } 1120 $buffer .= '<table:table-column/>'."\n"; 1121 } 1122 } 1123 while ($level > 0) { 1124 $buffer .= '</table:table-column-group>'; 1125 $level--; 1126 } 1127 1128 // Print all rows. 1129 $level = 0; 1130 for($r=0; $r<=$ws->maxr; $r++) { 1131 if (array_key_exists($r, $ws->rows)) { 1132 $row = $ws->rows[$r]; 1133 if ($row->level > $level) { 1134 while ($row->level > $level) { 1135 $buffer .= '<table:table-row-group>'; 1136 $level++; 1137 } 1138 } else if ($row->level < $level) { 1139 while ($row->level < $level) { 1140 $buffer .= '</table:table-row-group>'; 1141 $level--; 1142 } 1143 } 1144 $extra = ''; 1145 if (!empty($row->format)) { 1146 $extra .= ' table:default-cell-style-name="format'.$row->format->id.'"'; 1147 } 1148 if ($row->hidden) { 1149 $extra .= ' table:visibility="collapse"'; 1150 } 1151 $buffer .= '<table:table-row table:style-name="ws'.$wsnum.'ro'.$r.'"'.$extra.'>'."\n"; 1152 } else { 1153 while ($level > 0) { 1154 $buffer .= '</table:table-row-group>'; 1155 $level--; 1156 } 1157 $buffer .= '<table:table-row>'."\n"; 1158 } 1159 for($c=0; $c<=$ws->maxc; $c++) { 1160 if (isset($ws->data[$r][$c])) { 1161 $cell = $ws->data[$r][$c]; 1162 $extra = ''; 1163 if (!empty($cell->format)) { 1164 $extra .= ' table:style-name="format'.$cell->format->id.'"'; 1165 } 1166 if (!empty($cell->merge)) { 1167 $extra .= ' table:number-columns-spanned="'.$cell->merge['columns'].'" table:number-rows-spanned="'.$cell->merge['rows'].'"'; 1168 } 1169 $pretext = '<text:p>'; 1170 $posttext = '</text:p>'; 1171 if (!empty($cell->format->properties['sub_script'])) { 1172 $pretext = $pretext.'<text:span text:style-name="T2">'; 1173 $posttext = '</text:span>'.$posttext; 1174 } else if (!empty($cell->format->properties['super_script'])) { 1175 $pretext = $pretext.'<text:span text:style-name="T1">'; 1176 $posttext = '</text:span>'.$posttext; 1177 } 1178 1179 if (isset($cell->formula)) { 1180 $buffer .= '<table:table-cell table:formula="of:'.$cell->formula.'"'.$extra.'></table:table-cell>'."\n"; 1181 } else if ($cell->type == 'date') { 1182 $buffer .= '<table:table-cell office:value-type="date" office:date-value="' . date("Y-m-d\\TH:i:s", $cell->value) . '"'.$extra.'>' 1183 . $pretext . date("Y-m-d\\TH:i:s", $cell->value) . $posttext 1184 . '</table:table-cell>'."\n"; 1185 } else if ($cell->type == 'float') { 1186 $buffer .= '<table:table-cell office:value-type="float" office:value="' . htmlspecialchars($cell->value, ENT_QUOTES, 'utf-8') . '"'.$extra.'>' 1187 . $pretext . htmlspecialchars($cell->value, ENT_QUOTES, 'utf-8') . $posttext 1188 . '</table:table-cell>'."\n"; 1189 } else if ($cell->type == 'string') { 1190 $buffer .= '<table:table-cell office:value-type="string"'.$extra.'>' 1191 . $pretext . htmlspecialchars($cell->value, ENT_QUOTES, 'utf-8') . $posttext 1192 . '</table:table-cell>'."\n"; 1193 } else { 1194 $buffer .= '<table:table-cell office:value-type="string"'.$extra.'>' 1195 . $pretext . '!!Error - unknown type!!' . $posttext 1196 . '</table:table-cell>'."\n"; 1197 } 1198 } else { 1199 $buffer .= '<table:table-cell/>'."\n"; 1200 } 1201 } 1202 $buffer .= '</table:table-row>'."\n"; 1203 } 1204 while ($level > 0) { 1205 $buffer .= '</table:table-row-group>'; 1206 $level--; 1207 } 1208 $buffer .= '</table:table>'."\n"; 1209 1210 } 1211 1212 // Footer. 1213 $buffer .= ' 1214 </office:spreadsheet> 1215 </office:body> 1216 </office:document-content>'; 1217 1218 return $buffer; 1219 } 1220 1221 public function get_ods_mimetype() { 1222 return 'application/vnd.oasis.opendocument.spreadsheet'; 1223 } 1224 1225 protected function get_ods_settings() { 1226 $buffer = 1227 '<?xml version="1.0" encoding="UTF-8"?> 1228 <office:document-settings xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 1229 xmlns:xlink="http://www.w3.org/1999/xlink" 1230 xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" 1231 xmlns:ooo="http://openoffice.org/2004/office" office:version="1.2"> 1232 <office:settings> 1233 <config:config-item-set config:name="ooo:view-settings"> 1234 <config:config-item config:name="VisibleAreaTop" config:type="int">0</config:config-item> 1235 <config:config-item config:name="VisibleAreaLeft" config:type="int">0</config:config-item> 1236 <config:config-item-map-indexed config:name="Views"> 1237 <config:config-item-map-entry> 1238 <config:config-item config:name="ViewId" config:type="string">view1</config:config-item> 1239 <config:config-item-map-named config:name="Tables"> 1240 '; 1241 foreach ($this->worksheets as $ws) { 1242 $buffer .= ' <config:config-item-map-entry config:name="'.htmlspecialchars($ws->name, ENT_QUOTES, 'utf-8').'">'."\n"; 1243 $buffer .= ' <config:config-item config:name="ShowGrid" config:type="boolean">'.($ws->showgrid ? 'true' : 'false').'</config:config-item>'."\n"; 1244 $buffer .= ' </config:config-item-map-entry>."\n"'; 1245 } 1246 $buffer .= 1247 ' </config:config-item-map-named> 1248 </config:config-item-map-entry> 1249 </config:config-item-map-indexed> 1250 </config:config-item-set> 1251 <config:config-item-set config:name="ooo:configuration-settings"> 1252 <config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item> 1253 </config:config-item-set> 1254 </office:settings> 1255 </office:document-settings>'; 1256 1257 return $buffer; 1258 } 1259 protected function get_ods_meta() { 1260 global $CFG, $USER; 1261 1262 return 1263 '<?xml version="1.0" encoding="UTF-8"?> 1264 <office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 1265 xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" 1266 xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" 1267 xmlns:ooo="http://openoffice.org/2004/office" xmlns:grddl="http://www.w3.org/2003/g/data-view#" 1268 office:version="1.2"> 1269 <office:meta> 1270 <meta:generator>Moodle '.$CFG->release.'</meta:generator> 1271 <meta:initial-creator>' . htmlspecialchars(fullname($USER, true), ENT_QUOTES, 'utf-8') . '</meta:initial-creator> 1272 <meta:creation-date>'.date("Y-m-d\\TH:i:s").'</meta:creation-date> 1273 <meta:document-statistic meta:table-count="1" meta:cell-count="0" meta:object-count="0"/> 1274 </office:meta> 1275 </office:document-meta>'; 1276 } 1277 1278 protected function get_ods_styles() { 1279 return 1280 '<?xml version="1.0" encoding="UTF-8"?> 1281 <office:document-styles xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 1282 xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 1283 xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 1284 xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 1285 xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 1286 xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 1287 xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" 1288 xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" 1289 xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" 1290 xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" 1291 xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 1292 xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" 1293 xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" 1294 xmlns:math="http://www.w3.org/1998/Math/MathML" 1295 xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" 1296 xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" 1297 xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" 1298 xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" 1299 xmlns:rpt="http://openoffice.org/2005/report" 1300 xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" 1301 xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" 1302 xmlns:tableooo="http://openoffice.org/2009/table" 1303 xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" 1304 xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2"> 1305 <office:font-face-decls> 1306 <style:font-face style:name="Arial" svg:font-family="Arial" style:font-family-generic="swiss" 1307 style:font-pitch="variable"/> 1308 <style:font-face style:name="Arial Unicode MS" svg:font-family="'Arial Unicode MS'" 1309 style:font-family-generic="system" style:font-pitch="variable"/> 1310 <style:font-face style:name="Tahoma" svg:font-family="Tahoma" style:font-family-generic="system" 1311 style:font-pitch="variable"/> 1312 </office:font-face-decls> 1313 <office:styles> 1314 <style:default-style style:family="table-cell"> 1315 <style:paragraph-properties style:tab-stop-distance="1.25cm"/> 1316 </style:default-style> 1317 <number:number-style style:name="N0"> 1318 <number:number number:min-integer-digits="1"/> 1319 </number:number-style> 1320 <style:style style:name="Default" style:family="table-cell"> 1321 <style:text-properties style:font-name-asian="Arial Unicode MS" style:font-name-complex="Arial Unicode MS"/> 1322 </style:style> 1323 <style:style style:name="Result" style:family="table-cell" style:parent-style-name="Default"> 1324 <style:text-properties fo:font-style="italic" style:text-underline-style="solid" 1325 style:text-underline-width="auto" style:text-underline-color="font-color" 1326 fo:font-weight="bold"/> 1327 </style:style> 1328 <style:style style:name="Result2" style:family="table-cell" style:parent-style-name="Result" 1329 style:data-style-name="N104"/> 1330 <style:style style:name="Heading" style:family="table-cell" style:parent-style-name="Default"> 1331 <style:table-cell-properties style:text-align-source="fix" style:repeat-content="false"/> 1332 <style:paragraph-properties fo:text-align="center"/> 1333 <style:text-properties fo:font-size="16pt" fo:font-style="italic" fo:font-weight="bold"/> 1334 </style:style> 1335 <style:style style:name="Heading1" style:family="table-cell" style:parent-style-name="Heading"> 1336 <style:table-cell-properties style:rotation-angle="90"/> 1337 </style:style> 1338 </office:styles> 1339 <office:automatic-styles> 1340 <style:page-layout style:name="Mpm1"> 1341 <style:page-layout-properties style:writing-mode="lr-tb"/> 1342 <style:header-style> 1343 <style:header-footer-properties fo:min-height="0.75cm" fo:margin-left="0cm" fo:margin-right="0cm" 1344 fo:margin-bottom="0.25cm"/> 1345 </style:header-style> 1346 <style:footer-style> 1347 <style:header-footer-properties fo:min-height="0.75cm" fo:margin-left="0cm" fo:margin-right="0cm" 1348 fo:margin-top="0.25cm"/> 1349 </style:footer-style> 1350 </style:page-layout> 1351 <style:page-layout style:name="Mpm2"> 1352 <style:page-layout-properties style:writing-mode="lr-tb"/> 1353 <style:header-style> 1354 <style:header-footer-properties fo:min-height="0.75cm" fo:margin-left="0cm" fo:margin-right="0cm" 1355 fo:margin-bottom="0.25cm" fo:border="2.49pt solid #000000" 1356 fo:padding="0.018cm" fo:background-color="#c0c0c0"> 1357 <style:background-image/> 1358 </style:header-footer-properties> 1359 </style:header-style> 1360 <style:footer-style> 1361 <style:header-footer-properties fo:min-height="0.75cm" fo:margin-left="0cm" fo:margin-right="0cm" 1362 fo:margin-top="0.25cm" fo:border="2.49pt solid #000000" 1363 fo:padding="0.018cm" fo:background-color="#c0c0c0"> 1364 <style:background-image/> 1365 </style:header-footer-properties> 1366 </style:footer-style> 1367 </style:page-layout> 1368 </office:automatic-styles> 1369 <office:master-styles> 1370 <style:master-page style:name="Default" style:page-layout-name="Mpm1"> 1371 <style:header> 1372 <text:p> 1373 <text:sheet-name>???</text:sheet-name> 1374 </text:p> 1375 </style:header> 1376 <style:header-left style:display="false"/> 1377 <style:footer> 1378 <text:p>Page 1379 <text:page-number>1</text:page-number> 1380 </text:p> 1381 </style:footer> 1382 <style:footer-left style:display="false"/> 1383 </style:master-page> 1384 <style:master-page style:name="Report" style:page-layout-name="Mpm2"> 1385 <style:header> 1386 <style:region-left> 1387 <text:p> 1388 <text:sheet-name>???</text:sheet-name> 1389 (<text:title>???</text:title>) 1390 </text:p> 1391 </style:region-left> 1392 <style:region-right> 1393 <text:p><text:date style:data-style-name="N2" text:date-value="2013-01-05">00.00.0000</text:date>, 1394 <text:time>00:00:00</text:time> 1395 </text:p> 1396 </style:region-right> 1397 </style:header> 1398 <style:header-left style:display="false"/> 1399 <style:footer> 1400 <text:p>Page 1401 <text:page-number>1</text:page-number> 1402 / 1403 <text:page-count>99</text:page-count> 1404 </text:p> 1405 </style:footer> 1406 <style:footer-left style:display="false"/> 1407 </style:master-page> 1408 </office:master-styles> 1409 </office:document-styles>'; 1410 } 1411 1412 protected function get_ods_manifest() { 1413 return 1414 '<?xml version="1.0" encoding="UTF-8"?> 1415 <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2"> 1416 <manifest:file-entry manifest:full-path="/" manifest:version="1.2" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/> 1417 <manifest:file-entry manifest:full-path="meta.xml" manifest:media-type="text/xml"/> 1418 <manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/> 1419 <manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/> 1420 <manifest:file-entry manifest:full-path="settings.xml" manifest:media-type="text/xml"/> 1421 </manifest:manifest>'; 1422 } 1423 1424 protected function get_num_styles() { 1425 return ' 1426 <number:number-style style:name="NUM1"> 1427 <number:number number:decimal-places="0" number:min-integer-digits="1"/> 1428 </number:number-style> 1429 <number:number-style style:name="NUM2"> 1430 <number:number number:decimal-places="2" number:min-integer-digits="1"/> 1431 </number:number-style> 1432 <number:number-style style:name="NUM3"> 1433 <number:number number:decimal-places="0" number:min-integer-digits="1" number:grouping="true"/> 1434 </number:number-style> 1435 <number:number-style style:name="NUM4"> 1436 <number:number number:decimal-places="2" number:min-integer-digits="1" number:grouping="true"/> 1437 </number:number-style> 1438 <number:number-style style:name="NUM11"> 1439 <number:scientific-number number:decimal-places="2" number:min-integer-digits="1" 1440 number:min-exponent-digits="2"/> 1441 </number:number-style> 1442 <number:number-style style:name="NUM12"> 1443 <number:fraction number:min-integer-digits="0" number:min-numerator-digits="1" 1444 number:min-denominator-digits="1"/> 1445 </number:number-style> 1446 <number:number-style style:name="NUM13"> 1447 <number:fraction number:min-integer-digits="0" number:min-numerator-digits="2" 1448 number:min-denominator-digits="2"/> 1449 </number:number-style> 1450 <number:date-style style:name="NUM14" number:automatic-order="true"> 1451 <number:month number:style="long"/> 1452 <number:text>/</number:text> 1453 <number:day number:style="long"/> 1454 <number:text>/</number:text> 1455 <number:year/> 1456 </number:date-style> 1457 <number:date-style style:name="NUM15"> 1458 <number:day/> 1459 <number:text>.</number:text> 1460 <number:month number:textual="true"/> 1461 <number:text>.</number:text> 1462 <number:year number:style="long"/> 1463 </number:date-style> 1464 <number:date-style style:name="NUM16" number:automatic-order="true"> 1465 <number:month number:textual="true"/> 1466 <number:text></number:text> 1467 <number:day number:style="long"/> 1468 </number:date-style> 1469 <number:date-style style:name="NUM17"> 1470 <number:month number:style="long"/> 1471 <number:text>-</number:text> 1472 <number:day number:style="long"/> 1473 </number:date-style> 1474 <number:date-style style:name="NUM22" number:automatic-order="true" 1475 number:format-source="language"> 1476 <number:month/> 1477 <number:text>/</number:text> 1478 <number:day/> 1479 <number:text>/</number:text> 1480 <number:year/> 1481 <number:text></number:text> 1482 <number:hours number:style="long"/> 1483 <number:text>:</number:text> 1484 <number:minutes number:style="long"/> 1485 <number:text></number:text> 1486 <number:am-pm/> 1487 </number:date-style> 1488 <number:text-style style:name="NUM49"> 1489 <number:text-content/> 1490 </number:text-style> 1491 '; 1492 } 1493 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body