adding some strings
[moodle-linuxchix.git] / lib / pear / Spreadsheet / Excel / Writer / Worksheet.php
blob5d843dc31d67cfc67a3172dda377211446643fb3
1 <?php
2 /*
3 * Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
5 * The majority of this is _NOT_ my code. I simply ported it from the
6 * PERL Spreadsheet::WriteExcel module.
8 * The author of the Spreadsheet::WriteExcel module is John McNamara
9 * <jmcnamara@cpan.org>
11 * I _DO_ maintain this code, and John McNamara has nothing to do with the
12 * porting of this code to PHP. Any questions directly related to this
13 * class library should be directed to me.
15 * License Information:
17 * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
18 * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 require_once 'Spreadsheet/Excel/Writer/Parser.php';
36 require_once 'Spreadsheet/Excel/Writer/BIFFwriter.php';
38 /**
39 * Class for generating Excel Spreadsheets
41 * @author Xavier Noguer <xnoguer@rezebra.com>
42 * @category FileFormats
43 * @package Spreadsheet_Excel_Writer
46 class Spreadsheet_Excel_Writer_Worksheet extends Spreadsheet_Excel_Writer_BIFFwriter
48 /**
49 * Name of the Worksheet
50 * @var string
52 var $name;
54 /**
55 * Index for the Worksheet
56 * @var integer
58 var $index;
60 /**
61 * Reference to the (default) Format object for URLs
62 * @var object Format
64 var $_url_format;
66 /**
67 * Reference to the parser used for parsing formulas
68 * @var object Format
70 var $_parser;
72 /**
73 * Filehandle to the temporary file for storing data
74 * @var resource
76 var $_filehandle;
78 /**
79 * Boolean indicating if we are using a temporary file for storing data
80 * @var bool
82 var $_using_tmpfile;
84 /**
85 * Maximum number of rows for an Excel spreadsheet (BIFF5)
86 * @var integer
88 var $_xls_rowmax;
90 /**
91 * Maximum number of columns for an Excel spreadsheet (BIFF5)
92 * @var integer
94 var $_xls_colmax;
96 /**
97 * Maximum number of characters for a string (LABEL record in BIFF5)
98 * @var integer
100 var $_xls_strmax;
103 * First row for the DIMENSIONS record
104 * @var integer
105 * @see _storeDimensions()
107 var $_dim_rowmin;
110 * Last row for the DIMENSIONS record
111 * @var integer
112 * @see _storeDimensions()
114 var $_dim_rowmax;
117 * First column for the DIMENSIONS record
118 * @var integer
119 * @see _storeDimensions()
121 var $_dim_colmin;
124 * Last column for the DIMENSIONS record
125 * @var integer
126 * @see _storeDimensions()
128 var $_dim_colmax;
131 * Array containing format information for columns
132 * @var array
134 var $_colinfo;
137 * Array containing the selected area for the worksheet
138 * @var array
140 var $_selection;
143 * Array containing the panes for the worksheet
144 * @var array
146 var $_panes;
149 * The active pane for the worksheet
150 * @var integer
152 var $_active_pane;
155 * Bit specifying if panes are frozen
156 * @var integer
158 var $_frozen;
161 * Bit specifying if the worksheet is selected
162 * @var integer
164 var $selected;
167 * The paper size (for printing) (DOCUMENT!!!)
168 * @var integer
170 var $_paper_size;
173 * Bit specifying paper orientation (for printing). 0 => landscape, 1 => portrait
174 * @var integer
176 var $_orientation;
179 * The page header caption
180 * @var string
182 var $_header;
185 * The page footer caption
186 * @var string
188 var $_footer;
191 * The horizontal centering value for the page
192 * @var integer
194 var $_hcenter;
197 * The vertical centering value for the page
198 * @var integer
200 var $_vcenter;
203 * The margin for the header
204 * @var float
206 var $_margin_head;
209 * The margin for the footer
210 * @var float
212 var $_margin_foot;
215 * The left margin for the worksheet in inches
216 * @var float
218 var $_margin_left;
221 * The right margin for the worksheet in inches
222 * @var float
224 var $_margin_right;
227 * The top margin for the worksheet in inches
228 * @var float
230 var $_margin_top;
233 * The bottom margin for the worksheet in inches
234 * @var float
236 var $_margin_bottom;
239 * First row to reapeat on each printed page
240 * @var integer
242 var $title_rowmin;
245 * Last row to reapeat on each printed page
246 * @var integer
248 var $title_rowmax;
251 * First column to reapeat on each printed page
252 * @var integer
254 var $title_colmin;
257 * First row of the area to print
258 * @var integer
260 var $print_rowmin;
263 * Last row to of the area to print
264 * @var integer
266 var $print_rowmax;
269 * First column of the area to print
270 * @var integer
272 var $print_colmin;
275 * Last column of the area to print
276 * @var integer
278 var $print_colmax;
281 * Whether to use outline.
282 * @var integer
284 var $_outline_on;
287 * Auto outline styles.
288 * @var bool
290 var $_outline_style;
293 * Whether to have outline summary below.
294 * @var bool
296 var $_outline_below;
299 * Whether to have outline summary at the right.
300 * @var bool
302 var $_outline_right;
305 * Outline row level.
306 * @var integer
308 var $_outline_row_level;
311 * Whether to fit to page when printing or not.
312 * @var bool
314 var $_fit_page;
317 * Number of pages to fit wide
318 * @var integer
320 var $_fit_width;
323 * Number of pages to fit high
324 * @var integer
326 var $_fit_height;
329 * Reference to the total number of strings in the workbook
330 * @var integer
332 var $_str_total;
335 * Reference to the number of unique strings in the workbook
336 * @var integer
338 var $_str_unique;
341 * Reference to the array containing all the unique strings in the workbook
342 * @var array
344 var $_str_table;
347 * Merged cell ranges
348 * @var array
350 var $_merged_ranges;
353 * Charset encoding currently used when calling writeString()
354 * @var string
356 var $_input_encoding;
359 * Constructor
361 * @param string $name The name of the new worksheet
362 * @param integer $index The index of the new worksheet
363 * @param mixed &$activesheet The current activesheet of the workbook we belong to
364 * @param mixed &$firstsheet The first worksheet in the workbook we belong to
365 * @param mixed &$url_format The default format for hyperlinks
366 * @param mixed &$parser The formula parser created for the Workbook
367 * @access private
369 function Spreadsheet_Excel_Writer_Worksheet($BIFF_version, $name,
370 $index, &$activesheet,
371 &$firstsheet, &$str_total,
372 &$str_unique, &$str_table,
373 &$url_format, &$parser)
375 // It needs to call its parent's constructor explicitly
376 $this->Spreadsheet_Excel_Writer_BIFFwriter();
377 $this->_BIFF_version = $BIFF_version;
378 $rowmax = 65536; // 16384 in Excel 5
379 $colmax = 256;
381 $this->name = $name;
382 $this->index = $index;
383 $this->activesheet = &$activesheet;
384 $this->firstsheet = &$firstsheet;
385 $this->_str_total = &$str_total;
386 $this->_str_unique = &$str_unique;
387 $this->_str_table = &$str_table;
388 $this->_url_format = &$url_format;
389 $this->_parser = &$parser;
391 //$this->ext_sheets = array();
392 $this->_filehandle = '';
393 $this->_using_tmpfile = true;
394 //$this->fileclosed = 0;
395 //$this->offset = 0;
396 $this->_xls_rowmax = $rowmax;
397 $this->_xls_colmax = $colmax;
398 $this->_xls_strmax = 255;
399 $this->_dim_rowmin = $rowmax + 1;
400 $this->_dim_rowmax = 0;
401 $this->_dim_colmin = $colmax + 1;
402 $this->_dim_colmax = 0;
403 $this->_colinfo = array();
404 $this->_selection = array(0,0,0,0);
405 $this->_panes = array();
406 $this->_active_pane = 3;
407 $this->_frozen = 0;
408 $this->selected = 0;
410 $this->_paper_size = 0x0;
411 $this->_orientation = 0x1;
412 $this->_header = '';
413 $this->_footer = '';
414 $this->_hcenter = 0;
415 $this->_vcenter = 0;
416 $this->_margin_head = 0.50;
417 $this->_margin_foot = 0.50;
418 $this->_margin_left = 0.75;
419 $this->_margin_right = 0.75;
420 $this->_margin_top = 1.00;
421 $this->_margin_bottom = 1.00;
423 $this->title_rowmin = null;
424 $this->title_rowmax = null;
425 $this->title_colmin = null;
426 $this->title_colmax = null;
427 $this->print_rowmin = null;
428 $this->print_rowmax = null;
429 $this->print_colmin = null;
430 $this->print_colmax = null;
432 $this->_print_gridlines = 1;
433 $this->_screen_gridlines = 1;
434 $this->_print_headers = 0;
436 $this->_fit_page = 0;
437 $this->_fit_width = 0;
438 $this->_fit_height = 0;
440 $this->_hbreaks = array();
441 $this->_vbreaks = array();
443 $this->_protect = 0;
444 $this->_password = null;
446 $this->col_sizes = array();
447 $this->_row_sizes = array();
449 $this->_zoom = 100;
450 $this->_print_scale = 100;
452 $this->_outline_row_level = 0;
453 $this->_outline_style = 0;
454 $this->_outline_below = 1;
455 $this->_outline_right = 1;
456 $this->_outline_on = 1;
458 $this->_merged_ranges = array();
460 $this->_input_encoding = '';
462 $this->_dv = array();
464 $this->_initialize();
468 * Open a tmp file to store the majority of the Worksheet data. If this fails,
469 * for example due to write permissions, store the data in memory. This can be
470 * slow for large files.
472 * @access private
474 function _initialize()
476 // Open tmp file for storing Worksheet data
477 $fh = tmpfile();
478 if ($fh) {
479 // Store filehandle
480 $this->_filehandle = $fh;
481 } else {
482 // If tmpfile() fails store data in memory
483 $this->_using_tmpfile = false;
488 * Add data to the beginning of the workbook (note the reverse order)
489 * and to the end of the workbook.
491 * @access public
492 * @see Spreadsheet_Excel_Writer_Workbook::storeWorkbook()
493 * @param array $sheetnames The array of sheetnames from the Workbook this
494 * worksheet belongs to
496 function close($sheetnames)
498 $num_sheets = count($sheetnames);
500 /***********************************************
501 * Prepend in reverse order!!
504 // Prepend the sheet dimensions
505 $this->_storeDimensions();
507 // Prepend the sheet password
508 $this->_storePassword();
510 // Prepend the sheet protection
511 $this->_storeProtect();
513 // Prepend the page setup
514 $this->_storeSetup();
516 /* FIXME: margins are actually appended */
517 // Prepend the bottom margin
518 $this->_storeMarginBottom();
520 // Prepend the top margin
521 $this->_storeMarginTop();
523 // Prepend the right margin
524 $this->_storeMarginRight();
526 // Prepend the left margin
527 $this->_storeMarginLeft();
529 // Prepend the page vertical centering
530 $this->_storeVcenter();
532 // Prepend the page horizontal centering
533 $this->_storeHcenter();
535 // Prepend the page footer
536 $this->_storeFooter();
538 // Prepend the page header
539 $this->_storeHeader();
541 // Prepend the vertical page breaks
542 $this->_storeVbreak();
544 // Prepend the horizontal page breaks
545 $this->_storeHbreak();
547 // Prepend WSBOOL
548 $this->_storeWsbool();
550 // Prepend GRIDSET
551 $this->_storeGridset();
553 // Prepend GUTS
554 if ($this->_BIFF_version == 0x0500) {
555 $this->_storeGuts();
558 // Prepend PRINTGRIDLINES
559 $this->_storePrintGridlines();
561 // Prepend PRINTHEADERS
562 $this->_storePrintHeaders();
564 // Prepend EXTERNSHEET references
565 if ($this->_BIFF_version == 0x0500) {
566 for ($i = $num_sheets; $i > 0; $i--) {
567 $sheetname = $sheetnames[$i-1];
568 $this->_storeExternsheet($sheetname);
572 // Prepend the EXTERNCOUNT of external references.
573 if ($this->_BIFF_version == 0x0500) {
574 $this->_storeExterncount($num_sheets);
577 // Prepend the COLINFO records if they exist
578 if (!empty($this->_colinfo)) {
579 $colcount = count($this->_colinfo);
580 for ($i = 0; $i < $colcount; $i++) {
581 $this->_storeColinfo($this->_colinfo[$i]);
583 $this->_storeDefcol();
586 // Prepend the BOF record
587 $this->_storeBof(0x0010);
590 * End of prepend. Read upwards from here.
591 ***********************************************/
593 // Append
594 $this->_storeWindow2();
595 $this->_storeZoom();
596 if (!empty($this->_panes)) {
597 $this->_storePanes($this->_panes);
599 $this->_storeSelection($this->_selection);
600 $this->_storeMergedCells();
601 /* TODO: add data validity */
602 /*if ($this->_BIFF_version == 0x0600) {
603 $this->_storeDataValidity();
605 $this->_storeEof();
609 * Retrieve the worksheet name.
610 * This is usefull when creating worksheets without a name.
612 * @access public
613 * @return string The worksheet's name
615 function getName()
617 return $this->name;
621 * Retrieves data from memory in one chunk, or from disk in $buffer
622 * sized chunks.
624 * @return string The data
626 function getData()
628 $buffer = 4096;
630 // Return data stored in memory
631 if (isset($this->_data)) {
632 $tmp = $this->_data;
633 unset($this->_data);
634 $fh = $this->_filehandle;
635 if ($this->_using_tmpfile) {
636 fseek($fh, 0);
638 return $tmp;
640 // Return data stored on disk
641 if ($this->_using_tmpfile) {
642 if ($tmp = fread($this->_filehandle, $buffer)) {
643 return $tmp;
647 // No data to return
648 return '';
652 * Sets a merged cell range
654 * @access public
655 * @param integer $first_row First row of the area to merge
656 * @param integer $first_col First column of the area to merge
657 * @param integer $last_row Last row of the area to merge
658 * @param integer $last_col Last column of the area to merge
660 function setMerge($first_row, $first_col, $last_row, $last_col)
662 if (($last_row < $first_row) || ($last_col < $first_col)) {
663 return;
665 // don't check rowmin, rowmax, etc... because we don't know when this
666 // is going to be called
667 $this->_merged_ranges[] = array($first_row, $first_col, $last_row, $last_col);
671 * Set this worksheet as a selected worksheet,
672 * i.e. the worksheet has its tab highlighted.
674 * @access public
676 function select()
678 $this->selected = 1;
682 * Set this worksheet as the active worksheet,
683 * i.e. the worksheet that is displayed when the workbook is opened.
684 * Also set it as selected.
686 * @access public
688 function activate()
690 $this->selected = 1;
691 $this->activesheet = $this->index;
695 * Set this worksheet as the first visible sheet.
696 * This is necessary when there are a large number of worksheets and the
697 * activated worksheet is not visible on the screen.
699 * @access public
701 function setFirstSheet()
703 $this->firstsheet = $this->index;
707 * Set the worksheet protection flag
708 * to prevent accidental modification and to
709 * hide formulas if the locked and hidden format properties have been set.
711 * @access public
712 * @param string $password The password to use for protecting the sheet.
714 function protect($password)
716 $this->_protect = 1;
717 $this->_password = $this->_encodePassword($password);
721 * Set the width of a single column or a range of columns.
723 * @access public
724 * @param integer $firstcol first column on the range
725 * @param integer $lastcol last column on the range
726 * @param integer $width width to set
727 * @param mixed $format The optional XF format to apply to the columns
728 * @param integer $hidden The optional hidden atribute
729 * @param integer $level The optional outline level
731 function setColumn($firstcol, $lastcol, $width, $format = 0, $hidden = 0, $level = 0)
733 $this->_colinfo[] = array($firstcol, $lastcol, $width, &$format, $hidden, $level);
735 // Set width to zero if column is hidden
736 $width = ($hidden) ? 0 : $width;
738 for ($col = $firstcol; $col <= $lastcol; $col++) {
739 $this->col_sizes[$col] = $width;
744 * Set which cell or cells are selected in a worksheet
746 * @access public
747 * @param integer $first_row first row in the selected quadrant
748 * @param integer $first_column first column in the selected quadrant
749 * @param integer $last_row last row in the selected quadrant
750 * @param integer $last_column last column in the selected quadrant
752 function setSelection($first_row,$first_column,$last_row,$last_column)
754 $this->_selection = array($first_row,$first_column,$last_row,$last_column);
758 * Set panes and mark them as frozen.
760 * @access public
761 * @param array $panes This is the only parameter received and is composed of the following:
762 * 0 => Vertical split position,
763 * 1 => Horizontal split position
764 * 2 => Top row visible
765 * 3 => Leftmost column visible
766 * 4 => Active pane
768 function freezePanes($panes)
770 $this->_frozen = 1;
771 $this->_panes = $panes;
775 * Set panes and mark them as unfrozen.
777 * @access public
778 * @param array $panes This is the only parameter received and is composed of the following:
779 * 0 => Vertical split position,
780 * 1 => Horizontal split position
781 * 2 => Top row visible
782 * 3 => Leftmost column visible
783 * 4 => Active pane
785 function thawPanes($panes)
787 $this->_frozen = 0;
788 $this->_panes = $panes;
792 * Set the page orientation as portrait.
794 * @access public
796 function setPortrait()
798 $this->_orientation = 1;
802 * Set the page orientation as landscape.
804 * @access public
806 function setLandscape()
808 $this->_orientation = 0;
812 * Set the paper type. Ex. 1 = US Letter, 9 = A4
814 * @access public
815 * @param integer $size The type of paper size to use
817 function setPaper($size = 0)
819 $this->_paper_size = $size;
824 * Set the page header caption and optional margin.
826 * @access public
827 * @param string $string The header text
828 * @param float $margin optional head margin in inches.
830 function setHeader($string,$margin = 0.50)
832 if (strlen($string) >= 255) {
833 //carp 'Header string must be less than 255 characters';
834 return;
836 $this->_header = $string;
837 $this->_margin_head = $margin;
841 * Set the page footer caption and optional margin.
843 * @access public
844 * @param string $string The footer text
845 * @param float $margin optional foot margin in inches.
847 function setFooter($string,$margin = 0.50)
849 if (strlen($string) >= 255) {
850 //carp 'Footer string must be less than 255 characters';
851 return;
853 $this->_footer = $string;
854 $this->_margin_foot = $margin;
858 * Center the page horinzontally.
860 * @access public
861 * @param integer $center the optional value for centering. Defaults to 1 (center).
863 function centerHorizontally($center = 1)
865 $this->_hcenter = $center;
869 * Center the page vertically.
871 * @access public
872 * @param integer $center the optional value for centering. Defaults to 1 (center).
874 function centerVertically($center = 1)
876 $this->_vcenter = $center;
880 * Set all the page margins to the same value in inches.
882 * @access public
883 * @param float $margin The margin to set in inches
885 function setMargins($margin)
887 $this->setMarginLeft($margin);
888 $this->setMarginRight($margin);
889 $this->setMarginTop($margin);
890 $this->setMarginBottom($margin);
894 * Set the left and right margins to the same value in inches.
896 * @access public
897 * @param float $margin The margin to set in inches
899 function setMargins_LR($margin)
901 $this->setMarginLeft($margin);
902 $this->setMarginRight($margin);
906 * Set the top and bottom margins to the same value in inches.
908 * @access public
909 * @param float $margin The margin to set in inches
911 function setMargins_TB($margin)
913 $this->setMarginTop($margin);
914 $this->setMarginBottom($margin);
918 * Set the left margin in inches.
920 * @access public
921 * @param float $margin The margin to set in inches
923 function setMarginLeft($margin = 0.75)
925 $this->_margin_left = $margin;
929 * Set the right margin in inches.
931 * @access public
932 * @param float $margin The margin to set in inches
934 function setMarginRight($margin = 0.75)
936 $this->_margin_right = $margin;
940 * Set the top margin in inches.
942 * @access public
943 * @param float $margin The margin to set in inches
945 function setMarginTop($margin = 1.00)
947 $this->_margin_top = $margin;
951 * Set the bottom margin in inches.
953 * @access public
954 * @param float $margin The margin to set in inches
956 function setMarginBottom($margin = 1.00)
958 $this->_margin_bottom = $margin;
962 * Set the rows to repeat at the top of each printed page.
964 * @access public
965 * @param integer $first_row First row to repeat
966 * @param integer $last_row Last row to repeat. Optional.
968 function repeatRows($first_row, $last_row = null)
970 $this->title_rowmin = $first_row;
971 if (isset($last_row)) { //Second row is optional
972 $this->title_rowmax = $last_row;
973 } else {
974 $this->title_rowmax = $first_row;
979 * Set the columns to repeat at the left hand side of each printed page.
981 * @access public
982 * @param integer $first_col First column to repeat
983 * @param integer $last_col Last column to repeat. Optional.
985 function repeatColumns($first_col, $last_col = null)
987 $this->title_colmin = $first_col;
988 if (isset($last_col)) { // Second col is optional
989 $this->title_colmax = $last_col;
990 } else {
991 $this->title_colmax = $first_col;
996 * Set the area of each worksheet that will be printed.
998 * @access public
999 * @param integer $first_row First row of the area to print
1000 * @param integer $first_col First column of the area to print
1001 * @param integer $last_row Last row of the area to print
1002 * @param integer $last_col Last column of the area to print
1004 function printArea($first_row, $first_col, $last_row, $last_col)
1006 $this->print_rowmin = $first_row;
1007 $this->print_colmin = $first_col;
1008 $this->print_rowmax = $last_row;
1009 $this->print_colmax = $last_col;
1014 * Set the option to hide gridlines on the printed page.
1016 * @access public
1018 function hideGridlines()
1020 $this->_print_gridlines = 0;
1024 * Set the option to hide gridlines on the worksheet (as seen on the screen).
1026 * @access public
1028 function hideScreenGridlines()
1030 $this->_screen_gridlines = 0;
1034 * Set the option to print the row and column headers on the printed page.
1036 * @access public
1037 * @param integer $print Whether to print the headers or not. Defaults to 1 (print).
1039 function printRowColHeaders($print = 1)
1041 $this->_print_headers = $print;
1045 * Set the vertical and horizontal number of pages that will define the maximum area printed.
1046 * It doesn't seem to work with OpenOffice.
1048 * @access public
1049 * @param integer $width Maximun width of printed area in pages
1050 * @param integer $height Maximun heigth of printed area in pages
1051 * @see setPrintScale()
1053 function fitToPages($width, $height)
1055 $this->_fit_page = 1;
1056 $this->_fit_width = $width;
1057 $this->_fit_height = $height;
1061 * Store the horizontal page breaks on a worksheet (for printing).
1062 * The breaks represent the row after which the break is inserted.
1064 * @access public
1065 * @param array $breaks Array containing the horizontal page breaks
1067 function setHPagebreaks($breaks)
1069 foreach ($breaks as $break) {
1070 array_push($this->_hbreaks, $break);
1075 * Store the vertical page breaks on a worksheet (for printing).
1076 * The breaks represent the column after which the break is inserted.
1078 * @access public
1079 * @param array $breaks Array containing the vertical page breaks
1081 function setVPagebreaks($breaks)
1083 foreach ($breaks as $break) {
1084 array_push($this->_vbreaks, $break);
1090 * Set the worksheet zoom factor.
1092 * @access public
1093 * @param integer $scale The zoom factor
1095 function setZoom($scale = 100)
1097 // Confine the scale to Excel's range
1098 if ($scale < 10 || $scale > 400) {
1099 $this->raiseError("Zoom factor $scale outside range: 10 <= zoom <= 400");
1100 $scale = 100;
1103 $this->_zoom = floor($scale);
1107 * Set the scale factor for the printed page.
1108 * It turns off the "fit to page" option
1110 * @access public
1111 * @param integer $scale The optional scale factor. Defaults to 100
1113 function setPrintScale($scale = 100)
1115 // Confine the scale to Excel's range
1116 if ($scale < 10 || $scale > 400) {
1117 $this->raiseError("Print scale $scale outside range: 10 <= zoom <= 400");
1118 $scale = 100;
1121 // Turn off "fit to page" option
1122 $this->_fit_page = 0;
1124 $this->_print_scale = floor($scale);
1128 * Map to the appropriate write method acording to the token recieved.
1130 * @access public
1131 * @param integer $row The row of the cell we are writing to
1132 * @param integer $col The column of the cell we are writing to
1133 * @param mixed $token What we are writing
1134 * @param mixed $format The optional format to apply to the cell
1136 function write($row, $col, $token, $format = 0)
1138 // Check for a cell reference in A1 notation and substitute row and column
1139 /*if ($_[0] =~ /^\D/) {
1140 @_ = $this->_substituteCellref(@_);
1143 if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/", $token)) {
1144 // Match number
1145 return $this->writeNumber($row, $col, $token, $format);
1146 } elseif (preg_match("/^[fh]tt?p:\/\//", $token)) {
1147 // Match http or ftp URL
1148 return $this->writeUrl($row, $col, $token, '', $format);
1149 } elseif (preg_match("/^mailto:/", $token)) {
1150 // Match mailto:
1151 return $this->writeUrl($row, $col, $token, '', $format);
1152 } elseif (preg_match("/^(?:in|ex)ternal:/", $token)) {
1153 // Match internal or external sheet link
1154 return $this->writeUrl($row, $col, $token, '', $format);
1155 } elseif (preg_match("/^=/", $token)) {
1156 // Match formula
1157 return $this->writeFormula($row, $col, $token, $format);
1158 } elseif (preg_match("/^@/", $token)) {
1159 // Match formula
1160 return $this->writeFormula($row, $col, $token, $format);
1161 } elseif ($token == '') {
1162 // Match blank
1163 return $this->writeBlank($row, $col, $format);
1164 } else {
1165 // Default: match string
1166 return $this->writeString($row, $col, $token, $format);
1171 * Write an array of values as a row
1173 * @access public
1174 * @param integer $row The row we are writing to
1175 * @param integer $col The first col (leftmost col) we are writing to
1176 * @param array $val The array of values to write
1177 * @param mixed $format The optional format to apply to the cell
1178 * @return mixed PEAR_Error on failure
1181 function writeRow($row, $col, $val, $format = 0)
1183 $retval = '';
1184 if (is_array($val)) {
1185 foreach ($val as $v) {
1186 if (is_array($v)) {
1187 $this->writeCol($row, $col, $v, $format);
1188 } else {
1189 $this->write($row, $col, $v, $format);
1191 $col++;
1193 } else {
1194 $retval = new PEAR_Error('$val needs to be an array');
1196 return($retval);
1200 * Write an array of values as a column
1202 * @access public
1203 * @param integer $row The first row (uppermost row) we are writing to
1204 * @param integer $col The col we are writing to
1205 * @param array $val The array of values to write
1206 * @param mixed $format The optional format to apply to the cell
1207 * @return mixed PEAR_Error on failure
1210 function writeCol($row, $col, $val, $format=0)
1212 $retval = '';
1213 if (is_array($val)) {
1214 foreach ($val as $v) {
1215 $this->write($row, $col, $v, $format);
1216 $row++;
1218 } else {
1219 $retval = new PEAR_Error('$val needs to be an array');
1221 return($retval);
1225 * Returns an index to the XF record in the workbook
1227 * @access private
1228 * @param mixed &$format The optional XF format
1229 * @return integer The XF record index
1231 function _XF(&$format)
1233 if (is_object($format)) {
1234 return($format->getXfIndex());
1235 } else {
1236 return(0x0F);
1241 /******************************************************************************
1242 *******************************************************************************
1244 * Internal methods
1249 * Store Worksheet data in memory using the parent's class append() or to a
1250 * temporary file, the default.
1252 * @access private
1253 * @param string $data The binary data to append
1255 function _append($data)
1257 if ($this->_using_tmpfile) {
1258 // Add CONTINUE records if necessary
1259 if (strlen($data) > $this->_limit) {
1260 $data = $this->_addContinue($data);
1262 fwrite($this->_filehandle, $data);
1263 $this->_datasize += strlen($data);
1264 } else {
1265 parent::_append($data);
1270 * Substitute an Excel cell reference in A1 notation for zero based row and
1271 * column values in an argument list.
1273 * Ex: ("A4", "Hello") is converted to (3, 0, "Hello").
1275 * @access private
1276 * @param string $cell The cell reference. Or range of cells.
1277 * @return array
1279 function _substituteCellref($cell)
1281 $cell = strtoupper($cell);
1283 // Convert a column range: 'A:A' or 'B:G'
1284 if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/", $cell, $match)) {
1285 list($no_use, $col1) = $this->_cellToRowcol($match[1] .'1'); // Add a dummy row
1286 list($no_use, $col2) = $this->_cellToRowcol($match[2] .'1'); // Add a dummy row
1287 return(array($col1, $col2));
1290 // Convert a cell range: 'A1:B7'
1291 if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/", $cell, $match)) {
1292 list($row1, $col1) = $this->_cellToRowcol($match[1]);
1293 list($row2, $col2) = $this->_cellToRowcol($match[2]);
1294 return(array($row1, $col1, $row2, $col2));
1297 // Convert a cell reference: 'A1' or 'AD2000'
1298 if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/", $cell)) {
1299 list($row1, $col1) = $this->_cellToRowcol($match[1]);
1300 return(array($row1, $col1));
1303 // TODO use real error codes
1304 $this->raiseError("Unknown cell reference $cell", 0, PEAR_ERROR_DIE);
1308 * Convert an Excel cell reference in A1 notation to a zero based row and column
1309 * reference; converts C1 to (0, 2).
1311 * @access private
1312 * @param string $cell The cell reference.
1313 * @return array containing (row, column)
1315 function _cellToRowcol($cell)
1317 preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match);
1318 $col = $match[1];
1319 $row = $match[2];
1321 // Convert base26 column string to number
1322 $chars = split('', $col);
1323 $expn = 0;
1324 $col = 0;
1326 while ($chars) {
1327 $char = array_pop($chars); // LS char first
1328 $col += (ord($char) -ord('A') +1) * pow(26,$expn);
1329 $expn++;
1332 // Convert 1-index to zero-index
1333 $row--;
1334 $col--;
1336 return(array($row, $col));
1340 * Based on the algorithm provided by Daniel Rentz of OpenOffice.
1342 * @access private
1343 * @param string $plaintext The password to be encoded in plaintext.
1344 * @return string The encoded password
1346 function _encodePassword($plaintext)
1348 $password = 0x0000;
1349 $i = 1; // char position
1351 // split the plain text password in its component characters
1352 $chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY);
1353 foreach ($chars as $char) {
1354 $value = ord($char) << $i; // shifted ASCII value
1355 $rotated_bits = $value >> 15; // rotated bits beyond bit 15
1356 $value &= 0x7fff; // first 15 bits
1357 $password ^= ($value | $rotated_bits);
1358 $i++;
1361 $password ^= strlen($plaintext);
1362 $password ^= 0xCE4B;
1364 return($password);
1368 * This method sets the properties for outlining and grouping. The defaults
1369 * correspond to Excel's defaults.
1371 * @param bool $visible
1372 * @param bool $symbols_below
1373 * @param bool $symbols_right
1374 * @param bool $auto_style
1376 function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false)
1378 $this->_outline_on = $visible;
1379 $this->_outline_below = $symbols_below;
1380 $this->_outline_right = $symbols_right;
1381 $this->_outline_style = $auto_style;
1383 // Ensure this is a boolean vale for Window2
1384 if ($this->_outline_on) {
1385 $this->_outline_on = 1;
1389 /******************************************************************************
1390 *******************************************************************************
1392 * BIFF RECORDS
1397 * Write a double to the specified row and column (zero indexed).
1398 * An integer can be written as a double. Excel will display an
1399 * integer. $format is optional.
1401 * Returns 0 : normal termination
1402 * -2 : row or column out of range
1404 * @access public
1405 * @param integer $row Zero indexed row
1406 * @param integer $col Zero indexed column
1407 * @param float $num The number to write
1408 * @param mixed $format The optional XF format
1409 * @return integer
1411 function writeNumber($row, $col, $num, $format = 0)
1413 $record = 0x0203; // Record identifier
1414 $length = 0x000E; // Number of bytes to follow
1416 $xf = $this->_XF($format); // The cell format
1418 // Check that row and col are valid and store max and min values
1419 if ($row >= $this->_xls_rowmax) {
1420 return(-2);
1422 if ($col >= $this->_xls_colmax) {
1423 return(-2);
1425 if ($row < $this->_dim_rowmin) {
1426 $this->_dim_rowmin = $row;
1428 if ($row > $this->_dim_rowmax) {
1429 $this->_dim_rowmax = $row;
1431 if ($col < $this->_dim_colmin) {
1432 $this->_dim_colmin = $col;
1434 if ($col > $this->_dim_colmax) {
1435 $this->_dim_colmax = $col;
1438 $header = pack("vv", $record, $length);
1439 $data = pack("vvv", $row, $col, $xf);
1440 $xl_double = pack("d", $num);
1441 if ($this->_byte_order) { // if it's Big Endian
1442 $xl_double = strrev($xl_double);
1445 $this->_append($header.$data.$xl_double);
1446 return(0);
1450 * Write a string to the specified row and column (zero indexed).
1451 * NOTE: there is an Excel 5 defined limit of 255 characters.
1452 * $format is optional.
1453 * Returns 0 : normal termination
1454 * -2 : row or column out of range
1455 * -3 : long string truncated to 255 chars
1457 * @access public
1458 * @param integer $row Zero indexed row
1459 * @param integer $col Zero indexed column
1460 * @param string $str The string to write
1461 * @param mixed $format The XF format for the cell
1462 * @return integer
1464 function writeString($row, $col, $str, $format = 0)
1466 if ($this->_BIFF_version == 0x0600) {
1467 return $this->writeStringBIFF8($row, $col, $str, $format);
1469 $strlen = strlen($str);
1470 $record = 0x0204; // Record identifier
1471 $length = 0x0008 + $strlen; // Bytes to follow
1472 $xf = $this->_XF($format); // The cell format
1474 $str_error = 0;
1476 // Check that row and col are valid and store max and min values
1477 if ($row >= $this->_xls_rowmax) {
1478 return(-2);
1480 if ($col >= $this->_xls_colmax) {
1481 return(-2);
1483 if ($row < $this->_dim_rowmin) {
1484 $this->_dim_rowmin = $row;
1486 if ($row > $this->_dim_rowmax) {
1487 $this->_dim_rowmax = $row;
1489 if ($col < $this->_dim_colmin) {
1490 $this->_dim_colmin = $col;
1492 if ($col > $this->_dim_colmax) {
1493 $this->_dim_colmax = $col;
1496 if ($strlen > $this->_xls_strmax) { // LABEL must be < 255 chars
1497 $str = substr($str, 0, $this->_xls_strmax);
1498 $length = 0x0008 + $this->_xls_strmax;
1499 $strlen = $this->_xls_strmax;
1500 $str_error = -3;
1503 $header = pack("vv", $record, $length);
1504 $data = pack("vvvv", $row, $col, $xf, $strlen);
1505 $this->_append($header . $data . $str);
1506 return($str_error);
1510 * Sets Input Encoding for writing strings
1512 * @access public
1513 * @param string $encoding The encoding. Ex: 'UTF-16LE', 'utf-8', 'ISO-859-7'
1515 function setInputEncoding($encoding)
1517 if ($encoding != 'UTF-16LE' && !function_exists('iconv')) {
1518 $this->raiseError("Using an input encoding other than UTF-16LE requires PHP support for iconv");
1520 $this->_input_encoding = $encoding;
1524 * Write a string to the specified row and column (zero indexed).
1525 * This is the BIFF8 version (no 255 chars limit).
1526 * $format is optional.
1527 * Returns 0 : normal termination
1528 * -2 : row or column out of range
1529 * -3 : long string truncated to 255 chars
1531 * @access public
1532 * @param integer $row Zero indexed row
1533 * @param integer $col Zero indexed column
1534 * @param string $str The string to write
1535 * @param mixed $format The XF format for the cell
1536 * @return integer
1538 function writeStringBIFF8($row, $col, $str, $format = 0)
1540 if ($this->_input_encoding == 'UTF-16LE')
1542 $strlen = function_exists('mb_strlen') ? mb_strlen($str, 'UTF-16LE') : (strlen($str) / 2);
1543 $encoding = 0x1;
1545 elseif ($this->_input_encoding != '')
1547 $str = iconv($this->_input_encoding, 'UTF-16LE', $str);
1548 $strlen = function_exists('mb_strlen') ? mb_strlen($str, 'UTF-16LE') : (strlen($str) / 2);
1549 $encoding = 0x1;
1551 else
1553 $strlen = strlen($str);
1554 $encoding = 0x0;
1556 $record = 0x00FD; // Record identifier
1557 $length = 0x000A; // Bytes to follow
1558 $xf = $this->_XF($format); // The cell format
1560 $str_error = 0;
1562 // Check that row and col are valid and store max and min values
1563 if ($this->_checkRowCol($row, $col) == false) {
1564 return -2;
1567 $str = pack('vC', $strlen, $encoding).$str;
1569 /* check if string is already present */
1570 if (!isset($this->_str_table[$str])) {
1571 $this->_str_table[$str] = $this->_str_unique++;
1573 $this->_str_total++;
1575 $header = pack('vv', $record, $length);
1576 $data = pack('vvvV', $row, $col, $xf, $this->_str_table[$str]);
1577 $this->_append($header.$data);
1578 return $str_error;
1582 * Check row and col before writing to a cell, and update the sheet's
1583 * dimensions accordingly
1585 * @access private
1586 * @param integer $row Zero indexed row
1587 * @param integer $col Zero indexed column
1588 * @return boolean true for success, false if row and/or col are grester
1589 * then maximums allowed.
1591 function _checkRowCol($row, $col)
1593 if ($row >= $this->_xls_rowmax) {
1594 return false;
1596 if ($col >= $this->_xls_colmax) {
1597 return false;
1599 if ($row < $this->_dim_rowmin) {
1600 $this->_dim_rowmin = $row;
1602 if ($row > $this->_dim_rowmax) {
1603 $this->_dim_rowmax = $row;
1605 if ($col < $this->_dim_colmin) {
1606 $this->_dim_colmin = $col;
1608 if ($col > $this->_dim_colmax) {
1609 $this->_dim_colmax = $col;
1611 return true;
1615 * Writes a note associated with the cell given by the row and column.
1616 * NOTE records don't have a length limit.
1618 * @access public
1619 * @param integer $row Zero indexed row
1620 * @param integer $col Zero indexed column
1621 * @param string $note The note to write
1623 function writeNote($row, $col, $note)
1625 $note_length = strlen($note);
1626 $record = 0x001C; // Record identifier
1627 $max_length = 2048; // Maximun length for a NOTE record
1628 //$length = 0x0006 + $note_length; // Bytes to follow
1630 // Check that row and col are valid and store max and min values
1631 if ($row >= $this->_xls_rowmax) {
1632 return(-2);
1634 if ($col >= $this->_xls_colmax) {
1635 return(-2);
1637 if ($row < $this->_dim_rowmin) {
1638 $this->_dim_rowmin = $row;
1640 if ($row > $this->_dim_rowmax) {
1641 $this->_dim_rowmax = $row;
1643 if ($col < $this->_dim_colmin) {
1644 $this->_dim_colmin = $col;
1646 if ($col > $this->_dim_colmax) {
1647 $this->_dim_colmax = $col;
1650 // Length for this record is no more than 2048 + 6
1651 $length = 0x0006 + min($note_length, 2048);
1652 $header = pack("vv", $record, $length);
1653 $data = pack("vvv", $row, $col, $note_length);
1654 $this->_append($header . $data . substr($note, 0, 2048));
1656 for ($i = $max_length; $i < $note_length; $i += $max_length) {
1657 $chunk = substr($note, $i, $max_length);
1658 $length = 0x0006 + strlen($chunk);
1659 $header = pack("vv", $record, $length);
1660 $data = pack("vvv", -1, 0, strlen($chunk));
1661 $this->_append($header.$data.$chunk);
1663 return(0);
1667 * Write a blank cell to the specified row and column (zero indexed).
1668 * A blank cell is used to specify formatting without adding a string
1669 * or a number.
1671 * A blank cell without a format serves no purpose. Therefore, we don't write
1672 * a BLANK record unless a format is specified.
1674 * Returns 0 : normal termination (including no format)
1675 * -1 : insufficient number of arguments
1676 * -2 : row or column out of range
1678 * @access public
1679 * @param integer $row Zero indexed row
1680 * @param integer $col Zero indexed column
1681 * @param mixed $format The XF format
1683 function writeBlank($row, $col, $format)
1685 // Don't write a blank cell unless it has a format
1686 if (!is_object($format)) {
1687 return(0);
1690 $record = 0x0201; // Record identifier
1691 $length = 0x0006; // Number of bytes to follow
1692 $xf = $this->_XF($format); // The cell format
1694 // Check that row and col are valid and store max and min values
1695 if ($row >= $this->_xls_rowmax) {
1696 return(-2);
1698 if ($col >= $this->_xls_colmax) {
1699 return(-2);
1701 if ($row < $this->_dim_rowmin) {
1702 $this->_dim_rowmin = $row;
1704 if ($row > $this->_dim_rowmax) {
1705 $this->_dim_rowmax = $row;
1707 if ($col < $this->_dim_colmin) {
1708 $this->_dim_colmin = $col;
1710 if ($col > $this->_dim_colmax) {
1711 $this->_dim_colmax = $col;
1714 $header = pack("vv", $record, $length);
1715 $data = pack("vvv", $row, $col, $xf);
1716 $this->_append($header . $data);
1717 return 0;
1721 * Write a formula to the specified row and column (zero indexed).
1722 * The textual representation of the formula is passed to the parser in
1723 * Parser.php which returns a packed binary string.
1725 * Returns 0 : normal termination
1726 * -1 : formula errors (bad formula)
1727 * -2 : row or column out of range
1729 * @access public
1730 * @param integer $row Zero indexed row
1731 * @param integer $col Zero indexed column
1732 * @param string $formula The formula text string
1733 * @param mixed $format The optional XF format
1734 * @return integer
1736 function writeFormula($row, $col, $formula, $format = 0)
1738 $record = 0x0006; // Record identifier
1740 // Excel normally stores the last calculated value of the formula in $num.
1741 // Clearly we are not in a position to calculate this a priori. Instead
1742 // we set $num to zero and set the option flags in $grbit to ensure
1743 // automatic calculation of the formula when the file is opened.
1745 $xf = $this->_XF($format); // The cell format
1746 $num = 0x00; // Current value of formula
1747 $grbit = 0x03; // Option flags
1748 $unknown = 0x0000; // Must be zero
1751 // Check that row and col are valid and store max and min values
1752 if ($this->_checkRowCol($row, $col) == false) {
1753 return -2;
1756 // Strip the '=' or '@' sign at the beginning of the formula string
1757 if (preg_match("/^=/", $formula)) {
1758 $formula = preg_replace("/(^=)/", "", $formula);
1759 } elseif (preg_match("/^@/", $formula)) {
1760 $formula = preg_replace("/(^@)/", "", $formula);
1761 } else {
1762 // Error handling
1763 $this->writeString($row, $col, 'Unrecognised character for formula');
1764 return -1;
1767 // Parse the formula using the parser in Parser.php
1768 $error = $this->_parser->parse($formula);
1769 if ($this->isError($error)) {
1770 $this->writeString($row, $col, $error->getMessage());
1771 return -1;
1774 $formula = $this->_parser->toReversePolish();
1775 if ($this->isError($formula)) {
1776 $this->writeString($row, $col, $formula->getMessage());
1777 return -1;
1780 $formlen = strlen($formula); // Length of the binary string
1781 $length = 0x16 + $formlen; // Length of the record data
1783 $header = pack("vv", $record, $length);
1784 $data = pack("vvvdvVv", $row, $col, $xf, $num,
1785 $grbit, $unknown, $formlen);
1787 $this->_append($header . $data . $formula);
1788 return 0;
1792 * Write a hyperlink.
1793 * This is comprised of two elements: the visible label and
1794 * the invisible link. The visible label is the same as the link unless an
1795 * alternative string is specified. The label is written using the
1796 * writeString() method. Therefore the 255 characters string limit applies.
1797 * $string and $format are optional.
1799 * The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
1800 * directory url.
1802 * Returns 0 : normal termination
1803 * -2 : row or column out of range
1804 * -3 : long string truncated to 255 chars
1806 * @access public
1807 * @param integer $row Row
1808 * @param integer $col Column
1809 * @param string $url URL string
1810 * @param string $string Alternative label
1811 * @param mixed $format The cell format
1812 * @return integer
1814 function writeUrl($row, $col, $url, $string = '', $format = 0)
1816 // Add start row and col to arg list
1817 return($this->_writeUrlRange($row, $col, $row, $col, $url, $string, $format));
1821 * This is the more general form of writeUrl(). It allows a hyperlink to be
1822 * written to a range of cells. This function also decides the type of hyperlink
1823 * to be written. These are either, Web (http, ftp, mailto), Internal
1824 * (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
1826 * @access private
1827 * @see writeUrl()
1828 * @param integer $row1 Start row
1829 * @param integer $col1 Start column
1830 * @param integer $row2 End row
1831 * @param integer $col2 End column
1832 * @param string $url URL string
1833 * @param string $string Alternative label
1834 * @param mixed $format The cell format
1835 * @return integer
1838 function _writeUrlRange($row1, $col1, $row2, $col2, $url, $string = '', $format = 0)
1841 // Check for internal/external sheet links or default to web link
1842 if (preg_match('[^internal:]', $url)) {
1843 return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url, $string, $format));
1845 if (preg_match('[^external:]', $url)) {
1846 return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url, $string, $format));
1848 return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url, $string, $format));
1853 * Used to write http, ftp and mailto hyperlinks.
1854 * The link type ($options) is 0x03 is the same as absolute dir ref without
1855 * sheet. However it is differentiated by the $unknown2 data stream.
1857 * @access private
1858 * @see writeUrl()
1859 * @param integer $row1 Start row
1860 * @param integer $col1 Start column
1861 * @param integer $row2 End row
1862 * @param integer $col2 End column
1863 * @param string $url URL string
1864 * @param string $str Alternative label
1865 * @param mixed $format The cell format
1866 * @return integer
1868 function _writeUrlWeb($row1, $col1, $row2, $col2, $url, $str, $format = 0)
1870 $record = 0x01B8; // Record identifier
1871 $length = 0x00000; // Bytes to follow
1873 if (!is_object($format)) {
1874 $format = $this->_url_format;
1877 // Write the visible label using the writeString() method.
1878 if ($str == '') {
1879 $str = $url;
1881 $str_error = $this->writeString($row1, $col1, $str, $format);
1882 if (($str_error == -2) || ($str_error == -3)) {
1883 return $str_error;
1886 // Pack the undocumented parts of the hyperlink stream
1887 $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
1888 $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
1890 // Pack the option flags
1891 $options = pack("V", 0x03);
1893 // Convert URL to a null terminated wchar string
1894 $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
1895 $url = $url . "\0\0\0";
1897 // Pack the length of the URL
1898 $url_len = pack("V", strlen($url));
1900 // Calculate the data length
1901 $length = 0x34 + strlen($url);
1903 // Pack the header data
1904 $header = pack("vv", $record, $length);
1905 $data = pack("vvvv", $row1, $row2, $col1, $col2);
1907 // Write the packed data
1908 $this->_append($header . $data .
1909 $unknown1 . $options .
1910 $unknown2 . $url_len . $url);
1911 return($str_error);
1915 * Used to write internal reference hyperlinks such as "Sheet1!A1".
1917 * @access private
1918 * @see writeUrl()
1919 * @param integer $row1 Start row
1920 * @param integer $col1 Start column
1921 * @param integer $row2 End row
1922 * @param integer $col2 End column
1923 * @param string $url URL string
1924 * @param string $str Alternative label
1925 * @param mixed $format The cell format
1926 * @return integer
1928 function _writeUrlInternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
1930 $record = 0x01B8; // Record identifier
1931 $length = 0x00000; // Bytes to follow
1933 if (!is_object($format)) {
1934 $format = $this->_url_format;
1937 // Strip URL type
1938 $url = preg_replace('/^internal:/', '', $url);
1940 // Write the visible label
1941 if ($str == '') {
1942 $str = $url;
1944 $str_error = $this->writeString($row1, $col1, $str, $format);
1945 if (($str_error == -2) || ($str_error == -3)) {
1946 return $str_error;
1949 // Pack the undocumented parts of the hyperlink stream
1950 $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
1952 // Pack the option flags
1953 $options = pack("V", 0x08);
1955 // Convert the URL type and to a null terminated wchar string
1956 $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
1957 $url = $url . "\0\0\0";
1959 // Pack the length of the URL as chars (not wchars)
1960 $url_len = pack("V", floor(strlen($url)/2));
1962 // Calculate the data length
1963 $length = 0x24 + strlen($url);
1965 // Pack the header data
1966 $header = pack("vv", $record, $length);
1967 $data = pack("vvvv", $row1, $row2, $col1, $col2);
1969 // Write the packed data
1970 $this->_append($header . $data .
1971 $unknown1 . $options .
1972 $url_len . $url);
1973 return($str_error);
1977 * Write links to external directory names such as 'c:\foo.xls',
1978 * c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
1980 * Note: Excel writes some relative links with the $dir_long string. We ignore
1981 * these cases for the sake of simpler code.
1983 * @access private
1984 * @see writeUrl()
1985 * @param integer $row1 Start row
1986 * @param integer $col1 Start column
1987 * @param integer $row2 End row
1988 * @param integer $col2 End column
1989 * @param string $url URL string
1990 * @param string $str Alternative label
1991 * @param mixed $format The cell format
1992 * @return integer
1994 function _writeUrlExternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
1996 // Network drives are different. We will handle them separately
1997 // MS/Novell network drives and shares start with \\
1998 if (preg_match('[^external:\\\\]', $url)) {
1999 return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
2002 $record = 0x01B8; // Record identifier
2003 $length = 0x00000; // Bytes to follow
2005 if (!is_object($format)) {
2006 $format = $this->_url_format;
2009 // Strip URL type and change Unix dir separator to Dos style (if needed)
2011 $url = preg_replace('/^external:/', '', $url);
2012 $url = preg_replace('/\//', "\\", $url);
2014 // Write the visible label
2015 if ($str == '') {
2016 $str = preg_replace('/\#/', ' - ', $url);
2018 $str_error = $this->writeString($row1, $col1, $str, $format);
2019 if (($str_error == -2) or ($str_error == -3)) {
2020 return $str_error;
2023 // Determine if the link is relative or absolute:
2024 // relative if link contains no dir separator, "somefile.xls"
2025 // relative if link starts with up-dir, "..\..\somefile.xls"
2026 // otherwise, absolute
2028 $absolute = 0x02; // Bit mask
2029 if (!preg_match("/\\\/", $url)) {
2030 $absolute = 0x00;
2032 if (preg_match("/^\.\.\\\/", $url)) {
2033 $absolute = 0x00;
2035 $link_type = 0x01 | $absolute;
2037 // Determine if the link contains a sheet reference and change some of the
2038 // parameters accordingly.
2039 // Split the dir name and sheet name (if it exists)
2040 /*if (preg_match("/\#/", $url)) {
2041 list($dir_long, $sheet) = split("\#", $url);
2042 } else {
2043 $dir_long = $url;
2046 if (isset($sheet)) {
2047 $link_type |= 0x08;
2048 $sheet_len = pack("V", strlen($sheet) + 0x01);
2049 $sheet = join("\0", split('', $sheet));
2050 $sheet .= "\0\0\0";
2051 } else {
2052 $sheet_len = '';
2053 $sheet = '';
2055 $dir_long = $url;
2056 if (preg_match("/\#/", $url)) {
2057 $link_type |= 0x08;
2062 // Pack the link type
2063 $link_type = pack("V", $link_type);
2065 // Calculate the up-level dir count e.g.. (..\..\..\ == 3)
2066 $up_count = preg_match_all("/\.\.\\\/", $dir_long, $useless);
2067 $up_count = pack("v", $up_count);
2069 // Store the short dos dir name (null terminated)
2070 $dir_short = preg_replace("/\.\.\\\/", '', $dir_long) . "\0";
2072 // Store the long dir name as a wchar string (non-null terminated)
2073 //$dir_long = join("\0", split('', $dir_long));
2074 $dir_long = $dir_long . "\0";
2076 // Pack the lengths of the dir strings
2077 $dir_short_len = pack("V", strlen($dir_short) );
2078 $dir_long_len = pack("V", strlen($dir_long) );
2079 $stream_len = pack("V", 0);//strlen($dir_long) + 0x06);
2081 // Pack the undocumented parts of the hyperlink stream
2082 $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' );
2083 $unknown2 = pack("H*",'0303000000000000C000000000000046' );
2084 $unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000');
2085 $unknown4 = pack("v", 0x03 );
2087 // Pack the main data stream
2088 $data = pack("vvvv", $row1, $row2, $col1, $col2) .
2089 $unknown1 .
2090 $link_type .
2091 $unknown2 .
2092 $up_count .
2093 $dir_short_len.
2094 $dir_short .
2095 $unknown3 .
2096 $stream_len ;/*.
2097 $dir_long_len .
2098 $unknown4 .
2099 $dir_long .
2100 $sheet_len .
2101 $sheet ;*/
2103 // Pack the header data
2104 $length = strlen($data);
2105 $header = pack("vv", $record, $length);
2107 // Write the packed data
2108 $this->_append($header. $data);
2109 return($str_error);
2114 * This method is used to set the height and format for a row.
2116 * @access public
2117 * @param integer $row The row to set
2118 * @param integer $height Height we are giving to the row.
2119 * Use null to set XF without setting height
2120 * @param mixed $format XF format we are giving to the row
2121 * @param bool $hidden The optional hidden attribute
2122 * @param integer $level The optional outline level for row, in range [0,7]
2124 function setRow($row, $height, $format = 0, $hidden = false, $level = 0)
2126 $record = 0x0208; // Record identifier
2127 $length = 0x0010; // Number of bytes to follow
2129 $colMic = 0x0000; // First defined column
2130 $colMac = 0x0000; // Last defined column
2131 $irwMac = 0x0000; // Used by Excel to optimise loading
2132 $reserved = 0x0000; // Reserved
2133 $grbit = 0x0000; // Option flags
2134 $ixfe = $this->_XF($format); // XF index
2136 // set _row_sizes so _sizeRow() can use it
2137 $this->_row_sizes[$row] = $height;
2139 // Use setRow($row, null, $XF) to set XF format without setting height
2140 if ($height != null) {
2141 $miyRw = $height * 20; // row height
2142 } else {
2143 $miyRw = 0xff; // default row height is 256
2146 $level = max(0, min($level, 7)); // level should be between 0 and 7
2147 $this->_outline_row_level = max($level, $this->_outline_row_level);
2150 // Set the options flags. fUnsynced is used to show that the font and row
2151 // heights are not compatible. This is usually the case for WriteExcel.
2152 // The collapsed flag 0x10 doesn't seem to be used to indicate that a row
2153 // is collapsed. Instead it is used to indicate that the previous row is
2154 // collapsed. The zero height flag, 0x20, is used to collapse a row.
2156 $grbit |= $level;
2157 if ($hidden) {
2158 $grbit |= 0x0020;
2160 $grbit |= 0x0040; // fUnsynced
2161 if (is_object($format)) {
2162 $grbit |= 0x0080;
2164 $grbit |= 0x0100;
2166 $header = pack("vv", $record, $length);
2167 $data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw,
2168 $irwMac,$reserved, $grbit, $ixfe);
2169 $this->_append($header.$data);
2173 * Writes Excel DIMENSIONS to define the area in which there is data.
2175 * @access private
2177 function _storeDimensions()
2179 $record = 0x0200; // Record identifier
2180 $row_min = $this->_dim_rowmin; // First row
2181 $row_max = $this->_dim_rowmax + 1; // Last row plus 1
2182 $col_min = $this->_dim_colmin; // First column
2183 $col_max = $this->_dim_colmax + 1; // Last column plus 1
2184 $reserved = 0x0000; // Reserved by Excel
2186 if ($this->_BIFF_version == 0x0500) {
2187 $length = 0x000A; // Number of bytes to follow
2188 $data = pack("vvvvv", $row_min, $row_max,
2189 $col_min, $col_max, $reserved);
2190 } elseif ($this->_BIFF_version == 0x0600) {
2191 $length = 0x000E;
2192 $data = pack("VVvvv", $row_min, $row_max,
2193 $col_min, $col_max, $reserved);
2195 $header = pack("vv", $record, $length);
2196 $this->_prepend($header.$data);
2200 * Write BIFF record Window2.
2202 * @access private
2204 function _storeWindow2()
2206 $record = 0x023E; // Record identifier
2207 if ($this->_BIFF_version == 0x0500) {
2208 $length = 0x000A; // Number of bytes to follow
2209 } elseif ($this->_BIFF_version == 0x0600) {
2210 $length = 0x0012;
2213 $grbit = 0x00B6; // Option flags
2214 $rwTop = 0x0000; // Top row visible in window
2215 $colLeft = 0x0000; // Leftmost column visible in window
2218 // The options flags that comprise $grbit
2219 $fDspFmla = 0; // 0 - bit
2220 $fDspGrid = $this->_screen_gridlines; // 1
2221 $fDspRwCol = 1; // 2
2222 $fFrozen = $this->_frozen; // 3
2223 $fDspZeros = 1; // 4
2224 $fDefaultHdr = 1; // 5
2225 $fArabic = 0; // 6
2226 $fDspGuts = $this->_outline_on; // 7
2227 $fFrozenNoSplit = 0; // 0 - bit
2228 $fSelected = $this->selected; // 1
2229 $fPaged = 1; // 2
2231 $grbit = $fDspFmla;
2232 $grbit |= $fDspGrid << 1;
2233 $grbit |= $fDspRwCol << 2;
2234 $grbit |= $fFrozen << 3;
2235 $grbit |= $fDspZeros << 4;
2236 $grbit |= $fDefaultHdr << 5;
2237 $grbit |= $fArabic << 6;
2238 $grbit |= $fDspGuts << 7;
2239 $grbit |= $fFrozenNoSplit << 8;
2240 $grbit |= $fSelected << 9;
2241 $grbit |= $fPaged << 10;
2243 $header = pack("vv", $record, $length);
2244 $data = pack("vvv", $grbit, $rwTop, $colLeft);
2245 // FIXME !!!
2246 if ($this->_BIFF_version == 0x0500) {
2247 $rgbHdr = 0x00000000; // Row/column heading and gridline color
2248 $data .= pack("V", $rgbHdr);
2249 } elseif ($this->_BIFF_version == 0x0600) {
2250 $rgbHdr = 0x0040; // Row/column heading and gridline color index
2251 $zoom_factor_page_break = 0x0000;
2252 $zoom_factor_normal = 0x0000;
2253 $data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000);
2255 $this->_append($header.$data);
2259 * Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
2261 * @access private
2263 function _storeDefcol()
2265 $record = 0x0055; // Record identifier
2266 $length = 0x0002; // Number of bytes to follow
2267 $colwidth = 0x0008; // Default column width
2269 $header = pack("vv", $record, $length);
2270 $data = pack("v", $colwidth);
2271 $this->_prepend($header . $data);
2275 * Write BIFF record COLINFO to define column widths
2277 * Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
2278 * length record.
2280 * @access private
2281 * @param array $col_array This is the only parameter received and is composed of the following:
2282 * 0 => First formatted column,
2283 * 1 => Last formatted column,
2284 * 2 => Col width (8.43 is Excel default),
2285 * 3 => The optional XF format of the column,
2286 * 4 => Option flags.
2287 * 5 => Optional outline level
2289 function _storeColinfo($col_array)
2291 if (isset($col_array[0])) {
2292 $colFirst = $col_array[0];
2294 if (isset($col_array[1])) {
2295 $colLast = $col_array[1];
2297 if (isset($col_array[2])) {
2298 $coldx = $col_array[2];
2299 } else {
2300 $coldx = 8.43;
2302 if (isset($col_array[3])) {
2303 $format = $col_array[3];
2304 } else {
2305 $format = 0;
2307 if (isset($col_array[4])) {
2308 $grbit = $col_array[4];
2309 } else {
2310 $grbit = 0;
2312 if (isset($col_array[5])) {
2313 $level = $col_array[5];
2314 } else {
2315 $level = 0;
2317 $record = 0x007D; // Record identifier
2318 $length = 0x000B; // Number of bytes to follow
2320 $coldx += 0.72; // Fudge. Excel subtracts 0.72 !?
2321 $coldx *= 256; // Convert to units of 1/256 of a char
2323 $ixfe = $this->_XF($format);
2324 $reserved = 0x00; // Reserved
2326 $level = max(0, min($level, 7));
2327 $grbit |= $level << 8;
2329 $header = pack("vv", $record, $length);
2330 $data = pack("vvvvvC", $colFirst, $colLast, $coldx,
2331 $ixfe, $grbit, $reserved);
2332 $this->_prepend($header.$data);
2336 * Write BIFF record SELECTION.
2338 * @access private
2339 * @param array $array array containing ($rwFirst,$colFirst,$rwLast,$colLast)
2340 * @see setSelection()
2342 function _storeSelection($array)
2344 list($rwFirst,$colFirst,$rwLast,$colLast) = $array;
2345 $record = 0x001D; // Record identifier
2346 $length = 0x000F; // Number of bytes to follow
2348 $pnn = $this->_active_pane; // Pane position
2349 $rwAct = $rwFirst; // Active row
2350 $colAct = $colFirst; // Active column
2351 $irefAct = 0; // Active cell ref
2352 $cref = 1; // Number of refs
2354 if (!isset($rwLast)) {
2355 $rwLast = $rwFirst; // Last row in reference
2357 if (!isset($colLast)) {
2358 $colLast = $colFirst; // Last col in reference
2361 // Swap last row/col for first row/col as necessary
2362 if ($rwFirst > $rwLast) {
2363 list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
2366 if ($colFirst > $colLast) {
2367 list($colFirst, $colLast) = array($colLast, $colFirst);
2370 $header = pack("vv", $record, $length);
2371 $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct,
2372 $irefAct, $cref,
2373 $rwFirst, $rwLast,
2374 $colFirst, $colLast);
2375 $this->_append($header . $data);
2379 * Store the MERGEDCELLS record for all ranges of merged cells
2381 * @access private
2383 function _storeMergedCells()
2385 // if there are no merged cell ranges set, return
2386 if (count($this->_merged_ranges) == 0) {
2387 return;
2389 $record = 0x00E5;
2390 $length = 2 + count($this->_merged_ranges) * 8;
2392 $header = pack('vv', $record, $length);
2393 $data = pack('v', count($this->_merged_ranges));
2394 foreach ($this->_merged_ranges as $range) {
2395 $data .= pack('vvvv', $range[0], $range[2], $range[1], $range[3]);
2397 $this->_append($header . $data);
2401 * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
2402 * references in a worksheet.
2404 * Excel only stores references to external sheets that are used in formulas.
2405 * For simplicity we store references to all the sheets in the workbook
2406 * regardless of whether they are used or not. This reduces the overall
2407 * complexity and eliminates the need for a two way dialogue between the formula
2408 * parser the worksheet objects.
2410 * @access private
2411 * @param integer $count The number of external sheet references in this worksheet
2413 function _storeExterncount($count)
2415 $record = 0x0016; // Record identifier
2416 $length = 0x0002; // Number of bytes to follow
2418 $header = pack("vv", $record, $length);
2419 $data = pack("v", $count);
2420 $this->_prepend($header . $data);
2424 * Writes the Excel BIFF EXTERNSHEET record. These references are used by
2425 * formulas. A formula references a sheet name via an index. Since we store a
2426 * reference to all of the external worksheets the EXTERNSHEET index is the same
2427 * as the worksheet index.
2429 * @access private
2430 * @param string $sheetname The name of a external worksheet
2432 function _storeExternsheet($sheetname)
2434 $record = 0x0017; // Record identifier
2436 // References to the current sheet are encoded differently to references to
2437 // external sheets.
2439 if ($this->name == $sheetname) {
2440 $sheetname = '';
2441 $length = 0x02; // The following 2 bytes
2442 $cch = 1; // The following byte
2443 $rgch = 0x02; // Self reference
2444 } else {
2445 $length = 0x02 + strlen($sheetname);
2446 $cch = strlen($sheetname);
2447 $rgch = 0x03; // Reference to a sheet in the current workbook
2450 $header = pack("vv", $record, $length);
2451 $data = pack("CC", $cch, $rgch);
2452 $this->_prepend($header . $data . $sheetname);
2456 * Writes the Excel BIFF PANE record.
2457 * The panes can either be frozen or thawed (unfrozen).
2458 * Frozen panes are specified in terms of an integer number of rows and columns.
2459 * Thawed panes are specified in terms of Excel's units for rows and columns.
2461 * @access private
2462 * @param array $panes This is the only parameter received and is composed of the following:
2463 * 0 => Vertical split position,
2464 * 1 => Horizontal split position
2465 * 2 => Top row visible
2466 * 3 => Leftmost column visible
2467 * 4 => Active pane
2469 function _storePanes($panes)
2471 $y = $panes[0];
2472 $x = $panes[1];
2473 $rwTop = $panes[2];
2474 $colLeft = $panes[3];
2475 if (count($panes) > 4) { // if Active pane was received
2476 $pnnAct = $panes[4];
2477 } else {
2478 $pnnAct = null;
2480 $record = 0x0041; // Record identifier
2481 $length = 0x000A; // Number of bytes to follow
2483 // Code specific to frozen or thawed panes.
2484 if ($this->_frozen) {
2485 // Set default values for $rwTop and $colLeft
2486 if (!isset($rwTop)) {
2487 $rwTop = $y;
2489 if (!isset($colLeft)) {
2490 $colLeft = $x;
2492 } else {
2493 // Set default values for $rwTop and $colLeft
2494 if (!isset($rwTop)) {
2495 $rwTop = 0;
2497 if (!isset($colLeft)) {
2498 $colLeft = 0;
2501 // Convert Excel's row and column units to the internal units.
2502 // The default row height is 12.75
2503 // The default column width is 8.43
2504 // The following slope and intersection values were interpolated.
2506 $y = 20*$y + 255;
2507 $x = 113.879*$x + 390;
2511 // Determine which pane should be active. There is also the undocumented
2512 // option to override this should it be necessary: may be removed later.
2514 if (!isset($pnnAct)) {
2515 if ($x != 0 && $y != 0) {
2516 $pnnAct = 0; // Bottom right
2518 if ($x != 0 && $y == 0) {
2519 $pnnAct = 1; // Top right
2521 if ($x == 0 && $y != 0) {
2522 $pnnAct = 2; // Bottom left
2524 if ($x == 0 && $y == 0) {
2525 $pnnAct = 3; // Top left
2529 $this->_active_pane = $pnnAct; // Used in _storeSelection
2531 $header = pack("vv", $record, $length);
2532 $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
2533 $this->_append($header . $data);
2537 * Store the page setup SETUP BIFF record.
2539 * @access private
2541 function _storeSetup()
2543 $record = 0x00A1; // Record identifier
2544 $length = 0x0022; // Number of bytes to follow
2546 $iPaperSize = $this->_paper_size; // Paper size
2547 $iScale = $this->_print_scale; // Print scaling factor
2548 $iPageStart = 0x01; // Starting page number
2549 $iFitWidth = $this->_fit_width; // Fit to number of pages wide
2550 $iFitHeight = $this->_fit_height; // Fit to number of pages high
2551 $grbit = 0x00; // Option flags
2552 $iRes = 0x0258; // Print resolution
2553 $iVRes = 0x0258; // Vertical print resolution
2554 $numHdr = $this->_margin_head; // Header Margin
2555 $numFtr = $this->_margin_foot; // Footer Margin
2556 $iCopies = 0x01; // Number of copies
2558 $fLeftToRight = 0x0; // Print over then down
2559 $fLandscape = $this->_orientation; // Page orientation
2560 $fNoPls = 0x0; // Setup not read from printer
2561 $fNoColor = 0x0; // Print black and white
2562 $fDraft = 0x0; // Print draft quality
2563 $fNotes = 0x0; // Print notes
2564 $fNoOrient = 0x0; // Orientation not set
2565 $fUsePage = 0x0; // Use custom starting page
2567 $grbit = $fLeftToRight;
2568 $grbit |= $fLandscape << 1;
2569 $grbit |= $fNoPls << 2;
2570 $grbit |= $fNoColor << 3;
2571 $grbit |= $fDraft << 4;
2572 $grbit |= $fNotes << 5;
2573 $grbit |= $fNoOrient << 6;
2574 $grbit |= $fUsePage << 7;
2576 $numHdr = pack("d", $numHdr);
2577 $numFtr = pack("d", $numFtr);
2578 if ($this->_byte_order) { // if it's Big Endian
2579 $numHdr = strrev($numHdr);
2580 $numFtr = strrev($numFtr);
2583 $header = pack("vv", $record, $length);
2584 $data1 = pack("vvvvvvvv", $iPaperSize,
2585 $iScale,
2586 $iPageStart,
2587 $iFitWidth,
2588 $iFitHeight,
2589 $grbit,
2590 $iRes,
2591 $iVRes);
2592 $data2 = $numHdr.$numFtr;
2593 $data3 = pack("v", $iCopies);
2594 $this->_prepend($header . $data1 . $data2 . $data3);
2598 * Store the header caption BIFF record.
2600 * @access private
2602 function _storeHeader()
2604 $record = 0x0014; // Record identifier
2606 $str = $this->_header; // header string
2607 $cch = strlen($str); // Length of header string
2608 if ($this->_BIFF_version == 0x0600) {
2609 $encoding = 0x0; // TODO: Unicode support
2610 $length = 3 + $cch; // Bytes to follow
2611 } else {
2612 $length = 1 + $cch; // Bytes to follow
2615 $header = pack("vv", $record, $length);
2616 if ($this->_BIFF_version == 0x0600) {
2617 $data = pack("vC", $cch, $encoding);
2618 } else {
2619 $data = pack("C", $cch);
2622 $this->_prepend($header.$data.$str);
2626 * Store the footer caption BIFF record.
2628 * @access private
2630 function _storeFooter()
2632 $record = 0x0015; // Record identifier
2634 $str = $this->_footer; // Footer string
2635 $cch = strlen($str); // Length of footer string
2636 if ($this->_BIFF_version == 0x0600) {
2637 $encoding = 0x0; // TODO: Unicode support
2638 $length = 3 + $cch; // Bytes to follow
2639 } else {
2640 $length = 1 + $cch;
2643 $header = pack("vv", $record, $length);
2644 if ($this->_BIFF_version == 0x0600) {
2645 $data = pack("vC", $cch, $encoding);
2646 } else {
2647 $data = pack("C", $cch);
2650 $this->_prepend($header . $data . $str);
2654 * Store the horizontal centering HCENTER BIFF record.
2656 * @access private
2658 function _storeHcenter()
2660 $record = 0x0083; // Record identifier
2661 $length = 0x0002; // Bytes to follow
2663 $fHCenter = $this->_hcenter; // Horizontal centering
2665 $header = pack("vv", $record, $length);
2666 $data = pack("v", $fHCenter);
2668 $this->_prepend($header.$data);
2672 * Store the vertical centering VCENTER BIFF record.
2674 * @access private
2676 function _storeVcenter()
2678 $record = 0x0084; // Record identifier
2679 $length = 0x0002; // Bytes to follow
2681 $fVCenter = $this->_vcenter; // Horizontal centering
2683 $header = pack("vv", $record, $length);
2684 $data = pack("v", $fVCenter);
2685 $this->_prepend($header . $data);
2689 * Store the LEFTMARGIN BIFF record.
2691 * @access private
2693 function _storeMarginLeft()
2695 $record = 0x0026; // Record identifier
2696 $length = 0x0008; // Bytes to follow
2698 $margin = $this->_margin_left; // Margin in inches
2700 $header = pack("vv", $record, $length);
2701 $data = pack("d", $margin);
2702 if ($this->_byte_order) { // if it's Big Endian
2703 $data = strrev($data);
2706 $this->_prepend($header . $data);
2710 * Store the RIGHTMARGIN BIFF record.
2712 * @access private
2714 function _storeMarginRight()
2716 $record = 0x0027; // Record identifier
2717 $length = 0x0008; // Bytes to follow
2719 $margin = $this->_margin_right; // Margin in inches
2721 $header = pack("vv", $record, $length);
2722 $data = pack("d", $margin);
2723 if ($this->_byte_order) { // if it's Big Endian
2724 $data = strrev($data);
2727 $this->_prepend($header . $data);
2731 * Store the TOPMARGIN BIFF record.
2733 * @access private
2735 function _storeMarginTop()
2737 $record = 0x0028; // Record identifier
2738 $length = 0x0008; // Bytes to follow
2740 $margin = $this->_margin_top; // Margin in inches
2742 $header = pack("vv", $record, $length);
2743 $data = pack("d", $margin);
2744 if ($this->_byte_order) { // if it's Big Endian
2745 $data = strrev($data);
2748 $this->_prepend($header . $data);
2752 * Store the BOTTOMMARGIN BIFF record.
2754 * @access private
2756 function _storeMarginBottom()
2758 $record = 0x0029; // Record identifier
2759 $length = 0x0008; // Bytes to follow
2761 $margin = $this->_margin_bottom; // Margin in inches
2763 $header = pack("vv", $record, $length);
2764 $data = pack("d", $margin);
2765 if ($this->_byte_order) { // if it's Big Endian
2766 $data = strrev($data);
2769 $this->_prepend($header . $data);
2773 * Merges the area given by its arguments.
2774 * This is an Excel97/2000 method. It is required to perform more complicated
2775 * merging than the normal setAlign('merge').
2777 * @access public
2778 * @param integer $first_row First row of the area to merge
2779 * @param integer $first_col First column of the area to merge
2780 * @param integer $last_row Last row of the area to merge
2781 * @param integer $last_col Last column of the area to merge
2783 function mergeCells($first_row, $first_col, $last_row, $last_col)
2785 $record = 0x00E5; // Record identifier
2786 $length = 0x000A; // Bytes to follow
2787 $cref = 1; // Number of refs
2789 // Swap last row/col for first row/col as necessary
2790 if ($first_row > $last_row) {
2791 list($first_row, $last_row) = array($last_row, $first_row);
2794 if ($first_col > $last_col) {
2795 list($first_col, $last_col) = array($last_col, $first_col);
2798 $header = pack("vv", $record, $length);
2799 $data = pack("vvvvv", $cref, $first_row, $last_row,
2800 $first_col, $last_col);
2802 $this->_append($header.$data);
2806 * Write the PRINTHEADERS BIFF record.
2808 * @access private
2810 function _storePrintHeaders()
2812 $record = 0x002a; // Record identifier
2813 $length = 0x0002; // Bytes to follow
2815 $fPrintRwCol = $this->_print_headers; // Boolean flag
2817 $header = pack("vv", $record, $length);
2818 $data = pack("v", $fPrintRwCol);
2819 $this->_prepend($header . $data);
2823 * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
2824 * GRIDSET record.
2826 * @access private
2828 function _storePrintGridlines()
2830 $record = 0x002b; // Record identifier
2831 $length = 0x0002; // Bytes to follow
2833 $fPrintGrid = $this->_print_gridlines; // Boolean flag
2835 $header = pack("vv", $record, $length);
2836 $data = pack("v", $fPrintGrid);
2837 $this->_prepend($header . $data);
2841 * Write the GRIDSET BIFF record. Must be used in conjunction with the
2842 * PRINTGRIDLINES record.
2844 * @access private
2846 function _storeGridset()
2848 $record = 0x0082; // Record identifier
2849 $length = 0x0002; // Bytes to follow
2851 $fGridSet = !($this->_print_gridlines); // Boolean flag
2853 $header = pack("vv", $record, $length);
2854 $data = pack("v", $fGridSet);
2855 $this->_prepend($header . $data);
2859 * Write the GUTS BIFF record. This is used to configure the gutter margins
2860 * where Excel outline symbols are displayed. The visibility of the gutters is
2861 * controlled by a flag in WSBOOL.
2863 * @see _storeWsbool()
2864 * @access private
2866 function _storeGuts()
2868 $record = 0x0080; // Record identifier
2869 $length = 0x0008; // Bytes to follow
2871 $dxRwGut = 0x0000; // Size of row gutter
2872 $dxColGut = 0x0000; // Size of col gutter
2874 $row_level = $this->_outline_row_level;
2875 $col_level = 0;
2877 // Calculate the maximum column outline level. The equivalent calculation
2878 // for the row outline level is carried out in setRow().
2879 $colcount = count($this->_colinfo);
2880 for ($i = 0; $i < $colcount; $i++) {
2881 // Skip cols without outline level info.
2882 if (count($col_level) >= 6) {
2883 $col_level = max($this->_colinfo[$i][5], $col_level);
2887 // Set the limits for the outline levels (0 <= x <= 7).
2888 $col_level = max(0, min($col_level, 7));
2890 // The displayed level is one greater than the max outline levels
2891 if ($row_level) {
2892 $row_level++;
2894 if ($col_level) {
2895 $col_level++;
2898 $header = pack("vv", $record, $length);
2899 $data = pack("vvvv", $dxRwGut, $dxColGut, $row_level, $col_level);
2901 $this->_prepend($header.$data);
2906 * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
2907 * with the SETUP record.
2909 * @access private
2911 function _storeWsbool()
2913 $record = 0x0081; // Record identifier
2914 $length = 0x0002; // Bytes to follow
2915 $grbit = 0x0000;
2917 // The only option that is of interest is the flag for fit to page. So we
2918 // set all the options in one go.
2920 /*if ($this->_fit_page) {
2921 $grbit = 0x05c1;
2922 } else {
2923 $grbit = 0x04c1;
2925 // Set the option flags
2926 $grbit |= 0x0001; // Auto page breaks visible
2927 if ($this->_outline_style) {
2928 $grbit |= 0x0020; // Auto outline styles
2930 if ($this->_outline_below) {
2931 $grbit |= 0x0040; // Outline summary below
2933 if ($this->_outline_right) {
2934 $grbit |= 0x0080; // Outline summary right
2936 if ($this->_fit_page) {
2937 $grbit |= 0x0100; // Page setup fit to page
2939 if ($this->_outline_on) {
2940 $grbit |= 0x0400; // Outline symbols displayed
2943 $header = pack("vv", $record, $length);
2944 $data = pack("v", $grbit);
2945 $this->_prepend($header . $data);
2949 * Write the HORIZONTALPAGEBREAKS BIFF record.
2951 * @access private
2953 function _storeHbreak()
2955 // Return if the user hasn't specified pagebreaks
2956 if (empty($this->_hbreaks)) {
2957 return;
2960 // Sort and filter array of page breaks
2961 $breaks = $this->_hbreaks;
2962 sort($breaks, SORT_NUMERIC);
2963 if ($breaks[0] == 0) { // don't use first break if it's 0
2964 array_shift($breaks);
2967 $record = 0x001b; // Record identifier
2968 $cbrk = count($breaks); // Number of page breaks
2969 if ($this->_BIFF_version == 0x0600) {
2970 $length = 2 + 6*$cbrk; // Bytes to follow
2971 } else {
2972 $length = 2 + 2*$cbrk; // Bytes to follow
2975 $header = pack("vv", $record, $length);
2976 $data = pack("v", $cbrk);
2978 // Append each page break
2979 foreach ($breaks as $break) {
2980 if ($this->_BIFF_version == 0x0600) {
2981 $data .= pack("vvv", $break, 0x0000, 0x00ff);
2982 } else {
2983 $data .= pack("v", $break);
2987 $this->_prepend($header.$data);
2992 * Write the VERTICALPAGEBREAKS BIFF record.
2994 * @access private
2996 function _storeVbreak()
2998 // Return if the user hasn't specified pagebreaks
2999 if (empty($this->_vbreaks)) {
3000 return;
3003 // 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
3004 // It is slightly higher in Excel 97/200, approx. 1026
3005 $breaks = array_slice($this->_vbreaks,0,1000);
3007 // Sort and filter array of page breaks
3008 sort($breaks, SORT_NUMERIC);
3009 if ($breaks[0] == 0) { // don't use first break if it's 0
3010 array_shift($breaks);
3013 $record = 0x001a; // Record identifier
3014 $cbrk = count($breaks); // Number of page breaks
3015 if ($this->_BIFF_version == 0x0600) {
3016 $length = 2 + 6*$cbrk; // Bytes to follow
3017 } else {
3018 $length = 2 + 2*$cbrk; // Bytes to follow
3021 $header = pack("vv", $record, $length);
3022 $data = pack("v", $cbrk);
3024 // Append each page break
3025 foreach ($breaks as $break) {
3026 if ($this->_BIFF_version == 0x0600) {
3027 $data .= pack("vvv", $break, 0x0000, 0xffff);
3028 } else {
3029 $data .= pack("v", $break);
3033 $this->_prepend($header . $data);
3037 * Set the Biff PROTECT record to indicate that the worksheet is protected.
3039 * @access private
3041 function _storeProtect()
3043 // Exit unless sheet protection has been specified
3044 if ($this->_protect == 0) {
3045 return;
3048 $record = 0x0012; // Record identifier
3049 $length = 0x0002; // Bytes to follow
3051 $fLock = $this->_protect; // Worksheet is protected
3053 $header = pack("vv", $record, $length);
3054 $data = pack("v", $fLock);
3056 $this->_prepend($header.$data);
3060 * Write the worksheet PASSWORD record.
3062 * @access private
3064 function _storePassword()
3066 // Exit unless sheet protection and password have been specified
3067 if (($this->_protect == 0) || (!isset($this->_password))) {
3068 return;
3071 $record = 0x0013; // Record identifier
3072 $length = 0x0002; // Bytes to follow
3074 $wPassword = $this->_password; // Encoded password
3076 $header = pack("vv", $record, $length);
3077 $data = pack("v", $wPassword);
3079 $this->_prepend($header . $data);
3084 * Insert a 24bit bitmap image in a worksheet.
3086 * @access public
3087 * @param integer $row The row we are going to insert the bitmap into
3088 * @param integer $col The column we are going to insert the bitmap into
3089 * @param string $bitmap The bitmap filename
3090 * @param integer $x The horizontal position (offset) of the image inside the cell.
3091 * @param integer $y The vertical position (offset) of the image inside the cell.
3092 * @param integer $scale_x The horizontal scale
3093 * @param integer $scale_y The vertical scale
3095 function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1)
3097 $bitmap_array = $this->_processBitmap($bitmap);
3098 if ($this->isError($bitmap_array)) {
3099 $this->writeString($row, $col, $bitmap_array->getMessage());
3100 return;
3102 list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap);
3104 // Scale the frame of the image.
3105 $width *= $scale_x;
3106 $height *= $scale_y;
3108 // Calculate the vertices of the image and write the OBJ record
3109 $this->_positionImage($col, $row, $x, $y, $width, $height);
3111 // Write the IMDATA record to store the bitmap data
3112 $record = 0x007f;
3113 $length = 8 + $size;
3114 $cf = 0x09;
3115 $env = 0x01;
3116 $lcb = $size;
3118 $header = pack("vvvvV", $record, $length, $cf, $env, $lcb);
3119 $this->_append($header.$data);
3123 * Calculate the vertices that define the position of the image as required by
3124 * the OBJ record.
3126 * +------------+------------+
3127 * | A | B |
3128 * +-----+------------+------------+
3129 * | |(x1,y1) | |
3130 * | 1 |(A1)._______|______ |
3131 * | | | | |
3132 * | | | | |
3133 * +-----+----| BITMAP |-----+
3134 * | | | | |
3135 * | 2 | |______________. |
3136 * | | | (B2)|
3137 * | | | (x2,y2)|
3138 * +---- +------------+------------+
3140 * Example of a bitmap that covers some of the area from cell A1 to cell B2.
3142 * Based on the width and height of the bitmap we need to calculate 8 vars:
3143 * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
3144 * The width and height of the cells are also variable and have to be taken into
3145 * account.
3146 * The values of $col_start and $row_start are passed in from the calling
3147 * function. The values of $col_end and $row_end are calculated by subtracting
3148 * the width and height of the bitmap from the width and height of the
3149 * underlying cells.
3150 * The vertices are expressed as a percentage of the underlying cell width as
3151 * follows (rhs values are in pixels):
3153 * x1 = X / W *1024
3154 * y1 = Y / H *256
3155 * x2 = (X-1) / W *1024
3156 * y2 = (Y-1) / H *256
3158 * Where: X is distance from the left side of the underlying cell
3159 * Y is distance from the top of the underlying cell
3160 * W is the width of the cell
3161 * H is the height of the cell
3163 * @access private
3164 * @note the SDK incorrectly states that the height should be expressed as a
3165 * percentage of 1024.
3166 * @param integer $col_start Col containing upper left corner of object
3167 * @param integer $row_start Row containing top left corner of object
3168 * @param integer $x1 Distance to left side of object
3169 * @param integer $y1 Distance to top of object
3170 * @param integer $width Width of image frame
3171 * @param integer $height Height of image frame
3173 function _positionImage($col_start, $row_start, $x1, $y1, $width, $height)
3175 // Initialise end cell to the same as the start cell
3176 $col_end = $col_start; // Col containing lower right corner of object
3177 $row_end = $row_start; // Row containing bottom right corner of object
3179 // Zero the specified offset if greater than the cell dimensions
3180 if ($x1 >= $this->_sizeCol($col_start)) {
3181 $x1 = 0;
3183 if ($y1 >= $this->_sizeRow($row_start)) {
3184 $y1 = 0;
3187 $width = $width + $x1 -1;
3188 $height = $height + $y1 -1;
3190 // Subtract the underlying cell widths to find the end cell of the image
3191 while ($width >= $this->_sizeCol($col_end)) {
3192 $width -= $this->_sizeCol($col_end);
3193 $col_end++;
3196 // Subtract the underlying cell heights to find the end cell of the image
3197 while ($height >= $this->_sizeRow($row_end)) {
3198 $height -= $this->_sizeRow($row_end);
3199 $row_end++;
3202 // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
3203 // with zero eight or width.
3205 if ($this->_sizeCol($col_start) == 0) {
3206 return;
3208 if ($this->_sizeCol($col_end) == 0) {
3209 return;
3211 if ($this->_sizeRow($row_start) == 0) {
3212 return;
3214 if ($this->_sizeRow($row_end) == 0) {
3215 return;
3218 // Convert the pixel values to the percentage value expected by Excel
3219 $x1 = $x1 / $this->_sizeCol($col_start) * 1024;
3220 $y1 = $y1 / $this->_sizeRow($row_start) * 256;
3221 $x2 = $width / $this->_sizeCol($col_end) * 1024; // Distance to right side of object
3222 $y2 = $height / $this->_sizeRow($row_end) * 256; // Distance to bottom of object
3224 $this->_storeObjPicture($col_start, $x1,
3225 $row_start, $y1,
3226 $col_end, $x2,
3227 $row_end, $y2);
3231 * Convert the width of a cell from user's units to pixels. By interpolation
3232 * the relationship is: y = 7x +5. If the width hasn't been set by the user we
3233 * use the default value. If the col is hidden we use a value of zero.
3235 * @access private
3236 * @param integer $col The column
3237 * @return integer The width in pixels
3239 function _sizeCol($col)
3241 // Look up the cell value to see if it has been changed
3242 if (isset($this->col_sizes[$col])) {
3243 if ($this->col_sizes[$col] == 0) {
3244 return(0);
3245 } else {
3246 return(floor(7 * $this->col_sizes[$col] + 5));
3248 } else {
3249 return(64);
3254 * Convert the height of a cell from user's units to pixels. By interpolation
3255 * the relationship is: y = 4/3x. If the height hasn't been set by the user we
3256 * use the default value. If the row is hidden we use a value of zero. (Not
3257 * possible to hide row yet).
3259 * @access private
3260 * @param integer $row The row
3261 * @return integer The width in pixels
3263 function _sizeRow($row)
3265 // Look up the cell value to see if it has been changed
3266 if (isset($this->_row_sizes[$row])) {
3267 if ($this->_row_sizes[$row] == 0) {
3268 return(0);
3269 } else {
3270 return(floor(4/3 * $this->_row_sizes[$row]));
3272 } else {
3273 return(17);
3278 * Store the OBJ record that precedes an IMDATA record. This could be generalise
3279 * to support other Excel objects.
3281 * @access private
3282 * @param integer $colL Column containing upper left corner of object
3283 * @param integer $dxL Distance from left side of cell
3284 * @param integer $rwT Row containing top left corner of object
3285 * @param integer $dyT Distance from top of cell
3286 * @param integer $colR Column containing lower right corner of object
3287 * @param integer $dxR Distance from right of cell
3288 * @param integer $rwB Row containing bottom right corner of object
3289 * @param integer $dyB Distance from bottom of cell
3291 function _storeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB)
3293 $record = 0x005d; // Record identifier
3294 $length = 0x003c; // Bytes to follow
3296 $cObj = 0x0001; // Count of objects in file (set to 1)
3297 $OT = 0x0008; // Object type. 8 = Picture
3298 $id = 0x0001; // Object ID
3299 $grbit = 0x0614; // Option flags
3301 $cbMacro = 0x0000; // Length of FMLA structure
3302 $Reserved1 = 0x0000; // Reserved
3303 $Reserved2 = 0x0000; // Reserved
3305 $icvBack = 0x09; // Background colour
3306 $icvFore = 0x09; // Foreground colour
3307 $fls = 0x00; // Fill pattern
3308 $fAuto = 0x00; // Automatic fill
3309 $icv = 0x08; // Line colour
3310 $lns = 0xff; // Line style
3311 $lnw = 0x01; // Line weight
3312 $fAutoB = 0x00; // Automatic border
3313 $frs = 0x0000; // Frame style
3314 $cf = 0x0009; // Image format, 9 = bitmap
3315 $Reserved3 = 0x0000; // Reserved
3316 $cbPictFmla = 0x0000; // Length of FMLA structure
3317 $Reserved4 = 0x0000; // Reserved
3318 $grbit2 = 0x0001; // Option flags
3319 $Reserved5 = 0x0000; // Reserved
3322 $header = pack("vv", $record, $length);
3323 $data = pack("V", $cObj);
3324 $data .= pack("v", $OT);
3325 $data .= pack("v", $id);
3326 $data .= pack("v", $grbit);
3327 $data .= pack("v", $colL);
3328 $data .= pack("v", $dxL);
3329 $data .= pack("v", $rwT);
3330 $data .= pack("v", $dyT);
3331 $data .= pack("v", $colR);
3332 $data .= pack("v", $dxR);
3333 $data .= pack("v", $rwB);
3334 $data .= pack("v", $dyB);
3335 $data .= pack("v", $cbMacro);
3336 $data .= pack("V", $Reserved1);
3337 $data .= pack("v", $Reserved2);
3338 $data .= pack("C", $icvBack);
3339 $data .= pack("C", $icvFore);
3340 $data .= pack("C", $fls);
3341 $data .= pack("C", $fAuto);
3342 $data .= pack("C", $icv);
3343 $data .= pack("C", $lns);
3344 $data .= pack("C", $lnw);
3345 $data .= pack("C", $fAutoB);
3346 $data .= pack("v", $frs);
3347 $data .= pack("V", $cf);
3348 $data .= pack("v", $Reserved3);
3349 $data .= pack("v", $cbPictFmla);
3350 $data .= pack("v", $Reserved4);
3351 $data .= pack("v", $grbit2);
3352 $data .= pack("V", $Reserved5);
3354 $this->_append($header . $data);
3358 * Convert a 24 bit bitmap into the modified internal format used by Windows.
3359 * This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
3360 * MSDN library.
3362 * @access private
3363 * @param string $bitmap The bitmap to process
3364 * @return array Array with data and properties of the bitmap
3366 function _processBitmap($bitmap)
3368 // Open file.
3369 $bmp_fd = @fopen($bitmap,"rb");
3370 if (!$bmp_fd) {
3371 $this->raiseError("Couldn't import $bitmap");
3374 // Slurp the file into a string.
3375 $data = fread($bmp_fd, filesize($bitmap));
3377 // Check that the file is big enough to be a bitmap.
3378 if (strlen($data) <= 0x36) {
3379 $this->raiseError("$bitmap doesn't contain enough data.\n");
3382 // The first 2 bytes are used to identify the bitmap.
3383 $identity = unpack("A2ident", $data);
3384 if ($identity['ident'] != "BM") {
3385 $this->raiseError("$bitmap doesn't appear to be a valid bitmap image.\n");
3388 // Remove bitmap data: ID.
3389 $data = substr($data, 2);
3391 // Read and remove the bitmap size. This is more reliable than reading
3392 // the data size at offset 0x22.
3394 $size_array = unpack("Vsa", substr($data, 0, 4));
3395 $size = $size_array['sa'];
3396 $data = substr($data, 4);
3397 $size -= 0x36; // Subtract size of bitmap header.
3398 $size += 0x0C; // Add size of BIFF header.
3400 // Remove bitmap data: reserved, offset, header length.
3401 $data = substr($data, 12);
3403 // Read and remove the bitmap width and height. Verify the sizes.
3404 $width_and_height = unpack("V2", substr($data, 0, 8));
3405 $width = $width_and_height[1];
3406 $height = $width_and_height[2];
3407 $data = substr($data, 8);
3408 if ($width > 0xFFFF) {
3409 $this->raiseError("$bitmap: largest image width supported is 65k.\n");
3411 if ($height > 0xFFFF) {
3412 $this->raiseError("$bitmap: largest image height supported is 65k.\n");
3415 // Read and remove the bitmap planes and bpp data. Verify them.
3416 $planes_and_bitcount = unpack("v2", substr($data, 0, 4));
3417 $data = substr($data, 4);
3418 if ($planes_and_bitcount[2] != 24) { // Bitcount
3419 $this->raiseError("$bitmap isn't a 24bit true color bitmap.\n");
3421 if ($planes_and_bitcount[1] != 1) {
3422 $this->raiseError("$bitmap: only 1 plane supported in bitmap image.\n");
3425 // Read and remove the bitmap compression. Verify compression.
3426 $compression = unpack("Vcomp", substr($data, 0, 4));
3427 $data = substr($data, 4);
3429 //$compression = 0;
3430 if ($compression['comp'] != 0) {
3431 $this->raiseError("$bitmap: compression not supported in bitmap image.\n");
3434 // Remove bitmap data: data size, hres, vres, colours, imp. colours.
3435 $data = substr($data, 20);
3437 // Add the BITMAPCOREHEADER data
3438 $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
3439 $data = $header . $data;
3441 return (array($width, $height, $size, $data));
3445 * Store the window zoom factor. This should be a reduced fraction but for
3446 * simplicity we will store all fractions with a numerator of 100.
3448 * @access private
3450 function _storeZoom()
3452 // If scale is 100 we don't need to write a record
3453 if ($this->_zoom == 100) {
3454 return;
3457 $record = 0x00A0; // Record identifier
3458 $length = 0x0004; // Bytes to follow
3460 $header = pack("vv", $record, $length);
3461 $data = pack("vv", $this->_zoom, 100);
3462 $this->_append($header . $data);
3466 * FIXME: add comments
3468 function setValidation($row1, $col1, $row2, $col2, &$validator)
3470 $this->_dv[] = $validator->_getData() .
3471 pack("vvvvv", 1, $row1, $row2, $col1, $col2);
3475 * Store the DVAL and DV records.
3477 * @access private
3479 function _storeDataValidity()
3481 $record = 0x01b2; // Record identifier
3482 $length = 0x0012; // Bytes to follow
3484 $grbit = 0x0002; // Prompt box at cell, no cached validity data at DV records
3485 $horPos = 0x00000000; // Horizontal position of prompt box, if fixed position
3486 $verPos = 0x00000000; // Vertical position of prompt box, if fixed position
3487 $objId = 0xffffffff; // Object identifier of drop down arrow object, or -1 if not visible
3489 $header = pack('vv', $record, $length);
3490 $data = pack('vVVVV', $grbit, $horPos, $verPos, $objId,
3491 count($this->_dv));
3492 $this->_append($header.$data);
3494 $record = 0x01be; // Record identifier
3495 foreach ($this->_dv as $dv) {
3496 $length = strlen($dv); // Bytes to follow
3497 $header = pack("vv", $record, $length);
3498 $this->_append($header . $dv);