|
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_Excel_Writer: A library for generating Excel Spreadsheets 00018 * Copyright (c) 2002-2003 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 'Spreadsheet/Excel/Writer/Parser.php'; 00036 require_once 'Spreadsheet/Excel/Writer/BIFFwriter.php'; 00037 00046 class Spreadsheet_Excel_Writer_Worksheet extends Spreadsheet_Excel_Writer_BIFFwriter 00047 { 00052 var $name; 00053 00058 var $index; 00059 00064 var $_url_format; 00065 00070 var $_parser; 00071 00076 var $_filehandle; 00077 00082 var $_using_tmpfile; 00083 00088 var $_xls_rowmax; 00089 00094 var $_xls_colmax; 00095 00100 var $_xls_strmax; 00101 00107 var $_dim_rowmin; 00108 00114 var $_dim_rowmax; 00115 00121 var $_dim_colmin; 00122 00128 var $_dim_colmax; 00129 00134 var $_colinfo; 00135 00140 var $_selection; 00141 00146 var $_panes; 00147 00152 var $_active_pane; 00153 00158 var $_frozen; 00159 00164 var $selected; 00165 00170 var $_paper_size; 00171 00176 var $_orientation; 00177 00182 var $_header; 00183 00188 var $_footer; 00189 00194 var $_hcenter; 00195 00200 var $_vcenter; 00201 00206 var $_margin_head; 00207 00212 var $_margin_foot; 00213 00218 var $_margin_left; 00219 00224 var $_margin_right; 00225 00230 var $_margin_top; 00231 00236 var $_margin_bottom; 00237 00242 var $title_rowmin; 00243 00248 var $title_rowmax; 00249 00254 var $title_colmin; 00255 00260 var $print_rowmin; 00261 00266 var $print_rowmax; 00267 00272 var $print_colmin; 00273 00278 var $print_colmax; 00279 00284 var $_outline_on; 00285 00290 var $_outline_style; 00291 00296 var $_outline_below; 00297 00302 var $_outline_right; 00303 00308 var $_outline_row_level; 00309 00314 var $_fit_page; 00315 00320 var $_fit_width; 00321 00326 var $_fit_height; 00327 00332 var $_str_total; 00333 00338 var $_str_unique; 00339 00344 var $_str_table; 00345 00350 var $_merged_ranges; 00351 00356 var $_input_encoding; 00357 00369 function Spreadsheet_Excel_Writer_Worksheet($BIFF_version, $name, 00370 $index, &$activesheet, 00371 &$firstsheet, &$str_total, 00372 &$str_unique, &$str_table, 00373 &$url_format, &$parser) 00374 { 00375 // It needs to call its parent's constructor explicitly 00376 $this->Spreadsheet_Excel_Writer_BIFFwriter(); 00377 $this->_BIFF_version = $BIFF_version; 00378 $rowmax = 65536; // 16384 in Excel 5 00379 $colmax = 256; 00380 00381 $this->name = $name; 00382 $this->index = $index; 00383 $this->activesheet = &$activesheet; 00384 $this->firstsheet = &$firstsheet; 00385 $this->_str_total = &$str_total; 00386 $this->_str_unique = &$str_unique; 00387 $this->_str_table = &$str_table; 00388 $this->_url_format = &$url_format; 00389 $this->_parser = &$parser; 00390 00391 //$this->ext_sheets = array(); 00392 $this->_filehandle = ''; 00393 $this->_using_tmpfile = true; 00394 //$this->fileclosed = 0; 00395 //$this->offset = 0; 00396 $this->_xls_rowmax = $rowmax; 00397 $this->_xls_colmax = $colmax; 00398 $this->_xls_strmax = 255; 00399 $this->_dim_rowmin = $rowmax + 1; 00400 $this->_dim_rowmax = 0; 00401 $this->_dim_colmin = $colmax + 1; 00402 $this->_dim_colmax = 0; 00403 $this->_colinfo = array(); 00404 $this->_selection = array(0,0,0,0); 00405 $this->_panes = array(); 00406 $this->_active_pane = 3; 00407 $this->_frozen = 0; 00408 $this->selected = 0; 00409 00410 $this->_paper_size = 0x0; 00411 $this->_orientation = 0x1; 00412 $this->_header = ''; 00413 $this->_footer = ''; 00414 $this->_hcenter = 0; 00415 $this->_vcenter = 0; 00416 $this->_margin_head = 0.50; 00417 $this->_margin_foot = 0.50; 00418 $this->_margin_left = 0.75; 00419 $this->_margin_right = 0.75; 00420 $this->_margin_top = 1.00; 00421 $this->_margin_bottom = 1.00; 00422 00423 $this->title_rowmin = null; 00424 $this->title_rowmax = null; 00425 $this->title_colmin = null; 00426 $this->title_colmax = null; 00427 $this->print_rowmin = null; 00428 $this->print_rowmax = null; 00429 $this->print_colmin = null; 00430 $this->print_colmax = null; 00431 00432 $this->_print_gridlines = 1; 00433 $this->_screen_gridlines = 1; 00434 $this->_print_headers = 0; 00435 00436 $this->_fit_page = 0; 00437 $this->_fit_width = 0; 00438 $this->_fit_height = 0; 00439 00440 $this->_hbreaks = array(); 00441 $this->_vbreaks = array(); 00442 00443 $this->_protect = 0; 00444 $this->_password = null; 00445 00446 $this->col_sizes = array(); 00447 $this->_row_sizes = array(); 00448 00449 $this->_zoom = 100; 00450 $this->_print_scale = 100; 00451 00452 $this->_outline_row_level = 0; 00453 $this->_outline_style = 0; 00454 $this->_outline_below = 1; 00455 $this->_outline_right = 1; 00456 $this->_outline_on = 1; 00457 00458 $this->_merged_ranges = array(); 00459 00460 $this->_input_encoding = ''; 00461 00462 $this->_dv = array(); 00463 00464 $this->_initialize(); 00465 } 00466 00474 function _initialize() 00475 { 00476 // Open tmp file for storing Worksheet data 00477 $fh = tmpfile(); 00478 if ($fh) { 00479 // Store filehandle 00480 $this->_filehandle = $fh; 00481 } else { 00482 // If tmpfile() fails store data in memory 00483 $this->_using_tmpfile = false; 00484 } 00485 } 00486 00496 function close($sheetnames) 00497 { 00498 $num_sheets = count($sheetnames); 00499 00500 /*********************************************** 00501 * Prepend in reverse order!! 00502 */ 00503 00504 // Prepend the sheet dimensions 00505 $this->_storeDimensions(); 00506 00507 // Prepend the sheet password 00508 $this->_storePassword(); 00509 00510 // Prepend the sheet protection 00511 $this->_storeProtect(); 00512 00513 // Prepend the page setup 00514 $this->_storeSetup(); 00515 00516 /* FIXME: margins are actually appended */ 00517 // Prepend the bottom margin 00518 $this->_storeMarginBottom(); 00519 00520 // Prepend the top margin 00521 $this->_storeMarginTop(); 00522 00523 // Prepend the right margin 00524 $this->_storeMarginRight(); 00525 00526 // Prepend the left margin 00527 $this->_storeMarginLeft(); 00528 00529 // Prepend the page vertical centering 00530 $this->_storeVcenter(); 00531 00532 // Prepend the page horizontal centering 00533 $this->_storeHcenter(); 00534 00535 // Prepend the page footer 00536 $this->_storeFooter(); 00537 00538 // Prepend the page header 00539 $this->_storeHeader(); 00540 00541 // Prepend the vertical page breaks 00542 $this->_storeVbreak(); 00543 00544 // Prepend the horizontal page breaks 00545 $this->_storeHbreak(); 00546 00547 // Prepend WSBOOL 00548 $this->_storeWsbool(); 00549 00550 // Prepend GRIDSET 00551 $this->_storeGridset(); 00552 00553 // Prepend GUTS 00554 if ($this->_BIFF_version == 0x0500) { 00555 $this->_storeGuts(); 00556 } 00557 00558 // Prepend PRINTGRIDLINES 00559 $this->_storePrintGridlines(); 00560 00561 // Prepend PRINTHEADERS 00562 $this->_storePrintHeaders(); 00563 00564 // Prepend EXTERNSHEET references 00565 if ($this->_BIFF_version == 0x0500) { 00566 for ($i = $num_sheets; $i > 0; $i--) { 00567 $sheetname = $sheetnames[$i-1]; 00568 $this->_storeExternsheet($sheetname); 00569 } 00570 } 00571 00572 // Prepend the EXTERNCOUNT of external references. 00573 if ($this->_BIFF_version == 0x0500) { 00574 $this->_storeExterncount($num_sheets); 00575 } 00576 00577 // Prepend the COLINFO records if they exist 00578 if (!empty($this->_colinfo)) { 00579 $colcount = count($this->_colinfo); 00580 for ($i = 0; $i < $colcount; $i++) { 00581 $this->_storeColinfo($this->_colinfo[$i]); 00582 } 00583 $this->_storeDefcol(); 00584 } 00585 00586 // Prepend the BOF record 00587 $this->_storeBof(0x0010); 00588 00589 /* 00590 * End of prepend. Read upwards from here. 00591 ***********************************************/ 00592 00593 // Append 00594 $this->_storeWindow2(); 00595 $this->_storeZoom(); 00596 if (!empty($this->_panes)) { 00597 $this->_storePanes($this->_panes); 00598 } 00599 $this->_storeSelection($this->_selection); 00600 $this->_storeMergedCells(); 00601 /* TODO: add data validity */ 00602 /*if ($this->_BIFF_version == 0x0600) { 00603 $this->_storeDataValidity(); 00604 }*/ 00605 $this->_storeEof(); 00606 } 00607 00615 function getName() 00616 { 00617 return $this->name; 00618 } 00619 00626 function getData() 00627 { 00628 $buffer = 4096; 00629 00630 // Return data stored in memory 00631 if (isset($this->_data)) { 00632 $tmp = $this->_data; 00633 unset($this->_data); 00634 $fh = $this->_filehandle; 00635 if ($this->_using_tmpfile) { 00636 fseek($fh, 0); 00637 } 00638 return $tmp; 00639 } 00640 // Return data stored on disk 00641 if ($this->_using_tmpfile) { 00642 if ($tmp = fread($this->_filehandle, $buffer)) { 00643 return $tmp; 00644 } 00645 } 00646 00647 // No data to return 00648 return ''; 00649 } 00650 00660 function setMerge($first_row, $first_col, $last_row, $last_col) 00661 { 00662 if (($last_row < $first_row) || ($last_col < $first_col)) { 00663 return; 00664 } 00665 // don't check rowmin, rowmax, etc... because we don't know when this 00666 // is going to be called 00667 $this->_merged_ranges[] = array($first_row, $first_col, $last_row, $last_col); 00668 } 00669 00676 function select() 00677 { 00678 $this->selected = 1; 00679 } 00680 00688 function activate() 00689 { 00690 $this->selected = 1; 00691 $this->activesheet = $this->index; 00692 } 00693 00701 function setFirstSheet() 00702 { 00703 $this->firstsheet = $this->index; 00704 } 00705 00714 function protect($password) 00715 { 00716 $this->_protect = 1; 00717 $this->_password = $this->_encodePassword($password); 00718 } 00719 00731 function setColumn($firstcol, $lastcol, $width, $format = null, $hidden = 0, $level = 0) 00732 { 00733 $this->_colinfo[] = array($firstcol, $lastcol, $width, &$format, $hidden, $level); 00734 00735 // Set width to zero if column is hidden 00736 $width = ($hidden) ? 0 : $width; 00737 00738 for ($col = $firstcol; $col <= $lastcol; $col++) { 00739 $this->col_sizes[$col] = $width; 00740 } 00741 } 00742 00752 function setSelection($first_row,$first_column,$last_row,$last_column) 00753 { 00754 $this->_selection = array($first_row,$first_column,$last_row,$last_column); 00755 } 00756 00768 function freezePanes($panes) 00769 { 00770 $this->_frozen = 1; 00771 $this->_panes = $panes; 00772 } 00773 00785 function thawPanes($panes) 00786 { 00787 $this->_frozen = 0; 00788 $this->_panes = $panes; 00789 } 00790 00796 function setPortrait() 00797 { 00798 $this->_orientation = 1; 00799 } 00800 00806 function setLandscape() 00807 { 00808 $this->_orientation = 0; 00809 } 00810 00817 function setPaper($size = 0) 00818 { 00819 $this->_paper_size = $size; 00820 } 00821 00822 00830 function setHeader($string,$margin = 0.50) 00831 { 00832 if (strlen($string) >= 255) { 00833 //carp 'Header string must be less than 255 characters'; 00834 return; 00835 } 00836 $this->_header = $string; 00837 $this->_margin_head = $margin; 00838 } 00839 00847 function setFooter($string,$margin = 0.50) 00848 { 00849 if (strlen($string) >= 255) { 00850 //carp 'Footer string must be less than 255 characters'; 00851 return; 00852 } 00853 $this->_footer = $string; 00854 $this->_margin_foot = $margin; 00855 } 00856 00863 function centerHorizontally($center = 1) 00864 { 00865 $this->_hcenter = $center; 00866 } 00867 00874 function centerVertically($center = 1) 00875 { 00876 $this->_vcenter = $center; 00877 } 00878 00885 function setMargins($margin) 00886 { 00887 $this->setMarginLeft($margin); 00888 $this->setMarginRight($margin); 00889 $this->setMarginTop($margin); 00890 $this->setMarginBottom($margin); 00891 } 00892 00899 function setMargins_LR($margin) 00900 { 00901 $this->setMarginLeft($margin); 00902 $this->setMarginRight($margin); 00903 } 00904 00911 function setMargins_TB($margin) 00912 { 00913 $this->setMarginTop($margin); 00914 $this->setMarginBottom($margin); 00915 } 00916 00923 function setMarginLeft($margin = 0.75) 00924 { 00925 $this->_margin_left = $margin; 00926 } 00927 00934 function setMarginRight($margin = 0.75) 00935 { 00936 $this->_margin_right = $margin; 00937 } 00938 00945 function setMarginTop($margin = 1.00) 00946 { 00947 $this->_margin_top = $margin; 00948 } 00949 00956 function setMarginBottom($margin = 1.00) 00957 { 00958 $this->_margin_bottom = $margin; 00959 } 00960 00968 function repeatRows($first_row, $last_row = null) 00969 { 00970 $this->title_rowmin = $first_row; 00971 if (isset($last_row)) { //Second row is optional 00972 $this->title_rowmax = $last_row; 00973 } else { 00974 $this->title_rowmax = $first_row; 00975 } 00976 } 00977 00985 function repeatColumns($first_col, $last_col = null) 00986 { 00987 $this->title_colmin = $first_col; 00988 if (isset($last_col)) { // Second col is optional 00989 $this->title_colmax = $last_col; 00990 } else { 00991 $this->title_colmax = $first_col; 00992 } 00993 } 00994 01004 function printArea($first_row, $first_col, $last_row, $last_col) 01005 { 01006 $this->print_rowmin = $first_row; 01007 $this->print_colmin = $first_col; 01008 $this->print_rowmax = $last_row; 01009 $this->print_colmax = $last_col; 01010 } 01011 01012 01018 function hideGridlines() 01019 { 01020 $this->_print_gridlines = 0; 01021 } 01022 01028 function hideScreenGridlines() 01029 { 01030 $this->_screen_gridlines = 0; 01031 } 01032 01039 function printRowColHeaders($print = 1) 01040 { 01041 $this->_print_headers = $print; 01042 } 01043 01053 function fitToPages($width, $height) 01054 { 01055 $this->_fit_page = 1; 01056 $this->_fit_width = $width; 01057 $this->_fit_height = $height; 01058 } 01059 01067 function setHPagebreaks($breaks) 01068 { 01069 foreach ($breaks as $break) { 01070 array_push($this->_hbreaks, $break); 01071 } 01072 } 01073 01081 function setVPagebreaks($breaks) 01082 { 01083 foreach ($breaks as $break) { 01084 array_push($this->_vbreaks, $break); 01085 } 01086 } 01087 01088 01095 function setZoom($scale = 100) 01096 { 01097 // Confine the scale to Excel's range 01098 if ($scale < 10 || $scale > 400) { 01099 $this->raiseError("Zoom factor $scale outside range: 10 <= zoom <= 400"); 01100 $scale = 100; 01101 } 01102 01103 $this->_zoom = floor($scale); 01104 } 01105 01113 function setPrintScale($scale = 100) 01114 { 01115 // Confine the scale to Excel's range 01116 if ($scale < 10 || $scale > 400) { 01117 $this->raiseError("Print scale $scale outside range: 10 <= zoom <= 400"); 01118 $scale = 100; 01119 } 01120 01121 // Turn off "fit to page" option 01122 $this->_fit_page = 0; 01123 01124 $this->_print_scale = floor($scale); 01125 } 01126 01136 function write($row, $col, $token, $format = null) 01137 { 01138 // Check for a cell reference in A1 notation and substitute row and column 01139 /*if ($_[0] =~ /^\D/) { 01140 @_ = $this->_substituteCellref(@_); 01141 }*/ 01142 01143 if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/", $token)) { 01144 // Match number 01145 return $this->writeNumber($row, $col, $token, $format); 01146 } elseif (preg_match("/^[fh]tt?p:\/\//", $token)) { 01147 // Match http or ftp URL 01148 return $this->writeUrl($row, $col, $token, '', $format); 01149 } elseif (preg_match("/^mailto:/", $token)) { 01150 // Match mailto: 01151 return $this->writeUrl($row, $col, $token, '', $format); 01152 } elseif (preg_match("/^(?:in|ex)ternal:/", $token)) { 01153 // Match internal or external sheet link 01154 return $this->writeUrl($row, $col, $token, '', $format); 01155 } elseif (preg_match("/^=/", $token)) { 01156 // Match formula 01157 return $this->writeFormula($row, $col, $token, $format); 01158 } elseif (preg_match("/^@/", $token)) { 01159 // Match formula 01160 return $this->writeFormula($row, $col, $token, $format); 01161 } elseif ($token == '') { 01162 // Match blank 01163 return $this->writeBlank($row, $col, $format); 01164 } else { 01165 // Default: match string 01166 return $this->writeString($row, $col, $token, $format); 01167 } 01168 } 01169 01181 function writeRow($row, $col, $val, $format = null) 01182 { 01183 $retval = ''; 01184 if (is_array($val)) { 01185 foreach ($val as $v) { 01186 if (is_array($v)) { 01187 $this->writeCol($row, $col, $v, $format); 01188 } else { 01189 $this->write($row, $col, $v, $format); 01190 } 01191 $col++; 01192 } 01193 } else { 01194 $retval = new PEAR_Error('$val needs to be an array'); 01195 } 01196 return($retval); 01197 } 01198 01210 function writeCol($row, $col, $val, $format = null) 01211 { 01212 $retval = ''; 01213 if (is_array($val)) { 01214 foreach ($val as $v) { 01215 $this->write($row, $col, $v, $format); 01216 $row++; 01217 } 01218 } else { 01219 $retval = new PEAR_Error('$val needs to be an array'); 01220 } 01221 return($retval); 01222 } 01223 01231 function _XF(&$format) 01232 { 01233 if ($format) { 01234 return($format->getXfIndex()); 01235 } else { 01236 return(0x0F); 01237 } 01238 } 01239 01240 01241 /****************************************************************************** 01242 ******************************************************************************* 01243 * 01244 * Internal methods 01245 */ 01246 01247 01255 function _append($data) 01256 { 01257 if ($this->_using_tmpfile) { 01258 // Add CONTINUE records if necessary 01259 if (strlen($data) > $this->_limit) { 01260 $data = $this->_addContinue($data); 01261 } 01262 fwrite($this->_filehandle, $data); 01263 $this->_datasize += strlen($data); 01264 } else { 01265 parent::_append($data); 01266 } 01267 } 01268 01279 function _substituteCellref($cell) 01280 { 01281 $cell = strtoupper($cell); 01282 01283 // Convert a column range: 'A:A' or 'B:G' 01284 if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/", $cell, $match)) { 01285 list($no_use, $col1) = $this->_cellToRowcol($match[1] .'1'); // Add a dummy row 01286 list($no_use, $col2) = $this->_cellToRowcol($match[2] .'1'); // Add a dummy row 01287 return(array($col1, $col2)); 01288 } 01289 01290 // Convert a cell range: 'A1:B7' 01291 if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/", $cell, $match)) { 01292 list($row1, $col1) = $this->_cellToRowcol($match[1]); 01293 list($row2, $col2) = $this->_cellToRowcol($match[2]); 01294 return(array($row1, $col1, $row2, $col2)); 01295 } 01296 01297 // Convert a cell reference: 'A1' or 'AD2000' 01298 if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/", $cell)) { 01299 list($row1, $col1) = $this->_cellToRowcol($match[1]); 01300 return(array($row1, $col1)); 01301 } 01302 01303 // TODO use real error codes 01304 $this->raiseError("Unknown cell reference $cell", 0, PEAR_ERROR_DIE); 01305 } 01306 01315 function _cellToRowcol($cell) 01316 { 01317 preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match); 01318 $col = $match[1]; 01319 $row = $match[2]; 01320 01321 // Convert base26 column string to number 01322 $chars = str_split($col); 01323 $expn = 0; 01324 $col = 0; 01325 01326 while ($chars) { 01327 $char = array_pop($chars); // LS char first 01328 $col += (ord($char) -ord('A') +1) * pow(26,$expn); 01329 $expn++; 01330 } 01331 01332 // Convert 1-index to zero-index 01333 $row--; 01334 $col--; 01335 01336 return(array($row, $col)); 01337 } 01338 01346 function _encodePassword($plaintext) 01347 { 01348 $password = 0x0000; 01349 $i = 1; // char position 01350 01351 // split the plain text password in its component characters 01352 $chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY); 01353 foreach ($chars as $char) { 01354 $value = ord($char) << $i; // shifted ASCII value 01355 $rotated_bits = $value >> 15; // rotated bits beyond bit 15 01356 $value &= 0x7fff; // first 15 bits 01357 $password ^= ($value | $rotated_bits); 01358 $i++; 01359 } 01360 01361 $password ^= strlen($plaintext); 01362 $password ^= 0xCE4B; 01363 01364 return($password); 01365 } 01366 01376 function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false) 01377 { 01378 $this->_outline_on = $visible; 01379 $this->_outline_below = $symbols_below; 01380 $this->_outline_right = $symbols_right; 01381 $this->_outline_style = $auto_style; 01382 01383 // Ensure this is a boolean vale for Window2 01384 if ($this->_outline_on) { 01385 $this->_outline_on = 1; 01386 } 01387 } 01388 01389 /****************************************************************************** 01390 ******************************************************************************* 01391 * 01392 * BIFF RECORDS 01393 */ 01394 01395 01411 function writeNumber($row, $col, $num, $format = null) 01412 { 01413 $record = 0x0203; // Record identifier 01414 $length = 0x000E; // Number of bytes to follow 01415 01416 $xf = $this->_XF($format); // The cell format 01417 01418 // Check that row and col are valid and store max and min values 01419 if ($row >= $this->_xls_rowmax) { 01420 return(-2); 01421 } 01422 if ($col >= $this->_xls_colmax) { 01423 return(-2); 01424 } 01425 if ($row < $this->_dim_rowmin) { 01426 $this->_dim_rowmin = $row; 01427 } 01428 if ($row > $this->_dim_rowmax) { 01429 $this->_dim_rowmax = $row; 01430 } 01431 if ($col < $this->_dim_colmin) { 01432 $this->_dim_colmin = $col; 01433 } 01434 if ($col > $this->_dim_colmax) { 01435 $this->_dim_colmax = $col; 01436 } 01437 01438 $header = pack("vv", $record, $length); 01439 $data = pack("vvv", $row, $col, $xf); 01440 $xl_double = pack("d", $num); 01441 if ($this->_byte_order) { // if it's Big Endian 01442 $xl_double = strrev($xl_double); 01443 } 01444 01445 $this->_append($header.$data.$xl_double); 01446 return(0); 01447 } 01448 01464 function writeString($row, $col, $str, $format = null) 01465 { 01466 if ($this->_BIFF_version == 0x0600) { 01467 return $this->writeStringBIFF8($row, $col, $str, $format); 01468 } 01469 $strlen = strlen($str); 01470 $record = 0x0204; // Record identifier 01471 $length = 0x0008 + $strlen; // Bytes to follow 01472 $xf = $this->_XF($format); // The cell format 01473 01474 $str_error = 0; 01475 01476 // Check that row and col are valid and store max and min values 01477 if ($row >= $this->_xls_rowmax) { 01478 return(-2); 01479 } 01480 if ($col >= $this->_xls_colmax) { 01481 return(-2); 01482 } 01483 if ($row < $this->_dim_rowmin) { 01484 $this->_dim_rowmin = $row; 01485 } 01486 if ($row > $this->_dim_rowmax) { 01487 $this->_dim_rowmax = $row; 01488 } 01489 if ($col < $this->_dim_colmin) { 01490 $this->_dim_colmin = $col; 01491 } 01492 if ($col > $this->_dim_colmax) { 01493 $this->_dim_colmax = $col; 01494 } 01495 01496 if ($strlen > $this->_xls_strmax) { // LABEL must be < 255 chars 01497 $str = substr($str, 0, $this->_xls_strmax); 01498 $length = 0x0008 + $this->_xls_strmax; 01499 $strlen = $this->_xls_strmax; 01500 $str_error = -3; 01501 } 01502 01503 $header = pack("vv", $record, $length); 01504 $data = pack("vvvv", $row, $col, $xf, $strlen); 01505 $this->_append($header . $data . $str); 01506 return($str_error); 01507 } 01508 01515 function setInputEncoding($encoding) 01516 { 01517 if ($encoding != 'UTF-16LE' && !function_exists('iconv')) { 01518 $this->raiseError("Using an input encoding other than UTF-16LE requires PHP support for iconv"); 01519 } 01520 $this->_input_encoding = $encoding; 01521 } 01522 01538 function writeStringBIFF8($row, $col, $str, $format = null) 01539 { 01540 if ($this->_input_encoding == 'UTF-16LE') 01541 { 01542 $strlen = function_exists('mb_strlen') ? mb_strlen($str, 'UTF-16LE') : (strlen($str) / 2); 01543 $encoding = 0x1; 01544 } 01545 elseif ($this->_input_encoding != '') 01546 { 01547 $str = iconv($this->_input_encoding, 'UTF-16LE', $str); 01548 $strlen = function_exists('mb_strlen') ? mb_strlen($str, 'UTF-16LE') : (strlen($str) / 2); 01549 $encoding = 0x1; 01550 } 01551 else 01552 { 01553 $strlen = strlen($str); 01554 $encoding = 0x0; 01555 } 01556 $record = 0x00FD; // Record identifier 01557 $length = 0x000A; // Bytes to follow 01558 $xf = $this->_XF($format); // The cell format 01559 01560 $str_error = 0; 01561 01562 // Check that row and col are valid and store max and min values 01563 if ($this->_checkRowCol($row, $col) == false) { 01564 return -2; 01565 } 01566 01567 $str = pack('vC', $strlen, $encoding).$str; 01568 01569 /* check if string is already present */ 01570 if (!isset($this->_str_table[$str])) { 01571 $this->_str_table[$str] = $this->_str_unique++; 01572 } 01573 $this->_str_total++; 01574 01575 $header = pack('vv', $record, $length); 01576 $data = pack('vvvV', $row, $col, $xf, $this->_str_table[$str]); 01577 $this->_append($header.$data); 01578 return $str_error; 01579 } 01580 01591 function _checkRowCol($row, $col) 01592 { 01593 if ($row >= $this->_xls_rowmax) { 01594 return false; 01595 } 01596 if ($col >= $this->_xls_colmax) { 01597 return false; 01598 } 01599 if ($row < $this->_dim_rowmin) { 01600 $this->_dim_rowmin = $row; 01601 } 01602 if ($row > $this->_dim_rowmax) { 01603 $this->_dim_rowmax = $row; 01604 } 01605 if ($col < $this->_dim_colmin) { 01606 $this->_dim_colmin = $col; 01607 } 01608 if ($col > $this->_dim_colmax) { 01609 $this->_dim_colmax = $col; 01610 } 01611 return true; 01612 } 01613 01623 function writeNote($row, $col, $note) 01624 { 01625 $note_length = strlen($note); 01626 $record = 0x001C; // Record identifier 01627 $max_length = 2048; // Maximun length for a NOTE record 01628 //$length = 0x0006 + $note_length; // Bytes to follow 01629 01630 // Check that row and col are valid and store max and min values 01631 if ($row >= $this->_xls_rowmax) { 01632 return(-2); 01633 } 01634 if ($col >= $this->_xls_colmax) { 01635 return(-2); 01636 } 01637 if ($row < $this->_dim_rowmin) { 01638 $this->_dim_rowmin = $row; 01639 } 01640 if ($row > $this->_dim_rowmax) { 01641 $this->_dim_rowmax = $row; 01642 } 01643 if ($col < $this->_dim_colmin) { 01644 $this->_dim_colmin = $col; 01645 } 01646 if ($col > $this->_dim_colmax) { 01647 $this->_dim_colmax = $col; 01648 } 01649 01650 // Length for this record is no more than 2048 + 6 01651 $length = 0x0006 + min($note_length, 2048); 01652 $header = pack("vv", $record, $length); 01653 $data = pack("vvv", $row, $col, $note_length); 01654 $this->_append($header . $data . substr($note, 0, 2048)); 01655 01656 for ($i = $max_length; $i < $note_length; $i += $max_length) { 01657 $chunk = substr($note, $i, $max_length); 01658 $length = 0x0006 + strlen($chunk); 01659 $header = pack("vv", $record, $length); 01660 $data = pack("vvv", -1, 0, strlen($chunk)); 01661 $this->_append($header.$data.$chunk); 01662 } 01663 return(0); 01664 } 01665 01683 function writeBlank($row, $col, $format) 01684 { 01685 // Don't write a blank cell unless it has a format 01686 if (!$format) { 01687 return(0); 01688 } 01689 01690 $record = 0x0201; // Record identifier 01691 $length = 0x0006; // Number of bytes to follow 01692 $xf = $this->_XF($format); // The cell format 01693 01694 // Check that row and col are valid and store max and min values 01695 if ($row >= $this->_xls_rowmax) { 01696 return(-2); 01697 } 01698 if ($col >= $this->_xls_colmax) { 01699 return(-2); 01700 } 01701 if ($row < $this->_dim_rowmin) { 01702 $this->_dim_rowmin = $row; 01703 } 01704 if ($row > $this->_dim_rowmax) { 01705 $this->_dim_rowmax = $row; 01706 } 01707 if ($col < $this->_dim_colmin) { 01708 $this->_dim_colmin = $col; 01709 } 01710 if ($col > $this->_dim_colmax) { 01711 $this->_dim_colmax = $col; 01712 } 01713 01714 $header = pack("vv", $record, $length); 01715 $data = pack("vvv", $row, $col, $xf); 01716 $this->_append($header . $data); 01717 return 0; 01718 } 01719 01736 function writeFormula($row, $col, $formula, $format = null) 01737 { 01738 $record = 0x0006; // Record identifier 01739 01740 // Excel normally stores the last calculated value of the formula in $num. 01741 // Clearly we are not in a position to calculate this a priori. Instead 01742 // we set $num to zero and set the option flags in $grbit to ensure 01743 // automatic calculation of the formula when the file is opened. 01744 // 01745 $xf = $this->_XF($format); // The cell format 01746 $num = 0x00; // Current value of formula 01747 $grbit = 0x03; // Option flags 01748 $unknown = 0x0000; // Must be zero 01749 01750 01751 // Check that row and col are valid and store max and min values 01752 if ($this->_checkRowCol($row, $col) == false) { 01753 return -2; 01754 } 01755 01756 // Strip the '=' or '@' sign at the beginning of the formula string 01757 if (preg_match("/^=/", $formula)) { 01758 $formula = preg_replace("/(^=)/", "", $formula); 01759 } elseif (preg_match("/^@/", $formula)) { 01760 $formula = preg_replace("/(^@)/", "", $formula); 01761 } else { 01762 // Error handling 01763 $this->writeString($row, $col, 'Unrecognised character for formula'); 01764 return -1; 01765 } 01766 01767 // Parse the formula using the parser in Parser.php 01768 $error = $this->_parser->parse($formula); 01769 if ($this->isError($error)) { 01770 $this->writeString($row, $col, $error->getMessage()); 01771 return -1; 01772 } 01773 01774 $formula = $this->_parser->toReversePolish(); 01775 if ($this->isError($formula)) { 01776 $this->writeString($row, $col, $formula->getMessage()); 01777 return -1; 01778 } 01779 01780 $formlen = strlen($formula); // Length of the binary string 01781 $length = 0x16 + $formlen; // Length of the record data 01782 01783 $header = pack("vv", $record, $length); 01784 $data = pack("vvvdvVv", $row, $col, $xf, $num, 01785 $grbit, $unknown, $formlen); 01786 01787 $this->_append($header . $data . $formula); 01788 return 0; 01789 } 01790 01814 function writeUrl($row, $col, $url, $string = '', $format = null) 01815 { 01816 // Add start row and col to arg list 01817 return($this->_writeUrlRange($row, $col, $row, $col, $url, $string, $format)); 01818 } 01819 01838 function _writeUrlRange($row1, $col1, $row2, $col2, $url, $string = '', $format = null) 01839 { 01840 01841 // Check for internal/external sheet links or default to web link 01842 if (preg_match('[^internal:]', $url)) { 01843 return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url, $string, $format)); 01844 } 01845 if (preg_match('[^external:]', $url)) { 01846 return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url, $string, $format)); 01847 } 01848 return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url, $string, $format)); 01849 } 01850 01851 01868 function _writeUrlWeb($row1, $col1, $row2, $col2, $url, $str, $format = null) 01869 { 01870 $record = 0x01B8; // Record identifier 01871 $length = 0x00000; // Bytes to follow 01872 01873 if (!$format) { 01874 $format = $this->_url_format; 01875 } 01876 01877 // Write the visible label using the writeString() method. 01878 if ($str == '') { 01879 $str = $url; 01880 } 01881 $str_error = $this->writeString($row1, $col1, $str, $format); 01882 if (($str_error == -2) || ($str_error == -3)) { 01883 return $str_error; 01884 } 01885 01886 // Pack the undocumented parts of the hyperlink stream 01887 $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); 01888 $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B"); 01889 01890 // Pack the option flags 01891 $options = pack("V", 0x03); 01892 01893 // Convert URL to a null terminated wchar string 01894 $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); 01895 $url = $url . "\0\0\0"; 01896 01897 // Pack the length of the URL 01898 $url_len = pack("V", strlen($url)); 01899 01900 // Calculate the data length 01901 $length = 0x34 + strlen($url); 01902 01903 // Pack the header data 01904 $header = pack("vv", $record, $length); 01905 $data = pack("vvvv", $row1, $row2, $col1, $col2); 01906 01907 // Write the packed data 01908 $this->_append($header . $data . 01909 $unknown1 . $options . 01910 $unknown2 . $url_len . $url); 01911 return($str_error); 01912 } 01913 01928 function _writeUrlInternal($row1, $col1, $row2, $col2, $url, $str, $format = null) 01929 { 01930 $record = 0x01B8; // Record identifier 01931 $length = 0x00000; // Bytes to follow 01932 01933 if (!$format) { 01934 $format = $this->_url_format; 01935 } 01936 01937 // Strip URL type 01938 $url = preg_replace('/^internal:/', '', $url); 01939 01940 // Write the visible label 01941 if ($str == '') { 01942 $str = $url; 01943 } 01944 $str_error = $this->writeString($row1, $col1, $str, $format); 01945 if (($str_error == -2) || ($str_error == -3)) { 01946 return $str_error; 01947 } 01948 01949 // Pack the undocumented parts of the hyperlink stream 01950 $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); 01951 01952 // Pack the option flags 01953 $options = pack("V", 0x08); 01954 01955 // Convert the URL type and to a null terminated wchar string 01956 $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); 01957 $url = $url . "\0\0\0"; 01958 01959 // Pack the length of the URL as chars (not wchars) 01960 $url_len = pack("V", floor(strlen($url)/2)); 01961 01962 // Calculate the data length 01963 $length = 0x24 + strlen($url); 01964 01965 // Pack the header data 01966 $header = pack("vv", $record, $length); 01967 $data = pack("vvvv", $row1, $row2, $col1, $col2); 01968 01969 // Write the packed data 01970 $this->_append($header . $data . 01971 $unknown1 . $options . 01972 $url_len . $url); 01973 return($str_error); 01974 } 01975 01994 function _writeUrlExternal($row1, $col1, $row2, $col2, $url, $str, $format = null) 01995 { 01996 // Network drives are different. We will handle them separately 01997 // MS/Novell network drives and shares start with \\ 01998 if (preg_match('[^external:\\\\]', $url)) { 01999 return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format)); 02000 } 02001 02002 $record = 0x01B8; // Record identifier 02003 $length = 0x00000; // Bytes to follow 02004 02005 if (!$format) { 02006 $format = $this->_url_format; 02007 } 02008 02009 // Strip URL type and change Unix dir separator to Dos style (if needed) 02010 // 02011 $url = preg_replace('/^external:/', '', $url); 02012 $url = preg_replace('/\//', "\\", $url); 02013 02014 // Write the visible label 02015 if ($str == '') { 02016 $str = preg_replace('/\#/', ' - ', $url); 02017 } 02018 $str_error = $this->writeString($row1, $col1, $str, $format); 02019 if (($str_error == -2) or ($str_error == -3)) { 02020 return $str_error; 02021 } 02022 02023 // Determine if the link is relative or absolute: 02024 // relative if link contains no dir separator, "somefile.xls" 02025 // relative if link starts with up-dir, "..\..\somefile.xls" 02026 // otherwise, absolute 02027 02028 $absolute = 0x02; // Bit mask 02029 if (!preg_match("/\\\/", $url)) { 02030 $absolute = 0x00; 02031 } 02032 if (preg_match("/^\.\.\\\/", $url)) { 02033 $absolute = 0x00; 02034 } 02035 $link_type = 0x01 | $absolute; 02036 02037 // Determine if the link contains a sheet reference and change some of the 02038 // parameters accordingly. 02039 // Split the dir name and sheet name (if it exists) 02040 /*if (preg_match("/\#/", $url)) { 02041 list($dir_long, $sheet) = explode("#", $url); 02042 } else { 02043 $dir_long = $url; 02044 } 02045 02046 if (isset($sheet)) { 02047 $link_type |= 0x08; 02048 $sheet_len = pack("V", strlen($sheet) + 0x01); 02049 $sheet = join("\0", str_split($sheet)); 02050 $sheet .= "\0\0\0"; 02051 } else { 02052 $sheet_len = ''; 02053 $sheet = ''; 02054 }*/ 02055 $dir_long = $url; 02056 if (preg_match("/\#/", $url)) { 02057 $link_type |= 0x08; 02058 } 02059 02060 02061 02062 // Pack the link type 02063 $link_type = pack("V", $link_type); 02064 02065 // Calculate the up-level dir count e.g.. (..\..\..\ == 3) 02066 $up_count = preg_match_all("/\.\.\\\/", $dir_long, $useless); 02067 $up_count = pack("v", $up_count); 02068 02069 // Store the short dos dir name (null terminated) 02070 $dir_short = preg_replace("/\.\.\\\/", '', $dir_long) . "\0"; 02071 02072 // Store the long dir name as a wchar string (non-null terminated) 02073 //$dir_long = join("\0", str_split($dir_long)); 02074 $dir_long = $dir_long . "\0"; 02075 02076 // Pack the lengths of the dir strings 02077 $dir_short_len = pack("V", strlen($dir_short) ); 02078 $dir_long_len = pack("V", strlen($dir_long) ); 02079 $stream_len = pack("V", 0);//strlen($dir_long) + 0x06); 02080 02081 // Pack the undocumented parts of the hyperlink stream 02082 $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' ); 02083 $unknown2 = pack("H*",'0303000000000000C000000000000046' ); 02084 $unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000'); 02085 $unknown4 = pack("v", 0x03 ); 02086 02087 // Pack the main data stream 02088 $data = pack("vvvv", $row1, $row2, $col1, $col2) . 02089 $unknown1 . 02090 $link_type . 02091 $unknown2 . 02092 $up_count . 02093 $dir_short_len. 02094 $dir_short . 02095 $unknown3 . 02096 $stream_len ;/*. 02097 $dir_long_len . 02098 $unknown4 . 02099 $dir_long . 02100 $sheet_len . 02101 $sheet ;*/ 02102 02103 // Pack the header data 02104 $length = strlen($data); 02105 $header = pack("vv", $record, $length); 02106 02107 // Write the packed data 02108 $this->_append($header. $data); 02109 return($str_error); 02110 } 02111 02112 02124 function setRow($row, $height, $format = null, $hidden = false, $level = 0) 02125 { 02126 $record = 0x0208; // Record identifier 02127 $length = 0x0010; // Number of bytes to follow 02128 02129 $colMic = 0x0000; // First defined column 02130 $colMac = 0x0000; // Last defined column 02131 $irwMac = 0x0000; // Used by Excel to optimise loading 02132 $reserved = 0x0000; // Reserved 02133 $grbit = 0x0000; // Option flags 02134 $ixfe = $this->_XF($format); // XF index 02135 02136 // set _row_sizes so _sizeRow() can use it 02137 $this->_row_sizes[$row] = $height; 02138 02139 // Use setRow($row, null, $XF) to set XF format without setting height 02140 if ($height != null) { 02141 $miyRw = $height * 20; // row height 02142 } else { 02143 $miyRw = 0xff; // default row height is 256 02144 } 02145 02146 $level = max(0, min($level, 7)); // level should be between 0 and 7 02147 $this->_outline_row_level = max($level, $this->_outline_row_level); 02148 02149 02150 // Set the options flags. fUnsynced is used to show that the font and row 02151 // heights are not compatible. This is usually the case for WriteExcel. 02152 // The collapsed flag 0x10 doesn't seem to be used to indicate that a row 02153 // is collapsed. Instead it is used to indicate that the previous row is 02154 // collapsed. The zero height flag, 0x20, is used to collapse a row. 02155 02156 $grbit |= $level; 02157 if ($hidden) { 02158 $grbit |= 0x0020; 02159 } 02160 $grbit |= 0x0040; // fUnsynced 02161 if ($format) { 02162 $grbit |= 0x0080; 02163 } 02164 $grbit |= 0x0100; 02165 02166 $header = pack("vv", $record, $length); 02167 $data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw, 02168 $irwMac,$reserved, $grbit, $ixfe); 02169 $this->_append($header.$data); 02170 } 02171 02177 function _storeDimensions() 02178 { 02179 $record = 0x0200; // Record identifier 02180 $row_min = $this->_dim_rowmin; // First row 02181 $row_max = $this->_dim_rowmax + 1; // Last row plus 1 02182 $col_min = $this->_dim_colmin; // First column 02183 $col_max = $this->_dim_colmax + 1; // Last column plus 1 02184 $reserved = 0x0000; // Reserved by Excel 02185 02186 if ($this->_BIFF_version == 0x0500) { 02187 $length = 0x000A; // Number of bytes to follow 02188 $data = pack("vvvvv", $row_min, $row_max, 02189 $col_min, $col_max, $reserved); 02190 } elseif ($this->_BIFF_version == 0x0600) { 02191 $length = 0x000E; 02192 $data = pack("VVvvv", $row_min, $row_max, 02193 $col_min, $col_max, $reserved); 02194 } 02195 $header = pack("vv", $record, $length); 02196 $this->_prepend($header.$data); 02197 } 02198 02204 function _storeWindow2() 02205 { 02206 $record = 0x023E; // Record identifier 02207 if ($this->_BIFF_version == 0x0500) { 02208 $length = 0x000A; // Number of bytes to follow 02209 } elseif ($this->_BIFF_version == 0x0600) { 02210 $length = 0x0012; 02211 } 02212 02213 $grbit = 0x00B6; // Option flags 02214 $rwTop = 0x0000; // Top row visible in window 02215 $colLeft = 0x0000; // Leftmost column visible in window 02216 02217 02218 // The options flags that comprise $grbit 02219 $fDspFmla = 0; // 0 - bit 02220 $fDspGrid = $this->_screen_gridlines; // 1 02221 $fDspRwCol = 1; // 2 02222 $fFrozen = $this->_frozen; // 3 02223 $fDspZeros = 1; // 4 02224 $fDefaultHdr = 1; // 5 02225 $fArabic = 0; // 6 02226 $fDspGuts = $this->_outline_on; // 7 02227 $fFrozenNoSplit = 0; // 0 - bit 02228 $fSelected = $this->selected; // 1 02229 $fPaged = 1; // 2 02230 02231 $grbit = $fDspFmla; 02232 $grbit |= $fDspGrid << 1; 02233 $grbit |= $fDspRwCol << 2; 02234 $grbit |= $fFrozen << 3; 02235 $grbit |= $fDspZeros << 4; 02236 $grbit |= $fDefaultHdr << 5; 02237 $grbit |= $fArabic << 6; 02238 $grbit |= $fDspGuts << 7; 02239 $grbit |= $fFrozenNoSplit << 8; 02240 $grbit |= $fSelected << 9; 02241 $grbit |= $fPaged << 10; 02242 02243 $header = pack("vv", $record, $length); 02244 $data = pack("vvv", $grbit, $rwTop, $colLeft); 02245 // FIXME !!! 02246 if ($this->_BIFF_version == 0x0500) { 02247 $rgbHdr = 0x00000000; // Row/column heading and gridline color 02248 $data .= pack("V", $rgbHdr); 02249 } elseif ($this->_BIFF_version == 0x0600) { 02250 $rgbHdr = 0x0040; // Row/column heading and gridline color index 02251 $zoom_factor_page_break = 0x0000; 02252 $zoom_factor_normal = 0x0000; 02253 $data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000); 02254 } 02255 $this->_append($header.$data); 02256 } 02257 02263 function _storeDefcol() 02264 { 02265 $record = 0x0055; // Record identifier 02266 $length = 0x0002; // Number of bytes to follow 02267 $colwidth = 0x0008; // Default column width 02268 02269 $header = pack("vv", $record, $length); 02270 $data = pack("v", $colwidth); 02271 $this->_prepend($header . $data); 02272 } 02273 02289 function _storeColinfo($col_array) 02290 { 02291 if (isset($col_array[0])) { 02292 $colFirst = $col_array[0]; 02293 } 02294 if (isset($col_array[1])) { 02295 $colLast = $col_array[1]; 02296 } 02297 if (isset($col_array[2])) { 02298 $coldx = $col_array[2]; 02299 } else { 02300 $coldx = 8.43; 02301 } 02302 if (isset($col_array[3])) { 02303 $format = $col_array[3]; 02304 } else { 02305 $format = 0; 02306 } 02307 if (isset($col_array[4])) { 02308 $grbit = $col_array[4]; 02309 } else { 02310 $grbit = 0; 02311 } 02312 if (isset($col_array[5])) { 02313 $level = $col_array[5]; 02314 } else { 02315 $level = 0; 02316 } 02317 $record = 0x007D; // Record identifier 02318 $length = 0x000B; // Number of bytes to follow 02319 02320 $coldx += 0.72; // Fudge. Excel subtracts 0.72 !? 02321 $coldx *= 256; // Convert to units of 1/256 of a char 02322 02323 $ixfe = $this->_XF($format); 02324 $reserved = 0x00; // Reserved 02325 02326 $level = max(0, min($level, 7)); 02327 $grbit |= $level << 8; 02328 02329 $header = pack("vv", $record, $length); 02330 $data = pack("vvvvvC", $colFirst, $colLast, $coldx, 02331 $ixfe, $grbit, $reserved); 02332 $this->_prepend($header.$data); 02333 } 02334 02342 function _storeSelection($array) 02343 { 02344 list($rwFirst,$colFirst,$rwLast,$colLast) = $array; 02345 $record = 0x001D; // Record identifier 02346 $length = 0x000F; // Number of bytes to follow 02347 02348 $pnn = $this->_active_pane; // Pane position 02349 $rwAct = $rwFirst; // Active row 02350 $colAct = $colFirst; // Active column 02351 $irefAct = 0; // Active cell ref 02352 $cref = 1; // Number of refs 02353 02354 if (!isset($rwLast)) { 02355 $rwLast = $rwFirst; // Last row in reference 02356 } 02357 if (!isset($colLast)) { 02358 $colLast = $colFirst; // Last col in reference 02359 } 02360 02361 // Swap last row/col for first row/col as necessary 02362 if ($rwFirst > $rwLast) { 02363 list($rwFirst, $rwLast) = array($rwLast, $rwFirst); 02364 } 02365 02366 if ($colFirst > $colLast) { 02367 list($colFirst, $colLast) = array($colLast, $colFirst); 02368 } 02369 02370 $header = pack("vv", $record, $length); 02371 $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct, 02372 $irefAct, $cref, 02373 $rwFirst, $rwLast, 02374 $colFirst, $colLast); 02375 $this->_append($header . $data); 02376 } 02377 02383 function _storeMergedCells() 02384 { 02385 // if there are no merged cell ranges set, return 02386 if (count($this->_merged_ranges) == 0) { 02387 return; 02388 } 02389 $record = 0x00E5; 02390 $length = 2 + count($this->_merged_ranges) * 8; 02391 02392 $header = pack('vv', $record, $length); 02393 $data = pack('v', count($this->_merged_ranges)); 02394 foreach ($this->_merged_ranges as $range) { 02395 $data .= pack('vvvv', $range[0], $range[2], $range[1], $range[3]); 02396 } 02397 $this->_append($header . $data); 02398 } 02399 02413 function _storeExterncount($count) 02414 { 02415 $record = 0x0016; // Record identifier 02416 $length = 0x0002; // Number of bytes to follow 02417 02418 $header = pack("vv", $record, $length); 02419 $data = pack("v", $count); 02420 $this->_prepend($header . $data); 02421 } 02422 02432 function _storeExternsheet($sheetname) 02433 { 02434 $record = 0x0017; // Record identifier 02435 02436 // References to the current sheet are encoded differently to references to 02437 // external sheets. 02438 // 02439 if ($this->name == $sheetname) { 02440 $sheetname = ''; 02441 $length = 0x02; // The following 2 bytes 02442 $cch = 1; // The following byte 02443 $rgch = 0x02; // Self reference 02444 } else { 02445 $length = 0x02 + strlen($sheetname); 02446 $cch = strlen($sheetname); 02447 $rgch = 0x03; // Reference to a sheet in the current workbook 02448 } 02449 02450 $header = pack("vv", $record, $length); 02451 $data = pack("CC", $cch, $rgch); 02452 $this->_prepend($header . $data . $sheetname); 02453 } 02454 02469 function _storePanes($panes) 02470 { 02471 $y = $panes[0]; 02472 $x = $panes[1]; 02473 $rwTop = $panes[2]; 02474 $colLeft = $panes[3]; 02475 if (count($panes) > 4) { // if Active pane was received 02476 $pnnAct = $panes[4]; 02477 } else { 02478 $pnnAct = null; 02479 } 02480 $record = 0x0041; // Record identifier 02481 $length = 0x000A; // Number of bytes to follow 02482 02483 // Code specific to frozen or thawed panes. 02484 if ($this->_frozen) { 02485 // Set default values for $rwTop and $colLeft 02486 if (!isset($rwTop)) { 02487 $rwTop = $y; 02488 } 02489 if (!isset($colLeft)) { 02490 $colLeft = $x; 02491 } 02492 } else { 02493 // Set default values for $rwTop and $colLeft 02494 if (!isset($rwTop)) { 02495 $rwTop = 0; 02496 } 02497 if (!isset($colLeft)) { 02498 $colLeft = 0; 02499 } 02500 02501 // Convert Excel's row and column units to the internal units. 02502 // The default row height is 12.75 02503 // The default column width is 8.43 02504 // The following slope and intersection values were interpolated. 02505 // 02506 $y = 20*$y + 255; 02507 $x = 113.879*$x + 390; 02508 } 02509 02510 02511 // Determine which pane should be active. There is also the undocumented 02512 // option to override this should it be necessary: may be removed later. 02513 // 02514 if (!isset($pnnAct)) { 02515 if ($x != 0 && $y != 0) { 02516 $pnnAct = 0; // Bottom right 02517 } 02518 if ($x != 0 && $y == 0) { 02519 $pnnAct = 1; // Top right 02520 } 02521 if ($x == 0 && $y != 0) { 02522 $pnnAct = 2; // Bottom left 02523 } 02524 if ($x == 0 && $y == 0) { 02525 $pnnAct = 3; // Top left 02526 } 02527 } 02528 02529 $this->_active_pane = $pnnAct; // Used in _storeSelection 02530 02531 $header = pack("vv", $record, $length); 02532 $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct); 02533 $this->_append($header . $data); 02534 } 02535 02541 function _storeSetup() 02542 { 02543 $record = 0x00A1; // Record identifier 02544 $length = 0x0022; // Number of bytes to follow 02545 02546 $iPaperSize = $this->_paper_size; // Paper size 02547 $iScale = $this->_print_scale; // Print scaling factor 02548 $iPageStart = 0x01; // Starting page number 02549 $iFitWidth = $this->_fit_width; // Fit to number of pages wide 02550 $iFitHeight = $this->_fit_height; // Fit to number of pages high 02551 $grbit = 0x00; // Option flags 02552 $iRes = 0x0258; // Print resolution 02553 $iVRes = 0x0258; // Vertical print resolution 02554 $numHdr = $this->_margin_head; // Header Margin 02555 $numFtr = $this->_margin_foot; // Footer Margin 02556 $iCopies = 0x01; // Number of copies 02557 02558 $fLeftToRight = 0x0; // Print over then down 02559 $fLandscape = $this->_orientation; // Page orientation 02560 $fNoPls = 0x0; // Setup not read from printer 02561 $fNoColor = 0x0; // Print black and white 02562 $fDraft = 0x0; // Print draft quality 02563 $fNotes = 0x0; // Print notes 02564 $fNoOrient = 0x0; // Orientation not set 02565 $fUsePage = 0x0; // Use custom starting page 02566 02567 $grbit = $fLeftToRight; 02568 $grbit |= $fLandscape << 1; 02569 $grbit |= $fNoPls << 2; 02570 $grbit |= $fNoColor << 3; 02571 $grbit |= $fDraft << 4; 02572 $grbit |= $fNotes << 5; 02573 $grbit |= $fNoOrient << 6; 02574 $grbit |= $fUsePage << 7; 02575 02576 $numHdr = pack("d", $numHdr); 02577 $numFtr = pack("d", $numFtr); 02578 if ($this->_byte_order) { // if it's Big Endian 02579 $numHdr = strrev($numHdr); 02580 $numFtr = strrev($numFtr); 02581 } 02582 02583 $header = pack("vv", $record, $length); 02584 $data1 = pack("vvvvvvvv", $iPaperSize, 02585 $iScale, 02586 $iPageStart, 02587 $iFitWidth, 02588 $iFitHeight, 02589 $grbit, 02590 $iRes, 02591 $iVRes); 02592 $data2 = $numHdr.$numFtr; 02593 $data3 = pack("v", $iCopies); 02594 $this->_prepend($header . $data1 . $data2 . $data3); 02595 } 02596 02602 function _storeHeader() 02603 { 02604 $record = 0x0014; // Record identifier 02605 02606 $str = $this->_header; // header string 02607 $cch = strlen($str); // Length of header string 02608 if ($this->_BIFF_version == 0x0600) { 02609 $encoding = 0x0; // TODO: Unicode support 02610 $length = 3 + $cch; // Bytes to follow 02611 } else { 02612 $length = 1 + $cch; // Bytes to follow 02613 } 02614 02615 $header = pack("vv", $record, $length); 02616 if ($this->_BIFF_version == 0x0600) { 02617 $data = pack("vC", $cch, $encoding); 02618 } else { 02619 $data = pack("C", $cch); 02620 } 02621 02622 $this->_prepend($header.$data.$str); 02623 } 02624 02630 function _storeFooter() 02631 { 02632 $record = 0x0015; // Record identifier 02633 02634 $str = $this->_footer; // Footer string 02635 $cch = strlen($str); // Length of footer string 02636 if ($this->_BIFF_version == 0x0600) { 02637 $encoding = 0x0; // TODO: Unicode support 02638 $length = 3 + $cch; // Bytes to follow 02639 } else { 02640 $length = 1 + $cch; 02641 } 02642 02643 $header = pack("vv", $record, $length); 02644 if ($this->_BIFF_version == 0x0600) { 02645 $data = pack("vC", $cch, $encoding); 02646 } else { 02647 $data = pack("C", $cch); 02648 } 02649 02650 $this->_prepend($header . $data . $str); 02651 } 02652 02658 function _storeHcenter() 02659 { 02660 $record = 0x0083; // Record identifier 02661 $length = 0x0002; // Bytes to follow 02662 02663 $fHCenter = $this->_hcenter; // Horizontal centering 02664 02665 $header = pack("vv", $record, $length); 02666 $data = pack("v", $fHCenter); 02667 02668 $this->_prepend($header.$data); 02669 } 02670 02676 function _storeVcenter() 02677 { 02678 $record = 0x0084; // Record identifier 02679 $length = 0x0002; // Bytes to follow 02680 02681 $fVCenter = $this->_vcenter; // Horizontal centering 02682 02683 $header = pack("vv", $record, $length); 02684 $data = pack("v", $fVCenter); 02685 $this->_prepend($header . $data); 02686 } 02687 02693 function _storeMarginLeft() 02694 { 02695 $record = 0x0026; // Record identifier 02696 $length = 0x0008; // Bytes to follow 02697 02698 $margin = $this->_margin_left; // Margin in inches 02699 02700 $header = pack("vv", $record, $length); 02701 $data = pack("d", $margin); 02702 if ($this->_byte_order) { // if it's Big Endian 02703 $data = strrev($data); 02704 } 02705 02706 $this->_prepend($header . $data); 02707 } 02708 02714 function _storeMarginRight() 02715 { 02716 $record = 0x0027; // Record identifier 02717 $length = 0x0008; // Bytes to follow 02718 02719 $margin = $this->_margin_right; // Margin in inches 02720 02721 $header = pack("vv", $record, $length); 02722 $data = pack("d", $margin); 02723 if ($this->_byte_order) { // if it's Big Endian 02724 $data = strrev($data); 02725 } 02726 02727 $this->_prepend($header . $data); 02728 } 02729 02735 function _storeMarginTop() 02736 { 02737 $record = 0x0028; // Record identifier 02738 $length = 0x0008; // Bytes to follow 02739 02740 $margin = $this->_margin_top; // Margin in inches 02741 02742 $header = pack("vv", $record, $length); 02743 $data = pack("d", $margin); 02744 if ($this->_byte_order) { // if it's Big Endian 02745 $data = strrev($data); 02746 } 02747 02748 $this->_prepend($header . $data); 02749 } 02750 02756 function _storeMarginBottom() 02757 { 02758 $record = 0x0029; // Record identifier 02759 $length = 0x0008; // Bytes to follow 02760 02761 $margin = $this->_margin_bottom; // Margin in inches 02762 02763 $header = pack("vv", $record, $length); 02764 $data = pack("d", $margin); 02765 if ($this->_byte_order) { // if it's Big Endian 02766 $data = strrev($data); 02767 } 02768 02769 $this->_prepend($header . $data); 02770 } 02771 02783 function mergeCells($first_row, $first_col, $last_row, $last_col) 02784 { 02785 $record = 0x00E5; // Record identifier 02786 $length = 0x000A; // Bytes to follow 02787 $cref = 1; // Number of refs 02788 02789 // Swap last row/col for first row/col as necessary 02790 if ($first_row > $last_row) { 02791 list($first_row, $last_row) = array($last_row, $first_row); 02792 } 02793 02794 if ($first_col > $last_col) { 02795 list($first_col, $last_col) = array($last_col, $first_col); 02796 } 02797 02798 $header = pack("vv", $record, $length); 02799 $data = pack("vvvvv", $cref, $first_row, $last_row, 02800 $first_col, $last_col); 02801 02802 $this->_append($header.$data); 02803 } 02804 02810 function _storePrintHeaders() 02811 { 02812 $record = 0x002a; // Record identifier 02813 $length = 0x0002; // Bytes to follow 02814 02815 $fPrintRwCol = $this->_print_headers; // Boolean flag 02816 02817 $header = pack("vv", $record, $length); 02818 $data = pack("v", $fPrintRwCol); 02819 $this->_prepend($header . $data); 02820 } 02821 02828 function _storePrintGridlines() 02829 { 02830 $record = 0x002b; // Record identifier 02831 $length = 0x0002; // Bytes to follow 02832 02833 $fPrintGrid = $this->_print_gridlines; // Boolean flag 02834 02835 $header = pack("vv", $record, $length); 02836 $data = pack("v", $fPrintGrid); 02837 $this->_prepend($header . $data); 02838 } 02839 02846 function _storeGridset() 02847 { 02848 $record = 0x0082; // Record identifier 02849 $length = 0x0002; // Bytes to follow 02850 02851 $fGridSet = !($this->_print_gridlines); // Boolean flag 02852 02853 $header = pack("vv", $record, $length); 02854 $data = pack("v", $fGridSet); 02855 $this->_prepend($header . $data); 02856 } 02857 02866 function _storeGuts() 02867 { 02868 $record = 0x0080; // Record identifier 02869 $length = 0x0008; // Bytes to follow 02870 02871 $dxRwGut = 0x0000; // Size of row gutter 02872 $dxColGut = 0x0000; // Size of col gutter 02873 02874 $row_level = $this->_outline_row_level; 02875 $col_level = 0; 02876 02877 // Calculate the maximum column outline level. The equivalent calculation 02878 // for the row outline level is carried out in setRow(). 02879 $colcount = count($this->_colinfo); 02880 for ($i = 0; $i < $colcount; $i++) { 02881 // Skip cols without outline level info. 02882 if (count($col_level) >= 6) { 02883 $col_level = max($this->_colinfo[$i][5], $col_level); 02884 } 02885 } 02886 02887 // Set the limits for the outline levels (0 <= x <= 7). 02888 $col_level = max(0, min($col_level, 7)); 02889 02890 // The displayed level is one greater than the max outline levels 02891 if ($row_level) { 02892 $row_level++; 02893 } 02894 if ($col_level) { 02895 $col_level++; 02896 } 02897 02898 $header = pack("vv", $record, $length); 02899 $data = pack("vvvv", $dxRwGut, $dxColGut, $row_level, $col_level); 02900 02901 $this->_prepend($header.$data); 02902 } 02903 02904 02911 function _storeWsbool() 02912 { 02913 $record = 0x0081; // Record identifier 02914 $length = 0x0002; // Bytes to follow 02915 $grbit = 0x0000; 02916 02917 // The only option that is of interest is the flag for fit to page. So we 02918 // set all the options in one go. 02919 // 02920 /*if ($this->_fit_page) { 02921 $grbit = 0x05c1; 02922 } else { 02923 $grbit = 0x04c1; 02924 }*/ 02925 // Set the option flags 02926 $grbit |= 0x0001; // Auto page breaks visible 02927 if ($this->_outline_style) { 02928 $grbit |= 0x0020; // Auto outline styles 02929 } 02930 if ($this->_outline_below) { 02931 $grbit |= 0x0040; // Outline summary below 02932 } 02933 if ($this->_outline_right) { 02934 $grbit |= 0x0080; // Outline summary right 02935 } 02936 if ($this->_fit_page) { 02937 $grbit |= 0x0100; // Page setup fit to page 02938 } 02939 if ($this->_outline_on) { 02940 $grbit |= 0x0400; // Outline symbols displayed 02941 } 02942 02943 $header = pack("vv", $record, $length); 02944 $data = pack("v", $grbit); 02945 $this->_prepend($header . $data); 02946 } 02947 02953 function _storeHbreak() 02954 { 02955 // Return if the user hasn't specified pagebreaks 02956 if (empty($this->_hbreaks)) { 02957 return; 02958 } 02959 02960 // Sort and filter array of page breaks 02961 $breaks = $this->_hbreaks; 02962 sort($breaks, SORT_NUMERIC); 02963 if ($breaks[0] == 0) { // don't use first break if it's 0 02964 array_shift($breaks); 02965 } 02966 02967 $record = 0x001b; // Record identifier 02968 $cbrk = count($breaks); // Number of page breaks 02969 if ($this->_BIFF_version == 0x0600) { 02970 $length = 2 + 6*$cbrk; // Bytes to follow 02971 } else { 02972 $length = 2 + 2*$cbrk; // Bytes to follow 02973 } 02974 02975 $header = pack("vv", $record, $length); 02976 $data = pack("v", $cbrk); 02977 02978 // Append each page break 02979 foreach ($breaks as $break) { 02980 if ($this->_BIFF_version == 0x0600) { 02981 $data .= pack("vvv", $break, 0x0000, 0x00ff); 02982 } else { 02983 $data .= pack("v", $break); 02984 } 02985 } 02986 02987 $this->_prepend($header.$data); 02988 } 02989 02990 02996 function _storeVbreak() 02997 { 02998 // Return if the user hasn't specified pagebreaks 02999 if (empty($this->_vbreaks)) { 03000 return; 03001 } 03002 03003 // 1000 vertical pagebreaks appears to be an internal Excel 5 limit. 03004 // It is slightly higher in Excel 97/200, approx. 1026 03005 $breaks = array_slice($this->_vbreaks,0,1000); 03006 03007 // Sort and filter array of page breaks 03008 sort($breaks, SORT_NUMERIC); 03009 if ($breaks[0] == 0) { // don't use first break if it's 0 03010 array_shift($breaks); 03011 } 03012 03013 $record = 0x001a; // Record identifier 03014 $cbrk = count($breaks); // Number of page breaks 03015 if ($this->_BIFF_version == 0x0600) { 03016 $length = 2 + 6*$cbrk; // Bytes to follow 03017 } else { 03018 $length = 2 + 2*$cbrk; // Bytes to follow 03019 } 03020 03021 $header = pack("vv", $record, $length); 03022 $data = pack("v", $cbrk); 03023 03024 // Append each page break 03025 foreach ($breaks as $break) { 03026 if ($this->_BIFF_version == 0x0600) { 03027 $data .= pack("vvv", $break, 0x0000, 0xffff); 03028 } else { 03029 $data .= pack("v", $break); 03030 } 03031 } 03032 03033 $this->_prepend($header . $data); 03034 } 03035 03041 function _storeProtect() 03042 { 03043 // Exit unless sheet protection has been specified 03044 if ($this->_protect == 0) { 03045 return; 03046 } 03047 03048 $record = 0x0012; // Record identifier 03049 $length = 0x0002; // Bytes to follow 03050 03051 $fLock = $this->_protect; // Worksheet is protected 03052 03053 $header = pack("vv", $record, $length); 03054 $data = pack("v", $fLock); 03055 03056 $this->_prepend($header.$data); 03057 } 03058 03064 function _storePassword() 03065 { 03066 // Exit unless sheet protection and password have been specified 03067 if (($this->_protect == 0) || (!isset($this->_password))) { 03068 return; 03069 } 03070 03071 $record = 0x0013; // Record identifier 03072 $length = 0x0002; // Bytes to follow 03073 03074 $wPassword = $this->_password; // Encoded password 03075 03076 $header = pack("vv", $record, $length); 03077 $data = pack("v", $wPassword); 03078 03079 $this->_prepend($header . $data); 03080 } 03081 03082 03095 function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1) 03096 { 03097 $bitmap_array = $this->_processBitmap($bitmap); 03098 if ($this->isError($bitmap_array)) { 03099 $this->writeString($row, $col, $bitmap_array->getMessage()); 03100 return; 03101 } 03102 list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap); 03103 03104 // Scale the frame of the image. 03105 $width *= $scale_x; 03106 $height *= $scale_y; 03107 03108 // Calculate the vertices of the image and write the OBJ record 03109 $this->_positionImage($col, $row, $x, $y, $width, $height); 03110 03111 // Write the IMDATA record to store the bitmap data 03112 $record = 0x007f; 03113 $length = 8 + $size; 03114 $cf = 0x09; 03115 $env = 0x01; 03116 $lcb = $size; 03117 03118 $header = pack("vvvvV", $record, $length, $cf, $env, $lcb); 03119 $this->_append($header.$data); 03120 } 03121 03173 function _positionImage($col_start, $row_start, $x1, $y1, $width, $height) 03174 { 03175 // Initialise end cell to the same as the start cell 03176 $col_end = $col_start; // Col containing lower right corner of object 03177 $row_end = $row_start; // Row containing bottom right corner of object 03178 03179 // Zero the specified offset if greater than the cell dimensions 03180 if ($x1 >= $this->_sizeCol($col_start)) { 03181 $x1 = 0; 03182 } 03183 if ($y1 >= $this->_sizeRow($row_start)) { 03184 $y1 = 0; 03185 } 03186 03187 $width = $width + $x1 -1; 03188 $height = $height + $y1 -1; 03189 03190 // Subtract the underlying cell widths to find the end cell of the image 03191 while ($width >= $this->_sizeCol($col_end)) { 03192 $width -= $this->_sizeCol($col_end); 03193 $col_end++; 03194 } 03195 03196 // Subtract the underlying cell heights to find the end cell of the image 03197 while ($height >= $this->_sizeRow($row_end)) { 03198 $height -= $this->_sizeRow($row_end); 03199 $row_end++; 03200 } 03201 03202 // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell 03203 // with zero eight or width. 03204 // 03205 if ($this->_sizeCol($col_start) == 0) { 03206 return; 03207 } 03208 if ($this->_sizeCol($col_end) == 0) { 03209 return; 03210 } 03211 if ($this->_sizeRow($row_start) == 0) { 03212 return; 03213 } 03214 if ($this->_sizeRow($row_end) == 0) { 03215 return; 03216 } 03217 03218 // Convert the pixel values to the percentage value expected by Excel 03219 $x1 = $x1 / $this->_sizeCol($col_start) * 1024; 03220 $y1 = $y1 / $this->_sizeRow($row_start) * 256; 03221 $x2 = $width / $this->_sizeCol($col_end) * 1024; // Distance to right side of object 03222 $y2 = $height / $this->_sizeRow($row_end) * 256; // Distance to bottom of object 03223 03224 $this->_storeObjPicture($col_start, $x1, 03225 $row_start, $y1, 03226 $col_end, $x2, 03227 $row_end, $y2); 03228 } 03229 03239 function _sizeCol($col) 03240 { 03241 // Look up the cell value to see if it has been changed 03242 if (isset($this->col_sizes[$col])) { 03243 if ($this->col_sizes[$col] == 0) { 03244 return(0); 03245 } else { 03246 return(floor(7 * $this->col_sizes[$col] + 5)); 03247 } 03248 } else { 03249 return(64); 03250 } 03251 } 03252 03263 function _sizeRow($row) 03264 { 03265 // Look up the cell value to see if it has been changed 03266 if (isset($this->_row_sizes[$row])) { 03267 if ($this->_row_sizes[$row] == 0) { 03268 return(0); 03269 } else { 03270 return(floor(4/3 * $this->_row_sizes[$row])); 03271 } 03272 } else { 03273 return(17); 03274 } 03275 } 03276 03291 function _storeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB) 03292 { 03293 $record = 0x005d; // Record identifier 03294 $length = 0x003c; // Bytes to follow 03295 03296 $cObj = 0x0001; // Count of objects in file (set to 1) 03297 $OT = 0x0008; // Object type. 8 = Picture 03298 $id = 0x0001; // Object ID 03299 $grbit = 0x0614; // Option flags 03300 03301 $cbMacro = 0x0000; // Length of FMLA structure 03302 $Reserved1 = 0x0000; // Reserved 03303 $Reserved2 = 0x0000; // Reserved 03304 03305 $icvBack = 0x09; // Background colour 03306 $icvFore = 0x09; // Foreground colour 03307 $fls = 0x00; // Fill pattern 03308 $fAuto = 0x00; // Automatic fill 03309 $icv = 0x08; // Line colour 03310 $lns = 0xff; // Line style 03311 $lnw = 0x01; // Line weight 03312 $fAutoB = 0x00; // Automatic border 03313 $frs = 0x0000; // Frame style 03314 $cf = 0x0009; // Image format, 9 = bitmap 03315 $Reserved3 = 0x0000; // Reserved 03316 $cbPictFmla = 0x0000; // Length of FMLA structure 03317 $Reserved4 = 0x0000; // Reserved 03318 $grbit2 = 0x0001; // Option flags 03319 $Reserved5 = 0x0000; // Reserved 03320 03321 03322 $header = pack("vv", $record, $length); 03323 $data = pack("V", $cObj); 03324 $data .= pack("v", $OT); 03325 $data .= pack("v", $id); 03326 $data .= pack("v", $grbit); 03327 $data .= pack("v", $colL); 03328 $data .= pack("v", $dxL); 03329 $data .= pack("v", $rwT); 03330 $data .= pack("v", $dyT); 03331 $data .= pack("v", $colR); 03332 $data .= pack("v", $dxR); 03333 $data .= pack("v", $rwB); 03334 $data .= pack("v", $dyB); 03335 $data .= pack("v", $cbMacro); 03336 $data .= pack("V", $Reserved1); 03337 $data .= pack("v", $Reserved2); 03338 $data .= pack("C", $icvBack); 03339 $data .= pack("C", $icvFore); 03340 $data .= pack("C", $fls); 03341 $data .= pack("C", $fAuto); 03342 $data .= pack("C", $icv); 03343 $data .= pack("C", $lns); 03344 $data .= pack("C", $lnw); 03345 $data .= pack("C", $fAutoB); 03346 $data .= pack("v", $frs); 03347 $data .= pack("V", $cf); 03348 $data .= pack("v", $Reserved3); 03349 $data .= pack("v", $cbPictFmla); 03350 $data .= pack("v", $Reserved4); 03351 $data .= pack("v", $grbit2); 03352 $data .= pack("V", $Reserved5); 03353 03354 $this->_append($header . $data); 03355 } 03356 03366 function _processBitmap($bitmap) 03367 { 03368 // Open file. 03369 $bmp_fd = @fopen($bitmap,"rb"); 03370 if (!$bmp_fd) { 03371 $this->raiseError("Couldn't import $bitmap"); 03372 } 03373 03374 // Slurp the file into a string. 03375 $data = fread($bmp_fd, filesize($bitmap)); 03376 03377 // Check that the file is big enough to be a bitmap. 03378 if (strlen($data) <= 0x36) { 03379 $this->raiseError("$bitmap doesn't contain enough data.\n"); 03380 } 03381 03382 // The first 2 bytes are used to identify the bitmap. 03383 $identity = unpack("A2ident", $data); 03384 if ($identity['ident'] != "BM") { 03385 $this->raiseError("$bitmap doesn't appear to be a valid bitmap image.\n"); 03386 } 03387 03388 // Remove bitmap data: ID. 03389 $data = substr($data, 2); 03390 03391 // Read and remove the bitmap size. This is more reliable than reading 03392 // the data size at offset 0x22. 03393 // 03394 $size_array = unpack("Vsa", substr($data, 0, 4)); 03395 $size = $size_array['sa']; 03396 $data = substr($data, 4); 03397 $size -= 0x36; // Subtract size of bitmap header. 03398 $size += 0x0C; // Add size of BIFF header. 03399 03400 // Remove bitmap data: reserved, offset, header length. 03401 $data = substr($data, 12); 03402 03403 // Read and remove the bitmap width and height. Verify the sizes. 03404 $width_and_height = unpack("V2", substr($data, 0, 8)); 03405 $width = $width_and_height[1]; 03406 $height = $width_and_height[2]; 03407 $data = substr($data, 8); 03408 if ($width > 0xFFFF) { 03409 $this->raiseError("$bitmap: largest image width supported is 65k.\n"); 03410 } 03411 if ($height > 0xFFFF) { 03412 $this->raiseError("$bitmap: largest image height supported is 65k.\n"); 03413 } 03414 03415 // Read and remove the bitmap planes and bpp data. Verify them. 03416 $planes_and_bitcount = unpack("v2", substr($data, 0, 4)); 03417 $data = substr($data, 4); 03418 if ($planes_and_bitcount[2] != 24) { // Bitcount 03419 $this->raiseError("$bitmap isn't a 24bit true color bitmap.\n"); 03420 } 03421 if ($planes_and_bitcount[1] != 1) { 03422 $this->raiseError("$bitmap: only 1 plane supported in bitmap image.\n"); 03423 } 03424 03425 // Read and remove the bitmap compression. Verify compression. 03426 $compression = unpack("Vcomp", substr($data, 0, 4)); 03427 $data = substr($data, 4); 03428 03429 //$compression = 0; 03430 if ($compression['comp'] != 0) { 03431 $this->raiseError("$bitmap: compression not supported in bitmap image.\n"); 03432 } 03433 03434 // Remove bitmap data: data size, hres, vres, colours, imp. colours. 03435 $data = substr($data, 20); 03436 03437 // Add the BITMAPCOREHEADER data 03438 $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18); 03439 $data = $header . $data; 03440 03441 return (array($width, $height, $size, $data)); 03442 } 03443 03450 function _storeZoom() 03451 { 03452 // If scale is 100 we don't need to write a record 03453 if ($this->_zoom == 100) { 03454 return; 03455 } 03456 03457 $record = 0x00A0; // Record identifier 03458 $length = 0x0004; // Bytes to follow 03459 03460 $header = pack("vv", $record, $length); 03461 $data = pack("vv", $this->_zoom, 100); 03462 $this->_append($header . $data); 03463 } 03464 03468 function setValidation($row1, $col1, $row2, $col2, &$validator) 03469 { 03470 $this->_dv[] = $validator->_getData() . 03471 pack("vvvvv", 1, $row1, $row2, $col1, $col2); 03472 } 03473 03479 function _storeDataValidity() 03480 { 03481 $record = 0x01b2; // Record identifier 03482 $length = 0x0012; // Bytes to follow 03483 03484 $grbit = 0x0002; // Prompt box at cell, no cached validity data at DV records 03485 $horPos = 0x00000000; // Horizontal position of prompt box, if fixed position 03486 $verPos = 0x00000000; // Vertical position of prompt box, if fixed position 03487 $objId = 0xffffffff; // Object identifier of drop down arrow object, or -1 if not visible 03488 03489 $header = pack('vv', $record, $length); 03490 $data = pack('vVVVV', $grbit, $horPos, $verPos, $objId, 03491 count($this->_dv)); 03492 $this->_append($header.$data); 03493 03494 $record = 0x01be; // Record identifier 03495 foreach ($this->_dv as $dv) { 03496 $length = strlen($dv); // Bytes to follow 03497 $header = pack("vv", $record, $length); 03498 $this->_append($header . $dv); 03499 } 03500 } 03501 } 03502 ?>