Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/excel/Worksheet.php
Go to the documentation of this file.
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 ?>
 All Data Structures Namespaces Files Functions Variables Enumerations