• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

classes/Spreadsheet/Excel/Writer/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_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('classes/Spreadsheet/Excel/Writer/Parser.php');
00036 require_once('classes/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 
00363     function Spreadsheet_Excel_Writer_Worksheet($BIFF_version, $name,
00364                                                 $index, &$activesheet,
00365                                                 &$firstsheet, &$str_total,
00366                                                 &$str_unique, &$str_table,
00367                                                 &$url_format, &$parser)
00368     {
00369         // It needs to call its parent's constructor explicitly
00370         $this->Spreadsheet_Excel_Writer_BIFFwriter();
00371         $this->_BIFF_version   = $BIFF_version;
00372         $rowmax                = 65536; // 16384 in Excel 5
00373         $colmax                = 256;
00374     
00375         $this->name            = $name;
00376         $this->index           = $index;
00377         $this->activesheet     = &$activesheet;
00378         $this->firstsheet      = &$firstsheet;
00379         $this->_str_total      = &$str_total;
00380         $this->_str_unique     = &$str_unique;
00381         $this->_str_table      = &$str_table;
00382         $this->_url_format     = &$url_format;
00383         $this->_parser         = &$parser;
00384     
00385         //$this->ext_sheets      = array();
00386         $this->_filehandle     = "";
00387         $this->_using_tmpfile  = true;
00388         //$this->fileclosed      = 0;
00389         //$this->offset          = 0;
00390         $this->_xls_rowmax     = $rowmax;
00391         $this->_xls_colmax     = $colmax;
00392         $this->_xls_strmax     = 255;
00393         $this->_dim_rowmin     = $rowmax + 1;
00394         $this->_dim_rowmax     = 0;
00395         $this->_dim_colmin     = $colmax + 1;
00396         $this->_dim_colmax     = 0;
00397         $this->_colinfo        = array();
00398         $this->_selection      = array(0,0,0,0);
00399         $this->_panes          = array();
00400         $this->_active_pane    = 3;
00401         $this->_frozen         = 0;
00402         $this->selected        = 0;
00403     
00404         $this->_paper_size      = 0x0;
00405         $this->_orientation     = 0x1;
00406         $this->_header          = '';
00407         $this->_footer          = '';
00408         $this->_hcenter         = 0;
00409         $this->_vcenter         = 0;
00410         $this->_margin_head     = 0.50;
00411         $this->_margin_foot     = 0.50;
00412         $this->_margin_left     = 0.75;
00413         $this->_margin_right    = 0.75;
00414         $this->_margin_top      = 1.00;
00415         $this->_margin_bottom   = 1.00;
00416     
00417         $this->title_rowmin     = NULL;
00418         $this->title_rowmax     = NULL;
00419         $this->title_colmin     = NULL;
00420         $this->title_colmax     = NULL;
00421         $this->print_rowmin     = NULL;
00422         $this->print_rowmax     = NULL;
00423         $this->print_colmin     = NULL;
00424         $this->print_colmax     = NULL;
00425     
00426         $this->_print_gridlines = 1;
00427         $this->_print_headers   = 0;
00428     
00429         $this->_fit_page        = 0;
00430         $this->_fit_width       = 0;
00431         $this->_fit_height      = 0;
00432     
00433         $this->_hbreaks         = array();
00434         $this->_vbreaks         = array();
00435     
00436         $this->_protect         = 0;
00437         $this->_password        = NULL;
00438     
00439         $this->col_sizes        = array();
00440         $this->row_sizes        = array();
00441     
00442         $this->_zoom            = 100;
00443         $this->_print_scale     = 100;
00444  
00445         $this->_outline_row_level = 0;
00446         $this->_outline_style     = 0;
00447         $this->_outline_below     = 1;
00448         $this->_outline_right     = 1;
00449         $this->_outline_on        = 1;
00450 
00451         $this->_merged_ranges     = array();
00452 
00453         $this->_dv                = array();
00454 
00455         $this->_initialize();
00456     }
00457     
00465     function _initialize()
00466     {
00467                         if ($this->_using_tmpfile == false) {
00468                                 return;
00469                         }
00470                         // Open tmp file for storing Worksheet data
00471                         $tmp_filename = @tempnam("", "Worksheet_Data");
00472                         $fh = @fopen($tmp_filename, "w+b");
00473                         if ( $fh) {
00474                                 // Store filehandle
00475                                 $this->_filehandle = $fh;
00476                         }
00477                         else {
00478                                 // If tmpfile() fails store data in memory
00479                                 $this->_using_tmpfile = false;
00480                         }
00481                 }
00482     
00492     function close($sheetnames)
00493     {
00494         $num_sheets = count($sheetnames);
00495     
00496         /***********************************************
00497         * Prepend in reverse order!!
00498         */
00499     
00500         // Prepend the sheet dimensions
00501         $this->_storeDimensions();
00502     
00503         // Prepend the sheet password
00504         $this->_storePassword();
00505     
00506         // Prepend the sheet protection
00507         $this->_storeProtect();
00508     
00509         // Prepend the page setup
00510         $this->_storeSetup();
00511     
00512         /* FIXME: margins are actually appended */
00513         // Prepend the bottom margin
00514         $this->_storeMarginBottom();
00515     
00516         // Prepend the top margin
00517         $this->_storeMarginTop();
00518     
00519         // Prepend the right margin
00520         $this->_storeMarginRight();
00521     
00522         // Prepend the left margin
00523         $this->_storeMarginLeft();
00524     
00525         // Prepend the page vertical centering
00526         $this->_storeVcenter();
00527     
00528         // Prepend the page horizontal centering
00529         $this->_storeHcenter();
00530     
00531         // Prepend the page footer
00532         $this->_storeFooter();
00533     
00534         // Prepend the page header
00535         $this->_storeHeader();
00536     
00537         // Prepend the vertical page breaks
00538         $this->_storeVbreak();
00539     
00540         // Prepend the horizontal page breaks
00541         $this->_storeHbreak();
00542     
00543         // Prepend WSBOOL
00544         $this->_storeWsbool();
00545    
00546         // Prepend GRIDSET
00547         $this->_storeGridset();
00548 
00549          //  Prepend GUTS
00550         if ($this->_BIFF_version == 0x0500) {
00551             $this->_storeGuts();
00552         }
00553  
00554         // Prepend PRINTGRIDLINES
00555         $this->_storePrintGridlines();
00556     
00557         // Prepend PRINTHEADERS
00558         $this->_storePrintHeaders();
00559     
00560         // Prepend EXTERNSHEET references
00561         if ($this->_BIFF_version == 0x0500) {
00562             for ($i = $num_sheets; $i > 0; $i--) {
00563                 $sheetname = $sheetnames[$i-1];
00564                 $this->_storeExternsheet($sheetname);
00565             }
00566         }
00567     
00568         // Prepend the EXTERNCOUNT of external references.
00569         if ($this->_BIFF_version == 0x0500) {
00570             $this->_storeExterncount($num_sheets);
00571         }
00572     
00573         // Prepend the COLINFO records if they exist
00574         if (!empty($this->_colinfo))
00575         {
00576             for ($i=0; $i < count($this->_colinfo); $i++) {
00577                 $this->_storeColinfo($this->_colinfo[$i]);
00578             }
00579             $this->_storeDefcol();
00580         }
00581     
00582         // Prepend the BOF record
00583         $this->_storeBof(0x0010);
00584     
00585         /*
00586         * End of prepend. Read upwards from here.
00587         ***********************************************/
00588     
00589         // Append
00590         $this->_storeWindow2();
00591         $this->_storeZoom();
00592         if (!empty($this->_panes)) {
00593             $this->_storePanes($this->_panes);
00594         }
00595         $this->_storeSelection($this->_selection);
00596         $this->_storeMergedCells();
00597         /* TODO: add data validity */
00598         /*if ($this->_BIFF_version == 0x0600) {
00599             $this->_storeDataValidity();
00600         }*/
00601         $this->_storeEof();
00602     }
00603     
00611     function getName()
00612     {
00613         return $this->name;
00614     }
00615     
00622     function getData()
00623     {
00624         $buffer = 4096;
00625     
00626         // Return data stored in memory
00627         if (isset($this->_data))
00628         {
00629             $tmp   = $this->_data;
00630             unset($this->_data);
00631             $fh    = $this->_filehandle;
00632             if ($this->_using_tmpfile) {
00633                 fseek($fh, 0);
00634             }
00635             return $tmp;
00636         }
00637         // Return data stored on disk
00638         if ($this->_using_tmpfile)
00639         {
00640             if ($tmp = fread($this->_filehandle, $buffer)) {
00641                 return $tmp;
00642             }
00643         }
00644     
00645         // No data to return
00646         return '';
00647     }
00648  
00658     function setMerge($first_row, $first_col, $last_row, $last_col)
00659     {
00660         if (($last_row < $first_row) or ($last_col < $first_col)) {
00661             return;
00662         }
00663         // don't check rowmin, rowmax, etc... because we don't know when this
00664         // is going to be called
00665         $this->_merged_ranges[] = array($first_row, $first_col, $last_row, $last_col);
00666     }
00667 
00674     function select()
00675     {
00676         $this->selected = 1;
00677     }
00678     
00686     function activate()
00687     {
00688         $this->selected = 1;
00689         $this->activesheet = $this->index;
00690     }
00691     
00699     function setFirstSheet()
00700     {
00701         $this->firstsheet = $this->index;
00702     }
00703  
00712     function protect($password)
00713     {
00714         $this->_protect   = 1;
00715         $this->_password  = $this->_encodePassword($password);
00716     }
00717  
00729     function setColumn($firstcol, $lastcol, $width, $format = 0, $hidden = 0, $level = 0)
00730     {
00731         $this->_colinfo[] = array($firstcol, $lastcol, $width, &$format, $hidden, $level);
00732     
00733         // Set width to zero if column is hidden
00734         $width = ($hidden) ? 0 : $width;
00735     
00736         for ($col = $firstcol; $col <= $lastcol; $col++) {
00737             $this->col_sizes[$col] = $width;
00738         }
00739     }
00740     
00750     function setSelection($first_row,$first_column,$last_row,$last_column)
00751     {
00752         $this->_selection = array($first_row,$first_column,$last_row,$last_column);
00753     }
00754     
00766     function freezePanes($panes)
00767     {
00768         $this->_frozen = 1;
00769         $this->_panes  = $panes;
00770     }
00771     
00783     function thawPanes($panes)
00784     {
00785         $this->_frozen = 0;
00786         $this->_panes  = $panes;
00787     }
00788     
00794     function setPortrait()
00795     {
00796         $this->_orientation = 1;
00797     }
00798     
00804     function setLandscape()
00805     {
00806         $this->_orientation = 0;
00807     }
00808     
00815     function setPaper($size = 0)
00816     {
00817         $this->_paper_size = $size;
00818     }
00819     
00820     
00828     function setHeader($string,$margin = 0.50)
00829     {
00830         if (strlen($string) >= 255) {
00831             //carp 'Header string must be less than 255 characters';
00832             return;
00833         }
00834         $this->_header      = $string;
00835         $this->_margin_head = $margin;
00836     }
00837     
00845     function setFooter($string,$margin = 0.50)
00846     {
00847         if (strlen($string) >= 255) {
00848             //carp 'Footer string must be less than 255 characters';
00849             return;
00850         }
00851         $this->_footer      = $string;
00852         $this->_margin_foot = $margin;
00853     }
00854     
00861     function centerHorizontally($center = 1)
00862     {
00863         $this->_hcenter = $center;
00864     }
00865     
00872     function centerVertically($center = 1)
00873     {
00874         $this->_vcenter = $center;
00875     }
00876     
00883     function setMargins($margin)
00884     {
00885         $this->setMarginLeft($margin);
00886         $this->setMarginRight($margin);
00887         $this->setMarginTop($margin);
00888         $this->setMarginBottom($margin);
00889     }
00890     
00897     function setMargins_LR($margin)
00898     {
00899         $this->setMarginLeft($margin);
00900         $this->setMarginRight($margin);
00901     }
00902     
00909     function setMargins_TB($margin)
00910     {
00911         $this->setMarginTop($margin);
00912         $this->setMarginBottom($margin);
00913     }
00914     
00921     function setMarginLeft($margin = 0.75)
00922     {
00923         $this->_margin_left = $margin;
00924     }
00925     
00932     function setMarginRight($margin = 0.75)
00933     {
00934         $this->_margin_right = $margin;
00935     }
00936     
00943     function setMarginTop($margin = 1.00)
00944     {
00945         $this->_margin_top = $margin;
00946     }
00947     
00954     function setMarginBottom($margin = 1.00)
00955     {
00956         $this->_margin_bottom = $margin;
00957     }
00958     
00966     function repeatRows($first_row, $last_row = NULL)
00967     {
00968         $this->title_rowmin  = $first_row;
00969         if (isset($last_row)) { //Second row is optional
00970             $this->title_rowmax  = $last_row;
00971         }
00972         else {
00973             $this->title_rowmax  = $first_row;
00974         }
00975     }
00976     
00984     function repeatColumns($first_col, $last_col = NULL)
00985     {
00986         $this->title_colmin  = $first_col;
00987         if (isset($last_col)) { // Second col is optional
00988             $this->title_colmax  = $last_col;
00989         }
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     
01029     function printRowColHeaders($print = 1)
01030     {
01031         $this->_print_headers = $print;
01032     }
01033     
01043     function fitToPages($width, $height)
01044     {
01045         $this->_fit_page      = 1;
01046         $this->_fit_width     = $width;
01047         $this->_fit_height    = $height;
01048     }
01049     
01057     function setHPagebreaks($breaks)
01058     {
01059         foreach($breaks as $break) {
01060             array_push($this->_hbreaks,$break);
01061         }
01062     }
01063     
01071     function setVPagebreaks($breaks)
01072     {
01073         foreach($breaks as $break) {
01074             array_push($this->_vbreaks,$break);
01075         }
01076     }
01077     
01078     
01085     function setZoom($scale = 100)
01086     {
01087         // Confine the scale to Excel's range
01088         if ($scale < 10 or $scale > 400) 
01089         {
01090             $this->raiseError("Zoom factor $scale outside range: 10 <= zoom <= 400");
01091             $scale = 100;
01092         }
01093     
01094         $this->_zoom = floor($scale);
01095     }
01096     
01104     function setPrintScale($scale = 100)
01105     {
01106         // Confine the scale to Excel's range
01107         if ($scale < 10 or $scale > 400)
01108         {
01109             $this->raiseError("Print scale $scale outside range: 10 <= zoom <= 400");
01110             $scale = 100;
01111         }
01112     
01113         // Turn off "fit to page" option
01114         $this->_fit_page    = 0;
01115     
01116         $this->_print_scale = floor($scale);
01117     }
01118     
01128     function write($row, $col, $token, $format = 0)
01129     {
01130         // Check for a cell reference in A1 notation and substitute row and column
01131         /*if ($_[0] =~ /^\D/) {
01132             @_ = $this->_substituteCellref(@_);
01133     }*/
01134     
01135     
01136         // Match number
01137         if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/",$token)) {
01138             return $this->writeNumber($row,$col,$token,$format);
01139         }
01140         // Match http or ftp URL
01141         elseif (preg_match("/^[fh]tt?p:\/\//",$token)) {
01142             return $this->writeUrl($row, $col, $token, '', $format);
01143         }
01144         // Match mailto:
01145         elseif (preg_match("/^mailto:/",$token)) {
01146             return $this->writeUrl($row, $col, $token, '', $format);
01147         }
01148         // Match internal or external sheet link
01149         elseif (preg_match("/^(?:in|ex)ternal:/",$token)) {
01150             return $this->writeUrl($row, $col, $token, '', $format);
01151         }
01152         // Match formula
01153         elseif (preg_match("/^=/",$token)) {
01154             return $this->writeFormula($row, $col, $token, $format);
01155         }
01156         // Match formula
01157         elseif (preg_match("/^@/",$token)) {
01158             return $this->writeFormula($row, $col, $token, $format);
01159         }
01160         // Match blank
01161         elseif ($token == '') {
01162             return $this->writeBlank($row,$col,$format);
01163         }
01164         // Default: match string
01165         else {
01166             return $this->writeString($row,$col,$token,$format);
01167         }
01168     }
01169     
01181     function writeRow($row, $col, $val, $format=0)
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=0)
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 != 0) {
01234             return($format->getXfIndex());
01235         }
01236         else {
01237             return(0x0F);
01238         }
01239     }
01240     
01241     
01242     /******************************************************************************
01243     *******************************************************************************
01244     *
01245     * Internal methods
01246     */
01247     
01248     
01256     function _append($data)
01257     {
01258         if ($this->_using_tmpfile)
01259         {
01260             // Add CONTINUE records if necessary
01261             if (strlen($data) > $this->_limit) {
01262                 $data = $this->_addContinue($data);
01263             }
01264             fwrite($this->_filehandle,$data);
01265             $this->_datasize += strlen($data);
01266         }
01267         else {
01268             parent::_append($data);
01269         }
01270     }
01271     
01282     function _substituteCellref($cell)
01283     {
01284         $cell = strtoupper($cell);
01285     
01286         // Convert a column range: 'A:A' or 'B:G'
01287         if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/",$cell,$match)) {
01288             list($no_use, $col1) =  $this->_cellToRowcol($match[1] .'1'); // Add a dummy row
01289             list($no_use, $col2) =  $this->_cellToRowcol($match[2] .'1'); // Add a dummy row
01290             return(array($col1, $col2));
01291         }
01292     
01293         // Convert a cell range: 'A1:B7'
01294         if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/",$cell,$match)) {
01295             list($row1, $col1) =  $this->_cellToRowcol($match[1]);
01296             list($row2, $col2) =  $this->_cellToRowcol($match[2]);
01297             return(array($row1, $col1, $row2, $col2));
01298         }
01299     
01300         // Convert a cell reference: 'A1' or 'AD2000'
01301         if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/",$cell)) {
01302             list($row1, $col1) =  $this->_cellToRowcol($match[1]);
01303             return(array($row1, $col1));
01304         }
01305     
01306         // TODO use real error codes
01307         $this->raiseError("Unknown cell reference $cell", 0, PEAR_ERROR_DIE);
01308     }
01309     
01318     function _cellToRowcol($cell)
01319     {
01320         preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match);
01321         $col     = $match[1];
01322         $row     = $match[2];
01323     
01324         // Convert base26 column string to number
01325         $chars = split('', $col);
01326         $expn  = 0;
01327         $col   = 0;
01328     
01329         while ($chars) {
01330             $char = array_pop($chars);        // LS char first
01331             $col += (ord($char) -ord('A') +1) * pow(26,$expn);
01332             $expn++;
01333         }
01334     
01335         // Convert 1-index to zero-index
01336         $row--;
01337         $col--;
01338     
01339         return(array($row, $col));
01340     }
01341  
01349     function _encodePassword($plaintext)
01350     {
01351         $password = 0x0000;
01352         $i        = 1;       // char position
01353  
01354         // split the plain text password in its component characters
01355         $chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY);
01356         foreach($chars as $char)
01357         {
01358             $value        = ord($char) << $i;   // shifted ASCII value 
01359             $rotated_bits = $value >> 15;       // rotated bits beyond bit 15
01360             $value       &= 0x7fff;             // first 15 bits
01361             $password    ^= ($value | $rotated_bits);
01362             $i++;
01363         }
01364     
01365         $password ^= strlen($plaintext);
01366         $password ^= 0xCE4B;
01367 
01368         return($password);
01369     }
01370 
01380     function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false)
01381     {
01382         $this->_outline_on    = $visible;
01383         $this->_outline_below = $symbols_below;
01384         $this->_outline_right = $symbols_right;
01385         $this->_outline_style = $auto_style;
01386 
01387         // Ensure this is a boolean vale for Window2
01388         if ($this->_outline_on) {
01389             $this->_outline_on = 1;
01390         }
01391      }
01392 
01393     /******************************************************************************
01394     *******************************************************************************
01395     *
01396     * BIFF RECORDS
01397     */
01398     
01399     
01415     function writeNumber($row, $col, $num, $format = 0)
01416     {
01417         $record    = 0x0203;                 // Record identifier
01418         $length    = 0x000E;                 // Number of bytes to follow
01419     
01420         $xf        = $this->_XF($format);    // The cell format
01421     
01422         // Check that row and col are valid and store max and min values
01423         if ($row >= $this->_xls_rowmax)
01424         {
01425             return(-2);
01426         }
01427         if ($col >= $this->_xls_colmax)
01428         {
01429             return(-2);
01430         }
01431         if ($row <  $this->_dim_rowmin) 
01432         {
01433             $this->_dim_rowmin = $row;
01434         }
01435         if ($row >  $this->_dim_rowmax) 
01436         {
01437             $this->_dim_rowmax = $row;
01438         }
01439         if ($col <  $this->_dim_colmin) 
01440         {
01441             $this->_dim_colmin = $col;
01442         }
01443         if ($col >  $this->_dim_colmax) 
01444         {
01445             $this->_dim_colmax = $col;
01446         }
01447     
01448         $header    = pack("vv",  $record, $length);
01449         $data      = pack("vvv", $row, $col, $xf);
01450         $xl_double = pack("d",   $num);
01451         if ($this->_byte_order) // if it's Big Endian
01452         {
01453             $xl_double = strrev($xl_double);
01454         }
01455     
01456         $this->_append($header.$data.$xl_double);
01457         return(0);
01458     }
01459     
01475     function writeString($row, $col, $str, $format = 0)
01476     {
01477         if ($this->_BIFF_version == 0x0600) {
01478             return $this->writeStringBIFF8($row, $col, $str, $format);
01479         }
01480         $strlen    = strlen($str);
01481         $record    = 0x0204;                   // Record identifier
01482         $length    = 0x0008 + $strlen;         // Bytes to follow
01483         $xf        = $this->_XF($format);      // The cell format
01484         
01485         $str_error = 0;
01486     
01487         // Check that row and col are valid and store max and min values
01488         if ($row >= $this->_xls_rowmax) 
01489         {
01490             return(-2);
01491         }
01492         if ($col >= $this->_xls_colmax) 
01493         {
01494             return(-2);
01495         }
01496         if ($row <  $this->_dim_rowmin) 
01497         {
01498             $this->_dim_rowmin = $row;
01499         }
01500         if ($row >  $this->_dim_rowmax) 
01501         {
01502             $this->_dim_rowmax = $row;
01503         }
01504         if ($col <  $this->_dim_colmin) 
01505         {
01506             $this->_dim_colmin = $col;
01507         }
01508         if ($col >  $this->_dim_colmax) 
01509         {
01510             $this->_dim_colmax = $col;
01511         }
01512     
01513         if ($strlen > $this->_xls_strmax)  // LABEL must be < 255 chars
01514         {
01515             $str       = substr($str, 0, $this->_xls_strmax);
01516             $length    = 0x0008 + $this->_xls_strmax;
01517             $strlen    = $this->_xls_strmax;
01518             $str_error = -3;
01519         }
01520     
01521         $header    = pack("vv",   $record, $length);
01522         $data      = pack("vvvv", $row, $col, $xf, $strlen);
01523         $this->_append($header.$data.$str);
01524         return($str_error);
01525     }
01526 
01527     function writeStringBIFF8($row, $col, $str, $format = 0)
01528     {
01529         $strlen    = strlen($str);
01530         $record    = 0x00FD;                   // Record identifier
01531         $length    = 0x000A;                   // Bytes to follow
01532         $xf        = $this->_XF($format);      // The cell format
01533         $encoding  = 0x0;
01534         
01535         $str_error = 0;
01536     
01537         // Check that row and col are valid and store max and min values
01538         if ($this->_checkRowCol($row, $col) == false) {
01539             return -2;
01540         }
01541 
01542         $str = pack('vC', $strlen, $encoding).$str;
01543 
01544         /* check if string is already present */
01545         if (!isset($this->_str_table[$str])) {
01546             $this->_str_table[$str] = $this->_str_unique++;
01547         }
01548         $this->_str_total++;
01549     
01550         $header    = pack('vv',   $record, $length);
01551         $data      = pack('vvvV', $row, $col, $xf, $this->_str_table[$str]);
01552         $this->_append($header.$data);
01553         return $str_error;
01554     }
01555 
01566     function _checkRowCol($row, $col)
01567     {
01568         if ($row >= $this->_xls_rowmax) {
01569             return false;
01570         }
01571         if ($col >= $this->_xls_colmax) {
01572             return false;
01573         }
01574         if ($row <  $this->_dim_rowmin) {
01575             $this->_dim_rowmin = $row;
01576         }
01577         if ($row >  $this->_dim_rowmax) {
01578             $this->_dim_rowmax = $row;
01579         }
01580         if ($col <  $this->_dim_colmin) {
01581             $this->_dim_colmin = $col;
01582         }
01583         if ($col >  $this->_dim_colmax) {
01584             $this->_dim_colmax = $col;
01585         }
01586         return true;
01587     }
01588 
01598     function writeNote($row, $col, $note)
01599     {
01600         $note_length    = strlen($note);
01601         $record         = 0x001C;                // Record identifier
01602         $max_length     = 2048;                  // Maximun length for a NOTE record
01603         //$length      = 0x0006 + $note_length;    // Bytes to follow
01604 
01605         // Check that row and col are valid and store max and min values
01606         if ($row >= $this->_xls_rowmax) 
01607         {
01608             return(-2);
01609         }
01610         if ($col >= $this->_xls_colmax) 
01611         {
01612             return(-2);
01613         }
01614         if ($row <  $this->_dim_rowmin) 
01615         {
01616             $this->_dim_rowmin = $row;
01617         }
01618         if ($row >  $this->_dim_rowmax) 
01619         {
01620             $this->_dim_rowmax = $row;
01621         }
01622         if ($col <  $this->_dim_colmin) 
01623         {
01624             $this->_dim_colmin = $col;
01625         }
01626         if ($col >  $this->_dim_colmax) 
01627         {
01628             $this->_dim_colmax = $col;
01629         }
01630  
01631         // Length for this record is no more than 2048 + 6
01632         $length    = 0x0006 + min($note_length, 2048);
01633         $header    = pack("vv",   $record, $length);
01634         $data      = pack("vvv", $row, $col, $note_length);
01635         $this->_append($header.$data.substr($note, 0, 2048));
01636 
01637         for($i = $max_length; $i < $note_length; $i += $max_length)
01638         {
01639             $chunk  = substr($note, $i, $max_length);
01640             $length = 0x0006 + strlen($chunk);
01641             $header = pack("vv",   $record, $length);
01642             $data   = pack("vvv", -1, 0, strlen($chunk));
01643             $this->_append($header.$data.$chunk);
01644         }
01645         return(0);
01646     }
01647 
01665     function writeBlank($row, $col, $format)
01666     {
01667         // Don't write a blank cell unless it has a format
01668         if ($format == 0)
01669         {
01670             return(0);
01671         }
01672     
01673         $record    = 0x0201;                 // Record identifier
01674         $length    = 0x0006;                 // Number of bytes to follow
01675         $xf        = $this->_XF($format);    // The cell format
01676     
01677         // Check that row and col are valid and store max and min values
01678         if ($row >= $this->_xls_rowmax) 
01679         {
01680             return(-2);
01681         }
01682         if ($col >= $this->_xls_colmax) 
01683         {
01684             return(-2);
01685         }
01686         if ($row <  $this->_dim_rowmin) 
01687         {
01688             $this->_dim_rowmin = $row;
01689         }
01690         if ($row >  $this->_dim_rowmax) 
01691         {
01692             $this->_dim_rowmax = $row;
01693         }
01694         if ($col <  $this->_dim_colmin) 
01695         {
01696             $this->_dim_colmin = $col;
01697         }
01698         if ($col >  $this->_dim_colmax) 
01699         {
01700             $this->_dim_colmax = $col;
01701         }
01702     
01703         $header    = pack("vv",  $record, $length);
01704         $data      = pack("vvv", $row, $col, $xf);
01705         $this->_append($header.$data);
01706         return 0;
01707     }
01708 
01725     function writeFormula($row, $col, $formula, $format = 0)
01726     {
01727         $record    = 0x0006;     // Record identifier
01728     
01729         // Excel normally stores the last calculated value of the formula in $num.
01730         // Clearly we are not in a position to calculate this a priori. Instead
01731         // we set $num to zero and set the option flags in $grbit to ensure
01732         // automatic calculation of the formula when the file is opened.
01733         //
01734         $xf        = $this->_XF($format); // The cell format
01735         $num       = 0x00;                // Current value of formula
01736         $grbit     = 0x03;                // Option flags
01737         $unknown   = 0x0000;              // Must be zero
01738     
01739     
01740         // Check that row and col are valid and store max and min values
01741         if ($this->_checkRowCol($row, $col) == false) {
01742             return -2;
01743         }
01744     
01745         // Strip the '=' or '@' sign at the beginning of the formula string
01746         if (preg_match("/^=/",$formula)) {
01747             $formula = preg_replace("/(^=)/","",$formula);
01748         }
01749         elseif (preg_match("/^@/",$formula)) {
01750             $formula = preg_replace("/(^@)/","",$formula);
01751         }
01752         else
01753         {
01754             // Error handling
01755             $this->writeString($row, $col, 'Unrecognised character for formula');
01756             return -1;
01757         }
01758     
01759         // Parse the formula using the parser in Parser.php
01760         $error = $this->_parser->parse($formula);
01761         if ($this->isError($error))
01762         {
01763             $this->writeString($row, $col, $error->getMessage()); 
01764             return -1;
01765         }
01766     
01767         $formula = $this->_parser->toReversePolish();
01768         if ($this->isError($formula))
01769         {
01770             $this->writeString($row, $col, $formula->getMessage());
01771             return -1;
01772         }
01773     
01774         $formlen    = strlen($formula);    // Length of the binary string
01775         $length     = 0x16 + $formlen;     // Length of the record data
01776     
01777         $header    = pack("vv",      $record, $length);
01778         $data      = pack("vvvdvVv", $row, $col, $xf, $num,
01779                                      $grbit, $unknown, $formlen);
01780     
01781         $this->_append($header.$data.$formula);
01782         return 0;
01783     }
01784     
01808     function writeUrl($row, $col, $url, $string = '', $format = 0)
01809     {
01810         // Add start row and col to arg list
01811         return($this->_writeUrlRange($row, $col, $row, $col, $url, $string, $format));
01812     }
01813     
01832     function _writeUrlRange($row1, $col1, $row2, $col2, $url, $string = '', $format = 0)
01833     {
01834     
01835         // Check for internal/external sheet links or default to web link
01836         if (preg_match('[^internal:]', $url)) {
01837             return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url, $string, $format));
01838         }
01839         if (preg_match('[^external:]', $url)) {
01840             return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url, $string, $format));
01841         }
01842         return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url, $string, $format));
01843     }
01844     
01845     
01862     function _writeUrlWeb($row1, $col1, $row2, $col2, $url, $str, $format = 0)
01863     {
01864         $record      = 0x01B8;                       // Record identifier
01865         $length      = 0x00000;                      // Bytes to follow
01866     
01867         if ($format == 0) {
01868             $format = $this->_url_format;
01869         }
01870     
01871         // Write the visible label using the writeString() method.
01872         if ($str == '') {
01873             $str = $url;
01874         }
01875         $str_error = $this->writeString($row1, $col1, $str, $format);
01876         if (($str_error == -2) or ($str_error == -3)) {
01877             return $str_error;
01878         }
01879     
01880         // Pack the undocumented parts of the hyperlink stream
01881         $unknown1    = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
01882         $unknown2    = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
01883     
01884         // Pack the option flags
01885         $options     = pack("V", 0x03);
01886     
01887         // Convert URL to a null terminated wchar string
01888         $url         = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
01889         $url         = $url . "\0\0\0";
01890     
01891         // Pack the length of the URL
01892         $url_len     = pack("V", strlen($url));
01893     
01894         // Calculate the data length
01895         $length      = 0x34 + strlen($url);
01896     
01897         // Pack the header data
01898         $header      = pack("vv",   $record, $length);
01899         $data        = pack("vvvv", $row1, $row2, $col1, $col2);
01900     
01901         // Write the packed data
01902         $this->_append( $header. $data.
01903                         $unknown1. $options.
01904                         $unknown2. $url_len. $url);
01905         return($str_error);
01906     }
01907     
01922     function _writeUrlInternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
01923     {
01924         $record      = 0x01B8;                       // Record identifier
01925         $length      = 0x00000;                      // Bytes to follow
01926     
01927         if ($format == 0) {
01928             $format = $this->_url_format;
01929         }
01930     
01931         // Strip URL type
01932         $url = preg_replace('s[^internal:]', '', $url);
01933     
01934         // Write the visible label
01935         if ($str == '') {
01936             $str = $url;
01937         }
01938         $str_error = $this->writeString($row1, $col1, $str, $format);
01939         if (($str_error == -2) or ($str_error == -3)) {
01940             return $str_error;
01941         }
01942     
01943         // Pack the undocumented parts of the hyperlink stream
01944         $unknown1    = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
01945     
01946         // Pack the option flags
01947         $options     = pack("V", 0x08);
01948     
01949         // Convert the URL type and to a null terminated wchar string
01950         $url         = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
01951         $url         = $url . "\0\0\0";
01952     
01953         // Pack the length of the URL as chars (not wchars)
01954         $url_len     = pack("V", floor(strlen($url)/2));
01955     
01956         // Calculate the data length
01957         $length      = 0x24 + strlen($url);
01958     
01959         // Pack the header data
01960         $header      = pack("vv",   $record, $length);
01961         $data        = pack("vvvv", $row1, $row2, $col1, $col2);
01962     
01963         // Write the packed data
01964         $this->_append($header. $data.
01965                        $unknown1. $options.
01966                        $url_len. $url);
01967         return($str_error);
01968     }
01969     
01988     function _writeUrlExternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
01989     {
01990         // Network drives are different. We will handle them separately
01991         // MS/Novell network drives and shares start with \\
01992         if (preg_match('[^external:\\\\]', $url)) {
01993             return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
01994         }
01995     
01996         $record      = 0x01B8;                       // Record identifier
01997         $length      = 0x00000;                      // Bytes to follow
01998     
01999         if ($format == 0) {
02000             $format = $this->_url_format;
02001         }
02002     
02003         // Strip URL type and change Unix dir separator to Dos style (if needed)
02004         //
02005         $url = preg_replace('[^external:]', '', $url);
02006         $url = preg_replace('[/]', "\\", $url);
02007     
02008         // Write the visible label
02009         if ($str == '') {
02010             $str = preg_replace('[\#]', ' - ', $url);
02011         }
02012         $str_error = $this->writeString($row1, $col1, $str, $format);
02013         if (($str_error == -2) or ($str_error == -3)) {
02014             return $str_error;
02015         }
02016     
02017         // Determine if the link is relative or absolute:
02018         //   relative if link contains no dir separator, "somefile.xls"
02019         //   relative if link starts with up-dir, "..\..\somefile.xls"
02020         //   otherwise, absolute
02021         
02022         $absolute    = 0x02; // Bit mask
02023         if (!preg_match('[\\]', $url)) {
02024             $absolute    = 0x00;
02025         }
02026         if (preg_match('[^\.\.\\]', $url)) {
02027             $absolute    = 0x00;
02028         }
02029     
02030         // Determine if the link contains a sheet reference and change some of the
02031         // parameters accordingly.
02032         // Split the dir name and sheet name (if it exists)
02033         list($dir_long , $sheet) = split('/\#/', $url);
02034         $link_type               = 0x01 | $absolute;
02035     
02036         if (isset($sheet)) {
02037             $link_type |= 0x08;
02038             $sheet_len  = pack("V", strlen($sheet) + 0x01);
02039             $sheet      = join("\0", split('', $sheet));
02040             $sheet     .= "\0\0\0";
02041         }
02042         else {
02043             $sheet_len   = '';
02044             $sheet       = '';
02045         }
02046     
02047         // Pack the link type
02048         $link_type   = pack("V", $link_type);
02049     
02050         // Calculate the up-level dir count e.g.. (..\..\..\ == 3)
02051         $up_count    = preg_match_all("/\.\.\\/", $dir_long, $useless);
02052         $up_count    = pack("v", $up_count);
02053     
02054         // Store the short dos dir name (null terminated)
02055         $dir_short   = preg_replace('/\.\.\\/', '', $dir_long) . "\0";
02056     
02057         // Store the long dir name as a wchar string (non-null terminated)
02058         $dir_long       = join("\0", split('', $dir_long));
02059         $dir_long       = $dir_long . "\0";
02060     
02061         // Pack the lengths of the dir strings
02062         $dir_short_len = pack("V", strlen($dir_short)      );
02063         $dir_long_len  = pack("V", strlen($dir_long)       );
02064         $stream_len    = pack("V", strlen($dir_long) + 0x06);
02065     
02066         // Pack the undocumented parts of the hyperlink stream
02067         $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000'       );
02068         $unknown2 = pack("H*",'0303000000000000C000000000000046'               );
02069         $unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000');
02070         $unknown4 = pack("v",  0x03                                            );
02071     
02072         // Pack the main data stream
02073         $data        = pack("vvvv", $row1, $row2, $col1, $col2) .
02074                           $unknown1     .
02075                           $link_type    .
02076                           $unknown2     .
02077                           $up_count     .
02078                           $dir_short_len.
02079                           $dir_short    .
02080                           $unknown3     .
02081                           $stream_len   .
02082                           $dir_long_len .
02083                           $unknown4     .
02084                           $dir_long     .
02085                           $sheet_len    .
02086                           $sheet        ;
02087     
02088         // Pack the header data
02089         $length   = strlen($data);
02090         $header   = pack("vv", $record, $length);
02091     
02092         // Write the packed data
02093         $this->_append($header. $data);
02094         return($str_error);
02095     }
02096     
02097     
02109     function setRow($row, $height, $format = 0, $hidden = false, $level = 0)
02110     {
02111         $record      = 0x0208;               // Record identifier
02112         $length      = 0x0010;               // Number of bytes to follow
02113     
02114         $colMic      = 0x0000;               // First defined column
02115         $colMac      = 0x0000;               // Last defined column
02116         $irwMac      = 0x0000;               // Used by Excel to optimise loading
02117         $reserved    = 0x0000;               // Reserved
02118         $grbit       = 0x0000;               // Option flags
02119         $ixfe        = $this->_XF($format);  // XF index
02120     
02121         // Use setRow($row, NULL, $XF) to set XF format without setting height
02122         if ($height != NULL) {
02123             $miyRw = $height * 20;  // row height
02124         }
02125         else {
02126             $miyRw = 0xff;          // default row height is 256
02127         }
02128 
02129         $level = max(0, min($level, 7));  // level should be between 0 and 7
02130         $this->_outline_row_level = max($level, $this->_outline_row_level);
02131 
02132 
02133         // Set the options flags. fUnsynced is used to show that the font and row
02134         // heights are not compatible. This is usually the case for WriteExcel.
02135         // The collapsed flag 0x10 doesn't seem to be used to indicate that a row
02136         // is collapsed. Instead it is used to indicate that the previous row is
02137         // collapsed. The zero height flag, 0x20, is used to collapse a row.
02138 
02139         $grbit |= $level;
02140         if ($hidden) {
02141             $grbit |= 0x0020;
02142         }
02143         $grbit |= 0x0040; // fUnsynced
02144         if ($format) {
02145             $grbit |= 0x0080;
02146         }
02147         $grbit |= 0x0100;
02148 
02149         $header   = pack("vv",       $record, $length);
02150         $data     = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw,
02151                                      $irwMac,$reserved, $grbit, $ixfe);
02152         $this->_append($header.$data);
02153     }
02154     
02160     function _storeDimensions()
02161     {
02162         $record    = 0x0200;                 // Record identifier
02163         $row_min   = $this->_dim_rowmin;     // First row
02164         $row_max   = $this->_dim_rowmax + 1; // Last row plus 1
02165         $col_min   = $this->_dim_colmin;     // First column
02166         $col_max   = $this->_dim_colmax + 1; // Last column plus 1
02167         $reserved  = 0x0000;                 // Reserved by Excel
02168     
02169         if ($this->_BIFF_version == 0x0500) {
02170             $length    = 0x000A;               // Number of bytes to follow
02171             $data      = pack("vvvvv", $row_min, $row_max,
02172                                        $col_min, $col_max, $reserved);
02173         }
02174         elseif ($this->_BIFF_version == 0x0600) {
02175             $length    = 0x000E;
02176             $data      = pack("VVvvv", $row_min, $row_max,
02177                                        $col_min, $col_max, $reserved);
02178         }
02179         $header = pack("vv", $record, $length);
02180         $this->_prepend($header.$data);
02181     }
02182     
02188     function _storeWindow2()
02189     {
02190         $record         = 0x023E;     // Record identifier
02191         if ($this->_BIFF_version == 0x0500) {
02192             $length         = 0x000A;     // Number of bytes to follow
02193         }
02194         elseif ($this->_BIFF_version == 0x0600) {
02195             $length         = 0x0012;
02196         }
02197 
02198         $grbit          = 0x00B6;     // Option flags
02199         $rwTop          = 0x0000;     // Top row visible in window
02200         $colLeft        = 0x0000;     // Leftmost column visible in window
02201         
02202     
02203         // The options flags that comprise $grbit
02204         $fDspFmla       = 0;                     // 0 - bit
02205         $fDspGrid       = 1;                     // 1
02206         $fDspRwCol      = 1;                     // 2
02207         $fFrozen        = $this->_frozen;        // 3
02208         $fDspZeros      = 1;                     // 4
02209         $fDefaultHdr    = 1;                     // 5
02210         $fArabic        = 0;                     // 6
02211         $fDspGuts       = $this->_outline_on;    // 7
02212         $fFrozenNoSplit = 0;                     // 0 - bit
02213         $fSelected      = $this->selected;       // 1
02214         $fPaged         = 1;                     // 2
02215     
02216         $grbit             = $fDspFmla;
02217         $grbit            |= $fDspGrid       << 1;
02218         $grbit            |= $fDspRwCol      << 2;
02219         $grbit            |= $fFrozen        << 3;
02220         $grbit            |= $fDspZeros      << 4;
02221         $grbit            |= $fDefaultHdr    << 5;
02222         $grbit            |= $fArabic        << 6;
02223         $grbit            |= $fDspGuts       << 7;
02224         $grbit            |= $fFrozenNoSplit << 8;
02225         $grbit            |= $fSelected      << 9;
02226         $grbit            |= $fPaged         << 10;
02227     
02228         $header  = pack("vv",   $record, $length);
02229         $data    = pack("vvv", $grbit, $rwTop, $colLeft);
02230         // FIXME !!!
02231         if ($this->_BIFF_version == 0x0500) {
02232             $rgbHdr         = 0x00000000; // Row/column heading and gridline color
02233             $data .= pack("V", $rgbHdr);
02234         }
02235         elseif ($this->_BIFF_version == 0x0600) {
02236             $rgbHdr       = 0x0040; // Row/column heading and gridline color index
02237             $zoom_factor_page_break = 0x0000;
02238             $zoom_factor_normal     = 0x0000;
02239             $data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000);
02240         }
02241         $this->_append($header.$data);
02242     }
02243     
02249     function _storeDefcol()
02250     {
02251         $record   = 0x0055;      // Record identifier
02252         $length   = 0x0002;      // Number of bytes to follow
02253         $colwidth = 0x0008;      // Default column width
02254     
02255         $header   = pack("vv", $record, $length);
02256         $data     = pack("v",  $colwidth);
02257         $this->_prepend($header.$data);
02258     }
02259     
02275     function _storeColinfo($col_array)
02276     {
02277         if (isset($col_array[0])) {
02278             $colFirst = $col_array[0];
02279         }
02280         if (isset($col_array[1])) {
02281             $colLast = $col_array[1];
02282         }
02283         if (isset($col_array[2])) {
02284             $coldx = $col_array[2];
02285         }
02286         else {
02287             $coldx = 8.43;
02288         }
02289         if (isset($col_array[3])) {
02290             $format = $col_array[3];
02291         }
02292         else {
02293             $format = 0;
02294         }
02295         if (isset($col_array[4])) {
02296             $grbit = $col_array[4];
02297         }
02298         else {
02299             $grbit = 0;
02300         }
02301         if (isset($col_array[5])) {
02302             $level = $col_array[5];
02303         }
02304         else {
02305             $level = 0;
02306         }
02307         $record   = 0x007D;          // Record identifier
02308         $length   = 0x000B;          // Number of bytes to follow
02309     
02310         $coldx   += 0.72;            // Fudge. Excel subtracts 0.72 !?
02311         $coldx   *= 256;             // Convert to units of 1/256 of a char
02312     
02313         $ixfe     = $this->_XF($format);
02314         $reserved = 0x00;            // Reserved
02315 
02316         $level = max(0, min($level, 7));
02317         $grbit |= $level << 8;
02318 
02319         $header   = pack("vv",     $record, $length);
02320         $data     = pack("vvvvvC", $colFirst, $colLast, $coldx,
02321                                    $ixfe, $grbit, $reserved);
02322         $this->_prepend($header.$data);
02323     }
02324     
02332     function _storeSelection($array)
02333     {
02334         list($rwFirst,$colFirst,$rwLast,$colLast) = $array;
02335         $record   = 0x001D;                  // Record identifier
02336         $length   = 0x000F;                  // Number of bytes to follow
02337     
02338         $pnn      = $this->_active_pane;     // Pane position
02339         $rwAct    = $rwFirst;                // Active row
02340         $colAct   = $colFirst;               // Active column
02341         $irefAct  = 0;                       // Active cell ref
02342         $cref     = 1;                       // Number of refs
02343     
02344         if (!isset($rwLast)) {
02345             $rwLast   = $rwFirst;       // Last  row in reference
02346         }
02347         if (!isset($colLast)) {
02348             $colLast  = $colFirst;      // Last  col in reference
02349         }
02350     
02351         // Swap last row/col for first row/col as necessary
02352         if ($rwFirst > $rwLast)
02353         {
02354             list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
02355         }
02356     
02357         if ($colFirst > $colLast)
02358         {
02359             list($colFirst, $colLast) = array($colLast, $colFirst);
02360         }
02361     
02362         $header   = pack("vv",         $record, $length);
02363         $data     = pack("CvvvvvvCC",  $pnn, $rwAct, $colAct,
02364                                        $irefAct, $cref,
02365                                        $rwFirst, $rwLast,
02366                                        $colFirst, $colLast);
02367         $this->_append($header.$data);
02368     }
02369  
02375     function _storeMergedCells()
02376     {
02377         // if there are no merged cell ranges set, return
02378         if (count($this->_merged_ranges) == 0) {
02379             return;
02380         }
02381         $record   = 0x00E5;
02382         $length   = 2 + count($this->_merged_ranges) * 8; 
02383  
02384         $header   = pack('vv', $record, $length);
02385         $data     = pack('v',  count($this->_merged_ranges));
02386         foreach ($this->_merged_ranges as $range) {
02387             $data .= pack('vvvv', $range[0], $range[2], $range[1], $range[3]);
02388         }
02389         $this->_append($header.$data);
02390     }
02391     
02405     function _storeExterncount($count)
02406     {
02407         $record   = 0x0016;          // Record identifier
02408         $length   = 0x0002;          // Number of bytes to follow
02409     
02410         $header   = pack("vv", $record, $length);
02411         $data     = pack("v",  $count);
02412         $this->_prepend($header.$data);
02413     }
02414     
02424     function _storeExternsheet($sheetname)
02425     {
02426         $record    = 0x0017;         // Record identifier
02427     
02428         // References to the current sheet are encoded differently to references to
02429         // external sheets.
02430         //
02431         if ($this->name == $sheetname) {
02432             $sheetname = '';
02433             $length    = 0x02;  // The following 2 bytes
02434             $cch       = 1;     // The following byte
02435             $rgch      = 0x02;  // Self reference
02436         }
02437         else {
02438             $length    = 0x02 + strlen($sheetname);
02439             $cch       = strlen($sheetname);
02440             $rgch      = 0x03;  // Reference to a sheet in the current workbook
02441         }
02442     
02443         $header     = pack("vv",  $record, $length);
02444         $data       = pack("CC", $cch, $rgch);
02445         $this->_prepend($header.$data.$sheetname);
02446     }
02447     
02462     function _storePanes($panes)
02463     {
02464         $y       = $panes[0];
02465         $x       = $panes[1];
02466         $rwTop   = $panes[2];
02467         $colLeft = $panes[3];
02468         if (count($panes) > 4) { // if Active pane was received
02469             $pnnAct = $panes[4];
02470         }
02471         else {
02472             $pnnAct = NULL;
02473         }
02474         $record  = 0x0041;       // Record identifier
02475         $length  = 0x000A;       // Number of bytes to follow
02476     
02477         // Code specific to frozen or thawed panes.
02478         if ($this->_frozen)
02479         {
02480             // Set default values for $rwTop and $colLeft
02481             if (!isset($rwTop)) {
02482                 $rwTop   = $y;
02483             }
02484             if (!isset($colLeft)) {
02485                 $colLeft = $x;
02486             }
02487         }
02488         else
02489         {
02490             // Set default values for $rwTop and $colLeft
02491             if (!isset($rwTop)) {
02492                 $rwTop   = 0;
02493             }
02494             if (!isset($colLeft)) {
02495                 $colLeft = 0;
02496             }
02497     
02498             // Convert Excel's row and column units to the internal units.
02499             // The default row height is 12.75
02500             // The default column width is 8.43
02501             // The following slope and intersection values were interpolated.
02502             //
02503             $y = 20*$y      + 255;
02504             $x = 113.879*$x + 390;
02505         }
02506     
02507     
02508         // Determine which pane should be active. There is also the undocumented
02509         // option to override this should it be necessary: may be removed later.
02510         //
02511         if (!isset($pnnAct))
02512         {
02513             if ($x != 0 and $y != 0)
02514                 $pnnAct = 0; // Bottom right
02515             if ($x != 0 and $y == 0)
02516                 $pnnAct = 1; // Top right
02517             if ($x == 0 and $y != 0)
02518                 $pnnAct = 2; // Bottom left
02519             if ($x == 0 and $y == 0)
02520                 $pnnAct = 3; // Top left
02521         }
02522     
02523         $this->_active_pane = $pnnAct; // Used in _storeSelection
02524     
02525         $header     = pack("vv",    $record, $length);
02526         $data       = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
02527         $this->_append($header.$data);
02528     }
02529     
02535     function _storeSetup()
02536     {
02537         $record       = 0x00A1;                  // Record identifier
02538         $length       = 0x0022;                  // Number of bytes to follow
02539     
02540         $iPaperSize   = $this->_paper_size;    // Paper size
02541         $iScale       = $this->_print_scale;   // Print scaling factor
02542         $iPageStart   = 0x01;                 // Starting page number
02543         $iFitWidth    = $this->_fit_width;    // Fit to number of pages wide
02544         $iFitHeight   = $this->_fit_height;   // Fit to number of pages high
02545         $grbit        = 0x00;                 // Option flags
02546         $iRes         = 0x0258;               // Print resolution
02547         $iVRes        = 0x0258;               // Vertical print resolution
02548         $numHdr       = $this->_margin_head;  // Header Margin
02549         $numFtr       = $this->_margin_foot;   // Footer Margin
02550         $iCopies      = 0x01;                 // Number of copies
02551     
02552         $fLeftToRight = 0x0;                     // Print over then down
02553         $fLandscape   = $this->_orientation;     // Page orientation
02554         $fNoPls       = 0x0;                     // Setup not read from printer
02555         $fNoColor     = 0x0;                     // Print black and white
02556         $fDraft       = 0x0;                     // Print draft quality
02557         $fNotes       = 0x0;                     // Print notes
02558         $fNoOrient    = 0x0;                     // Orientation not set
02559         $fUsePage     = 0x0;                     // Use custom starting page
02560     
02561         $grbit           = $fLeftToRight;
02562         $grbit          |= $fLandscape    << 1;
02563         $grbit          |= $fNoPls        << 2;
02564         $grbit          |= $fNoColor      << 3;
02565         $grbit          |= $fDraft        << 4;
02566         $grbit          |= $fNotes        << 5;
02567         $grbit          |= $fNoOrient     << 6;
02568         $grbit          |= $fUsePage      << 7;
02569     
02570         $numHdr = pack("d", $numHdr);
02571         $numFtr = pack("d", $numFtr);
02572         if ($this->_byte_order) // if it's Big Endian
02573         {
02574             $numHdr = strrev($numHdr);
02575             $numFtr = strrev($numFtr);
02576         }
02577     
02578         $header = pack("vv", $record, $length);
02579         $data1  = pack("vvvvvvvv", $iPaperSize,
02580                                    $iScale,
02581                                    $iPageStart,
02582                                    $iFitWidth,
02583                                    $iFitHeight,
02584                                    $grbit,
02585                                    $iRes,
02586                                    $iVRes);
02587         $data2  = $numHdr.$numFtr;
02588         $data3  = pack("v", $iCopies);
02589         $this->_prepend($header.$data1.$data2.$data3);
02590     }
02591     
02597     function _storeHeader()
02598     {
02599         $record  = 0x0014;               // Record identifier
02600     
02601         $str      = $this->_header;       // header string
02602         $cch      = strlen($str);         // Length of header string
02603         if ($this->_BIFF_version == 0x0600) {
02604             $encoding = 0x0;                  // TODO: Unicode support
02605             $length   = 3 + $cch;             // Bytes to follow
02606         }
02607         else {
02608             $length  = 1 + $cch;             // Bytes to follow
02609         }
02610         $header   = pack("vv", $record, $length);
02611         if ($this->_BIFF_version == 0x0600) {
02612             $data     = pack("vC",  $cch, $encoding);
02613         }
02614         else {
02615             $data      = pack("C",  $cch);
02616         }
02617     
02618         $this->_append($header.$data.$str);
02619     }
02620     
02626     function _storeFooter()
02627     {
02628         $record  = 0x0015;               // Record identifier
02629     
02630         $str      = $this->_footer;       // Footer string
02631         $cch      = strlen($str);         // Length of footer string
02632         if ($this->_BIFF_version == 0x0600) {
02633             $encoding = 0x0;                  // TODO: Unicode support
02634             $length   = 3 + $cch;             // Bytes to follow
02635         }
02636         else {
02637             $length  = 1 + $cch;
02638         }
02639         $header    = pack("vv", $record, $length);
02640         if ($this->_BIFF_version == 0x0600) {
02641             $data      = pack("vC",  $cch, $encoding);
02642         }
02643         else {
02644             $data      = pack("C",  $cch);
02645         }
02646     
02647         $this->_append($header.$data.$str);
02648     }
02649     
02655     function _storeHcenter()
02656     {
02657         $record   = 0x0083;              // Record identifier
02658         $length   = 0x0002;              // Bytes to follow
02659     
02660         $fHCenter = $this->_hcenter;     // Horizontal centering
02661     
02662         $header    = pack("vv", $record, $length);
02663         $data      = pack("v",  $fHCenter);
02664     
02665         $this->_append($header.$data);
02666     }
02667     
02673     function _storeVcenter()
02674     {
02675         $record   = 0x0084;              // Record identifier
02676         $length   = 0x0002;              // Bytes to follow
02677     
02678         $fVCenter = $this->_vcenter;     // Horizontal centering
02679     
02680         $header    = pack("vv", $record, $length);
02681         $data      = pack("v",  $fVCenter);
02682         $this->_append($header.$data);
02683     }
02684     
02690     function _storeMarginLeft()
02691     {
02692         $record  = 0x0026;                   // Record identifier
02693         $length  = 0x0008;                   // Bytes to follow
02694     
02695         $margin  = $this->_margin_left;       // Margin in inches
02696     
02697         $header    = pack("vv",  $record, $length);
02698         $data      = pack("d",   $margin);
02699         if ($this->_byte_order) // if it's Big Endian
02700         { 
02701             $data = strrev($data);
02702         }
02703     
02704         $this->_append($header.$data);
02705     }
02706     
02712     function _storeMarginRight()
02713     {
02714         $record  = 0x0027;                   // Record identifier
02715         $length  = 0x0008;                   // Bytes to follow
02716     
02717         $margin  = $this->_margin_right;      // Margin in inches
02718     
02719         $header    = pack("vv",  $record, $length);
02720         $data      = pack("d",   $margin);
02721         if ($this->_byte_order) // if it's Big Endian
02722         { 
02723             $data = strrev($data);
02724         }
02725     
02726         $this->_append($header.$data);
02727     }
02728     
02734     function _storeMarginTop()
02735     {
02736         $record  = 0x0028;                   // Record identifier
02737         $length  = 0x0008;                   // Bytes to follow
02738     
02739         $margin  = $this->_margin_top;        // Margin in inches
02740     
02741         $header    = pack("vv",  $record, $length);
02742         $data      = pack("d",   $margin);
02743         if ($this->_byte_order) // if it's Big Endian
02744         { 
02745             $data = strrev($data);
02746         }
02747     
02748         $this->_append($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         { 
02767             $data = strrev($data);
02768         }
02769     
02770         $this->_append($header.$data);
02771     }
02772 
02784     function mergeCells($first_row, $first_col, $last_row, $last_col)
02785     {
02786         $record  = 0x00E5;                   // Record identifier
02787         $length  = 0x000A;                   // Bytes to follow
02788         $cref     = 1;                       // Number of refs
02789 
02790         // Swap last row/col for first row/col as necessary
02791         if ($first_row > $last_row) {
02792             list($first_row, $last_row) = array($last_row, $first_row);
02793         }
02794     
02795         if ($first_col > $last_col) {
02796             list($first_col, $last_col) = array($last_col, $first_col);
02797         }
02798     
02799         $header   = pack("vv",    $record, $length);
02800         $data     = pack("vvvvv", $cref, $first_row, $last_row,
02801                                   $first_col, $last_col);
02802     
02803         $this->_append($header.$data);
02804     }
02805 
02811     function _storePrintHeaders()
02812     {
02813         $record      = 0x002a;                   // Record identifier
02814         $length      = 0x0002;                   // Bytes to follow
02815     
02816         $fPrintRwCol = $this->_print_headers;     // Boolean flag
02817     
02818         $header      = pack("vv", $record, $length);
02819         $data        = pack("v", $fPrintRwCol);
02820         $this->_prepend($header.$data);
02821     }
02822     
02829     function _storePrintGridlines()
02830     {
02831         $record      = 0x002b;                    // Record identifier
02832         $length      = 0x0002;                    // Bytes to follow
02833     
02834         $fPrintGrid  = $this->_print_gridlines;    // Boolean flag
02835     
02836         $header      = pack("vv", $record, $length);
02837         $data        = pack("v", $fPrintGrid);
02838         $this->_prepend($header.$data);
02839     }
02840     
02847     function _storeGridset()
02848     {
02849         $record      = 0x0082;                        // Record identifier
02850         $length      = 0x0002;                        // Bytes to follow
02851     
02852         $fGridSet    = !($this->_print_gridlines);     // Boolean flag
02853     
02854         $header      = pack("vv",  $record, $length);
02855         $data        = pack("v",   $fGridSet);
02856         $this->_prepend($header.$data);
02857     }
02858  
02867     function _storeGuts()
02868     {
02869         $record      = 0x0080;   // Record identifier
02870         $length      = 0x0008;   // Bytes to follow
02871    
02872         $dxRwGut     = 0x0000;   // Size of row gutter
02873         $dxColGut    = 0x0000;   // Size of col gutter
02874    
02875         $row_level   = $this->_outline_row_level;
02876         $col_level   = 0;
02877    
02878         // Calculate the maximum column outline level. The equivalent calculation
02879         // for the row outline level is carried out in setRow().
02880         for ($i=0; $i < count($this->_colinfo); $i++)
02881         {
02882            // Skip cols without outline level info.
02883            if (count($col_level) >= 6) {
02884               $col_level = max($this->_colinfo[$i][5], $col_level);
02885            }
02886         }
02887    
02888         // Set the limits for the outline levels (0 <= x <= 7).
02889         $col_level = max(0, min($col_level, 7));
02890    
02891         // The displayed level is one greater than the max outline levels
02892         if ($row_level) {
02893             $row_level++;
02894         }
02895         if ($col_level) {
02896             $col_level++;
02897         }
02898    
02899         $header      = pack("vv",   $record, $length);
02900         $data        = pack("vvvv", $dxRwGut, $dxColGut, $row_level, $col_level);
02901    
02902         $this->_prepend($header.$data);
02903     }
02904 
02905 
02912     function _storeWsbool()
02913     {
02914         $record      = 0x0081;   // Record identifier
02915         $length      = 0x0002;   // Bytes to follow
02916         $grbit       = 0x0000;
02917     
02918         // The only option that is of interest is the flag for fit to page. So we
02919         // set all the options in one go.
02920         //
02921         /*if ($this->_fit_page) {
02922             $grbit = 0x05c1;
02923         }
02924         else {
02925             $grbit = 0x04c1;
02926         }*/
02927         // Set the option flags
02928         $grbit |= 0x0001;                           // Auto page breaks visible
02929         if ($this->_outline_style) {
02930             $grbit |= 0x0020; // Auto outline styles
02931         }
02932         if ($this->_outline_below) {
02933             $grbit |= 0x0040; // Outline summary below
02934         }
02935         if ($this->_outline_right) {
02936             $grbit |= 0x0080; // Outline summary right
02937         }
02938         if ($this->_fit_page) {
02939             $grbit |= 0x0100; // Page setup fit to page
02940         }
02941         if ($this->_outline_on) {
02942             $grbit |= 0x0400; // Outline symbols displayed
02943         }
02944 
02945         $header      = pack("vv", $record, $length);
02946         $data        = pack("v",  $grbit);
02947         $this->_prepend($header.$data);
02948     }
02949 
02955     function _storeHbreak()
02956     {
02957         // Return if the user hasn't specified pagebreaks
02958         if (empty($this->_hbreaks)) {
02959             return;
02960         }
02961     
02962         // Sort and filter array of page breaks
02963         $breaks = $this->_hbreaks;
02964         sort($breaks, SORT_NUMERIC);
02965         if ($breaks[0] == 0) { // don't use first break if it's 0
02966             array_shift($breaks);
02967         }
02968     
02969         $record  = 0x001b;               // Record identifier
02970         $cbrk    = count($breaks);       // Number of page breaks
02971         $length  = 2 + 6*$cbrk;          // Bytes to follow
02972     
02973         $header  = pack("vv", $record, $length);
02974         $data    = pack("v",  $cbrk);
02975     
02976         // Append each page break
02977         foreach($breaks as $break) {
02978             $data .= pack("vvv", $break, 0x0000, 0x00ff);
02979         }
02980     
02981         $this->_prepend($header.$data);
02982     }
02983     
02984     
02990     function _storeVbreak()
02991     {
02992         // Return if the user hasn't specified pagebreaks
02993         if (empty($this->_vbreaks)) {
02994             return;
02995         }
02996     
02997         // 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
02998         // It is slightly higher in Excel 97/200, approx. 1026
02999         $breaks = array_slice($this->_vbreaks,0,1000);
03000     
03001         // Sort and filter array of page breaks
03002         sort($breaks, SORT_NUMERIC);
03003         if ($breaks[0] == 0) { // don't use first break if it's 0
03004             array_shift($breaks);
03005         }
03006     
03007         $record  = 0x001a;               // Record identifier
03008         $cbrk    = count($breaks);       // Number of page breaks
03009         $length  = 2 + 6*$cbrk;          // Bytes to follow
03010     
03011         $header  = pack("vv",  $record, $length);
03012         $data    = pack("v",   $cbrk);
03013     
03014         // Append each page break
03015         foreach ($breaks as $break) {
03016             $data .= pack("vvv", $break, 0x0000, 0xffff);
03017         }
03018     
03019         $this->_prepend($header.$data);
03020     }
03021     
03027     function _storeProtect()
03028     {
03029         // Exit unless sheet protection has been specified
03030         if ($this->_protect == 0) {
03031             return;
03032         }
03033     
03034         $record      = 0x0012;             // Record identifier
03035         $length      = 0x0002;             // Bytes to follow
03036     
03037         $fLock       = $this->_protect;    // Worksheet is protected
03038     
03039         $header      = pack("vv", $record, $length);
03040         $data        = pack("v",  $fLock);
03041     
03042         $this->_prepend($header.$data);
03043     }
03044     
03050     function _storePassword()
03051     {
03052         // Exit unless sheet protection and password have been specified
03053         if (($this->_protect == 0) or (!isset($this->_password))) {
03054             return;
03055         }
03056     
03057         $record      = 0x0013;               // Record identifier
03058         $length      = 0x0002;               // Bytes to follow
03059     
03060         $wPassword   = $this->_password;     // Encoded password
03061     
03062         $header      = pack("vv", $record, $length);
03063         $data        = pack("v",  $wPassword);
03064     
03065         $this->_prepend($header.$data);
03066     }
03067     
03068 
03081     function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1)
03082     {
03083         $bitmap_array = $this->_processBitmap($bitmap);
03084         if ($this->isError($bitmap_array))
03085         {
03086             $this->writeString($row, $col, $bitmap_array->getMessage());
03087             return;
03088         }
03089         list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap);
03090     
03091         // Scale the frame of the image.
03092         $width  *= $scale_x;
03093         $height *= $scale_y;
03094     
03095         // Calculate the vertices of the image and write the OBJ record
03096         $this->_positionImage($col, $row, $x, $y, $width, $height);
03097     
03098         // Write the IMDATA record to store the bitmap data
03099         $record      = 0x007f;
03100         $length      = 8 + $size;
03101         $cf          = 0x09;
03102         $env         = 0x01;
03103         $lcb         = $size;
03104     
03105         $header      = pack("vvvvV", $record, $length, $cf, $env, $lcb);
03106         $this->_append($header.$data);
03107     }
03108     
03160     function _positionImage($col_start, $row_start, $x1, $y1, $width, $height)
03161     {
03162         // Initialise end cell to the same as the start cell
03163         $col_end    = $col_start;  // Col containing lower right corner of object
03164         $row_end    = $row_start;  // Row containing bottom right corner of object
03165     
03166         // Zero the specified offset if greater than the cell dimensions
03167         if ($x1 >= $this->_sizeCol($col_start))
03168         {
03169             $x1 = 0;
03170         }
03171         if ($y1 >= $this->_sizeRow($row_start))
03172         {
03173             $y1 = 0;
03174         }
03175     
03176         $width      = $width  + $x1 -1;
03177         $height     = $height + $y1 -1;
03178     
03179         // Subtract the underlying cell widths to find the end cell of the image
03180         while ($width >= $this->_sizeCol($col_end)) {
03181             $width -= $this->_sizeCol($col_end);
03182             $col_end++;
03183         }
03184     
03185         // Subtract the underlying cell heights to find the end cell of the image
03186         while ($height >= $this->_sizeRow($row_end)) {
03187             $height -= $this->_sizeRow($row_end);
03188             $row_end++;
03189         }
03190     
03191         // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
03192         // with zero eight or width.
03193         //
03194         if ($this->_sizeCol($col_start) == 0)
03195             return;
03196         if ($this->_sizeCol($col_end)   == 0)
03197             return;
03198         if ($this->_sizeRow($row_start) == 0)
03199             return;
03200         if ($this->_sizeRow($row_end)   == 0)
03201             return;
03202     
03203         // Convert the pixel values to the percentage value expected by Excel
03204         $x1 = $x1     / $this->_sizeCol($col_start)   * 1024;
03205         $y1 = $y1     / $this->_sizeRow($row_start)   *  256;
03206         $x2 = $width  / $this->_sizeCol($col_end)     * 1024; // Distance to right side of object
03207         $y2 = $height / $this->_sizeRow($row_end)     *  256; // Distance to bottom of object
03208     
03209         $this->_storeObjPicture( $col_start, $x1,
03210                                   $row_start, $y1,
03211                                   $col_end, $x2,
03212                                   $row_end, $y2
03213                                 );
03214     }
03215     
03225     function _sizeCol($col)
03226     {
03227         // Look up the cell value to see if it has been changed
03228         if (isset($this->col_sizes[$col])) {
03229             if ($this->col_sizes[$col] == 0) {
03230                 return(0);
03231             }
03232             else {
03233                 return(floor(7 * $this->col_sizes[$col] + 5));
03234             }
03235         }
03236         else {
03237             return(64);
03238         }
03239     }
03240     
03251     function _sizeRow($row)
03252     {
03253         // Look up the cell value to see if it has been changed
03254         if (isset($this->row_sizes[$row])) {
03255             if ($this->row_sizes[$row] == 0) {
03256                 return(0);
03257             }
03258             else {
03259                 return(floor(4/3 * $this->row_sizes[$row]));
03260             }
03261         }
03262         else {
03263             return(17);
03264         }
03265     }
03266     
03281     function _storeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB)
03282     {
03283         $record      = 0x005d;   // Record identifier
03284         $length      = 0x003c;   // Bytes to follow
03285     
03286         $cObj        = 0x0001;   // Count of objects in file (set to 1)
03287         $OT          = 0x0008;   // Object type. 8 = Picture
03288         $id          = 0x0001;   // Object ID
03289         $grbit       = 0x0614;   // Option flags
03290     
03291         $cbMacro     = 0x0000;   // Length of FMLA structure
03292         $Reserved1   = 0x0000;   // Reserved
03293         $Reserved2   = 0x0000;   // Reserved
03294     
03295         $icvBack     = 0x09;     // Background colour
03296         $icvFore     = 0x09;     // Foreground colour
03297         $fls         = 0x00;     // Fill pattern
03298         $fAuto       = 0x00;     // Automatic fill
03299         $icv         = 0x08;     // Line colour
03300         $lns         = 0xff;     // Line style
03301         $lnw         = 0x01;     // Line weight
03302         $fAutoB      = 0x00;     // Automatic border
03303         $frs         = 0x0000;   // Frame style
03304         $cf          = 0x0009;   // Image format, 9 = bitmap
03305         $Reserved3   = 0x0000;   // Reserved
03306         $cbPictFmla  = 0x0000;   // Length of FMLA structure
03307         $Reserved4   = 0x0000;   // Reserved
03308         $grbit2      = 0x0001;   // Option flags
03309         $Reserved5   = 0x0000;   // Reserved
03310     
03311     
03312         $header      = pack("vv", $record, $length);
03313         $data        = pack("V", $cObj);
03314         $data       .= pack("v", $OT);
03315         $data       .= pack("v", $id);
03316         $data       .= pack("v", $grbit);
03317         $data       .= pack("v", $colL);
03318         $data       .= pack("v", $dxL);
03319         $data       .= pack("v", $rwT);
03320         $data       .= pack("v", $dyT);
03321         $data       .= pack("v", $colR);
03322         $data       .= pack("v", $dxR);
03323         $data       .= pack("v", $rwB);
03324         $data       .= pack("v", $dyB);
03325         $data       .= pack("v", $cbMacro);
03326         $data       .= pack("V", $Reserved1);
03327         $data       .= pack("v", $Reserved2);
03328         $data       .= pack("C", $icvBack);
03329         $data       .= pack("C", $icvFore);
03330         $data       .= pack("C", $fls);
03331         $data       .= pack("C", $fAuto);
03332         $data       .= pack("C", $icv);
03333         $data       .= pack("C", $lns);
03334         $data       .= pack("C", $lnw);
03335         $data       .= pack("C", $fAutoB);
03336         $data       .= pack("v", $frs);
03337         $data       .= pack("V", $cf);
03338         $data       .= pack("v", $Reserved3);
03339         $data       .= pack("v", $cbPictFmla);
03340         $data       .= pack("v", $Reserved4);
03341         $data       .= pack("v", $grbit2);
03342         $data       .= pack("V", $Reserved5);
03343     
03344         $this->_append($header.$data);
03345     }
03346     
03356     function _processBitmap($bitmap)
03357     {
03358         // Open file.
03359         $bmp_fd = @fopen($bitmap,"rb");
03360         if (!$bmp_fd) {
03361             $this->raiseError("Couldn't import $bitmap");
03362         }
03363             
03364         // Slurp the file into a string.
03365         $data = fread($bmp_fd, filesize($bitmap));
03366     
03367         // Check that the file is big enough to be a bitmap.
03368         if (strlen($data) <= 0x36) {
03369             $this->raiseError("$bitmap doesn't contain enough data.\n");
03370         }
03371     
03372         // The first 2 bytes are used to identify the bitmap.
03373         $identity = unpack("A2", $data);
03374         if ($identity[''] != "BM") {
03375             $this->raiseError("$bitmap doesn't appear to be a valid bitmap image.\n");
03376         }
03377     
03378         // Remove bitmap data: ID.
03379         $data = substr($data, 2);
03380     
03381         // Read and remove the bitmap size. This is more reliable than reading
03382         // the data size at offset 0x22.
03383         //
03384         $size_array   = unpack("V", substr($data, 0, 4));
03385         $size   = $size_array[''];
03386         $data   = substr($data, 4);
03387         $size  -= 0x36; // Subtract size of bitmap header.
03388         $size  += 0x0C; // Add size of BIFF header.
03389     
03390         // Remove bitmap data: reserved, offset, header length.
03391         $data = substr($data, 12);
03392     
03393         // Read and remove the bitmap width and height. Verify the sizes.
03394         $width_and_height = unpack("V2", substr($data, 0, 8));
03395         $width  = $width_and_height[1];
03396         $height = $width_and_height[2];
03397         $data   = substr($data, 8);
03398         if ($width > 0xFFFF) { 
03399             $this->raiseError("$bitmap: largest image width supported is 65k.\n");
03400         }
03401         if ($height > 0xFFFF) { 
03402             $this->raiseError("$bitmap: largest image height supported is 65k.\n");
03403         }
03404     
03405         // Read and remove the bitmap planes and bpp data. Verify them.
03406         $planes_and_bitcount = unpack("v2", substr($data, 0, 4));
03407         $data = substr($data, 4);
03408         if ($planes_and_bitcount[2] != 24) { // Bitcount
03409             $this->raiseError("$bitmap isn't a 24bit true color bitmap.\n");
03410         }
03411         if ($planes_and_bitcount[1] != 1) {
03412             $this->raiseError("$bitmap: only 1 plane nupported in bitmap image.\n");
03413         }
03414     
03415         // Read and remove the bitmap compression. Verify compression.
03416         $compression = unpack("V", substr($data, 0, 4));
03417         $data = substr($data, 4);
03418       
03419         //$compression = 0;
03420         if ($compression[""] != 0) {
03421             $this->raiseError("$bitmap: compression not supported in bitmap image.\n");
03422         }
03423     
03424         // Remove bitmap data: data size, hres, vres, colours, imp. colours.
03425         $data = substr($data, 20);
03426     
03427         // Add the BITMAPCOREHEADER data
03428         $header  = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
03429         $data    = $header . $data;
03430     
03431         return (array($width, $height, $size, $data));
03432     }
03433     
03440     function _storeZoom()
03441     {
03442         // If scale is 100 we don't need to write a record
03443         if ($this->_zoom == 100) {
03444             return;
03445         }
03446     
03447         $record      = 0x00A0;               // Record identifier
03448         $length      = 0x0004;               // Bytes to follow
03449     
03450         $header      = pack("vv", $record, $length);
03451         $data        = pack("vv", $this->_zoom, 100);
03452         $this->_append($header.$data);
03453     }
03454 
03458     function setValidation($row1, $col1, $row2, $col2, &$validator)
03459     {
03460         $this->_dv[] = $validator->_getData() .
03461                        pack("vvvvv", 1, $row1, $row2, $col1, $col2);
03462     }
03463    
03469     function _storeDataValidity()
03470     {
03471         $record      = 0x01b2;      // Record identifier
03472         $length      = 0x0012;      // Bytes to follow
03473    
03474         $grbit       = 0x0002;      // Prompt box at cell, no cached validity data at DV records
03475         $horPos      = 0x00000000;  // Horizontal position of prompt box, if fixed position
03476         $verPos      = 0x00000000;  // Vertical position of prompt box, if fixed position
03477         $objId       = 0xffffffff;  // Object identifier of drop down arrow object, or -1 if not visible
03478       
03479         $header      = pack('vv', $record, $length);
03480         $data        = pack('vVVVV', $grbit, $horPos, $verPos, $objId,
03481                                      count($this->_dv));
03482         $this->_append($header.$data);
03483       
03484         $record = 0x01be;              // Record identifier
03485         foreach($this->_dv as $dv)
03486         {
03487             $length = strlen($dv);      // Bytes to follow
03488             $header = pack("vv", $record, $length);
03489             $this->_append($header.$dv);
03490         }
03491     }
03492 }
03493 ?>

Generated on Fri Dec 13 2013 17:56:49 for ILIAS Release_3_9_x_branch .rev 46835 by  doxygen 1.7.1