|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 /* 00003 * Module written/ported by Xavier Noguer <xnoguer@rezebra.com> 00004 * 00005 * The majority of this is _NOT_ my code. I simply ported it from the 00006 * PERL Spreadsheet::WriteExcel module. 00007 * 00008 * The author of the Spreadsheet::WriteExcel module is John McNamara 00009 * <jmcnamara@cpan.org> 00010 * 00011 * I _DO_ maintain this code, and John McNamara has nothing to do with the 00012 * porting of this code to PHP. Any questions directly related to this 00013 * class library should be directed to me. 00014 * 00015 * License Information: 00016 * 00017 * Spreadsheet::WriteExcel: A library for generating Excel Spreadsheets 00018 * Copyright (C) 2002 Xavier Noguer xnoguer@rezebra.com 00019 * 00020 * This library is free software; you can redistribute it and/or 00021 * modify it under the terms of the GNU Lesser General Public 00022 * License as published by the Free Software Foundation; either 00023 * version 2.1 of the License, or (at your option) any later version. 00024 * 00025 * This library is distributed in the hope that it will be useful, 00026 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00027 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00028 * Lesser General Public License for more details. 00029 * 00030 * You should have received a copy of the GNU Lesser General Public 00031 * License along with this library; if not, write to the Free Software 00032 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00033 */ 00034 00035 require_once('Parser.php'); 00036 require_once('BIFFwriter.php'); 00037 00045 class Worksheet extends BIFFwriter 00046 { 00047 00058 function Worksheet($name,$index,&$activesheet,&$firstsheet,&$url_format,&$parser) 00059 { 00060 $this->BIFFwriter(); // It needs to call its parent's constructor explicitly 00061 $rowmax = 65536; // 16384 in Excel 5 00062 $colmax = 256; 00063 $strmax = 255; 00064 00065 $this->name = $name; 00066 $this->index = $index; 00067 $this->activesheet = &$activesheet; 00068 $this->firstsheet = &$firstsheet; 00069 $this->_url_format = $url_format; 00070 $this->_parser = &$parser; 00071 00072 $this->ext_sheets = array(); 00073 $this->_using_tmpfile = 1; 00074 $this->_filehandle = ""; 00075 $this->fileclosed = 0; 00076 $this->offset = 0; 00077 $this->xls_rowmax = $rowmax; 00078 $this->xls_colmax = $colmax; 00079 $this->xls_strmax = $strmax; 00080 $this->dim_rowmin = $rowmax +1; 00081 $this->dim_rowmax = 0; 00082 $this->dim_colmin = $colmax +1; 00083 $this->dim_colmax = 0; 00084 $this->colinfo = array(); 00085 $this->_selection = array(0,0,0,0); 00086 $this->_panes = array(); 00087 $this->_active_pane = 3; 00088 $this->_frozen = 0; 00089 $this->selected = 0; 00090 00091 $this->_paper_size = 0x0; 00092 $this->_orientation = 0x1; 00093 $this->_header = ''; 00094 $this->_footer = ''; 00095 $this->_hcenter = 0; 00096 $this->_vcenter = 0; 00097 $this->_margin_head = 0.50; 00098 $this->_margin_foot = 0.50; 00099 $this->_margin_left = 0.75; 00100 $this->_margin_right = 0.75; 00101 $this->_margin_top = 1.00; 00102 $this->_margin_bottom = 1.00; 00103 00104 $this->_title_rowmin = NULL; 00105 $this->_title_rowmax = NULL; 00106 $this->_title_colmin = NULL; 00107 $this->_title_colmax = NULL; 00108 $this->_print_rowmin = NULL; 00109 $this->_print_rowmax = NULL; 00110 $this->_print_colmin = NULL; 00111 $this->_print_colmax = NULL; 00112 00113 $this->_print_gridlines = 1; 00114 $this->_print_headers = 0; 00115 00116 $this->_fit_page = 0; 00117 $this->_fit_width = 0; 00118 $this->_fit_height = 0; 00119 00120 $this->_hbreaks = array(); 00121 $this->_vbreaks = array(); 00122 00123 $this->_protect = 0; 00124 $this->_password = NULL; 00125 00126 $this->col_sizes = array(); 00127 $this->row_sizes = array(); 00128 00129 $this->_zoom = 100; 00130 $this->_print_scale = 100; 00131 00132 $this->_initialize(); 00133 } 00134 00140 function _initialize() 00141 { 00142 // Open tmp file for storing Worksheet data 00143 $fh = tmpfile(); 00144 if ( $fh) { 00145 // Store filehandle 00146 $this->_filehandle = $fh; 00147 } 00148 else { 00149 // If tmpfile() fails store data in memory 00150 $this->_using_tmpfile = 0; 00151 } 00152 } 00153 00163 function close($sheetnames) 00164 { 00165 $num_sheets = count($sheetnames); 00166 00167 /*********************************************** 00168 * Prepend in reverse order!! 00169 */ 00170 00171 // Prepend the sheet dimensions 00172 $this->_store_dimensions(); 00173 00174 // Prepend the sheet password 00175 $this->_store_password(); 00176 00177 // Prepend the sheet protection 00178 $this->_store_protect(); 00179 00180 // Prepend the page setup 00181 $this->_store_setup(); 00182 00183 // Prepend the bottom margin 00184 $this->_store_margin_bottom(); 00185 00186 // Prepend the top margin 00187 $this->_store_margin_top(); 00188 00189 // Prepend the right margin 00190 $this->_store_margin_right(); 00191 00192 // Prepend the left margin 00193 $this->_store_margin_left(); 00194 00195 // Prepend the page vertical centering 00196 $this->store_vcenter(); 00197 00198 // Prepend the page horizontal centering 00199 $this->store_hcenter(); 00200 00201 // Prepend the page footer 00202 $this->store_footer(); 00203 00204 // Prepend the page header 00205 $this->store_header(); 00206 00207 // Prepend the vertical page breaks 00208 $this->_store_vbreak(); 00209 00210 // Prepend the horizontal page breaks 00211 $this->_store_hbreak(); 00212 00213 // Prepend WSBOOL 00214 $this->_store_wsbool(); 00215 00216 // Prepend GRIDSET 00217 $this->_store_gridset(); 00218 00219 // Prepend PRINTGRIDLINES 00220 $this->_store_print_gridlines(); 00221 00222 // Prepend PRINTHEADERS 00223 $this->_store_print_headers(); 00224 00225 // Prepend EXTERNSHEET references 00226 for ($i = $num_sheets; $i > 0; $i--) { 00227 $sheetname = $sheetnames[$i-1]; 00228 $this->_store_externsheet($sheetname); 00229 } 00230 00231 // Prepend the EXTERNCOUNT of external references. 00232 $this->_store_externcount($num_sheets); 00233 00234 // Prepend the COLINFO records if they exist 00235 if (!empty($this->colinfo)){ 00236 for($i=0; $i < count($this->colinfo); $i++) 00237 { 00238 $this->_store_colinfo($this->colinfo[$i]); 00239 } 00240 $this->_store_defcol(); 00241 } 00242 00243 // Prepend the BOF record 00244 $this->_store_bof(0x0010); 00245 00246 /* 00247 * End of prepend. Read upwards from here. 00248 ***********************************************/ 00249 00250 // Append 00251 $this->_store_window2(); 00252 $this->_store_zoom(); 00253 if(!empty($this->_panes)) 00254 $this->_store_panes($this->_panes); 00255 $this->_store_selection($this->_selection); 00256 $this->_store_eof(); 00257 } 00258 00266 function get_name() 00267 { 00268 return($this->name); 00269 } 00270 00277 function get_data() 00278 { 00279 $buffer = 4096; 00280 00281 // Return data stored in memory 00282 if (isset($this->_data)) { 00283 $tmp = $this->_data; 00284 unset($this->_data); 00285 $fh = $this->_filehandle; 00286 if ($this->_using_tmpfile) { 00287 fseek($fh, 0); 00288 } 00289 return($tmp); 00290 } 00291 // Return data stored on disk 00292 if ($this->_using_tmpfile) { 00293 if ($tmp = fread($this->_filehandle, $buffer)) { 00294 return($tmp); 00295 } 00296 } 00297 00298 // No data to return 00299 return(''); 00300 } 00301 00308 function select() 00309 { 00310 $this->selected = 1; 00311 } 00312 00319 function activate() 00320 { 00321 $this->selected = 1; 00322 $this->activesheet =& $this->index; 00323 } 00324 00332 function set_first_sheet() 00333 { 00334 $this->firstsheet = $this->index; 00335 } 00336 00344 function protect($password) 00345 { 00346 $this->_protect = 1; 00347 $this->_password = $this->_encode_password($password); 00348 } 00349 00361 function set_column($firstcol, $lastcol, $width, $format = 0, $hidden = 0) 00362 { 00363 $this->colinfo[] = array($firstcol, $lastcol, $width, $format, $hidden); 00364 00365 // Set width to zero if column is hidden 00366 $width = ($hidden) ? 0 : $width; 00367 00368 for($col = $firstcol; $col <= $lastcol; $col++) { 00369 $this->col_sizes[$col] = $width; 00370 } 00371 } 00372 00383 function set_selection($first_row,$first_column,$last_row,$last_column) 00384 { 00385 $this->_selection = array($first_row,$first_column,$last_row,$last_column); 00386 } 00387 00399 function freeze_panes($panes) 00400 { 00401 $this->_frozen = 1; 00402 $this->_panes = $panes; 00403 } 00404 00416 function thaw_panes($panes) 00417 { 00418 $this->_frozen = 0; 00419 $this->_panes = $panes; 00420 } 00421 00427 function set_portrait() 00428 { 00429 $this->_orientation = 1; 00430 } 00431 00437 function set_landscape() 00438 { 00439 $this->_orientation = 0; 00440 } 00441 00448 function set_paper($size = 0) 00449 { 00450 $this->_paper_size = $size; 00451 } 00452 00453 00461 function set_header($string,$margin = 0.50) 00462 { 00463 if (strlen($string) >= 255) { 00464 //carp 'Header string must be less than 255 characters'; 00465 return; 00466 } 00467 $this->_header = $string; 00468 $this->_margin_head = $margin; 00469 } 00470 00478 function set_footer($string,$margin = 0.50) 00479 { 00480 if (strlen($string) >= 255) { 00481 //carp 'Footer string must be less than 255 characters'; 00482 return; 00483 } 00484 $this->_footer = $string; 00485 $this->_margin_foot = $margin; 00486 } 00487 00494 function center_horizontally($center = 1) 00495 { 00496 $this->_hcenter = $center; 00497 } 00498 00505 function center_vertically($center = 1) 00506 { 00507 $this->_vcenter = $center; 00508 } 00509 00516 function set_margins($margin) 00517 { 00518 $this->set_margin_left($margin); 00519 $this->set_margin_right($margin); 00520 $this->set_margin_top($margin); 00521 $this->set_margin_bottom($margin); 00522 } 00523 00530 function set_margins_LR($margin) 00531 { 00532 $this->set_margin_left($margin); 00533 $this->set_margin_right($margin); 00534 } 00535 00542 function set_margins_TB($margin) 00543 { 00544 $this->set_margin_top($margin); 00545 $this->set_margin_bottom($margin); 00546 } 00547 00554 function set_margin_left($margin = 0.75) 00555 { 00556 $this->_margin_left = $margin; 00557 } 00558 00565 function set_margin_right($margin = 0.75) 00566 { 00567 $this->_margin_right = $margin; 00568 } 00569 00576 function set_margin_top($margin = 1.00) 00577 { 00578 $this->_margin_top = $margin; 00579 } 00580 00587 function set_margin_bottom($margin = 1.00) 00588 { 00589 $this->_margin_bottom = $margin; 00590 } 00591 00600 function repeat_rows($first_row, $last_row = NULL) 00601 { 00602 $this->_title_rowmin = $first_row; 00603 if(isset($last_row)) { //Second row is optional 00604 $this->_title_rowmax = $last_row; 00605 } 00606 else { 00607 $this->_title_rowmax = $first_row; 00608 } 00609 } 00610 00619 function repeat_columns($first_col, $last_col = NULL) 00620 { 00621 $this->_title_colmin = $first_col; 00622 if(isset($last_col)) { // Second col is optional 00623 $this->_title_colmax = $last_col; 00624 } 00625 else { 00626 $this->_title_colmax = $first_col; 00627 } 00628 } 00629 00640 function print_area($first_row, $first_col, $last_row, $last_col) 00641 { 00642 $this->_print_rowmin = $first_row; 00643 $this->_print_colmin = $first_col; 00644 $this->_print_rowmax = $last_row; 00645 $this->_print_colmax = $last_col; 00646 } 00647 00648 00655 function hide_gridlines() 00656 { 00657 $this->_print_gridlines = 0; 00658 } 00659 00668 function print_row_col_headers($print = 1) 00669 { 00670 $this->_print_headers = $print; 00671 } 00672 00682 function fit_to_pages($width, $height) 00683 { 00684 $this->_fit_page = 1; 00685 $this->_fit_width = $width; 00686 $this->_fit_height = $height; 00687 } 00688 00696 function set_h_pagebreaks($breaks) 00697 { 00698 foreach($breaks as $break) { 00699 array_push($this->_hbreaks,$break); 00700 } 00701 } 00702 00710 function set_v_pagebreaks($breaks) 00711 { 00712 foreach($breaks as $break) { 00713 array_push($this->_vbreaks,$break); 00714 } 00715 } 00716 00717 00724 function set_zoom($scale = 100) 00725 { 00726 // Confine the scale to Excel's range 00727 if ($scale < 10 or $scale > 400) { 00728 //carp "Zoom factor $scale outside range: 10 <= zoom <= 400"; 00729 $scale = 100; 00730 } 00731 00732 $this->_zoom = floor($scale); 00733 } 00734 00742 function set_print_scale($scale = 100) 00743 { 00744 // Confine the scale to Excel's range 00745 if ($scale < 10 or $scale > 400) 00746 { 00747 // REPLACE THIS FOR A WARNING 00748 die("Print scale $scale outside range: 10 <= zoom <= 400"); 00749 $scale = 100; 00750 } 00751 00752 // Turn off "fit to page" option 00753 $this->_fit_page = 0; 00754 00755 $this->_print_scale = floor($scale); 00756 } 00757 00767 function write($row, $col, $token, $format = 0) 00768 { 00769 // Check for a cell reference in A1 notation and substitute row and column 00770 /*if ($_[0] =~ /^\D/) { 00771 @_ = $this->_substitute_cellref(@_); 00772 }*/ 00773 00774 /* 00775 # Match an array ref. 00776 if (ref $token eq "ARRAY") { 00777 return $this->write_row(@_); 00778 }*/ 00779 00780 // Match number 00781 if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/",$token)) { 00782 return $this->write_number($row,$col,$token,$format); 00783 } 00784 // Match http or ftp URL 00785 elseif (preg_match("/^[fh]tt?p:\/\//",$token)) { 00786 return $this->write_url($row, $col, $token, $format); 00787 } 00788 // Match mailto: 00789 elseif (preg_match("/^mailto:/",$token)) { 00790 return $this->write_url($row, $col, $token, $format); 00791 } 00792 // Match internal or external sheet link 00793 elseif (preg_match("/^(?:in|ex)ternal:/",$token)) { 00794 return $this->write_url($row, $col, $token, $format); 00795 } 00796 // Match formula 00797 elseif (preg_match("/^=/",$token)) { 00798 return $this->write_formula($row, $col, $token, $format); 00799 } 00800 // Match formula 00801 elseif (preg_match("/^@/",$token)) { 00802 return $this->write_formula($row, $col, $token, $format); 00803 } 00804 // Match blank 00805 elseif ($token == '') { 00806 return $this->write_blank($row,$col,$format); 00807 } 00808 // Default: match string 00809 else { 00810 return $this->write_string($row,$col,$token,$format); 00811 } 00812 } 00813 00820 function _XF(&$format) 00821 { 00822 if($format != 0) 00823 { 00824 return($format->get_xf_index()); 00825 } 00826 else 00827 { 00828 return(0x0F); 00829 } 00830 } 00831 00832 00833 /****************************************************************************** 00834 ******************************************************************************* 00835 * 00836 * Internal methods 00837 */ 00838 00839 00846 function _append($data) 00847 { 00848 if ($this->_using_tmpfile) 00849 { 00850 // Add CONTINUE records if necessary 00851 if (strlen($data) > $this->_limit) { 00852 $data = $this->_add_continue($data); 00853 } 00854 fwrite($this->_filehandle,$data); 00855 $this->_datasize += strlen($data); 00856 } 00857 else { 00858 parent::_append($data); 00859 } 00860 } 00861 00871 function _substitute_cellref($cell) 00872 { 00873 $cell = strtoupper($cell); 00874 00875 // Convert a column range: 'A:A' or 'B:G' 00876 if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/",$cell,$match)) { 00877 list($no_use, $col1) = $this->_cell_to_rowcol($match[1] .'1'); // Add a dummy row 00878 list($no_use, $col2) = $this->_cell_to_rowcol($match[2] .'1'); // Add a dummy row 00879 return(array($col1, $col2)); 00880 } 00881 00882 // Convert a cell range: 'A1:B7' 00883 if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/",$cell,$match)) { 00884 list($row1, $col1) = $this->_cell_to_rowcol($match[1]); 00885 list($row2, $col2) = $this->_cell_to_rowcol($match[2]); 00886 return(array($row1, $col1, $row2, $col2)); 00887 } 00888 00889 // Convert a cell reference: 'A1' or 'AD2000' 00890 if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/",$cell)) { 00891 list($row1, $col1) = $this->_cell_to_rowcol($match[1]); 00892 return(array($row1, $col1)); 00893 } 00894 00895 die("Unknown cell reference $cell "); 00896 } 00897 00905 function _cell_to_rowcol($cell) 00906 { 00907 preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match); 00908 $col = $match[1]; 00909 $row = $match[2]; 00910 00911 // Convert base26 column string to number 00912 $chars = str_split($col); 00913 $expn = 0; 00914 $col = 0; 00915 00916 while ($chars) { 00917 $char = array_pop($chars); // LS char first 00918 $col += (ord($char) -ord('A') +1) * pow(26,$expn); 00919 $expn++; 00920 } 00921 00922 // Convert 1-index to zero-index 00923 $row--; 00924 $col--; 00925 00926 return(array($row, $col)); 00927 } 00928 00935 function _encode_password($plaintext) 00936 { 00937 $password = 0x0000; 00938 $i = 1; // char position 00939 00940 // split the plain text password in its component characters 00941 $chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY); 00942 foreach($chars as $char) 00943 { 00944 $value = ord($char) << $i; // shifted ASCII value 00945 $bit_16 = $value & 0x8000; // the bit 16 00946 $bit_16 >>= 15; // 0x0000 or 0x0001 00947 //$bit_17 = $value & 0x00010000; 00948 //$bit_17 >>= 15; 00949 $value &= 0x7fff; // first 15 bits 00950 $password ^= ($value | $bit_16); 00951 //$password ^= ($value | $bit_16 | $bit_17); 00952 $i++; 00953 } 00954 00955 $password ^= strlen($plaintext); 00956 $password ^= 0xCE4B; 00957 00958 return($password); 00959 } 00960 00961 /****************************************************************************** 00962 ******************************************************************************* 00963 * 00964 * BIFF RECORDS 00965 */ 00966 00967 00982 function write_number($row, $col, $num, $format = 0) 00983 { 00984 $record = 0x0203; // Record identifier 00985 $length = 0x000E; // Number of bytes to follow 00986 $xf = $this->_XF($format); // The cell format 00987 00988 // Check that row and col are valid and store max and min values 00989 if ($row >= $this->xls_rowmax) 00990 { 00991 return(-2); 00992 } 00993 if ($col >= $this->xls_colmax) 00994 { 00995 return(-2); 00996 } 00997 if ($row < $this->dim_rowmin) 00998 { 00999 $this->dim_rowmin = $row; 01000 } 01001 if ($row > $this->dim_rowmax) 01002 { 01003 $this->dim_rowmax = $row; 01004 } 01005 if ($col < $this->dim_colmin) 01006 { 01007 $this->dim_colmin = $col; 01008 } 01009 if ($col > $this->dim_colmax) 01010 { 01011 $this->dim_colmax = $col; 01012 } 01013 01014 $header = pack("vv", $record, $length); 01015 $data = pack("vvv", $row, $col, $xf); 01016 $xl_double = pack("d", $num); 01017 if ($this->_byte_order) // if it's Big Endian 01018 { 01019 $xl_double = strrev($xl_double); 01020 } 01021 01022 $this->_append($header.$data.$xl_double); 01023 return(0); 01024 } 01025 01041 function write_string($row, $col, $str, $format = 0) 01042 { 01043 $strlen = strlen($str); 01044 $record = 0x0204; // Record identifier 01045 $length = 0x0008 + $strlen; // Bytes to follow 01046 $xf = $this->_XF($format); // The cell format 01047 01048 $str_error = 0; 01049 01050 // Check that row and col are valid and store max and min values 01051 if ($row >= $this->xls_rowmax) 01052 { 01053 return(-2); 01054 } 01055 if ($col >= $this->xls_colmax) 01056 { 01057 return(-2); 01058 } 01059 if ($row < $this->dim_rowmin) 01060 { 01061 $this->dim_rowmin = $row; 01062 } 01063 if ($row > $this->dim_rowmax) 01064 { 01065 $this->dim_rowmax = $row; 01066 } 01067 if ($col < $this->dim_colmin) 01068 { 01069 $this->dim_colmin = $col; 01070 } 01071 if ($col > $this->dim_colmax) 01072 { 01073 $this->dim_colmax = $col; 01074 } 01075 01076 if ($strlen > $this->xls_strmax) // LABEL must be < 255 chars 01077 { 01078 $str = substr($str, 0, $this->xls_strmax); 01079 $length = 0x0008 + $this->xls_strmax; 01080 $strlen = $this->xls_strmax; 01081 $str_error = -3; 01082 } 01083 01084 $header = pack("vv", $record, $length); 01085 $data = pack("vvvv", $row, $col, $xf, $strlen); 01086 $this->_append($header.$data.$str); 01087 return($str_error); 01088 } 01089 01099 function write_note($row, $col, $note) 01100 { 01101 $note_length = strlen($note); 01102 $record = 0x001C; // Record identifier 01103 $max_length = 2048; // Maximun length for a NOTE record 01104 //$length = 0x0006 + $note_length; // Bytes to follow 01105 01106 // Check that row and col are valid and store max and min values 01107 if ($row >= $this->xls_rowmax) 01108 { 01109 return(-2); 01110 } 01111 if ($col >= $this->xls_colmax) 01112 { 01113 return(-2); 01114 } 01115 if ($row < $this->dim_rowmin) 01116 { 01117 $this->dim_rowmin = $row; 01118 } 01119 if ($row > $this->dim_rowmax) 01120 { 01121 $this->dim_rowmax = $row; 01122 } 01123 if ($col < $this->dim_colmin) 01124 { 01125 $this->dim_colmin = $col; 01126 } 01127 if ($col > $this->dim_colmax) 01128 { 01129 $this->dim_colmax = $col; 01130 } 01131 01132 // Length for this record is no more than 2048 + 6 01133 $length = 0x0006 + min($note_length, 2048); 01134 $header = pack("vv", $record, $length); 01135 $data = pack("vvv", $row, $col, $note_length); 01136 $this->_append($header.$data.substr($note, 0, 2048)); 01137 01138 for($i = $max_length; $i < $note_length; $i += $max_length) 01139 { 01140 $chunk = substr($note, $i, $max_length); 01141 $length = 0x0006 + strlen($chunk); 01142 $header = pack("vv", $record, $length); 01143 $data = pack("vvv", -1, 0, strlen($chunk)); 01144 $this->_append($header.$data.$chunk); 01145 } 01146 return(0); 01147 } 01148 01167 function write_blank($row, $col, $format = 0) 01168 { 01169 // Don't write a blank cell unless it has a format 01170 if ($format == 0) 01171 { 01172 return(0); 01173 } 01174 01175 $record = 0x0201; // Record identifier 01176 $length = 0x0006; // Number of bytes to follow 01177 $xf = $this->_XF($format); // The cell format 01178 01179 // Check that row and col are valid and store max and min values 01180 if ($row >= $this->xls_rowmax) 01181 { 01182 return(-2); 01183 } 01184 if ($col >= $this->xls_colmax) 01185 { 01186 return(-2); 01187 } 01188 if ($row < $this->dim_rowmin) 01189 { 01190 $this->dim_rowmin = $row; 01191 } 01192 if ($row > $this->dim_rowmax) 01193 { 01194 $this->dim_rowmax = $row; 01195 } 01196 if ($col < $this->dim_colmin) 01197 { 01198 $this->dim_colmin = $col; 01199 } 01200 if ($col > $this->dim_colmax) 01201 { 01202 $this->dim_colmax = $col; 01203 } 01204 01205 $header = pack("vv", $record, $length); 01206 $data = pack("vvv", $row, $col, $xf); 01207 $this->_append($header.$data); 01208 return 0; 01209 } 01210 01225 function write_formula($row, $col, $formula, $format = 0) 01226 { 01227 $record = 0x0006; // Record identifier 01228 01229 // Excel normally stores the last calculated value of the formula in $num. 01230 // Clearly we are not in a position to calculate this a priori. Instead 01231 // we set $num to zero and set the option flags in $grbit to ensure 01232 // automatic calculation of the formula when the file is opened. 01233 // 01234 $xf = $this->_XF($format); // The cell format 01235 $num = 0x00; // Current value of formula 01236 $grbit = 0x03; // Option flags 01237 $chn = 0x0000; // Must be zero 01238 01239 01240 // Check that row and col are valid and store max and min values 01241 if ($row >= $this->xls_rowmax) 01242 { 01243 return(-2); 01244 } 01245 if ($col >= $this->xls_colmax) 01246 { 01247 return(-2); 01248 } 01249 if ($row < $this->dim_rowmin) 01250 { 01251 $this->dim_rowmin = $row; 01252 } 01253 if ($row > $this->dim_rowmax) 01254 { 01255 $this->dim_rowmax = $row; 01256 } 01257 if ($col < $this->dim_colmin) 01258 { 01259 $this->dim_colmin = $col; 01260 } 01261 if ($col > $this->dim_colmax) 01262 { 01263 $this->dim_colmax = $col; 01264 } 01265 01266 // Strip the '=' or '@' sign at the beginning of the formula string 01267 if (preg_match("/^=/",$formula)) { 01268 $formula = preg_replace("/(^=)/","",$formula); 01269 } 01270 elseif(preg_match("/^@/",$formula)) { 01271 $formula = preg_replace("/(^@)/","",$formula); 01272 } 01273 else { 01274 die("Unrecognised character for formula"); 01275 } 01276 01277 // Parse the formula using the parser in Parser.php 01278 //$tree = new Parser($this->_byte_order); 01279 $this->_parser->parse($formula); 01280 //$tree->parse($formula); 01281 $formula = $this->_parser->to_reverse_polish(); 01282 01283 $formlen = strlen($formula); // Length of the binary string 01284 $length = 0x16 + $formlen; // Length of the record data 01285 01286 $header = pack("vv", $record, $length); 01287 $data = pack("vvvdvVv", $row, $col, $xf, $num, 01288 $grbit, $chn, $formlen); 01289 01290 $this->_append($header.$data.$formula); 01291 return 0; 01292 } 01293 01316 function write_url($row, $col, $url, $string = '', $format = 0) 01317 { 01318 // Add start row and col to arg list 01319 return($this->_write_url_range($row, $col, $row, $col, $url, $string, $format)); 01320 } 01321 01339 function _write_url_range($row1, $col1, $row2, $col2, $url, $string = '', $format = 0) 01340 { 01341 // Check for internal/external sheet links or default to web link 01342 if (preg_match('[^internal:]', $url)) { 01343 return($this->_write_url_internal($row1, $col1, $row2, $col2, $url, $string, $format)); 01344 } 01345 if (preg_match('[^external:]', $url)) { 01346 return($this->_write_url_external($row1, $col1, $row2, $col2, $url, $string, $format)); 01347 } 01348 return($this->_write_url_web($row1, $col1, $row2, $col2, $url, $string, $format)); 01349 } 01350 01351 01366 function _write_url_web($row1, $col1, $row2, $col2, $url, $str, $format = 0) 01367 { 01368 $record = 0x01B8; // Record identifier 01369 $length = 0x00000; // Bytes to follow 01370 01371 if($format == 0) { 01372 $format = $this->_url_format; 01373 } 01374 01375 // Write the visible label using the write_string() method. 01376 if($str == '') { 01377 $str = $url; 01378 } 01379 $str_error = $this->write_string($row1, $col1, $str, $format); 01380 if ($str_error == -2) { 01381 return($str_error); 01382 } 01383 01384 // Pack the undocumented parts of the hyperlink stream 01385 $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); 01386 $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B"); 01387 01388 // Pack the option flags 01389 $options = pack("V", 0x03); 01390 01391 // Convert URL to a null terminated wchar string 01392 $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); 01393 $url = $url . "\0\0\0"; 01394 01395 // Pack the length of the URL 01396 $url_len = pack("V", strlen($url)); 01397 01398 // Calculate the data length 01399 $length = 0x34 + strlen($url); 01400 01401 // Pack the header data 01402 $header = pack("vv", $record, $length); 01403 $data = pack("vvvv", $row1, $row2, $col1, $col2); 01404 01405 // Write the packed data 01406 $this->_append( $header. $data. 01407 $unknown1. $options. 01408 $unknown2. $url_len. $url); 01409 return($str_error); 01410 } 01411 01424 function _write_url_internal($row1, $col1, $row2, $col2, $url, $str, $format = 0) 01425 { 01426 $record = 0x01B8; // Record identifier 01427 $length = 0x00000; // Bytes to follow 01428 01429 if ($format == 0) { 01430 $format = $this->_url_format; 01431 } 01432 01433 // Strip URL type 01434 $url = preg_replace('s[^internal:]', '', $url); 01435 01436 // Write the visible label 01437 if($str == '') { 01438 $str = $url; 01439 } 01440 $str_error = $this->write_string($row1, $col1, $str, $format); 01441 if ($str_error == -2) { 01442 return($str_error); 01443 } 01444 01445 // Pack the undocumented parts of the hyperlink stream 01446 $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); 01447 01448 // Pack the option flags 01449 $options = pack("V", 0x08); 01450 01451 // Convert the URL type and to a null terminated wchar string 01452 $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); 01453 $url = $url . "\0\0\0"; 01454 01455 // Pack the length of the URL as chars (not wchars) 01456 $url_len = pack("V", floor(strlen($url)/2)); 01457 01458 // Calculate the data length 01459 $length = 0x24 + strlen($url); 01460 01461 // Pack the header data 01462 $header = pack("vv", $record, $length); 01463 $data = pack("vvvv", $row1, $row2, $col1, $col2); 01464 01465 // Write the packed data 01466 $this->_append($header. $data. 01467 $unknown1. $options. 01468 $url_len. $url); 01469 return($str_error); 01470 } 01471 01488 function _write_url_external($row1, $col1, $row2, $col2, $url, $str, $format = 0) 01489 { 01490 // Network drives are different. We will handle them separately 01491 // MS/Novell network drives and shares start with \\ 01492 if (preg_match('[^external:\\\\]', $url)) { 01493 return($this->_write_url_external_net($row1, $col1, $row2, $col2, $url, $str, $format)); 01494 } 01495 01496 $record = 0x01B8; // Record identifier 01497 $length = 0x00000; // Bytes to follow 01498 01499 if ($format == 0) { 01500 $format = $this->_url_format; 01501 } 01502 01503 // Strip URL type and change Unix dir separator to Dos style (if needed) 01504 // 01505 $url = preg_replace('[^external:]', '', $url); 01506 $url = preg_replace('[/]', "\\", $url); 01507 01508 // Write the visible label 01509 if ($str == '') { 01510 $str = preg_replace('[\#]', ' - ', $url); 01511 } 01512 $str_error = $this->write_string($row1, $col1, $str, $format); 01513 if ($str_error == -2) { 01514 return($str_error); 01515 } 01516 01517 // Determine if the link is relative or absolute: 01518 // relative if link contains no dir separator, "somefile.xls" 01519 // relative if link starts with up-dir, "..\..\somefile.xls" 01520 // otherwise, absolute 01521 01522 $absolute = 0x02; // Bit mask 01523 if (!preg_match('[\\]', $url)) { 01524 $absolute = 0x00; 01525 } 01526 if (preg_match('[^\.\.\\]', $url)) { 01527 $absolute = 0x00; 01528 } 01529 01530 // Determine if the link contains a sheet reference and change some of the 01531 // parameters accordingly. 01532 // Split the dir name and sheet name (if it exists) 01533 list($dir_long , $sheet) = explode('/#/', $url); 01534 $link_type = 0x01 | $absolute; 01535 01536 if (isset($sheet)) { 01537 $link_type |= 0x08; 01538 $sheet_len = pack("V", strlen($sheet) + 0x01); 01539 $sheet = join("\0", str_split($sheet)); 01540 $sheet .= "\0\0\0"; 01541 } 01542 else { 01543 $sheet_len = ''; 01544 $sheet = ''; 01545 } 01546 01547 // Pack the link type 01548 $link_type = pack("V", $link_type); 01549 01550 // Calculate the up-level dir count e.g.. (..\..\..\ == 3) 01551 $up_count = preg_match_all("/\.\.\\/", $dir_long, $useless); 01552 $up_count = pack("v", $up_count); 01553 01554 // Store the short dos dir name (null terminated) 01555 $dir_short = preg_replace('/\.\.\\/', '', $dir_long) . "\0"; 01556 01557 // Store the long dir name as a wchar string (non-null terminated) 01558 $dir_long = join("\0", str_split($dir_long)); 01559 $dir_long = $dir_long . "\0"; 01560 01561 // Pack the lengths of the dir strings 01562 $dir_short_len = pack("V", strlen($dir_short) ); 01563 $dir_long_len = pack("V", strlen($dir_long) ); 01564 $stream_len = pack("V", strlen($dir_long) + 0x06); 01565 01566 // Pack the undocumented parts of the hyperlink stream 01567 $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' ); 01568 $unknown2 = pack("H*",'0303000000000000C000000000000046' ); 01569 $unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000'); 01570 $unknown4 = pack("v", 0x03 ); 01571 01572 // Pack the main data stream 01573 $data = pack("vvvv", $row1, $row2, $col1, $col2) . 01574 $unknown1 . 01575 $link_type . 01576 $unknown2 . 01577 $up_count . 01578 $dir_short_len. 01579 $dir_short . 01580 $unknown3 . 01581 $stream_len . 01582 $dir_long_len . 01583 $unknown4 . 01584 $dir_long . 01585 $sheet_len . 01586 $sheet ; 01587 01588 // Pack the header data 01589 $length = strlen($data); 01590 $header = pack("vv", $record, $length); 01591 01592 // Write the packed data 01593 $this->_append($header. $data); 01594 return($str_error); 01595 } 01596 01597 01598 /* 01599 ############################################################################### 01600 # 01601 # write_url_xxx($row1, $col1, $row2, $col2, $url, $string, $format) 01602 # 01603 # Write links to external MS/Novell network drives and shares such as 01604 # '//NETWORK/share/foo.xls' and '//NETWORK/share/foo.xls#Sheet1!A1'. 01605 # 01606 # See also write_url() above for a general description and return values. 01607 # 01608 sub _write_url_external_net { 01609 01610 my $this = shift; 01611 01612 my $record = 0x01B8; # Record identifier 01613 my $length = 0x00000; # Bytes to follow 01614 01615 my $row1 = $_[0]; # Start row 01616 my $col1 = $_[1]; # Start column 01617 my $row2 = $_[2]; # End row 01618 my $col2 = $_[3]; # End column 01619 my $url = $_[4]; # URL string 01620 my $str = $_[5]; # Alternative label 01621 my $xf = $_[6] || $this->{_url_format};# The cell format 01622 01623 01624 # Strip URL type and change Unix dir separator to Dos style (if needed) 01625 # 01626 $url =~ s[^external:][]; 01627 $url =~ s[/][\\]g; 01628 01629 01630 # Write the visible label 01631 ($str = $url) =~ s[\#][ - ] unless defined $str; 01632 my $str_error = $this->write_string($row1, $col1, $str, $xf); 01633 return $str_error if $str_error == -2; 01634 01635 01636 # Determine if the link contains a sheet reference and change some of the 01637 # parameters accordingly. 01638 # Split the dir name and sheet name (if it exists) 01639 # 01640 my ($dir_long , $sheet) = split /\#/, $url; 01641 my $link_type = 0x0103; # Always absolute 01642 my $sheet_len; 01643 01644 if (defined $sheet) { 01645 $link_type |= 0x08; 01646 $sheet_len = pack("V", length($sheet) + 0x01); 01647 $sheet = join("\0", str_split($sheet)); 01648 $sheet .= "\0\0\0"; 01649 } 01650 else { 01651 $sheet_len = ''; 01652 $sheet = ''; 01653 } 01654 01655 # Pack the link type 01656 $link_type = pack("V", $link_type); 01657 01658 01659 # Make the string null terminated 01660 $dir_long = $dir_long . "\0"; 01661 01662 01663 # Pack the lengths of the dir string 01664 my $dir_long_len = pack("V", length $dir_long); 01665 01666 01667 # Store the long dir name as a wchar string (non-null terminated) 01668 $dir_long = join("\0", str_split($dir_long)); 01669 $dir_long = $dir_long . "\0"; 01670 01671 01672 # Pack the undocumented part of the hyperlink stream 01673 my $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000'); 01674 01675 01676 # Pack the main data stream 01677 my $data = pack("vvvv", $row1, $row2, $col1, $col2) . 01678 $unknown1 . 01679 $link_type . 01680 $dir_long_len . 01681 $dir_long . 01682 $sheet_len . 01683 $sheet ; 01684 01685 01686 # Pack the header data 01687 $length = length $data; 01688 my $header = pack("vv", $record, $length); 01689 01690 01691 # Write the packed data 01692 $this->_append( $header, $data); 01693 01694 return $str_error; 01695 }*/ 01696 01707 function set_row($row, $height, $format = 0) 01708 { 01709 $record = 0x0208; // Record identifier 01710 $length = 0x0010; // Number of bytes to follow 01711 01712 $colMic = 0x0000; // First defined column 01713 $colMac = 0x0000; // Last defined column 01714 $irwMac = 0x0000; // Used by Excel to optimise loading 01715 $reserved = 0x0000; // Reserved 01716 $grbit = 0x01C0; // Option flags. (monkey) see $1 do 01717 $ixfe = $this->_XF($format); // XF index 01718 01719 // Use set_row($row, NULL, $XF) to set XF without setting height 01720 if ($height != NULL) { 01721 $miyRw = $height * 20; // row height 01722 } 01723 else { 01724 $miyRw = 0xff; // default row height is 256 01725 } 01726 01727 $header = pack("vv", $record, $length); 01728 $data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw, 01729 $irwMac,$reserved, $grbit, $ixfe); 01730 $this->_append($header.$data); 01731 } 01732 01736 function _store_dimensions() 01737 { 01738 $record = 0x0000; // Record identifier 01739 $length = 0x000A; // Number of bytes to follow 01740 $row_min = $this->dim_rowmin; // First row 01741 $row_max = $this->dim_rowmax; // Last row plus 1 01742 $col_min = $this->dim_colmin; // First column 01743 $col_max = $this->dim_colmax; // Last column plus 1 01744 $reserved = 0x0000; // Reserved by Excel 01745 01746 $header = pack("vv", $record, $length); 01747 $data = pack("vvvvv", $row_min, $row_max, 01748 $col_min, $col_max, $reserved); 01749 $this->_prepend($header.$data); 01750 } 01751 01755 function _store_window2() 01756 { 01757 $record = 0x023E; // Record identifier 01758 $length = 0x000A; // Number of bytes to follow 01759 01760 $grbit = 0x00B6; // Option flags 01761 $rwTop = 0x0000; // Top row visible in window 01762 $colLeft = 0x0000; // Leftmost column visible in window 01763 $rgbHdr = 0x00000000; // Row/column heading and gridline color 01764 01765 // The options flags that comprise $grbit 01766 $fDspFmla = 0; // 0 - bit 01767 $fDspGrid = 1; // 1 01768 $fDspRwCol = 1; // 2 01769 $fFrozen = $this->_frozen; // 3 01770 $fDspZeros = 1; // 4 01771 $fDefaultHdr = 1; // 5 01772 $fArabic = 0; // 6 01773 $fDspGuts = 1; // 7 01774 $fFrozenNoSplit = 0; // 0 - bit 01775 $fSelected = $this->selected; // 1 01776 $fPaged = 1; // 2 01777 01778 $grbit = $fDspFmla; 01779 $grbit |= $fDspGrid << 1; 01780 $grbit |= $fDspRwCol << 2; 01781 $grbit |= $fFrozen << 3; 01782 $grbit |= $fDspZeros << 4; 01783 $grbit |= $fDefaultHdr << 5; 01784 $grbit |= $fArabic << 6; 01785 $grbit |= $fDspGuts << 7; 01786 $grbit |= $fFrozenNoSplit << 8; 01787 $grbit |= $fSelected << 9; 01788 $grbit |= $fPaged << 10; 01789 01790 $header = pack("vv", $record, $length); 01791 $data = pack("vvvV", $grbit, $rwTop, $colLeft, $rgbHdr); 01792 $this->_append($header.$data); 01793 } 01794 01798 function _store_defcol() 01799 { 01800 $record = 0x0055; // Record identifier 01801 $length = 0x0002; // Number of bytes to follow 01802 $colwidth = 0x0008; // Default column width 01803 01804 $header = pack("vv", $record, $length); 01805 $data = pack("v", $colwidth); 01806 $this->_prepend($header.$data); 01807 } 01808 01822 function _store_colinfo($col_array) 01823 { 01824 if(isset($col_array[0])) { 01825 $colFirst = $col_array[0]; 01826 } 01827 if(isset($col_array[1])) { 01828 $colLast = $col_array[1]; 01829 } 01830 if(isset($col_array[2])) { 01831 $coldx = $col_array[2]; 01832 } 01833 else { 01834 $coldx = 8.43; 01835 } 01836 if(isset($col_array[3])) { 01837 $format = $col_array[3]; 01838 } 01839 else { 01840 $format = 0; 01841 } 01842 if(isset($col_array[4])) { 01843 $grbit = $col_array[4]; 01844 } 01845 else { 01846 $grbit = 0; 01847 } 01848 $record = 0x007D; // Record identifier 01849 $length = 0x000B; // Number of bytes to follow 01850 01851 $coldx += 0.72; // Fudge. Excel subtracts 0.72 !? 01852 $coldx *= 256; // Convert to units of 1/256 of a char 01853 01854 $ixfe = $this->_XF($format); 01855 $reserved = 0x00; // Reserved 01856 01857 $header = pack("vv", $record, $length); 01858 $data = pack("vvvvvC", $colFirst, $colLast, $coldx, 01859 $ixfe, $grbit, $reserved); 01860 $this->_prepend($header.$data); 01861 } 01862 01869 function _store_selection($array) 01870 { 01871 list($rwFirst,$colFirst,$rwLast,$colLast) = $array; 01872 $record = 0x001D; // Record identifier 01873 $length = 0x000F; // Number of bytes to follow 01874 01875 $pnn = $this->_active_pane; // Pane position 01876 $rwAct = $rwFirst; // Active row 01877 $colAct = $colFirst; // Active column 01878 $irefAct = 0; // Active cell ref 01879 $cref = 1; // Number of refs 01880 01881 if (!isset($rwLast)) { 01882 $rwLast = $rwFirst; // Last row in reference 01883 } 01884 if (!isset($colLast)) { 01885 $colLast = $colFirst; // Last col in reference 01886 } 01887 01888 // Swap last row/col for first row/col as necessary 01889 if ($rwFirst > $rwLast) 01890 { 01891 list($rwFirst, $rwLast) = array($rwLast, $rwFirst); 01892 } 01893 01894 if ($colFirst > $colLast) 01895 { 01896 list($colFirst, $colLast) = array($colLast, $colFirst); 01897 } 01898 01899 $header = pack("vv", $record, $length); 01900 $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct, 01901 $irefAct, $cref, 01902 $rwFirst, $rwLast, 01903 $colFirst, $colLast); 01904 $this->_append($header.$data); 01905 } 01906 01907 01920 function _store_externcount($count) 01921 { 01922 $record = 0x0016; // Record identifier 01923 $length = 0x0002; // Number of bytes to follow 01924 01925 $header = pack("vv", $record, $length); 01926 $data = pack("v", $count); 01927 $this->_prepend($header.$data); 01928 } 01929 01938 function _store_externsheet($sheetname) 01939 { 01940 $record = 0x0017; // Record identifier 01941 01942 // References to the current sheet are encoded differently to references to 01943 // external sheets. 01944 // 01945 if ($this->name == $sheetname) { 01946 $sheetname = ''; 01947 $length = 0x02; // The following 2 bytes 01948 $cch = 1; // The following byte 01949 $rgch = 0x02; // Self reference 01950 } 01951 else { 01952 $length = 0x02 + strlen($sheetname); 01953 $cch = strlen($sheetname); 01954 $rgch = 0x03; // Reference to a sheet in the current workbook 01955 } 01956 01957 $header = pack("vv", $record, $length); 01958 $data = pack("CC", $cch, $rgch); 01959 $this->_prepend($header.$data.$sheetname); 01960 } 01961 01975 function _store_panes($panes) 01976 { 01977 $y = $panes[0]; 01978 $x = $panes[1]; 01979 $rwTop = $panes[2]; 01980 $colLeft = $panes[3]; 01981 if(count($panes) > 4) { // if Active pane was received 01982 $pnnAct = $panes[4]; 01983 } 01984 else { 01985 $pnnAct = NULL; 01986 } 01987 $record = 0x0041; // Record identifier 01988 $length = 0x000A; // Number of bytes to follow 01989 01990 // Code specific to frozen or thawed panes. 01991 if ($this->_frozen) { 01992 // Set default values for $rwTop and $colLeft 01993 if(!isset($rwTop)) { 01994 $rwTop = $y; 01995 } 01996 if(!isset($colLeft)) { 01997 $colLeft = $x; 01998 } 01999 } 02000 else { 02001 // Set default values for $rwTop and $colLeft 02002 if(!isset($rwTop)) { 02003 $rwTop = 0; 02004 } 02005 if(!isset($colLeft)) { 02006 $colLeft = 0; 02007 } 02008 02009 // Convert Excel's row and column units to the internal units. 02010 // The default row height is 12.75 02011 // The default column width is 8.43 02012 // The following slope and intersection values were interpolated. 02013 // 02014 $y = 20*$y + 255; 02015 $x = 113.879*$x + 390; 02016 } 02017 02018 02019 // Determine which pane should be active. There is also the undocumented 02020 // option to override this should it be necessary: may be removed later. 02021 // 02022 if (!isset($pnnAct)) 02023 { 02024 if ($x != 0 and $y != 0) 02025 $pnnAct = 0; // Bottom right 02026 if ($x != 0 and $y == 0) 02027 $pnnAct = 1; // Top right 02028 if ($x == 0 and $y != 0) 02029 $pnnAct = 2; // Bottom left 02030 if ($x == 0 and $y == 0) 02031 $pnnAct = 3; // Top left 02032 } 02033 02034 $this->_active_pane = $pnnAct; // Used in _store_selection 02035 02036 $header = pack("vv", $record, $length); 02037 $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct); 02038 $this->_append($header.$data); 02039 } 02040 02044 function _store_setup() 02045 { 02046 $record = 0x00A1; // Record identifier 02047 $length = 0x0022; // Number of bytes to follow 02048 02049 $iPaperSize = $this->_paper_size; // Paper size 02050 $iScale = $this->_print_scale; // Print scaling factor 02051 $iPageStart = 0x01; // Starting page number 02052 $iFitWidth = $this->_fit_width; // Fit to number of pages wide 02053 $iFitHeight = $this->_fit_height; // Fit to number of pages high 02054 $grbit = 0x00; // Option flags 02055 $iRes = 0x0258; // Print resolution 02056 $iVRes = 0x0258; // Vertical print resolution 02057 $numHdr = $this->_margin_head; // Header Margin 02058 $numFtr = $this->_margin_foot; // Footer Margin 02059 $iCopies = 0x01; // Number of copies 02060 02061 $fLeftToRight = 0x0; // Print over then down 02062 $fLandscape = $this->_orientation; // Page orientation 02063 $fNoPls = 0x0; // Setup not read from printer 02064 $fNoColor = 0x0; // Print black and white 02065 $fDraft = 0x0; // Print draft quality 02066 $fNotes = 0x0; // Print notes 02067 $fNoOrient = 0x0; // Orientation not set 02068 $fUsePage = 0x0; // Use custom starting page 02069 02070 $grbit = $fLeftToRight; 02071 $grbit |= $fLandscape << 1; 02072 $grbit |= $fNoPls << 2; 02073 $grbit |= $fNoColor << 3; 02074 $grbit |= $fDraft << 4; 02075 $grbit |= $fNotes << 5; 02076 $grbit |= $fNoOrient << 6; 02077 $grbit |= $fUsePage << 7; 02078 02079 $numHdr = pack("d", $numHdr); 02080 $numFtr = pack("d", $numFtr); 02081 if ($this->_byte_order) // if it's Big Endian 02082 { 02083 $numHdr = strrev($numHdr); 02084 $numFtr = strrev($numFtr); 02085 } 02086 02087 $header = pack("vv", $record, $length); 02088 $data1 = pack("vvvvvvvv", $iPaperSize, 02089 $iScale, 02090 $iPageStart, 02091 $iFitWidth, 02092 $iFitHeight, 02093 $grbit, 02094 $iRes, 02095 $iVRes); 02096 $data2 = $numHdr .$numFtr; 02097 $data3 = pack("v", $iCopies); 02098 $this->_prepend($header.$data1.$data2.$data3); 02099 } 02100 02104 function store_header() 02105 { 02106 $record = 0x0014; // Record identifier 02107 02108 $str = $this->_header; // header string 02109 $cch = strlen($str); // Length of header string 02110 $length = 1 + $cch; // Bytes to follow 02111 02112 $header = pack("vv", $record, $length); 02113 $data = pack("C", $cch); 02114 02115 $this->_append($header.$data.$str); 02116 } 02117 02121 function store_footer() 02122 { 02123 $record = 0x0015; // Record identifier 02124 02125 $str = $this->_footer; // Footer string 02126 $cch = strlen($str); // Length of footer string 02127 $length = 1 + $cch; // Bytes to follow 02128 02129 $header = pack("vv", $record, $length); 02130 $data = pack("C", $cch); 02131 02132 $this->_append($header.$data.$str); 02133 } 02134 02138 function store_hcenter() 02139 { 02140 $record = 0x0083; // Record identifier 02141 $length = 0x0002; // Bytes to follow 02142 02143 $fHCenter = $this->_hcenter; // Horizontal centering 02144 02145 $header = pack("vv", $record, $length); 02146 $data = pack("v", $fHCenter); 02147 02148 $this->_append($header.$data); 02149 } 02150 02154 function store_vcenter() 02155 { 02156 $record = 0x0084; // Record identifier 02157 $length = 0x0002; // Bytes to follow 02158 02159 $fVCenter = $this->_vcenter; // Horizontal centering 02160 02161 $header = pack("vv", $record, $length); 02162 $data = pack("v", $fVCenter); 02163 $this->_append($header.$data); 02164 } 02165 02169 function _store_margin_left() 02170 { 02171 $record = 0x0026; // Record identifier 02172 $length = 0x0008; // Bytes to follow 02173 02174 $margin = $this->_margin_left; // Margin in inches 02175 02176 $header = pack("vv", $record, $length); 02177 $data = pack("d", $margin); 02178 if ($this->_byte_order) // if it's Big Endian 02179 { 02180 $data = strrev($data); 02181 } 02182 02183 $this->_append($header.$data); 02184 } 02185 02189 function _store_margin_right() 02190 { 02191 $record = 0x0027; // Record identifier 02192 $length = 0x0008; // Bytes to follow 02193 02194 $margin = $this->_margin_right; // Margin in inches 02195 02196 $header = pack("vv", $record, $length); 02197 $data = pack("d", $margin); 02198 if ($this->_byte_order) // if it's Big Endian 02199 { 02200 $data = strrev($data); 02201 } 02202 02203 $this->_append($header.$data); 02204 } 02205 02209 function _store_margin_top() 02210 { 02211 $record = 0x0028; // Record identifier 02212 $length = 0x0008; // Bytes to follow 02213 02214 $margin = $this->_margin_top; // Margin in inches 02215 02216 $header = pack("vv", $record, $length); 02217 $data = pack("d", $margin); 02218 if ($this->_byte_order) // if it's Big Endian 02219 { 02220 $data = strrev($data); 02221 } 02222 02223 $this->_append($header.$data); 02224 } 02225 02229 function _store_margin_bottom() 02230 { 02231 $record = 0x0029; // Record identifier 02232 $length = 0x0008; // Bytes to follow 02233 02234 $margin = $this->_margin_bottom; // Margin in inches 02235 02236 $header = pack("vv", $record, $length); 02237 $data = pack("d", $margin); 02238 if ($this->_byte_order) // if it's Big Endian 02239 { 02240 $data = strrev($data); 02241 } 02242 02243 $this->_append($header.$data); 02244 } 02245 02257 function merge_cells($first_row, $first_col, $last_row, $last_col) 02258 { 02259 $record = 0x00E5; // Record identifier 02260 $length = 0x000A; // Bytes to follow 02261 $cref = 1; // Number of refs 02262 02263 // Swap last row/col for first row/col as necessary 02264 if ($first_row > $last_row) { 02265 list($first_row, $last_row) = array($last_row, $first_row); 02266 } 02267 02268 if ($first_col > $last_col) { 02269 list($first_col, $last_col) = array($last_col, $first_col); 02270 } 02271 02272 $header = pack("vv", $record, $length); 02273 $data = pack("vvvvv", $cref, $first_row, $last_row, 02274 $first_col, $last_col); 02275 02276 $this->_append($header.$data); 02277 } 02278 02282 function _store_print_headers() 02283 { 02284 $record = 0x002a; // Record identifier 02285 $length = 0x0002; // Bytes to follow 02286 02287 $fPrintRwCol = $this->_print_headers; // Boolean flag 02288 02289 $header = pack("vv", $record, $length); 02290 $data = pack("v", $fPrintRwCol); 02291 $this->_prepend($header.$data); 02292 } 02293 02298 function _store_print_gridlines() 02299 { 02300 $record = 0x002b; // Record identifier 02301 $length = 0x0002; // Bytes to follow 02302 02303 $fPrintGrid = $this->_print_gridlines; // Boolean flag 02304 02305 $header = pack("vv", $record, $length); 02306 $data = pack("v", $fPrintGrid); 02307 $this->_prepend($header.$data); 02308 } 02309 02314 function _store_gridset() 02315 { 02316 $record = 0x0082; // Record identifier 02317 $length = 0x0002; // Bytes to follow 02318 02319 $fGridSet = !($this->_print_gridlines); // Boolean flag 02320 02321 $header = pack("vv", $record, $length); 02322 $data = pack("v", $fGridSet); 02323 $this->_prepend($header.$data); 02324 } 02325 02330 function _store_wsbool() 02331 { 02332 $record = 0x0081; // Record identifier 02333 $length = 0x0002; // Bytes to follow 02334 02335 // The only option that is of interest is the flag for fit to page. So we 02336 // set all the options in one go. 02337 // 02338 if ($this->_fit_page) { 02339 $grbit = 0x05c1; 02340 } 02341 else { 02342 $grbit = 0x04c1; 02343 } 02344 02345 $header = pack("vv", $record, $length); 02346 $data = pack("v", $grbit); 02347 $this->_prepend($header.$data); 02348 } 02349 02350 02354 function _store_hbreak() 02355 { 02356 // Return if the user hasn't specified pagebreaks 02357 if(empty($this->_hbreaks)) { 02358 return; 02359 } 02360 02361 // Sort and filter array of page breaks 02362 $breaks = $this->_hbreaks; 02363 sort($breaks,SORT_NUMERIC); 02364 if($breaks[0] == 0) { // don't use first break if it's 0 02365 array_shift($breaks); 02366 } 02367 02368 $record = 0x001b; // Record identifier 02369 $cbrk = count($breaks); // Number of page breaks 02370 $length = ($cbrk + 1) * 2; // Bytes to follow 02371 02372 $header = pack("vv", $record, $length); 02373 $data = pack("v", $cbrk); 02374 02375 // Append each page break 02376 foreach($breaks as $break) { 02377 $data .= pack("v", $break); 02378 } 02379 02380 $this->_prepend($header.$data); 02381 } 02382 02383 02387 function _store_vbreak() 02388 { 02389 // Return if the user hasn't specified pagebreaks 02390 if(empty($this->_vbreaks)) { 02391 return; 02392 } 02393 02394 // 1000 vertical pagebreaks appears to be an internal Excel 5 limit. 02395 // It is slightly higher in Excel 97/200, approx. 1026 02396 $breaks = array_slice($this->_vbreaks,0,1000); 02397 02398 // Sort and filter array of page breaks 02399 sort($breaks,SORT_NUMERIC); 02400 if($breaks[0] == 0) { // don't use first break if it's 0 02401 array_shift($breaks); 02402 } 02403 02404 $record = 0x001a; // Record identifier 02405 $cbrk = count($breaks); // Number of page breaks 02406 $length = ($cbrk + 1) * 2; // Bytes to follow 02407 02408 $header = pack("vv", $record, $length); 02409 $data = pack("v", $cbrk); 02410 02411 // Append each page break 02412 foreach ($breaks as $break) { 02413 $data .= pack("v", $break); 02414 } 02415 02416 $this->_prepend($header.$data); 02417 } 02418 02422 function _store_protect() 02423 { 02424 // Exit unless sheet protection has been specified 02425 if($this->_protect == 0) { 02426 return; 02427 } 02428 02429 $record = 0x0012; // Record identifier 02430 $length = 0x0002; // Bytes to follow 02431 02432 $fLock = $this->_protect; // Worksheet is protected 02433 02434 $header = pack("vv", $record, $length); 02435 $data = pack("v", $fLock); 02436 02437 $this->_prepend($header.$data); 02438 } 02439 02443 function _store_password() 02444 { 02445 // Exit unless sheet protection and password have been specified 02446 if(($this->_protect == 0) or (!isset($this->_password))) { 02447 return; 02448 } 02449 02450 $record = 0x0013; // Record identifier 02451 $length = 0x0002; // Bytes to follow 02452 02453 $wPassword = $this->_password; // Encoded password 02454 02455 $header = pack("vv", $record, $length); 02456 $data = pack("v", $wPassword); 02457 02458 $this->_prepend($header.$data); 02459 } 02460 02474 function insert_bitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) 02475 { 02476 list($width, $height, $size, $data) = $this->_process_bitmap($bitmap); 02477 02478 // Scale the frame of the image. 02479 $width *= $scale_x; 02480 $height *= $scale_y; 02481 02482 // Calculate the vertices of the image and write the OBJ record 02483 $this->_position_image($col, $row, $x, $y, $width, $height); 02484 02485 // Write the IMDATA record to store the bitmap data 02486 $record = 0x007f; 02487 $length = 8 + $size; 02488 $cf = 0x09; 02489 $env = 0x01; 02490 $lcb = $size; 02491 02492 $header = pack("vvvvV", $record, $length, $cf, $env, $lcb); 02493 $this->_append($header.$data); 02494 } 02495 02546 function _position_image($col_start, $row_start, $x1, $y1, $width, $height) 02547 { 02548 // Initialise end cell to the same as the start cell 02549 $col_end = $col_start; // Col containing lower right corner of object 02550 $row_end = $row_start; // Row containing bottom right corner of object 02551 02552 // Zero the specified offset if greater than the cell dimensions 02553 if ($x1 >= $this->size_col($col_start)) 02554 { 02555 $x1 = 0; 02556 } 02557 if ($y1 >= $this->size_row($row_start)) 02558 { 02559 $y1 = 0; 02560 } 02561 02562 $width = $width + $x1 -1; 02563 $height = $height + $y1 -1; 02564 02565 // Subtract the underlying cell widths to find the end cell of the image 02566 while ($width >= $this->size_col($col_end)) { 02567 $width -= $this->size_col($col_end); 02568 $col_end++; 02569 } 02570 02571 // Subtract the underlying cell heights to find the end cell of the image 02572 while ($height >= $this->size_row($row_end)) { 02573 $height -= $this->size_row($row_end); 02574 $row_end++; 02575 } 02576 02577 // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell 02578 // with zero eight or width. 02579 // 02580 if ($this->size_col($col_start) == 0) 02581 return; 02582 if ($this->size_col($col_end) == 0) 02583 return; 02584 if ($this->size_row($row_start) == 0) 02585 return; 02586 if ($this->size_row($row_end) == 0) 02587 return; 02588 02589 // Convert the pixel values to the percentage value expected by Excel 02590 $x1 = $x1 / $this->size_col($col_start) * 1024; 02591 $y1 = $y1 / $this->size_row($row_start) * 256; 02592 $x2 = $width / $this->size_col($col_end) * 1024; // Distance to right side of object 02593 $y2 = $height / $this->size_row($row_end) * 256; // Distance to bottom of object 02594 02595 $this->_store_obj_picture( $col_start, $x1, 02596 $row_start, $y1, 02597 $col_end, $x2, 02598 $row_end, $y2 02599 ); 02600 } 02601 02610 function size_col($col) 02611 { 02612 // Look up the cell value to see if it has been changed 02613 if (isset($this->col_sizes[$col])) { 02614 if ($this->col_sizes[$col] == 0) { 02615 return(0); 02616 } 02617 else { 02618 return(floor(7 * $this->col_sizes[$col] + 5)); 02619 } 02620 } 02621 else { 02622 return(64); 02623 } 02624 } 02625 02635 function size_row($row) 02636 { 02637 // Look up the cell value to see if it has been changed 02638 if (isset($this->row_sizes[$row])) { 02639 if ($this->row_sizes[$row] == 0) { 02640 return(0); 02641 } 02642 else { 02643 return(floor(4/3 * $this->row_sizes[$row])); 02644 } 02645 } 02646 else { 02647 return(17); 02648 } 02649 } 02650 02664 function _store_obj_picture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB) 02665 { 02666 $record = 0x005d; // Record identifier 02667 $length = 0x003c; // Bytes to follow 02668 02669 $cObj = 0x0001; // Count of objects in file (set to 1) 02670 $OT = 0x0008; // Object type. 8 = Picture 02671 $id = 0x0001; // Object ID 02672 $grbit = 0x0614; // Option flags 02673 02674 $cbMacro = 0x0000; // Length of FMLA structure 02675 $Reserved1 = 0x0000; // Reserved 02676 $Reserved2 = 0x0000; // Reserved 02677 02678 $icvBack = 0x09; // Background colour 02679 $icvFore = 0x09; // Foreground colour 02680 $fls = 0x00; // Fill pattern 02681 $fAuto = 0x00; // Automatic fill 02682 $icv = 0x08; // Line colour 02683 $lns = 0xff; // Line style 02684 $lnw = 0x01; // Line weight 02685 $fAutoB = 0x00; // Automatic border 02686 $frs = 0x0000; // Frame style 02687 $cf = 0x0009; // Image format, 9 = bitmap 02688 $Reserved3 = 0x0000; // Reserved 02689 $cbPictFmla = 0x0000; // Length of FMLA structure 02690 $Reserved4 = 0x0000; // Reserved 02691 $grbit2 = 0x0001; // Option flags 02692 $Reserved5 = 0x0000; // Reserved 02693 02694 02695 $header = pack("vv", $record, $length); 02696 $data = pack("V", $cObj); 02697 $data .= pack("v", $OT); 02698 $data .= pack("v", $id); 02699 $data .= pack("v", $grbit); 02700 $data .= pack("v", $colL); 02701 $data .= pack("v", $dxL); 02702 $data .= pack("v", $rwT); 02703 $data .= pack("v", $dyT); 02704 $data .= pack("v", $colR); 02705 $data .= pack("v", $dxR); 02706 $data .= pack("v", $rwB); 02707 $data .= pack("v", $dyB); 02708 $data .= pack("v", $cbMacro); 02709 $data .= pack("V", $Reserved1); 02710 $data .= pack("v", $Reserved2); 02711 $data .= pack("C", $icvBack); 02712 $data .= pack("C", $icvFore); 02713 $data .= pack("C", $fls); 02714 $data .= pack("C", $fAuto); 02715 $data .= pack("C", $icv); 02716 $data .= pack("C", $lns); 02717 $data .= pack("C", $lnw); 02718 $data .= pack("C", $fAutoB); 02719 $data .= pack("v", $frs); 02720 $data .= pack("V", $cf); 02721 $data .= pack("v", $Reserved3); 02722 $data .= pack("v", $cbPictFmla); 02723 $data .= pack("v", $Reserved4); 02724 $data .= pack("v", $grbit2); 02725 $data .= pack("V", $Reserved5); 02726 02727 $this->_append($header.$data); 02728 } 02729 02738 function _process_bitmap($bitmap) 02739 { 02740 // Open file. 02741 $bmp_fd = fopen($bitmap,"rb"); 02742 if (!$bmp_fd) { 02743 die("Couldn't import $bitmap"); 02744 } 02745 02746 // Slurp the file into a string. 02747 $data = fread($bmp_fd, filesize($bitmap)); 02748 02749 // Check that the file is big enough to be a bitmap. 02750 if (strlen($data) <= 0x36) { 02751 die("$bitmap doesn't contain enough data.\n"); 02752 } 02753 02754 // The first 2 bytes are used to identify the bitmap. 02755 $identity = unpack("A2", $data); 02756 if ($identity[''] != "BM") { 02757 die("$bitmap doesn't appear to be a valid bitmap image.\n"); 02758 } 02759 02760 // Remove bitmap data: ID. 02761 $data = substr($data, 2); 02762 02763 // Read and remove the bitmap size. This is more reliable than reading 02764 // the data size at offset 0x22. 02765 // 02766 $size_array = unpack("V", substr($data, 0, 4)); 02767 $size = $size_array['']; 02768 $data = substr($data, 4); 02769 $size -= 0x36; // Subtract size of bitmap header. 02770 $size += 0x0C; // Add size of BIFF header. 02771 02772 // Remove bitmap data: reserved, offset, header length. 02773 $data = substr($data, 12); 02774 02775 // Read and remove the bitmap width and height. Verify the sizes. 02776 $width_and_height = unpack("V2", substr($data, 0, 8)); 02777 $width = $width_and_height[1]; 02778 $height = $width_and_height[2]; 02779 $data = substr($data, 8); 02780 if ($width > 0xFFFF) { 02781 die("$bitmap: largest image width supported is 65k.\n"); 02782 } 02783 if ($height > 0xFFFF) { 02784 die("$bitmap: largest image height supported is 65k.\n"); 02785 } 02786 02787 // Read and remove the bitmap planes and bpp data. Verify them. 02788 $planes_and_bitcount = unpack("v2", substr($data, 0, 4)); 02789 $data = substr($data, 4); 02790 if ($planes_and_bitcount[2] != 24) { // Bitcount 02791 die("$bitmap isn't a 24bit true color bitmap.\n"); 02792 } 02793 if ($planes_and_bitcount[1] != 1) { 02794 die("$bitmap: only 1 plane supported in bitmap image.\n"); 02795 } 02796 02797 // Read and remove the bitmap compression. Verify compression. 02798 $compression = unpack("V", substr($data, 0, 4)); 02799 $data = substr($data, 4); 02800 02801 //$compression = 0; 02802 if ($compression[""] != 0) { 02803 die("$bitmap: compression not supported in bitmap image.\n"); 02804 } 02805 02806 // Remove bitmap data: data size, hres, vres, colours, imp. colours. 02807 $data = substr($data, 20); 02808 02809 // Add the BITMAPCOREHEADER data 02810 $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18); 02811 $data = $header . $data; 02812 02813 return (array($width, $height, $size, $data)); 02814 } 02815 02820 function _store_zoom() 02821 { 02822 // If scale is 100 we don't need to write a record 02823 if ($this->_zoom == 100) { 02824 return; 02825 } 02826 02827 $record = 0x00A0; // Record identifier 02828 $length = 0x0004; // Bytes to follow 02829 02830 $header = pack("vv", $record, $length); 02831 $data = pack("vv", $this->_zoom, 100); 02832 $this->_append($header.$data); 02833 } 02834 } 02835 ?>