• Main Page
  • Related Pages
  • 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         // Open tmp file for storing Worksheet data
00468         $fh = tmpfile();
00469         if ( $fh) {
00470             // Store filehandle
00471             $this->_filehandle = $fh;
00472         }
00473         else {
00474             // If tmpfile() fails store data in memory
00475             $this->_using_tmpfile = false;
00476         }
00477     }
00478     
00488     function close($sheetnames)
00489     {
00490         $num_sheets = count($sheetnames);
00491     
00492         /***********************************************
00493         * Prepend in reverse order!!
00494         */
00495     
00496         // Prepend the sheet dimensions
00497         $this->_storeDimensions();
00498     
00499         // Prepend the sheet password
00500         $this->_storePassword();
00501     
00502         // Prepend the sheet protection
00503         $this->_storeProtect();
00504     
00505         // Prepend the page setup
00506         $this->_storeSetup();
00507     
00508         /* FIXME: margins are actually appended */
00509         // Prepend the bottom margin
00510         $this->_storeMarginBottom();
00511     
00512         // Prepend the top margin
00513         $this->_storeMarginTop();
00514     
00515         // Prepend the right margin
00516         $this->_storeMarginRight();
00517     
00518         // Prepend the left margin
00519         $this->_storeMarginLeft();
00520     
00521         // Prepend the page vertical centering
00522         $this->_storeVcenter();
00523     
00524         // Prepend the page horizontal centering
00525         $this->_storeHcenter();
00526     
00527         // Prepend the page footer
00528         $this->_storeFooter();
00529     
00530         // Prepend the page header
00531         $this->_storeHeader();
00532     
00533         // Prepend the vertical page breaks
00534         $this->_storeVbreak();
00535     
00536         // Prepend the horizontal page breaks
00537         $this->_storeHbreak();
00538     
00539         // Prepend WSBOOL
00540         $this->_storeWsbool();
00541    
00542         // Prepend GRIDSET
00543         $this->_storeGridset();
00544 
00545          //  Prepend GUTS
00546         if ($this->_BIFF_version == 0x0500) {
00547             $this->_storeGuts();
00548         }
00549  
00550         // Prepend PRINTGRIDLINES
00551         $this->_storePrintGridlines();
00552     
00553         // Prepend PRINTHEADERS
00554         $this->_storePrintHeaders();
00555     
00556         // Prepend EXTERNSHEET references
00557         if ($this->_BIFF_version == 0x0500) {
00558             for ($i = $num_sheets; $i > 0; $i--) {
00559                 $sheetname = $sheetnames[$i-1];
00560                 $this->_storeExternsheet($sheetname);
00561             }
00562         }
00563     
00564         // Prepend the EXTERNCOUNT of external references.
00565         if ($this->_BIFF_version == 0x0500) {
00566             $this->_storeExterncount($num_sheets);
00567         }
00568     
00569         // Prepend the COLINFO records if they exist
00570         if (!empty($this->_colinfo))
00571         {
00572             for ($i=0; $i < count($this->_colinfo); $i++) {
00573                 $this->_storeColinfo($this->_colinfo[$i]);
00574             }
00575             $this->_storeDefcol();
00576         }
00577     
00578         // Prepend the BOF record
00579         $this->_storeBof(0x0010);
00580     
00581         /*
00582         * End of prepend. Read upwards from here.
00583         ***********************************************/
00584     
00585         // Append
00586         $this->_storeWindow2();
00587         $this->_storeZoom();
00588         if (!empty($this->_panes)) {
00589             $this->_storePanes($this->_panes);
00590         }
00591         $this->_storeSelection($this->_selection);
00592         $this->_storeMergedCells();
00593         /* TODO: add data validity */
00594         /*if ($this->_BIFF_version == 0x0600) {
00595             $this->_storeDataValidity();
00596         }*/
00597         $this->_storeEof();
00598     }
00599     
00607     function getName()
00608     {
00609         return $this->name;
00610     }
00611     
00618     function getData()
00619     {
00620         $buffer = 4096;
00621     
00622         // Return data stored in memory
00623         if (isset($this->_data))
00624         {
00625             $tmp   = $this->_data;
00626             unset($this->_data);
00627             $fh    = $this->_filehandle;
00628             if ($this->_using_tmpfile) {
00629                 fseek($fh, 0);
00630             }
00631             return $tmp;
00632         }
00633         // Return data stored on disk
00634         if ($this->_using_tmpfile)
00635         {
00636             if ($tmp = fread($this->_filehandle, $buffer)) {
00637                 return $tmp;
00638             }
00639         }
00640     
00641         // No data to return
00642         return '';
00643     }
00644  
00654     function setMerge($first_row, $first_col, $last_row, $last_col)
00655     {
00656         if (($last_row < $first_row) or ($last_col < $first_col)) {
00657             return;
00658         }
00659         // don't check rowmin, rowmax, etc... because we don't know when this
00660         // is going to be called
00661         $this->_merged_ranges[] = array($first_row, $first_col, $last_row, $last_col);
00662     }
00663 
00670     function select()
00671     {
00672         $this->selected = 1;
00673     }
00674     
00682     function activate()
00683     {
00684         $this->selected = 1;
00685         $this->activesheet = $this->index;
00686     }
00687     
00695     function setFirstSheet()
00696     {
00697         $this->firstsheet = $this->index;
00698     }
00699  
00708     function protect($password)
00709     {
00710         $this->_protect   = 1;
00711         $this->_password  = $this->_encodePassword($password);
00712     }
00713  
00725     function setColumn($firstcol, $lastcol, $width, $format = 0, $hidden = 0, $level = 0)
00726     {
00727         $this->_colinfo[] = array($firstcol, $lastcol, $width, &$format, $hidden, $level);
00728     
00729         // Set width to zero if column is hidden
00730         $width = ($hidden) ? 0 : $width;
00731     
00732         for ($col = $firstcol; $col <= $lastcol; $col++) {
00733             $this->col_sizes[$col] = $width;
00734         }
00735     }
00736     
00746     function setSelection($first_row,$first_column,$last_row,$last_column)
00747     {
00748         $this->_selection = array($first_row,$first_column,$last_row,$last_column);
00749     }
00750     
00762     function freezePanes($panes)
00763     {
00764         $this->_frozen = 1;
00765         $this->_panes  = $panes;
00766     }
00767     
00779     function thawPanes($panes)
00780     {
00781         $this->_frozen = 0;
00782         $this->_panes  = $panes;
00783     }
00784     
00790     function setPortrait()
00791     {
00792         $this->_orientation = 1;
00793     }
00794     
00800     function setLandscape()
00801     {
00802         $this->_orientation = 0;
00803     }
00804     
00811     function setPaper($size = 0)
00812     {
00813         $this->_paper_size = $size;
00814     }
00815     
00816     
00824     function setHeader($string,$margin = 0.50)
00825     {
00826         if (strlen($string) >= 255) {
00827             //carp 'Header string must be less than 255 characters';
00828             return;
00829         }
00830         $this->_header      = $string;
00831         $this->_margin_head = $margin;
00832     }
00833     
00841     function setFooter($string,$margin = 0.50)
00842     {
00843         if (strlen($string) >= 255) {
00844             //carp 'Footer string must be less than 255 characters';
00845             return;
00846         }
00847         $this->_footer      = $string;
00848         $this->_margin_foot = $margin;
00849     }
00850     
00857     function centerHorizontally($center = 1)
00858     {
00859         $this->_hcenter = $center;
00860     }
00861     
00868     function centerVertically($center = 1)
00869     {
00870         $this->_vcenter = $center;
00871     }
00872     
00879     function setMargins($margin)
00880     {
00881         $this->setMarginLeft($margin);
00882         $this->setMarginRight($margin);
00883         $this->setMarginTop($margin);
00884         $this->setMarginBottom($margin);
00885     }
00886     
00893     function setMargins_LR($margin)
00894     {
00895         $this->setMarginLeft($margin);
00896         $this->setMarginRight($margin);
00897     }
00898     
00905     function setMargins_TB($margin)
00906     {
00907         $this->setMarginTop($margin);
00908         $this->setMarginBottom($margin);
00909     }
00910     
00917     function setMarginLeft($margin = 0.75)
00918     {
00919         $this->_margin_left = $margin;
00920     }
00921     
00928     function setMarginRight($margin = 0.75)
00929     {
00930         $this->_margin_right = $margin;
00931     }
00932     
00939     function setMarginTop($margin = 1.00)
00940     {
00941         $this->_margin_top = $margin;
00942     }
00943     
00950     function setMarginBottom($margin = 1.00)
00951     {
00952         $this->_margin_bottom = $margin;
00953     }
00954     
00962     function repeatRows($first_row, $last_row = NULL)
00963     {
00964         $this->title_rowmin  = $first_row;
00965         if (isset($last_row)) { //Second row is optional
00966             $this->title_rowmax  = $last_row;
00967         }
00968         else {
00969             $this->title_rowmax  = $first_row;
00970         }
00971     }
00972     
00980     function repeatColumns($first_col, $last_col = NULL)
00981     {
00982         $this->title_colmin  = $first_col;
00983         if (isset($last_col)) { // Second col is optional
00984             $this->title_colmax  = $last_col;
00985         }
00986         else {
00987             $this->title_colmax  = $first_col;
00988         }
00989     }
00990     
01000     function printArea($first_row, $first_col, $last_row, $last_col)
01001     {
01002         $this->print_rowmin  = $first_row;
01003         $this->print_colmin  = $first_col;
01004         $this->print_rowmax  = $last_row;
01005         $this->print_colmax  = $last_col;
01006     }
01007     
01008     
01014     function hideGridlines()
01015     {
01016         $this->_print_gridlines = 0;
01017     }
01018     
01025     function printRowColHeaders($print = 1)
01026     {
01027         $this->_print_headers = $print;
01028     }
01029     
01039     function fitToPages($width, $height)
01040     {
01041         $this->_fit_page      = 1;
01042         $this->_fit_width     = $width;
01043         $this->_fit_height    = $height;
01044     }
01045     
01053     function setHPagebreaks($breaks)
01054     {
01055         foreach($breaks as $break) {
01056             array_push($this->_hbreaks,$break);
01057         }
01058     }
01059     
01067     function setVPagebreaks($breaks)
01068     {
01069         foreach($breaks as $break) {
01070             array_push($this->_vbreaks,$break);
01071         }
01072     }
01073     
01074     
01081     function setZoom($scale = 100)
01082     {
01083         // Confine the scale to Excel's range
01084         if ($scale < 10 or $scale > 400) 
01085         {
01086             $this->raiseError("Zoom factor $scale outside range: 10 <= zoom <= 400");
01087             $scale = 100;
01088         }
01089     
01090         $this->_zoom = floor($scale);
01091     }
01092     
01100     function setPrintScale($scale = 100)
01101     {
01102         // Confine the scale to Excel's range
01103         if ($scale < 10 or $scale > 400)
01104         {
01105             $this->raiseError("Print scale $scale outside range: 10 <= zoom <= 400");
01106             $scale = 100;
01107         }
01108     
01109         // Turn off "fit to page" option
01110         $this->_fit_page    = 0;
01111     
01112         $this->_print_scale = floor($scale);
01113     }
01114     
01124     function write($row, $col, $token, $format = 0)
01125     {
01126         // Check for a cell reference in A1 notation and substitute row and column
01127         /*if ($_[0] =~ /^\D/) {
01128             @_ = $this->_substituteCellref(@_);
01129     }*/
01130     
01131     
01132         // Match number
01133         if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/",$token)) {
01134             return $this->writeNumber($row,$col,$token,$format);
01135         }
01136         // Match http or ftp URL
01137         elseif (preg_match("/^[fh]tt?p:\/\//",$token)) {
01138             return $this->writeUrl($row, $col, $token, '', $format);
01139         }
01140         // Match mailto:
01141         elseif (preg_match("/^mailto:/",$token)) {
01142             return $this->writeUrl($row, $col, $token, '', $format);
01143         }
01144         // Match internal or external sheet link
01145         elseif (preg_match("/^(?:in|ex)ternal:/",$token)) {
01146             return $this->writeUrl($row, $col, $token, '', $format);
01147         }
01148         // Match formula
01149         elseif (preg_match("/^=/",$token)) {
01150             return $this->writeFormula($row, $col, $token, $format);
01151         }
01152         // Match formula
01153         elseif (preg_match("/^@/",$token)) {
01154             return $this->writeFormula($row, $col, $token, $format);
01155         }
01156         // Match blank
01157         elseif ($token == '') {
01158             return $this->writeBlank($row,$col,$format);
01159         }
01160         // Default: match string
01161         else {
01162             return $this->writeString($row,$col,$token,$format);
01163         }
01164     }
01165     
01177     function writeRow($row, $col, $val, $format=0)
01178     {   
01179         $retval = '';
01180         if (is_array($val)) {
01181             foreach($val as $v) {
01182                 if (is_array($v)) {
01183                     $this->writeCol($row, $col, $v, $format);
01184                 } else {
01185                     $this->write($row, $col, $v, $format);
01186                 }
01187                 $col++;
01188             }
01189         } else {
01190             $retval = new PEAR_Error('$val needs to be an array');
01191         }
01192         return($retval);
01193     }
01194       
01206     function writeCol($row, $col, $val, $format=0)
01207     {
01208         $retval = '';
01209         if (is_array($val)) {
01210             foreach($val as $v) { 
01211                 $this->write($row, $col, $v, $format);
01212                 $row++;
01213             }
01214         } else {
01215             $retval = new PEAR_Error('$val needs to be an array');
01216         }
01217         return($retval);
01218     }
01219     
01227     function _XF(&$format)
01228     {
01229         if ($format != 0) {
01230             return($format->getXfIndex());
01231         }
01232         else {
01233             return(0x0F);
01234         }
01235     }
01236     
01237     
01238     /******************************************************************************
01239     *******************************************************************************
01240     *
01241     * Internal methods
01242     */
01243     
01244     
01252     function _append($data)
01253     {
01254         if ($this->_using_tmpfile)
01255         {
01256             // Add CONTINUE records if necessary
01257             if (strlen($data) > $this->_limit) {
01258                 $data = $this->_addContinue($data);
01259             }
01260             fwrite($this->_filehandle,$data);
01261             $this->_datasize += strlen($data);
01262         }
01263         else {
01264             parent::_append($data);
01265         }
01266     }
01267     
01278     function _substituteCellref($cell)
01279     {
01280         $cell = strtoupper($cell);
01281     
01282         // Convert a column range: 'A:A' or 'B:G'
01283         if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/",$cell,$match)) {
01284             list($no_use, $col1) =  $this->_cellToRowcol($match[1] .'1'); // Add a dummy row
01285             list($no_use, $col2) =  $this->_cellToRowcol($match[2] .'1'); // Add a dummy row
01286             return(array($col1, $col2));
01287         }
01288     
01289         // Convert a cell range: 'A1:B7'
01290         if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/",$cell,$match)) {
01291             list($row1, $col1) =  $this->_cellToRowcol($match[1]);
01292             list($row2, $col2) =  $this->_cellToRowcol($match[2]);
01293             return(array($row1, $col1, $row2, $col2));
01294         }
01295     
01296         // Convert a cell reference: 'A1' or 'AD2000'
01297         if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/",$cell)) {
01298             list($row1, $col1) =  $this->_cellToRowcol($match[1]);
01299             return(array($row1, $col1));
01300         }
01301     
01302         // TODO use real error codes
01303         $this->raiseError("Unknown cell reference $cell", 0, PEAR_ERROR_DIE);
01304     }
01305     
01314     function _cellToRowcol($cell)
01315     {
01316         preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match);
01317         $col     = $match[1];
01318         $row     = $match[2];
01319     
01320         // Convert base26 column string to number
01321         $chars = split('', $col);
01322         $expn  = 0;
01323         $col   = 0;
01324     
01325         while ($chars) {
01326             $char = array_pop($chars);        // LS char first
01327             $col += (ord($char) -ord('A') +1) * pow(26,$expn);
01328             $expn++;
01329         }
01330     
01331         // Convert 1-index to zero-index
01332         $row--;
01333         $col--;
01334     
01335         return(array($row, $col));
01336     }
01337  
01345     function _encodePassword($plaintext)
01346     {
01347         $password = 0x0000;
01348         $i        = 1;       // char position
01349  
01350         // split the plain text password in its component characters
01351         $chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY);
01352         foreach($chars as $char)
01353         {
01354             $value        = ord($char) << $i;   // shifted ASCII value 
01355             $rotated_bits = $value >> 15;       // rotated bits beyond bit 15
01356             $value       &= 0x7fff;             // first 15 bits
01357             $password    ^= ($value | $rotated_bits);
01358             $i++;
01359         }
01360     
01361         $password ^= strlen($plaintext);
01362         $password ^= 0xCE4B;
01363 
01364         return($password);
01365     }
01366 
01376     function setOutline($visible = true, $symbols_below = true, $symbols_right = true, $auto_style = false)
01377     {
01378         $this->_outline_on    = $visible;
01379         $this->_outline_below = $symbols_below;
01380         $this->_outline_right = $symbols_right;
01381         $this->_outline_style = $auto_style;
01382 
01383         // Ensure this is a boolean vale for Window2
01384         if ($this->_outline_on) {
01385             $this->_outline_on = 1;
01386         }
01387      }
01388 
01389     /******************************************************************************
01390     *******************************************************************************
01391     *
01392     * BIFF RECORDS
01393     */
01394     
01395     
01411     function writeNumber($row, $col, $num, $format = 0)
01412     {
01413         $record    = 0x0203;                 // Record identifier
01414         $length    = 0x000E;                 // Number of bytes to follow
01415     
01416         $xf        = $this->_XF($format);    // The cell format
01417     
01418         // Check that row and col are valid and store max and min values
01419         if ($row >= $this->_xls_rowmax)
01420         {
01421             return(-2);
01422         }
01423         if ($col >= $this->_xls_colmax)
01424         {
01425             return(-2);
01426         }
01427         if ($row <  $this->_dim_rowmin) 
01428         {
01429             $this->_dim_rowmin = $row;
01430         }
01431         if ($row >  $this->_dim_rowmax) 
01432         {
01433             $this->_dim_rowmax = $row;
01434         }
01435         if ($col <  $this->_dim_colmin) 
01436         {
01437             $this->_dim_colmin = $col;
01438         }
01439         if ($col >  $this->_dim_colmax) 
01440         {
01441             $this->_dim_colmax = $col;
01442         }
01443     
01444         $header    = pack("vv",  $record, $length);
01445         $data      = pack("vvv", $row, $col, $xf);
01446         $xl_double = pack("d",   $num);
01447         if ($this->_byte_order) // if it's Big Endian
01448         {
01449             $xl_double = strrev($xl_double);
01450         }
01451     
01452         $this->_append($header.$data.$xl_double);
01453         return(0);
01454     }
01455     
01471     function writeString($row, $col, $str, $format = 0)
01472     {
01473         if ($this->_BIFF_version == 0x0600) {
01474             return $this->writeStringBIFF8($row, $col, $str, $format);
01475         }
01476         $strlen    = strlen($str);
01477         $record    = 0x0204;                   // Record identifier
01478         $length    = 0x0008 + $strlen;         // Bytes to follow
01479         $xf        = $this->_XF($format);      // The cell format
01480         
01481         $str_error = 0;
01482     
01483         // Check that row and col are valid and store max and min values
01484         if ($row >= $this->_xls_rowmax) 
01485         {
01486             return(-2);
01487         }
01488         if ($col >= $this->_xls_colmax) 
01489         {
01490             return(-2);
01491         }
01492         if ($row <  $this->_dim_rowmin) 
01493         {
01494             $this->_dim_rowmin = $row;
01495         }
01496         if ($row >  $this->_dim_rowmax) 
01497         {
01498             $this->_dim_rowmax = $row;
01499         }
01500         if ($col <  $this->_dim_colmin) 
01501         {
01502             $this->_dim_colmin = $col;
01503         }
01504         if ($col >  $this->_dim_colmax) 
01505         {
01506             $this->_dim_colmax = $col;
01507         }
01508     
01509         if ($strlen > $this->_xls_strmax)  // LABEL must be < 255 chars
01510         {
01511             $str       = substr($str, 0, $this->_xls_strmax);
01512             $length    = 0x0008 + $this->_xls_strmax;
01513             $strlen    = $this->_xls_strmax;
01514             $str_error = -3;
01515         }
01516     
01517         $header    = pack("vv",   $record, $length);
01518         $data      = pack("vvvv", $row, $col, $xf, $strlen);
01519         $this->_append($header.$data.$str);
01520         return($str_error);
01521     }
01522 
01523     function writeStringBIFF8($row, $col, $str, $format = 0)
01524     {
01525         $strlen    = strlen($str);
01526         $record    = 0x00FD;                   // Record identifier
01527         $length    = 0x000A;                   // Bytes to follow
01528         $xf        = $this->_XF($format);      // The cell format
01529         $encoding  = 0x0;
01530         
01531         $str_error = 0;
01532     
01533         // Check that row and col are valid and store max and min values
01534         if ($this->_checkRowCol($row, $col) == false) {
01535             return -2;
01536         }
01537 
01538         $str = pack('vC', $strlen, $encoding).$str;
01539 
01540         /* check if string is already present */
01541         if (!isset($this->_str_table[$str])) {
01542             $this->_str_table[$str] = $this->_str_unique++;
01543         }
01544         $this->_str_total++;
01545     
01546         $header    = pack('vv',   $record, $length);
01547         $data      = pack('vvvV', $row, $col, $xf, $this->_str_table[$str]);
01548         $this->_append($header.$data);
01549         return $str_error;
01550     }
01551 
01562     function _checkRowCol($row, $col)
01563     {
01564         if ($row >= $this->_xls_rowmax) {
01565             return false;
01566         }
01567         if ($col >= $this->_xls_colmax) {
01568             return false;
01569         }
01570         if ($row <  $this->_dim_rowmin) {
01571             $this->_dim_rowmin = $row;
01572         }
01573         if ($row >  $this->_dim_rowmax) {
01574             $this->_dim_rowmax = $row;
01575         }
01576         if ($col <  $this->_dim_colmin) {
01577             $this->_dim_colmin = $col;
01578         }
01579         if ($col >  $this->_dim_colmax) {
01580             $this->_dim_colmax = $col;
01581         }
01582         return true;
01583     }
01584 
01594     function writeNote($row, $col, $note)
01595     {
01596         $note_length    = strlen($note);
01597         $record         = 0x001C;                // Record identifier
01598         $max_length     = 2048;                  // Maximun length for a NOTE record
01599         //$length      = 0x0006 + $note_length;    // Bytes to follow
01600 
01601         // Check that row and col are valid and store max and min values
01602         if ($row >= $this->_xls_rowmax) 
01603         {
01604             return(-2);
01605         }
01606         if ($col >= $this->_xls_colmax) 
01607         {
01608             return(-2);
01609         }
01610         if ($row <  $this->_dim_rowmin) 
01611         {
01612             $this->_dim_rowmin = $row;
01613         }
01614         if ($row >  $this->_dim_rowmax) 
01615         {
01616             $this->_dim_rowmax = $row;
01617         }
01618         if ($col <  $this->_dim_colmin) 
01619         {
01620             $this->_dim_colmin = $col;
01621         }
01622         if ($col >  $this->_dim_colmax) 
01623         {
01624             $this->_dim_colmax = $col;
01625         }
01626  
01627         // Length for this record is no more than 2048 + 6
01628         $length    = 0x0006 + min($note_length, 2048);
01629         $header    = pack("vv",   $record, $length);
01630         $data      = pack("vvv", $row, $col, $note_length);
01631         $this->_append($header.$data.substr($note, 0, 2048));
01632 
01633         for($i = $max_length; $i < $note_length; $i += $max_length)
01634         {
01635             $chunk  = substr($note, $i, $max_length);
01636             $length = 0x0006 + strlen($chunk);
01637             $header = pack("vv",   $record, $length);
01638             $data   = pack("vvv", -1, 0, strlen($chunk));
01639             $this->_append($header.$data.$chunk);
01640         }
01641         return(0);
01642     }
01643 
01661     function writeBlank($row, $col, $format)
01662     {
01663         // Don't write a blank cell unless it has a format
01664         if ($format == 0)
01665         {
01666             return(0);
01667         }
01668     
01669         $record    = 0x0201;                 // Record identifier
01670         $length    = 0x0006;                 // Number of bytes to follow
01671         $xf        = $this->_XF($format);    // The cell format
01672     
01673         // Check that row and col are valid and store max and min values
01674         if ($row >= $this->_xls_rowmax) 
01675         {
01676             return(-2);
01677         }
01678         if ($col >= $this->_xls_colmax) 
01679         {
01680             return(-2);
01681         }
01682         if ($row <  $this->_dim_rowmin) 
01683         {
01684             $this->_dim_rowmin = $row;
01685         }
01686         if ($row >  $this->_dim_rowmax) 
01687         {
01688             $this->_dim_rowmax = $row;
01689         }
01690         if ($col <  $this->_dim_colmin) 
01691         {
01692             $this->_dim_colmin = $col;
01693         }
01694         if ($col >  $this->_dim_colmax) 
01695         {
01696             $this->_dim_colmax = $col;
01697         }
01698     
01699         $header    = pack("vv",  $record, $length);
01700         $data      = pack("vvv", $row, $col, $xf);
01701         $this->_append($header.$data);
01702         return 0;
01703     }
01704 
01721     function writeFormula($row, $col, $formula, $format = 0)
01722     {
01723         $record    = 0x0006;     // Record identifier
01724     
01725         // Excel normally stores the last calculated value of the formula in $num.
01726         // Clearly we are not in a position to calculate this a priori. Instead
01727         // we set $num to zero and set the option flags in $grbit to ensure
01728         // automatic calculation of the formula when the file is opened.
01729         //
01730         $xf        = $this->_XF($format); // The cell format
01731         $num       = 0x00;                // Current value of formula
01732         $grbit     = 0x03;                // Option flags
01733         $unknown   = 0x0000;              // Must be zero
01734     
01735     
01736         // Check that row and col are valid and store max and min values
01737         if ($this->_checkRowCol($row, $col) == false) {
01738             return -2;
01739         }
01740     
01741         // Strip the '=' or '@' sign at the beginning of the formula string
01742         if (preg_match("/^=/",$formula)) {
01743             $formula = preg_replace("/(^=)/","",$formula);
01744         }
01745         elseif (preg_match("/^@/",$formula)) {
01746             $formula = preg_replace("/(^@)/","",$formula);
01747         }
01748         else
01749         {
01750             // Error handling
01751             $this->writeString($row, $col, 'Unrecognised character for formula');
01752             return -1;
01753         }
01754     
01755         // Parse the formula using the parser in Parser.php
01756         $error = $this->_parser->parse($formula);
01757         if ($this->isError($error))
01758         {
01759             $this->writeString($row, $col, $error->getMessage()); 
01760             return -1;
01761         }
01762     
01763         $formula = $this->_parser->toReversePolish();
01764         if ($this->isError($formula))
01765         {
01766             $this->writeString($row, $col, $formula->getMessage());
01767             return -1;
01768         }
01769     
01770         $formlen    = strlen($formula);    // Length of the binary string
01771         $length     = 0x16 + $formlen;     // Length of the record data
01772     
01773         $header    = pack("vv",      $record, $length);
01774         $data      = pack("vvvdvVv", $row, $col, $xf, $num,
01775                                      $grbit, $unknown, $formlen);
01776     
01777         $this->_append($header.$data.$formula);
01778         return 0;
01779     }
01780     
01804     function writeUrl($row, $col, $url, $string = '', $format = 0)
01805     {
01806         // Add start row and col to arg list
01807         return($this->_writeUrlRange($row, $col, $row, $col, $url, $string, $format));
01808     }
01809     
01828     function _writeUrlRange($row1, $col1, $row2, $col2, $url, $string = '', $format = 0)
01829     {
01830     
01831         // Check for internal/external sheet links or default to web link
01832         if (preg_match('[^internal:]', $url)) {
01833             return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url, $string, $format));
01834         }
01835         if (preg_match('[^external:]', $url)) {
01836             return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url, $string, $format));
01837         }
01838         return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url, $string, $format));
01839     }
01840     
01841     
01858     function _writeUrlWeb($row1, $col1, $row2, $col2, $url, $str, $format = 0)
01859     {
01860         $record      = 0x01B8;                       // Record identifier
01861         $length      = 0x00000;                      // Bytes to follow
01862     
01863         if ($format == 0) {
01864             $format = $this->_url_format;
01865         }
01866     
01867         // Write the visible label using the writeString() method.
01868         if ($str == '') {
01869             $str = $url;
01870         }
01871         $str_error = $this->writeString($row1, $col1, $str, $format);
01872         if (($str_error == -2) or ($str_error == -3)) {
01873             return $str_error;
01874         }
01875     
01876         // Pack the undocumented parts of the hyperlink stream
01877         $unknown1    = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
01878         $unknown2    = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
01879     
01880         // Pack the option flags
01881         $options     = pack("V", 0x03);
01882     
01883         // Convert URL to a null terminated wchar string
01884         $url         = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
01885         $url         = $url . "\0\0\0";
01886     
01887         // Pack the length of the URL
01888         $url_len     = pack("V", strlen($url));
01889     
01890         // Calculate the data length
01891         $length      = 0x34 + strlen($url);
01892     
01893         // Pack the header data
01894         $header      = pack("vv",   $record, $length);
01895         $data        = pack("vvvv", $row1, $row2, $col1, $col2);
01896     
01897         // Write the packed data
01898         $this->_append( $header. $data.
01899                         $unknown1. $options.
01900                         $unknown2. $url_len. $url);
01901         return($str_error);
01902     }
01903     
01918     function _writeUrlInternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
01919     {
01920         $record      = 0x01B8;                       // Record identifier
01921         $length      = 0x00000;                      // Bytes to follow
01922     
01923         if ($format == 0) {
01924             $format = $this->_url_format;
01925         }
01926     
01927         // Strip URL type
01928         $url = preg_replace('s[^internal:]', '', $url);
01929     
01930         // Write the visible label
01931         if ($str == '') {
01932             $str = $url;
01933         }
01934         $str_error = $this->writeString($row1, $col1, $str, $format);
01935         if (($str_error == -2) or ($str_error == -3)) {
01936             return $str_error;
01937         }
01938     
01939         // Pack the undocumented parts of the hyperlink stream
01940         $unknown1    = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
01941     
01942         // Pack the option flags
01943         $options     = pack("V", 0x08);
01944     
01945         // Convert the URL type and to a null terminated wchar string
01946         $url         = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
01947         $url         = $url . "\0\0\0";
01948     
01949         // Pack the length of the URL as chars (not wchars)
01950         $url_len     = pack("V", floor(strlen($url)/2));
01951     
01952         // Calculate the data length
01953         $length      = 0x24 + strlen($url);
01954     
01955         // Pack the header data
01956         $header      = pack("vv",   $record, $length);
01957         $data        = pack("vvvv", $row1, $row2, $col1, $col2);
01958     
01959         // Write the packed data
01960         $this->_append($header. $data.
01961                        $unknown1. $options.
01962                        $url_len. $url);
01963         return($str_error);
01964     }
01965     
01984     function _writeUrlExternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
01985     {
01986         // Network drives are different. We will handle them separately
01987         // MS/Novell network drives and shares start with \\
01988         if (preg_match('[^external:\\\\]', $url)) {
01989             return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
01990         }
01991     
01992         $record      = 0x01B8;                       // Record identifier
01993         $length      = 0x00000;                      // Bytes to follow
01994     
01995         if ($format == 0) {
01996             $format = $this->_url_format;
01997         }
01998     
01999         // Strip URL type and change Unix dir separator to Dos style (if needed)
02000         //
02001         $url = preg_replace('[^external:]', '', $url);
02002         $url = preg_replace('[/]', "\\", $url);
02003     
02004         // Write the visible label
02005         if ($str == '') {
02006             $str = preg_replace('[\#]', ' - ', $url);
02007         }
02008         $str_error = $this->writeString($row1, $col1, $str, $format);
02009         if (($str_error == -2) or ($str_error == -3)) {
02010             return $str_error;
02011         }
02012     
02013         // Determine if the link is relative or absolute:
02014         //   relative if link contains no dir separator, "somefile.xls"
02015         //   relative if link starts with up-dir, "..\..\somefile.xls"
02016         //   otherwise, absolute
02017         
02018         $absolute    = 0x02; // Bit mask
02019         if (!preg_match('[\\]', $url)) {
02020             $absolute    = 0x00;
02021         }
02022         if (preg_match('[^\.\.\\]', $url)) {
02023             $absolute    = 0x00;
02024         }
02025     
02026         // Determine if the link contains a sheet reference and change some of the
02027         // parameters accordingly.
02028         // Split the dir name and sheet name (if it exists)
02029         list($dir_long , $sheet) = split('/\#/', $url);
02030         $link_type               = 0x01 | $absolute;
02031     
02032         if (isset($sheet)) {
02033             $link_type |= 0x08;
02034             $sheet_len  = pack("V", strlen($sheet) + 0x01);
02035             $sheet      = join("\0", split('', $sheet));
02036             $sheet     .= "\0\0\0";
02037         }
02038         else {
02039             $sheet_len   = '';
02040             $sheet       = '';
02041         }
02042     
02043         // Pack the link type
02044         $link_type   = pack("V", $link_type);
02045     
02046         // Calculate the up-level dir count e.g.. (..\..\..\ == 3)
02047         $up_count    = preg_match_all("/\.\.\\/", $dir_long, $useless);
02048         $up_count    = pack("v", $up_count);
02049     
02050         // Store the short dos dir name (null terminated)
02051         $dir_short   = preg_replace('/\.\.\\/', '', $dir_long) . "\0";
02052     
02053         // Store the long dir name as a wchar string (non-null terminated)
02054         $dir_long       = join("\0", split('', $dir_long));
02055         $dir_long       = $dir_long . "\0";
02056     
02057         // Pack the lengths of the dir strings
02058         $dir_short_len = pack("V", strlen($dir_short)      );
02059         $dir_long_len  = pack("V", strlen($dir_long)       );
02060         $stream_len    = pack("V", strlen($dir_long) + 0x06);
02061     
02062         // Pack the undocumented parts of the hyperlink stream
02063         $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000'       );
02064         $unknown2 = pack("H*",'0303000000000000C000000000000046'               );
02065         $unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000');
02066         $unknown4 = pack("v",  0x03                                            );
02067     
02068         // Pack the main data stream
02069         $data        = pack("vvvv", $row1, $row2, $col1, $col2) .
02070                           $unknown1     .
02071                           $link_type    .
02072                           $unknown2     .
02073                           $up_count     .
02074                           $dir_short_len.
02075                           $dir_short    .
02076                           $unknown3     .
02077                           $stream_len   .
02078                           $dir_long_len .
02079                           $unknown4     .
02080                           $dir_long     .
02081                           $sheet_len    .
02082                           $sheet        ;
02083     
02084         // Pack the header data
02085         $length   = strlen($data);
02086         $header   = pack("vv", $record, $length);
02087     
02088         // Write the packed data
02089         $this->_append($header. $data);
02090         return($str_error);
02091     }
02092     
02093     
02105     function setRow($row, $height, $format = 0, $hidden = false, $level = 0)
02106     {
02107         $record      = 0x0208;               // Record identifier
02108         $length      = 0x0010;               // Number of bytes to follow
02109     
02110         $colMic      = 0x0000;               // First defined column
02111         $colMac      = 0x0000;               // Last defined column
02112         $irwMac      = 0x0000;               // Used by Excel to optimise loading
02113         $reserved    = 0x0000;               // Reserved
02114         $grbit       = 0x0000;               // Option flags
02115         $ixfe        = $this->_XF($format);  // XF index
02116     
02117         // Use setRow($row, NULL, $XF) to set XF format without setting height
02118         if ($height != NULL) {
02119             $miyRw = $height * 20;  // row height
02120         }
02121         else {
02122             $miyRw = 0xff;          // default row height is 256
02123         }
02124 
02125         $level = max(0, min($level, 7));  // level should be between 0 and 7
02126         $this->_outline_row_level = max($level, $this->_outline_row_level);
02127 
02128 
02129         // Set the options flags. fUnsynced is used to show that the font and row
02130         // heights are not compatible. This is usually the case for WriteExcel.
02131         // The collapsed flag 0x10 doesn't seem to be used to indicate that a row
02132         // is collapsed. Instead it is used to indicate that the previous row is
02133         // collapsed. The zero height flag, 0x20, is used to collapse a row.
02134 
02135         $grbit |= $level;
02136         if ($hidden) {
02137             $grbit |= 0x0020;
02138         }
02139         $grbit |= 0x0040; // fUnsynced
02140         if ($format) {
02141             $grbit |= 0x0080;
02142         }
02143         $grbit |= 0x0100;
02144 
02145         $header   = pack("vv",       $record, $length);
02146         $data     = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw,
02147                                      $irwMac,$reserved, $grbit, $ixfe);
02148         $this->_append($header.$data);
02149     }
02150     
02156     function _storeDimensions()
02157     {
02158         $record    = 0x0200;                 // Record identifier
02159         $row_min   = $this->_dim_rowmin;     // First row
02160         $row_max   = $this->_dim_rowmax + 1; // Last row plus 1
02161         $col_min   = $this->_dim_colmin;     // First column
02162         $col_max   = $this->_dim_colmax + 1; // Last column plus 1
02163         $reserved  = 0x0000;                 // Reserved by Excel
02164     
02165         if ($this->_BIFF_version == 0x0500) {
02166             $length    = 0x000A;               // Number of bytes to follow
02167             $data      = pack("vvvvv", $row_min, $row_max,
02168                                        $col_min, $col_max, $reserved);
02169         }
02170         elseif ($this->_BIFF_version == 0x0600) {
02171             $length    = 0x000E;
02172             $data      = pack("VVvvv", $row_min, $row_max,
02173                                        $col_min, $col_max, $reserved);
02174         }
02175         $header = pack("vv", $record, $length);
02176         $this->_prepend($header.$data);
02177     }
02178     
02184     function _storeWindow2()
02185     {
02186         $record         = 0x023E;     // Record identifier
02187         if ($this->_BIFF_version == 0x0500) {
02188             $length         = 0x000A;     // Number of bytes to follow
02189         }
02190         elseif ($this->_BIFF_version == 0x0600) {
02191             $length         = 0x0012;
02192         }
02193 
02194         $grbit          = 0x00B6;     // Option flags
02195         $rwTop          = 0x0000;     // Top row visible in window
02196         $colLeft        = 0x0000;     // Leftmost column visible in window
02197         
02198     
02199         // The options flags that comprise $grbit
02200         $fDspFmla       = 0;                     // 0 - bit
02201         $fDspGrid       = 1;                     // 1
02202         $fDspRwCol      = 1;                     // 2
02203         $fFrozen        = $this->_frozen;        // 3
02204         $fDspZeros      = 1;                     // 4
02205         $fDefaultHdr    = 1;                     // 5
02206         $fArabic        = 0;                     // 6
02207         $fDspGuts       = $this->_outline_on;    // 7
02208         $fFrozenNoSplit = 0;                     // 0 - bit
02209         $fSelected      = $this->selected;       // 1
02210         $fPaged         = 1;                     // 2
02211     
02212         $grbit             = $fDspFmla;
02213         $grbit            |= $fDspGrid       << 1;
02214         $grbit            |= $fDspRwCol      << 2;
02215         $grbit            |= $fFrozen        << 3;
02216         $grbit            |= $fDspZeros      << 4;
02217         $grbit            |= $fDefaultHdr    << 5;
02218         $grbit            |= $fArabic        << 6;
02219         $grbit            |= $fDspGuts       << 7;
02220         $grbit            |= $fFrozenNoSplit << 8;
02221         $grbit            |= $fSelected      << 9;
02222         $grbit            |= $fPaged         << 10;
02223     
02224         $header  = pack("vv",   $record, $length);
02225         $data    = pack("vvv", $grbit, $rwTop, $colLeft);
02226         // FIXME !!!
02227         if ($this->_BIFF_version == 0x0500) {
02228             $rgbHdr         = 0x00000000; // Row/column heading and gridline color
02229             $data .= pack("V", $rgbHdr);
02230         }
02231         elseif ($this->_BIFF_version == 0x0600) {
02232             $rgbHdr       = 0x0040; // Row/column heading and gridline color index
02233             $zoom_factor_page_break = 0x0000;
02234             $zoom_factor_normal     = 0x0000;
02235             $data .= pack("vvvvV", $rgbHdr, 0x0000, $zoom_factor_page_break, $zoom_factor_normal, 0x00000000);
02236         }
02237         $this->_append($header.$data);
02238     }
02239     
02245     function _storeDefcol()
02246     {
02247         $record   = 0x0055;      // Record identifier
02248         $length   = 0x0002;      // Number of bytes to follow
02249         $colwidth = 0x0008;      // Default column width
02250     
02251         $header   = pack("vv", $record, $length);
02252         $data     = pack("v",  $colwidth);
02253         $this->_prepend($header.$data);
02254     }
02255     
02271     function _storeColinfo($col_array)
02272     {
02273         if (isset($col_array[0])) {
02274             $colFirst = $col_array[0];
02275         }
02276         if (isset($col_array[1])) {
02277             $colLast = $col_array[1];
02278         }
02279         if (isset($col_array[2])) {
02280             $coldx = $col_array[2];
02281         }
02282         else {
02283             $coldx = 8.43;
02284         }
02285         if (isset($col_array[3])) {
02286             $format = $col_array[3];
02287         }
02288         else {
02289             $format = 0;
02290         }
02291         if (isset($col_array[4])) {
02292             $grbit = $col_array[4];
02293         }
02294         else {
02295             $grbit = 0;
02296         }
02297         if (isset($col_array[5])) {
02298             $level = $col_array[5];
02299         }
02300         else {
02301             $level = 0;
02302         }
02303         $record   = 0x007D;          // Record identifier
02304         $length   = 0x000B;          // Number of bytes to follow
02305     
02306         $coldx   += 0.72;            // Fudge. Excel subtracts 0.72 !?
02307         $coldx   *= 256;             // Convert to units of 1/256 of a char
02308     
02309         $ixfe     = $this->_XF($format);
02310         $reserved = 0x00;            // Reserved
02311 
02312         $level = max(0, min($level, 7));
02313         $grbit |= $level << 8;
02314 
02315         $header   = pack("vv",     $record, $length);
02316         $data     = pack("vvvvvC", $colFirst, $colLast, $coldx,
02317                                    $ixfe, $grbit, $reserved);
02318         $this->_prepend($header.$data);
02319     }
02320     
02328     function _storeSelection($array)
02329     {
02330         list($rwFirst,$colFirst,$rwLast,$colLast) = $array;
02331         $record   = 0x001D;                  // Record identifier
02332         $length   = 0x000F;                  // Number of bytes to follow
02333     
02334         $pnn      = $this->_active_pane;     // Pane position
02335         $rwAct    = $rwFirst;                // Active row
02336         $colAct   = $colFirst;               // Active column
02337         $irefAct  = 0;                       // Active cell ref
02338         $cref     = 1;                       // Number of refs
02339     
02340         if (!isset($rwLast)) {
02341             $rwLast   = $rwFirst;       // Last  row in reference
02342         }
02343         if (!isset($colLast)) {
02344             $colLast  = $colFirst;      // Last  col in reference
02345         }
02346     
02347         // Swap last row/col for first row/col as necessary
02348         if ($rwFirst > $rwLast)
02349         {
02350             list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
02351         }
02352     
02353         if ($colFirst > $colLast)
02354         {
02355             list($colFirst, $colLast) = array($colLast, $colFirst);
02356         }
02357     
02358         $header   = pack("vv",         $record, $length);
02359         $data     = pack("CvvvvvvCC",  $pnn, $rwAct, $colAct,
02360                                        $irefAct, $cref,
02361                                        $rwFirst, $rwLast,
02362                                        $colFirst, $colLast);
02363         $this->_append($header.$data);
02364     }
02365  
02371     function _storeMergedCells()
02372     {
02373         // if there are no merged cell ranges set, return
02374         if (count($this->_merged_ranges) == 0) {
02375             return;
02376         }
02377         $record   = 0x00E5;
02378         $length   = 2 + count($this->_merged_ranges) * 8; 
02379  
02380         $header   = pack('vv', $record, $length);
02381         $data     = pack('v',  count($this->_merged_ranges));
02382         foreach ($this->_merged_ranges as $range) {
02383             $data .= pack('vvvv', $range[0], $range[2], $range[1], $range[3]);
02384         }
02385         $this->_append($header.$data);
02386     }
02387     
02401     function _storeExterncount($count)
02402     {
02403         $record   = 0x0016;          // Record identifier
02404         $length   = 0x0002;          // Number of bytes to follow
02405     
02406         $header   = pack("vv", $record, $length);
02407         $data     = pack("v",  $count);
02408         $this->_prepend($header.$data);
02409     }
02410     
02420     function _storeExternsheet($sheetname)
02421     {
02422         $record    = 0x0017;         // Record identifier
02423     
02424         // References to the current sheet are encoded differently to references to
02425         // external sheets.
02426         //
02427         if ($this->name == $sheetname) {
02428             $sheetname = '';
02429             $length    = 0x02;  // The following 2 bytes
02430             $cch       = 1;     // The following byte
02431             $rgch      = 0x02;  // Self reference
02432         }
02433         else {
02434             $length    = 0x02 + strlen($sheetname);
02435             $cch       = strlen($sheetname);
02436             $rgch      = 0x03;  // Reference to a sheet in the current workbook
02437         }
02438     
02439         $header     = pack("vv",  $record, $length);
02440         $data       = pack("CC", $cch, $rgch);
02441         $this->_prepend($header.$data.$sheetname);
02442     }
02443     
02458     function _storePanes($panes)
02459     {
02460         $y       = $panes[0];
02461         $x       = $panes[1];
02462         $rwTop   = $panes[2];
02463         $colLeft = $panes[3];
02464         if (count($panes) > 4) { // if Active pane was received
02465             $pnnAct = $panes[4];
02466         }
02467         else {
02468             $pnnAct = NULL;
02469         }
02470         $record  = 0x0041;       // Record identifier
02471         $length  = 0x000A;       // Number of bytes to follow
02472     
02473         // Code specific to frozen or thawed panes.
02474         if ($this->_frozen)
02475         {
02476             // Set default values for $rwTop and $colLeft
02477             if (!isset($rwTop)) {
02478                 $rwTop   = $y;
02479             }
02480             if (!isset($colLeft)) {
02481                 $colLeft = $x;
02482             }
02483         }
02484         else
02485         {
02486             // Set default values for $rwTop and $colLeft
02487             if (!isset($rwTop)) {
02488                 $rwTop   = 0;
02489             }
02490             if (!isset($colLeft)) {
02491                 $colLeft = 0;
02492             }
02493     
02494             // Convert Excel's row and column units to the internal units.
02495             // The default row height is 12.75
02496             // The default column width is 8.43
02497             // The following slope and intersection values were interpolated.
02498             //
02499             $y = 20*$y      + 255;
02500             $x = 113.879*$x + 390;
02501         }
02502     
02503     
02504         // Determine which pane should be active. There is also the undocumented
02505         // option to override this should it be necessary: may be removed later.
02506         //
02507         if (!isset($pnnAct))
02508         {
02509             if ($x != 0 and $y != 0)
02510                 $pnnAct = 0; // Bottom right
02511             if ($x != 0 and $y == 0)
02512                 $pnnAct = 1; // Top right
02513             if ($x == 0 and $y != 0)
02514                 $pnnAct = 2; // Bottom left
02515             if ($x == 0 and $y == 0)
02516                 $pnnAct = 3; // Top left
02517         }
02518     
02519         $this->_active_pane = $pnnAct; // Used in _storeSelection
02520     
02521         $header     = pack("vv",    $record, $length);
02522         $data       = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
02523         $this->_append($header.$data);
02524     }
02525     
02531     function _storeSetup()
02532     {
02533         $record       = 0x00A1;                  // Record identifier
02534         $length       = 0x0022;                  // Number of bytes to follow
02535     
02536         $iPaperSize   = $this->_paper_size;    // Paper size
02537         $iScale       = $this->_print_scale;   // Print scaling factor
02538         $iPageStart   = 0x01;                 // Starting page number
02539         $iFitWidth    = $this->_fit_width;    // Fit to number of pages wide
02540         $iFitHeight   = $this->_fit_height;   // Fit to number of pages high
02541         $grbit        = 0x00;                 // Option flags
02542         $iRes         = 0x0258;               // Print resolution
02543         $iVRes        = 0x0258;               // Vertical print resolution
02544         $numHdr       = $this->_margin_head;  // Header Margin
02545         $numFtr       = $this->_margin_foot;   // Footer Margin
02546         $iCopies      = 0x01;                 // Number of copies
02547     
02548         $fLeftToRight = 0x0;                     // Print over then down
02549         $fLandscape   = $this->_orientation;     // Page orientation
02550         $fNoPls       = 0x0;                     // Setup not read from printer
02551         $fNoColor     = 0x0;                     // Print black and white
02552         $fDraft       = 0x0;                     // Print draft quality
02553         $fNotes       = 0x0;                     // Print notes
02554         $fNoOrient    = 0x0;                     // Orientation not set
02555         $fUsePage     = 0x0;                     // Use custom starting page
02556     
02557         $grbit           = $fLeftToRight;
02558         $grbit          |= $fLandscape    << 1;
02559         $grbit          |= $fNoPls        << 2;
02560         $grbit          |= $fNoColor      << 3;
02561         $grbit          |= $fDraft        << 4;
02562         $grbit          |= $fNotes        << 5;
02563         $grbit          |= $fNoOrient     << 6;
02564         $grbit          |= $fUsePage      << 7;
02565     
02566         $numHdr = pack("d", $numHdr);
02567         $numFtr = pack("d", $numFtr);
02568         if ($this->_byte_order) // if it's Big Endian
02569         {
02570             $numHdr = strrev($numHdr);
02571             $numFtr = strrev($numFtr);
02572         }
02573     
02574         $header = pack("vv", $record, $length);
02575         $data1  = pack("vvvvvvvv", $iPaperSize,
02576                                    $iScale,
02577                                    $iPageStart,
02578                                    $iFitWidth,
02579                                    $iFitHeight,
02580                                    $grbit,
02581                                    $iRes,
02582                                    $iVRes);
02583         $data2  = $numHdr.$numFtr;
02584         $data3  = pack("v", $iCopies);
02585         $this->_prepend($header.$data1.$data2.$data3);
02586     }
02587     
02593     function _storeHeader()
02594     {
02595         $record  = 0x0014;               // Record identifier
02596     
02597         $str      = $this->_header;       // header string
02598         $cch      = strlen($str);         // Length of header string
02599         if ($this->_BIFF_version == 0x0600) {
02600             $encoding = 0x0;                  // TODO: Unicode support
02601             $length   = 3 + $cch;             // Bytes to follow
02602         }
02603         else {
02604             $length  = 1 + $cch;             // Bytes to follow
02605         }
02606         $header   = pack("vv", $record, $length);
02607         if ($this->_BIFF_version == 0x0600) {
02608             $data     = pack("vC",  $cch, $encoding);
02609         }
02610         else {
02611             $data      = pack("C",  $cch);
02612         }
02613     
02614         $this->_append($header.$data.$str);
02615     }
02616     
02622     function _storeFooter()
02623     {
02624         $record  = 0x0015;               // Record identifier
02625     
02626         $str      = $this->_footer;       // Footer string
02627         $cch      = strlen($str);         // Length of footer string
02628         if ($this->_BIFF_version == 0x0600) {
02629             $encoding = 0x0;                  // TODO: Unicode support
02630             $length   = 3 + $cch;             // Bytes to follow
02631         }
02632         else {
02633             $length  = 1 + $cch;
02634         }
02635         $header    = pack("vv", $record, $length);
02636         if ($this->_BIFF_version == 0x0600) {
02637             $data      = pack("vC",  $cch, $encoding);
02638         }
02639         else {
02640             $data      = pack("C",  $cch);
02641         }
02642     
02643         $this->_append($header.$data.$str);
02644     }
02645     
02651     function _storeHcenter()
02652     {
02653         $record   = 0x0083;              // Record identifier
02654         $length   = 0x0002;              // Bytes to follow
02655     
02656         $fHCenter = $this->_hcenter;     // Horizontal centering
02657     
02658         $header    = pack("vv", $record, $length);
02659         $data      = pack("v",  $fHCenter);
02660     
02661         $this->_append($header.$data);
02662     }
02663     
02669     function _storeVcenter()
02670     {
02671         $record   = 0x0084;              // Record identifier
02672         $length   = 0x0002;              // Bytes to follow
02673     
02674         $fVCenter = $this->_vcenter;     // Horizontal centering
02675     
02676         $header    = pack("vv", $record, $length);
02677         $data      = pack("v",  $fVCenter);
02678         $this->_append($header.$data);
02679     }
02680     
02686     function _storeMarginLeft()
02687     {
02688         $record  = 0x0026;                   // Record identifier
02689         $length  = 0x0008;                   // Bytes to follow
02690     
02691         $margin  = $this->_margin_left;       // Margin in inches
02692     
02693         $header    = pack("vv",  $record, $length);
02694         $data      = pack("d",   $margin);
02695         if ($this->_byte_order) // if it's Big Endian
02696         { 
02697             $data = strrev($data);
02698         }
02699     
02700         $this->_append($header.$data);
02701     }
02702     
02708     function _storeMarginRight()
02709     {
02710         $record  = 0x0027;                   // Record identifier
02711         $length  = 0x0008;                   // Bytes to follow
02712     
02713         $margin  = $this->_margin_right;      // Margin in inches
02714     
02715         $header    = pack("vv",  $record, $length);
02716         $data      = pack("d",   $margin);
02717         if ($this->_byte_order) // if it's Big Endian
02718         { 
02719             $data = strrev($data);
02720         }
02721     
02722         $this->_append($header.$data);
02723     }
02724     
02730     function _storeMarginTop()
02731     {
02732         $record  = 0x0028;                   // Record identifier
02733         $length  = 0x0008;                   // Bytes to follow
02734     
02735         $margin  = $this->_margin_top;        // Margin in inches
02736     
02737         $header    = pack("vv",  $record, $length);
02738         $data      = pack("d",   $margin);
02739         if ($this->_byte_order) // if it's Big Endian
02740         { 
02741             $data = strrev($data);
02742         }
02743     
02744         $this->_append($header.$data);
02745     }
02746     
02752     function _storeMarginBottom()
02753     {
02754         $record  = 0x0029;                   // Record identifier
02755         $length  = 0x0008;                   // Bytes to follow
02756     
02757         $margin  = $this->_margin_bottom;     // Margin in inches
02758     
02759         $header    = pack("vv",  $record, $length);
02760         $data      = pack("d",   $margin);
02761         if ($this->_byte_order) // if it's Big Endian
02762         { 
02763             $data = strrev($data);
02764         }
02765     
02766         $this->_append($header.$data);
02767     }
02768 
02780     function mergeCells($first_row, $first_col, $last_row, $last_col)
02781     {
02782         $record  = 0x00E5;                   // Record identifier
02783         $length  = 0x000A;                   // Bytes to follow
02784         $cref     = 1;                       // Number of refs
02785 
02786         // Swap last row/col for first row/col as necessary
02787         if ($first_row > $last_row) {
02788             list($first_row, $last_row) = array($last_row, $first_row);
02789         }
02790     
02791         if ($first_col > $last_col) {
02792             list($first_col, $last_col) = array($last_col, $first_col);
02793         }
02794     
02795         $header   = pack("vv",    $record, $length);
02796         $data     = pack("vvvvv", $cref, $first_row, $last_row,
02797                                   $first_col, $last_col);
02798     
02799         $this->_append($header.$data);
02800     }
02801 
02807     function _storePrintHeaders()
02808     {
02809         $record      = 0x002a;                   // Record identifier
02810         $length      = 0x0002;                   // Bytes to follow
02811     
02812         $fPrintRwCol = $this->_print_headers;     // Boolean flag
02813     
02814         $header      = pack("vv", $record, $length);
02815         $data        = pack("v", $fPrintRwCol);
02816         $this->_prepend($header.$data);
02817     }
02818     
02825     function _storePrintGridlines()
02826     {
02827         $record      = 0x002b;                    // Record identifier
02828         $length      = 0x0002;                    // Bytes to follow
02829     
02830         $fPrintGrid  = $this->_print_gridlines;    // Boolean flag
02831     
02832         $header      = pack("vv", $record, $length);
02833         $data        = pack("v", $fPrintGrid);
02834         $this->_prepend($header.$data);
02835     }
02836     
02843     function _storeGridset()
02844     {
02845         $record      = 0x0082;                        // Record identifier
02846         $length      = 0x0002;                        // Bytes to follow
02847     
02848         $fGridSet    = !($this->_print_gridlines);     // Boolean flag
02849     
02850         $header      = pack("vv",  $record, $length);
02851         $data        = pack("v",   $fGridSet);
02852         $this->_prepend($header.$data);
02853     }
02854  
02863     function _storeGuts()
02864     {
02865         $record      = 0x0080;   // Record identifier
02866         $length      = 0x0008;   // Bytes to follow
02867    
02868         $dxRwGut     = 0x0000;   // Size of row gutter
02869         $dxColGut    = 0x0000;   // Size of col gutter
02870    
02871         $row_level   = $this->_outline_row_level;
02872         $col_level   = 0;
02873    
02874         // Calculate the maximum column outline level. The equivalent calculation
02875         // for the row outline level is carried out in setRow().
02876         for ($i=0; $i < count($this->_colinfo); $i++)
02877         {
02878            // Skip cols without outline level info.
02879            if (count($col_level) >= 6) {
02880               $col_level = max($this->_colinfo[$i][5], $col_level);
02881            }
02882         }
02883    
02884         // Set the limits for the outline levels (0 <= x <= 7).
02885         $col_level = max(0, min($col_level, 7));
02886    
02887         // The displayed level is one greater than the max outline levels
02888         if ($row_level) {
02889             $row_level++;
02890         }
02891         if ($col_level) {
02892             $col_level++;
02893         }
02894    
02895         $header      = pack("vv",   $record, $length);
02896         $data        = pack("vvvv", $dxRwGut, $dxColGut, $row_level, $col_level);
02897    
02898         $this->_prepend($header.$data);
02899     }
02900 
02901 
02908     function _storeWsbool()
02909     {
02910         $record      = 0x0081;   // Record identifier
02911         $length      = 0x0002;   // Bytes to follow
02912         $grbit       = 0x0000;
02913     
02914         // The only option that is of interest is the flag for fit to page. So we
02915         // set all the options in one go.
02916         //
02917         /*if ($this->_fit_page) {
02918             $grbit = 0x05c1;
02919         }
02920         else {
02921             $grbit = 0x04c1;
02922         }*/
02923         // Set the option flags
02924         $grbit |= 0x0001;                           // Auto page breaks visible
02925         if ($this->_outline_style) {
02926             $grbit |= 0x0020; // Auto outline styles
02927         }
02928         if ($this->_outline_below) {
02929             $grbit |= 0x0040; // Outline summary below
02930         }
02931         if ($this->_outline_right) {
02932             $grbit |= 0x0080; // Outline summary right
02933         }
02934         if ($this->_fit_page) {
02935             $grbit |= 0x0100; // Page setup fit to page
02936         }
02937         if ($this->_outline_on) {
02938             $grbit |= 0x0400; // Outline symbols displayed
02939         }
02940 
02941         $header      = pack("vv", $record, $length);
02942         $data        = pack("v",  $grbit);
02943         $this->_prepend($header.$data);
02944     }
02945 
02951     function _storeHbreak()
02952     {
02953         // Return if the user hasn't specified pagebreaks
02954         if (empty($this->_hbreaks)) {
02955             return;
02956         }
02957     
02958         // Sort and filter array of page breaks
02959         $breaks = $this->_hbreaks;
02960         sort($breaks, SORT_NUMERIC);
02961         if ($breaks[0] == 0) { // don't use first break if it's 0
02962             array_shift($breaks);
02963         }
02964     
02965         $record  = 0x001b;               // Record identifier
02966         $cbrk    = count($breaks);       // Number of page breaks
02967         $length  = 2 + 6*$cbrk;          // Bytes to follow
02968     
02969         $header  = pack("vv", $record, $length);
02970         $data    = pack("v",  $cbrk);
02971     
02972         // Append each page break
02973         foreach($breaks as $break) {
02974             $data .= pack("vvv", $break, 0x0000, 0x00ff);
02975         }
02976     
02977         $this->_prepend($header.$data);
02978     }
02979     
02980     
02986     function _storeVbreak()
02987     {
02988         // Return if the user hasn't specified pagebreaks
02989         if (empty($this->_vbreaks)) {
02990             return;
02991         }
02992     
02993         // 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
02994         // It is slightly higher in Excel 97/200, approx. 1026
02995         $breaks = array_slice($this->_vbreaks,0,1000);
02996     
02997         // Sort and filter array of page breaks
02998         sort($breaks, SORT_NUMERIC);
02999         if ($breaks[0] == 0) { // don't use first break if it's 0
03000             array_shift($breaks);
03001         }
03002     
03003         $record  = 0x001a;               // Record identifier
03004         $cbrk    = count($breaks);       // Number of page breaks
03005         $length  = 2 + 6*$cbrk;          // Bytes to follow
03006     
03007         $header  = pack("vv",  $record, $length);
03008         $data    = pack("v",   $cbrk);
03009     
03010         // Append each page break
03011         foreach ($breaks as $break) {
03012             $data .= pack("vvv", $break, 0x0000, 0xffff);
03013         }
03014     
03015         $this->_prepend($header.$data);
03016     }
03017     
03023     function _storeProtect()
03024     {
03025         // Exit unless sheet protection has been specified
03026         if ($this->_protect == 0) {
03027             return;
03028         }
03029     
03030         $record      = 0x0012;             // Record identifier
03031         $length      = 0x0002;             // Bytes to follow
03032     
03033         $fLock       = $this->_protect;    // Worksheet is protected
03034     
03035         $header      = pack("vv", $record, $length);
03036         $data        = pack("v",  $fLock);
03037     
03038         $this->_prepend($header.$data);
03039     }
03040     
03046     function _storePassword()
03047     {
03048         // Exit unless sheet protection and password have been specified
03049         if (($this->_protect == 0) or (!isset($this->_password))) {
03050             return;
03051         }
03052     
03053         $record      = 0x0013;               // Record identifier
03054         $length      = 0x0002;               // Bytes to follow
03055     
03056         $wPassword   = $this->_password;     // Encoded password
03057     
03058         $header      = pack("vv", $record, $length);
03059         $data        = pack("v",  $wPassword);
03060     
03061         $this->_prepend($header.$data);
03062     }
03063     
03064 
03077     function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1)
03078     {
03079         $bitmap_array = $this->_processBitmap($bitmap);
03080         if ($this->isError($bitmap_array))
03081         {
03082             $this->writeString($row, $col, $bitmap_array->getMessage());
03083             return;
03084         }
03085         list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap);
03086     
03087         // Scale the frame of the image.
03088         $width  *= $scale_x;
03089         $height *= $scale_y;
03090     
03091         // Calculate the vertices of the image and write the OBJ record
03092         $this->_positionImage($col, $row, $x, $y, $width, $height);
03093     
03094         // Write the IMDATA record to store the bitmap data
03095         $record      = 0x007f;
03096         $length      = 8 + $size;
03097         $cf          = 0x09;
03098         $env         = 0x01;
03099         $lcb         = $size;
03100     
03101         $header      = pack("vvvvV", $record, $length, $cf, $env, $lcb);
03102         $this->_append($header.$data);
03103     }
03104     
03156     function _positionImage($col_start, $row_start, $x1, $y1, $width, $height)
03157     {
03158         // Initialise end cell to the same as the start cell
03159         $col_end    = $col_start;  // Col containing lower right corner of object
03160         $row_end    = $row_start;  // Row containing bottom right corner of object
03161     
03162         // Zero the specified offset if greater than the cell dimensions
03163         if ($x1 >= $this->_sizeCol($col_start))
03164         {
03165             $x1 = 0;
03166         }
03167         if ($y1 >= $this->_sizeRow($row_start))
03168         {
03169             $y1 = 0;
03170         }
03171     
03172         $width      = $width  + $x1 -1;
03173         $height     = $height + $y1 -1;
03174     
03175         // Subtract the underlying cell widths to find the end cell of the image
03176         while ($width >= $this->_sizeCol($col_end)) {
03177             $width -= $this->_sizeCol($col_end);
03178             $col_end++;
03179         }
03180     
03181         // Subtract the underlying cell heights to find the end cell of the image
03182         while ($height >= $this->_sizeRow($row_end)) {
03183             $height -= $this->_sizeRow($row_end);
03184             $row_end++;
03185         }
03186     
03187         // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
03188         // with zero eight or width.
03189         //
03190         if ($this->_sizeCol($col_start) == 0)
03191             return;
03192         if ($this->_sizeCol($col_end)   == 0)
03193             return;
03194         if ($this->_sizeRow($row_start) == 0)
03195             return;
03196         if ($this->_sizeRow($row_end)   == 0)
03197             return;
03198     
03199         // Convert the pixel values to the percentage value expected by Excel
03200         $x1 = $x1     / $this->_sizeCol($col_start)   * 1024;
03201         $y1 = $y1     / $this->_sizeRow($row_start)   *  256;
03202         $x2 = $width  / $this->_sizeCol($col_end)     * 1024; // Distance to right side of object
03203         $y2 = $height / $this->_sizeRow($row_end)     *  256; // Distance to bottom of object
03204     
03205         $this->_storeObjPicture( $col_start, $x1,
03206                                   $row_start, $y1,
03207                                   $col_end, $x2,
03208                                   $row_end, $y2
03209                                 );
03210     }
03211     
03221     function _sizeCol($col)
03222     {
03223         // Look up the cell value to see if it has been changed
03224         if (isset($this->col_sizes[$col])) {
03225             if ($this->col_sizes[$col] == 0) {
03226                 return(0);
03227             }
03228             else {
03229                 return(floor(7 * $this->col_sizes[$col] + 5));
03230             }
03231         }
03232         else {
03233             return(64);
03234         }
03235     }
03236     
03247     function _sizeRow($row)
03248     {
03249         // Look up the cell value to see if it has been changed
03250         if (isset($this->row_sizes[$row])) {
03251             if ($this->row_sizes[$row] == 0) {
03252                 return(0);
03253             }
03254             else {
03255                 return(floor(4/3 * $this->row_sizes[$row]));
03256             }
03257         }
03258         else {
03259             return(17);
03260         }
03261     }
03262     
03277     function _storeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB)
03278     {
03279         $record      = 0x005d;   // Record identifier
03280         $length      = 0x003c;   // Bytes to follow
03281     
03282         $cObj        = 0x0001;   // Count of objects in file (set to 1)
03283         $OT          = 0x0008;   // Object type. 8 = Picture
03284         $id          = 0x0001;   // Object ID
03285         $grbit       = 0x0614;   // Option flags
03286     
03287         $cbMacro     = 0x0000;   // Length of FMLA structure
03288         $Reserved1   = 0x0000;   // Reserved
03289         $Reserved2   = 0x0000;   // Reserved
03290     
03291         $icvBack     = 0x09;     // Background colour
03292         $icvFore     = 0x09;     // Foreground colour
03293         $fls         = 0x00;     // Fill pattern
03294         $fAuto       = 0x00;     // Automatic fill
03295         $icv         = 0x08;     // Line colour
03296         $lns         = 0xff;     // Line style
03297         $lnw         = 0x01;     // Line weight
03298         $fAutoB      = 0x00;     // Automatic border
03299         $frs         = 0x0000;   // Frame style
03300         $cf          = 0x0009;   // Image format, 9 = bitmap
03301         $Reserved3   = 0x0000;   // Reserved
03302         $cbPictFmla  = 0x0000;   // Length of FMLA structure
03303         $Reserved4   = 0x0000;   // Reserved
03304         $grbit2      = 0x0001;   // Option flags
03305         $Reserved5   = 0x0000;   // Reserved
03306     
03307     
03308         $header      = pack("vv", $record, $length);
03309         $data        = pack("V", $cObj);
03310         $data       .= pack("v", $OT);
03311         $data       .= pack("v", $id);
03312         $data       .= pack("v", $grbit);
03313         $data       .= pack("v", $colL);
03314         $data       .= pack("v", $dxL);
03315         $data       .= pack("v", $rwT);
03316         $data       .= pack("v", $dyT);
03317         $data       .= pack("v", $colR);
03318         $data       .= pack("v", $dxR);
03319         $data       .= pack("v", $rwB);
03320         $data       .= pack("v", $dyB);
03321         $data       .= pack("v", $cbMacro);
03322         $data       .= pack("V", $Reserved1);
03323         $data       .= pack("v", $Reserved2);
03324         $data       .= pack("C", $icvBack);
03325         $data       .= pack("C", $icvFore);
03326         $data       .= pack("C", $fls);
03327         $data       .= pack("C", $fAuto);
03328         $data       .= pack("C", $icv);
03329         $data       .= pack("C", $lns);
03330         $data       .= pack("C", $lnw);
03331         $data       .= pack("C", $fAutoB);
03332         $data       .= pack("v", $frs);
03333         $data       .= pack("V", $cf);
03334         $data       .= pack("v", $Reserved3);
03335         $data       .= pack("v", $cbPictFmla);
03336         $data       .= pack("v", $Reserved4);
03337         $data       .= pack("v", $grbit2);
03338         $data       .= pack("V", $Reserved5);
03339     
03340         $this->_append($header.$data);
03341     }
03342     
03352     function _processBitmap($bitmap)
03353     {
03354         // Open file.
03355         $bmp_fd = @fopen($bitmap,"rb");
03356         if (!$bmp_fd) {
03357             $this->raiseError("Couldn't import $bitmap");
03358         }
03359             
03360         // Slurp the file into a string.
03361         $data = fread($bmp_fd, filesize($bitmap));
03362     
03363         // Check that the file is big enough to be a bitmap.
03364         if (strlen($data) <= 0x36) {
03365             $this->raiseError("$bitmap doesn't contain enough data.\n");
03366         }
03367     
03368         // The first 2 bytes are used to identify the bitmap.
03369         $identity = unpack("A2", $data);
03370         if ($identity[''] != "BM") {
03371             $this->raiseError("$bitmap doesn't appear to be a valid bitmap image.\n");
03372         }
03373     
03374         // Remove bitmap data: ID.
03375         $data = substr($data, 2);
03376     
03377         // Read and remove the bitmap size. This is more reliable than reading
03378         // the data size at offset 0x22.
03379         //
03380         $size_array   = unpack("V", substr($data, 0, 4));
03381         $size   = $size_array[''];
03382         $data   = substr($data, 4);
03383         $size  -= 0x36; // Subtract size of bitmap header.
03384         $size  += 0x0C; // Add size of BIFF header.
03385     
03386         // Remove bitmap data: reserved, offset, header length.
03387         $data = substr($data, 12);
03388     
03389         // Read and remove the bitmap width and height. Verify the sizes.
03390         $width_and_height = unpack("V2", substr($data, 0, 8));
03391         $width  = $width_and_height[1];
03392         $height = $width_and_height[2];
03393         $data   = substr($data, 8);
03394         if ($width > 0xFFFF) { 
03395             $this->raiseError("$bitmap: largest image width supported is 65k.\n");
03396         }
03397         if ($height > 0xFFFF) { 
03398             $this->raiseError("$bitmap: largest image height supported is 65k.\n");
03399         }
03400     
03401         // Read and remove the bitmap planes and bpp data. Verify them.
03402         $planes_and_bitcount = unpack("v2", substr($data, 0, 4));
03403         $data = substr($data, 4);
03404         if ($planes_and_bitcount[2] != 24) { // Bitcount
03405             $this->raiseError("$bitmap isn't a 24bit true color bitmap.\n");
03406         }
03407         if ($planes_and_bitcount[1] != 1) {
03408             $this->raiseError("$bitmap: only 1 plane nupported in bitmap image.\n");
03409         }
03410     
03411         // Read and remove the bitmap compression. Verify compression.
03412         $compression = unpack("V", substr($data, 0, 4));
03413         $data = substr($data, 4);
03414       
03415         //$compression = 0;
03416         if ($compression[""] != 0) {
03417             $this->raiseError("$bitmap: compression not supported in bitmap image.\n");
03418         }
03419     
03420         // Remove bitmap data: data size, hres, vres, colours, imp. colours.
03421         $data = substr($data, 20);
03422     
03423         // Add the BITMAPCOREHEADER data
03424         $header  = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
03425         $data    = $header . $data;
03426     
03427         return (array($width, $height, $size, $data));
03428     }
03429     
03436     function _storeZoom()
03437     {
03438         // If scale is 100 we don't need to write a record
03439         if ($this->_zoom == 100) {
03440             return;
03441         }
03442     
03443         $record      = 0x00A0;               // Record identifier
03444         $length      = 0x0004;               // Bytes to follow
03445     
03446         $header      = pack("vv", $record, $length);
03447         $data        = pack("vv", $this->_zoom, 100);
03448         $this->_append($header.$data);
03449     }
03450 
03454     function setValidation($row1, $col1, $row2, $col2, &$validator)
03455     {
03456         $this->_dv[] = $validator->_getData() .
03457                        pack("vvvvv", 1, $row1, $row2, $col1, $col2);
03458     }
03459    
03465     function _storeDataValidity()
03466     {
03467         $record      = 0x01b2;      // Record identifier
03468         $length      = 0x0012;      // Bytes to follow
03469    
03470         $grbit       = 0x0002;      // Prompt box at cell, no cached validity data at DV records
03471         $horPos      = 0x00000000;  // Horizontal position of prompt box, if fixed position
03472         $verPos      = 0x00000000;  // Vertical position of prompt box, if fixed position
03473         $objId       = 0xffffffff;  // Object identifier of drop down arrow object, or -1 if not visible
03474       
03475         $header      = pack('vv', $record, $length);
03476         $data        = pack('vVVVV', $grbit, $horPos, $verPos, $objId,
03477                                      count($this->_dv));
03478         $this->_append($header.$data);
03479       
03480         $record = 0x01be;              // Record identifier
03481         foreach($this->_dv as $dv)
03482         {
03483             $length = strlen($dv);      // Bytes to follow
03484             $header = pack("vv", $record, $length);
03485             $this->_append($header.$dv);
03486         }
03487     }
03488 }
03489 ?>

Generated on Fri Dec 13 2013 09:06:35 for ILIAS Release_3_4_x_branch .rev 46804 by  doxygen 1.7.1