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