PHPExcel_Writer_Excel5
[ class tree: PHPExcel_Writer_Excel5 ] [ index: PHPExcel_Writer_Excel5 ] [ all elements ]

Source for file Workbook.php

Documentation is available at Workbook.php

  1. <?php
  2. /**
  3.  * PHPExcel
  4.  *
  5.  * Copyright (c) 2006 - 2010 PHPExcel
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  20.  *
  21.  * @category   PHPExcel
  22.  * @package    PHPExcel_Writer_Excel5
  23.  * @copyright  Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)
  24.  * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  25.  * @version    1.7.2, 2010-01-11
  26.  */
  27.  
  28. // Original file header of PEAR::Spreadsheet_Excel_Writer_Workbook (used as the base for this class):
  29. // -----------------------------------------------------------------------------------------
  30. // /*
  31. // *  Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
  32. // *
  33. // *  The majority of this is _NOT_ my code.  I simply ported it from the
  34. // *  PERL Spreadsheet::WriteExcel module.
  35. // *
  36. // *  The author of the Spreadsheet::WriteExcel module is John McNamara 
  37. // *  <jmcnamara@cpan.org>
  38. // *
  39. // *  I _DO_ maintain this code, and John McNamara has nothing to do with the
  40. // *  porting of this code to PHP.  Any questions directly related to this
  41. // *  class library should be directed to me.
  42. // *
  43. // *  License Information:
  44. // *
  45. // *    Spreadsheet_Excel_Writer:  A library for generating Excel Spreadsheets
  46. // *    Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
  47. // *
  48. // *    This library is free software; you can redistribute it and/or
  49. // *    modify it under the terms of the GNU Lesser General Public
  50. // *    License as published by the Free Software Foundation; either
  51. // *    version 2.1 of the License, or (at your option) any later version.
  52. // *
  53. // *    This library is distributed in the hope that it will be useful,
  54. // *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  55. // *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  56. // *    Lesser General Public License for more details.
  57. // *
  58. // *    You should have received a copy of the GNU Lesser General Public
  59. // *    License along with this library; if not, write to the Free Software
  60. // *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  61. // */
  62.  
  63.  
  64. /** PHPExcel root directory */
  65. if (!defined('PHPEXCEL_ROOT')) {
  66.     /**
  67.      * @ignore
  68.      */
  69.     define('PHPEXCEL_ROOT'dirname(__FILE__'/../../../');
  70. }
  71.  
  72. /** PHPExcel_Shared_Date */
  73. require_once PHPEXCEL_ROOT 'PHPExcel/Shared/Date.php';
  74.  
  75. /** PHPExcel_Shared_String */
  76. require_once PHPEXCEL_ROOT 'PHPExcel/Shared/String.php';
  77.  
  78. /** PHPExcel_Shared_Escher */
  79. require_once PHPEXCEL_ROOT 'PHPExcel/Shared/Escher.php';
  80.  
  81. /** PHPExcel_Shared_Escher_DggContainer */
  82. require_once PHPEXCEL_ROOT 'PHPExcel/Shared/Escher/DggContainer.php';
  83.  
  84. /** PHPExcel_Shared_Escher_DggContainer_BstoreContainer */
  85. require_once PHPEXCEL_ROOT 'PHPExcel/Shared/Escher/DggContainer/BstoreContainer.php';
  86.  
  87. /** PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE */
  88. require_once PHPEXCEL_ROOT 'PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE.php';
  89.  
  90. /** PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip */
  91. require_once PHPEXCEL_ROOT 'PHPExcel/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php';
  92.  
  93. /** PHPExcel_Worksheet */
  94. require_once PHPEXCEL_ROOT 'PHPExcel/Worksheet.php';
  95.  
  96. /** PHPExcel_Writer_Excel5_Xf */
  97. require_once PHPEXCEL_ROOT 'PHPExcel/Writer/Excel5/Xf.php';
  98.  
  99. /** PHPExcel_Writer_Excel5_BIFFwriter */
  100. require_once PHPEXCEL_ROOT 'PHPExcel/Writer/Excel5/BIFFwriter.php';
  101.  
  102. /** PHPExcel_Writer_Excel5_Worksheet */
  103. require_once PHPEXCEL_ROOT 'PHPExcel/Writer/Excel5/Worksheet.php';
  104.  
  105. /** PHPExcel_Writer_Excel5_Font */
  106. require_once PHPEXCEL_ROOT 'PHPExcel/Writer/Excel5/Font.php';
  107.  
  108. /** PHPExcel_Writer_Excel5_Escher */
  109. require_once PHPEXCEL_ROOT 'PHPExcel/Writer/Excel5/Escher.php';
  110.  
  111.  
  112. /**
  113.  * PHPExcel_Writer_Excel5_Workbook
  114.  *
  115.  * @category   PHPExcel
  116.  * @package    PHPExcel_Writer_Excel5
  117.  * @copyright  Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)
  118.  */
  119. {
  120.     /**
  121.      * Formula parser
  122.      *
  123.      * @var PHPExcel_Writer_Excel5_Parser 
  124.      */
  125.     private $_parser;
  126.  
  127.     /**
  128.      * The BIFF file size for the workbook.
  129.      * @var integer 
  130.      * @see _calcSheetOffsets()
  131.      */
  132.     var $_biffsize;
  133.  
  134.     /**
  135.      * XF Writers
  136.      * @var PHPExcel_Writer_Excel5_Xf[] 
  137.      */
  138.     private $_xfWriters = array();
  139.  
  140.     /**
  141.      * Array containing the colour palette
  142.      * @var array 
  143.      */
  144.     var $_palette;
  145.  
  146.     /**
  147.      * The codepage indicates the text encoding used for strings
  148.      * @var integer 
  149.      */
  150.     var $_codepage;
  151.  
  152.     /**
  153.      * The country code used for localization
  154.      * @var integer 
  155.      */
  156.     var $_country_code;
  157.  
  158.     /**
  159.      * The temporary dir for storing the OLE file
  160.      * @var string 
  161.      */
  162.     var $_tmp_dir;
  163.  
  164.     /**
  165.      * Workbook
  166.      * @var PHPExcel 
  167.      */
  168.     private $_phpExcel;
  169.  
  170.     /**
  171.      * Fonts writers
  172.      *
  173.      * @var PHPExcel_Writer_Excel5_Font[] 
  174.      */
  175.     private $_fontWriters = array();
  176.  
  177.     /**
  178.      * Added fonts. Maps from font's hash => index in workbook
  179.      *
  180.      * @var array 
  181.      */
  182.     private $_addedFonts = array();
  183.  
  184.     /**
  185.      * Shared number formats
  186.      *
  187.      * @var array 
  188.      */
  189.     private $_numberFormats = array();
  190.  
  191.     /**
  192.      * Added number formats. Maps from numberFormat's hash => index in workbook
  193.      *
  194.      * @var array 
  195.      */
  196.     private $_addedNumberFormats = array();
  197.  
  198.     /**
  199.      * Sizes of the binary worksheet streams
  200.      *
  201.      * @var array 
  202.      */
  203.     private $_worksheetSizes = array();
  204.  
  205.     /**
  206.      * Offsets of the binary worksheet streams relative to the start of the global workbook stream
  207.      *
  208.      * @var array 
  209.      */
  210.     private $_worksheetOffsets = array();
  211.  
  212.     /**
  213.      * Total number of shared strings in workbook
  214.      *
  215.      * @var int 
  216.      */
  217.     private $_str_total;
  218.  
  219.     /**
  220.      * Number of unique shared strings in workbook
  221.      *
  222.      * @var int 
  223.      */
  224.     private $_str_unique;
  225.  
  226.     /**
  227.      * Array of unique shared strings in workbook
  228.      *
  229.      * @var array 
  230.      */
  231.     private $_str_table;
  232.  
  233.     /**
  234.      * Color cache
  235.      */
  236.     private $_colors;
  237.  
  238.  
  239.     /**
  240.      * Class constructor
  241.      *
  242.      * @param PHPExcel $phpExcel The Workbook
  243.      * @param int $BIFF_verions BIFF version
  244.      * @param int  $str_total        Total number of strings
  245.      * @param int  $str_unique        Total number of unique strings
  246.      * @param array  $str_table 
  247.      * @param mixed   $parser      The formula parser created for the Workbook
  248.      */
  249.     public function __construct(PHPExcel $phpExcel null$BIFF_version 0x0600,
  250.                                                 &$str_total,
  251.                                                 &$str_unique&$str_table&$colors$parser$tempDir ''
  252.                                 )
  253.     {
  254.         // It needs to call its parent's constructor explicitly
  255.         parent::__construct();
  256.  
  257.         $this->_parser           = $parser;
  258.         $this->_biffsize         = 0;
  259.         $this->_palette          = array();
  260.         $this->_codepage         = 0x04E4// FIXME: should change for BIFF8
  261.         $this->_country_code     = -1;
  262.  
  263.         $this->_str_total       = &$str_total;
  264.         $this->_str_unique      = &$str_unique;
  265.         $this->_str_table       = &$str_table;
  266.         $this->_colors          = &$colors;
  267.         $this->_setPaletteXl97();
  268.         $this->_tmp_dir         = $tempDir;
  269.         
  270.         $this->_phpExcel = $phpExcel;
  271.         
  272.         if ($BIFF_version == 0x0600{
  273.             $this->_BIFF_version = 0x0600;
  274.             // change BIFFwriter limit for CONTINUE records
  275.             $this->_limit = 8224;
  276.             $this->_codepage = 0x04B0;
  277.         }
  278.  
  279.         // Add empty sheets
  280.         $countSheets count($phpExcel->getAllSheets());
  281.         for ($i 0$i $countSheets++$i{
  282.             $phpSheet  $phpExcel->getSheet($i);
  283.             
  284.             $this->_parser->setExtSheet($phpSheet->getTitle()$i);  // Register worksheet name with parser
  285.  
  286.             // for BIFF8
  287.             if ($this->_BIFF_version == 0x0600{
  288.                 $supbook_index 0x00;
  289.                 $ref pack('vvv'$supbook_index$i$i);
  290.                 $this->_parser->_references[$ref;  // Register reference with parser
  291.             }
  292.         }
  293.  
  294.         // Build color cache
  295.  
  296.         // Sheet tab colors?
  297.         $countSheets count($phpExcel->getAllSheets());
  298.         for ($i 0$i $countSheets++$i{
  299.             $phpSheet  $phpExcel->getSheet($i);
  300.             if ($phpSheet->isTabColorSet()) {
  301.                 $this->_addColor($phpSheet->getTabColor()->getRGB());
  302.             }
  303.         }
  304.  
  305.     }
  306.  
  307.     /**
  308.      * Add a new XF writer
  309.      *
  310.      * @param PHPExcel_Style 
  311.      * @param boolean Is it a style XF?
  312.      * @return int Index to XF record
  313.      */
  314.     public function addXfWriter($style$isStyleXf false)
  315.     {
  316.         $xfWriter new PHPExcel_Writer_Excel5_Xf($style);
  317.         $xfWriter->setBIFFVersion($this->_BIFF_version);
  318.         $xfWriter->setIsStyleXf($isStyleXf);
  319.  
  320.         // Add the font if not already added
  321.         $fontHashCode $style->getFont()->getHashCode();
  322.  
  323.         if (isset($this->_addedFonts[$fontHashCode])) {
  324.             $fontIndex $this->_addedFonts[$fontHashCode];
  325.         else {
  326.             $countFonts count($this->_fontWriters);
  327.             $fontIndex ($countFonts 4$countFonts $countFonts 1;
  328.  
  329.             $fontWriter new PHPExcel_Writer_Excel5_Font($style->getFont());
  330.             $fontWriter->setBIFFVersion($this->_BIFF_version);
  331.             $fontWriter->setColorIndex($this->_addColor($style->getFont()->getColor()->getRGB()));
  332.             $this->_fontWriters[$fontWriter;
  333.  
  334.             $this->_addedFonts[$fontHashCode$fontIndex;
  335.         }
  336.  
  337.         // Assign the font index to the xf record
  338.         $xfWriter->setFontIndex($fontIndex);
  339.  
  340.         // Background colors, best to treat these after the font so black will come after white in custom palette
  341.         $xfWriter->setFgColor($this->_addColor($style->getFill()->getStartColor()->getRGB()));
  342.         $xfWriter->setBgColor($this->_addColor($style->getFill()->getEndColor()->getRGB()));
  343.         $xfWriter->setBottomColor($this->_addColor($style->getBorders()->getBottom()->getColor()->getRGB()));
  344.         $xfWriter->setTopColor($this->_addColor($style->getBorders()->getTop()->getColor()->getRGB()));
  345.         $xfWriter->setRightColor($this->_addColor($style->getBorders()->getRight()->getColor()->getRGB()));
  346.         $xfWriter->setLeftColor($this->_addColor($style->getBorders()->getLeft()->getColor()->getRGB()));
  347.         $xfWriter->setDiagColor($this->_addColor($style->getBorders()->getDiagonal()->getColor()->getRGB()));
  348.  
  349.         // Add the number format if it is not a built-in one and not already added
  350.         if ($style->getNumberFormat()->getBuiltInFormatCode(=== false{
  351.             $numberFormatHashCode $style->getNumberFormat()->getHashCode();
  352.  
  353.             if (isset($this->_addedNumberFormats[$numberFormatHashCode])) {
  354.                 $numberFormatIndex $this->_addedNumberFormats[$numberFormatHashCode];
  355.             else {
  356.                 $numberFormatIndex 164 count($this->_numberFormats);
  357.                 $this->_numberFormats[$numberFormatIndex$style->getNumberFormat();
  358.                 $this->_addedNumberFormats[$numberFormatHashCode$numberFormatIndex;
  359.             }
  360.         }
  361.         else {
  362.             $numberFormatIndex = (int) $style->getNumberFormat()->getBuiltInFormatCode();
  363.         }
  364.  
  365.         // Assign the number format index to xf record
  366.         $xfWriter->setNumberFormatIndex($numberFormatIndex);
  367.  
  368.         $this->_xfWriters[$xfWriter;
  369.  
  370.         $xfIndex count($this->_xfWriters1;
  371.         return $xfIndex;
  372.     }
  373.  
  374.     /**
  375.      * Alter color palette adding a custom color
  376.      *
  377.      * @param string $rgb E.g. 'FF00AA'
  378.      * @return int Color index
  379.      */
  380.     private function _addColor($rgb{
  381.         if (!isset($this->_colors[$rgb])) {
  382.             if (count($this->_colors57{
  383.                 // then we add a custom color altering the palette
  384.                 $colorIndex count($this->_colors);
  385.                 $this->_palette[$colorIndex=
  386.                     array(
  387.                         hexdec(substr($rgb02)),
  388.                         hexdec(substr($rgb22)),
  389.                         hexdec(substr($rgb4)),
  390.                         0
  391.                     );
  392.                 $this->_colors[$rgb$colorIndex;
  393.             else {
  394.                 // no room for more custom colors, just map to black
  395.                 $colorIndex 0;
  396.             }
  397.         else {
  398.             // fetch already added custom color
  399.             $colorIndex $this->_colors[$rgb];
  400.         }
  401.  
  402.         return $colorIndex;
  403.     }
  404.  
  405.     /**
  406.      * Sets the colour palette to the Excel 97+ default.
  407.      *
  408.      * @access private
  409.      */
  410.     function _setPaletteXl97()
  411.     {
  412.         $this->_palette = array(
  413.             0x08 => array(0x000x000x000x00),
  414.             0x09 => array(0xff0xff0xff0x00),
  415.             0x0A => array(0xff0x000x000x00),
  416.             0x0B => array(0x000xff0x000x00),
  417.             0x0C => array(0x000x000xff0x00),
  418.             0x0D => array(0xff0xff0x000x00),
  419.             0x0E => array(0xff0x000xff0x00),
  420.             0x0F => array(0x000xff0xff0x00),
  421.             0x10 => array(0x800x000x000x00),
  422.             0x11 => array(0x000x800x000x00),
  423.             0x12 => array(0x000x000x800x00),
  424.             0x13 => array(0x800x800x000x00),
  425.             0x14 => array(0x800x000x800x00),
  426.             0x15 => array(0x000x800x800x00),
  427.             0x16 => array(0xc00xc00xc00x00),
  428.             0x17 => array(0x800x800x800x00),
  429.             0x18 => array(0x990x990xff0x00),
  430.             0x19 => array(0x990x330x660x00),
  431.             0x1A => array(0xff0xff0xcc0x00),
  432.             0x1B => array(0xcc0xff0xff0x00),
  433.             0x1C => array(0x660x000x660x00),
  434.             0x1D => array(0xff0x800x800x00),
  435.             0x1E => array(0x000x660xcc0x00),
  436.             0x1F => array(0xcc0xcc0xff0x00),
  437.             0x20 => array(0x000x000x800x00),
  438.             0x21 => array(0xff0x000xff0x00),
  439.             0x22 => array(0xff0xff0x000x00),
  440.             0x23 => array(0x000xff0xff0x00),
  441.             0x24 => array(0x800x000x800x00),
  442.             0x25 => array(0x800x000x000x00),
  443.             0x26 => array(0x000x800x800x00),
  444.             0x27 => array(0x000x000xff0x00),
  445.             0x28 => array(0x000xcc0xff0x00),
  446.             0x29 => array(0xcc0xff0xff0x00),
  447.             0x2A => array(0xcc0xff0xcc0x00),
  448.             0x2B => array(0xff0xff0x990x00),
  449.             0x2C => array(0x990xcc0xff0x00),
  450.             0x2D => array(0xff0x990xcc0x00),
  451.             0x2E => array(0xcc0x990xff0x00),
  452.             0x2F => array(0xff0xcc0x990x00),
  453.             0x30 => array(0x330x660xff0x00),
  454.             0x31 => array(0x330xcc0xcc0x00),
  455.             0x32 => array(0x990xcc0x000x00),
  456.             0x33 => array(0xff0xcc0x000x00),
  457.             0x34 => array(0xff0x990x000x00),
  458.             0x35 => array(0xff0x660x000x00),
  459.             0x36 => array(0x660x660x990x00),
  460.             0x37 => array(0x960x960x960x00),
  461.             0x38 => array(0x000x330x660x00),
  462.             0x39 => array(0x330x990x660x00),
  463.             0x3A => array(0x000x330x000x00),
  464.             0x3B => array(0x330x330x000x00),
  465.             0x3C => array(0x990x330x000x00),
  466.             0x3D => array(0x990x330x660x00),
  467.             0x3E => array(0x330x330x990x00),
  468.             0x3F => array(0x330x330x330x00),
  469.         );
  470.     }
  471.  
  472.     /**
  473.      * Assemble worksheets into a workbook and send the BIFF data to an OLE
  474.      * storage.
  475.      *
  476.      * @param array $worksheetSizes The sizes in bytes of the binary worksheet streams
  477.      * @return string Binary data for workbook stream
  478.      */
  479.     public function writeWorkbook($pWorksheetSizes null)
  480.     {
  481.         $this->_worksheetSizes = $pWorksheetSizes;
  482.         
  483.         // Calculate the number of selected worksheet tabs and call the finalization
  484.         // methods for each worksheet
  485.         $total_worksheets count($this->_phpExcel->getAllSheets());
  486.  
  487.         // Add part 1 of the Workbook globals, what goes before the SHEET records
  488.         $this->_storeBof(0x0005);
  489.         $this->_storeCodepage();
  490.         if ($this->_BIFF_version == 0x0600{
  491.             $this->_storeWindow1();
  492.         }
  493.         if ($this->_BIFF_version == 0x0500{
  494.             $this->_storeExterns();    // For print area and repeat rows
  495.             $this->_storeNames();      // For print area and repeat rows
  496.         }
  497.         if ($this->_BIFF_version == 0x0500{
  498.             $this->_storeWindow1();
  499.         }
  500.         $this->_storeDatemode();
  501.         $this->_storeAllFonts();
  502.         $this->_storeAllNumFormats();
  503.         $this->_storeAllXfs();
  504.         $this->_storeAllStyles();
  505.         $this->_storePalette();
  506.  
  507.         // Prepare part 3 of the workbook global stream, what goes after the SHEET records
  508.         $part3 '';
  509.         if ($this->_country_code != -1{
  510.             $part3 .= $this->_writeCountry();
  511.         }
  512.         $part3 .= $this->_writeRecalcId();
  513.  
  514.         if ($this->_BIFF_version == 0x0600{
  515.             $part3 .= $this->_writeSupbookInternal();
  516.             /* TODO: store external SUPBOOK records and XCT and CRN records
  517.             in case of external references for BIFF8 */
  518.             $part3 .= $this->_writeExternsheetBiff8();
  519.             $part3 .= $this->_writeAllDefinedNamesBiff8();
  520.             $part3 .= $this->_writeMsoDrawingGroup();
  521.             $part3 .= $this->_writeSharedStringsTable();
  522.         }
  523.  
  524.         $part3 .= $this->writeEof();
  525.  
  526.         // Add part 2 of the Workbook globals, the SHEET records
  527.         $this->_calcSheetOffsets();
  528.         for ($i 0$i $total_worksheets++$i{
  529.             $this->_storeBoundsheet($this->_phpExcel->getSheet($i)$this->_worksheetOffsets[$i]);
  530.         }
  531.  
  532.         // Add part 3 of the Workbook globals
  533.         $this->_data .= $part3;
  534.         
  535.         return $this->_data;
  536.     }
  537.  
  538.     /**
  539.      * Calculate offsets for Worksheet BOF records.
  540.      *
  541.      * @access private
  542.      */
  543.     function _calcSheetOffsets()
  544.     {
  545.         if ($this->_BIFF_version == 0x0600{
  546.             $boundsheet_length 10;  // fixed length for a BOUNDSHEET record
  547.         else {
  548.             $boundsheet_length 11;
  549.         }
  550.  
  551.         // size of Workbook globals part 1 + 3
  552.         $offset            $this->_datasize;
  553.  
  554.         // add size of Workbook globals part 2, the length of the SHEET records
  555.         $total_worksheets count($this->_phpExcel->getAllSheets());
  556.         foreach ($this->_phpExcel->getWorksheetIterator(as $sheet{
  557.             if ($this->_BIFF_version == 0x0600{
  558.                 $offset += $boundsheet_length strlen(PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($sheet->getTitle()));
  559.             else {
  560.                 $offset += $boundsheet_length strlen($sheet->getTitle());
  561.             }
  562.         }
  563.  
  564.         // add the sizes of each of the Sheet substreams, respectively
  565.         for ($i 0$i $total_worksheets++$i{
  566.             $this->_worksheetOffsets[$i$offset;
  567.             $offset += $this->_worksheetSizes[$i];
  568.         }
  569.         $this->_biffsize = $offset;
  570.     }
  571.  
  572.     /**
  573.      * Store the Excel FONT records.
  574.      *
  575.      * @access private
  576.      */
  577.     function _storeAllFonts()
  578.     {
  579.         foreach ($this->_fontWriters as $fontWriter{
  580.             $this->_append($fontWriter->writeFont());
  581.         }
  582.     }
  583.  
  584.     /**
  585.      * Store user defined numerical formats i.e. FORMAT records
  586.      *
  587.      * @access private
  588.      */
  589.     function _storeAllNumFormats()
  590.     {
  591.         foreach ($this->_numberFormats as $numberFormatIndex => $numberFormat{
  592.             $this->_storeNumFormat($numberFormat->getFormatCode()$numberFormatIndex);
  593.         }
  594.     }
  595.  
  596.     /**
  597.      * Write all XF records.
  598.      *
  599.      * @access private
  600.      */
  601.     function _storeAllXfs()
  602.     {
  603.         foreach ($this->_xfWriters as $xfWriter{
  604.             $this->_append($xfWriter->writeXf());
  605.         }
  606.     }
  607.  
  608.     /**
  609.      * Write all STYLE records.
  610.      *
  611.      * @access private
  612.      */
  613.     function _storeAllStyles()
  614.     {
  615.         $this->_storeStyle();
  616.     }
  617.  
  618.     /**
  619.      * Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
  620.      * the NAME records.
  621.      *
  622.      * @access private
  623.      */
  624.     function _storeExterns()
  625.     {
  626.         // Create EXTERNCOUNT with number of worksheets
  627.         $this->_storeExterncount(count($this->_phpExcel->getAllSheets()));
  628.  
  629.         // Create EXTERNSHEET for each worksheet
  630.         foreach ($this->_phpExcel->getWorksheetIterator(as $sheet{
  631.             $this->_storeExternsheet($sheet->getTitle());
  632.         }
  633.     }
  634.  
  635.     /**
  636.      * Write the NAME record to define the print area and the repeat rows and cols.
  637.      *
  638.      * @access private
  639.      */
  640.     function _storeNames()
  641.     {
  642.         // total number of sheets
  643.         $total_worksheets count($this->_phpExcel->getAllSheets());
  644.  
  645.         // Create the print area NAME records
  646.         for ($i 0$i $total_worksheets++$i{
  647.             // Write a Name record if the print area has been defined
  648.             if ($this->_phpExcel->getSheet($i)->getPageSetup()->isPrintAreaSet()) {
  649.                 // Print area
  650.                 $printArea PHPExcel_Cell::splitRange($this->_phpExcel->getSheet($i)->getPageSetup()->getPrintArea());
  651.                 $printArea $printArea[0];
  652.                 $printArea[0PHPExcel_Cell::coordinateFromString($printArea[0]);
  653.                 $printArea[1PHPExcel_Cell::coordinateFromString($printArea[1]);
  654.             
  655.                 $print_rowmin $printArea[0][11;
  656.                 $print_rowmax $printArea[1][11;
  657.                 $print_colmin PHPExcel_Cell::columnIndexFromString($printArea[0][0]1;
  658.                 $print_colmax PHPExcel_Cell::columnIndexFromString($printArea[1][0]1;
  659.  
  660.                 $this->_storeNameShort(
  661.                     $i// sheet index
  662.                     0x06// NAME type
  663.                     $print_rowmin,
  664.                     $print_rowmax,
  665.                     $print_colmin,
  666.                     $print_colmax
  667.                     );
  668.             }
  669.         }
  670.  
  671.         // Create the print title NAME records
  672.         for ($i 0$i $total_worksheets++$i{
  673.  
  674.             // simultaneous repeatColumns repeatRows
  675.             if ($this->_phpExcel->getSheet($i)->getPageSetup()->isColumnsToRepeatAtLeftSet(&& $this->_phpExcel->getSheet($i)->getPageSetup()->isRowsToRepeatAtTopSet()) {
  676.                 $repeat $this->_phpExcel->getSheet($i)->getPageSetup()->getColumnsToRepeatAtLeft();
  677.                 $colmin PHPExcel_Cell::columnIndexFromString($repeat[0]1;
  678.                 $colmax PHPExcel_Cell::columnIndexFromString($repeat[1]1;
  679.  
  680.                 $repeat $this->_phpExcel->getSheet($i)->getPageSetup()->getRowsToRepeatAtTop();
  681.                 $rowmin $repeat[01;
  682.                 $rowmax $repeat[11;
  683.                 
  684.                 $this->_storeNameLong(
  685.                     $i// sheet index
  686.                     0x07// NAME type
  687.                     $rowmin,
  688.                     $rowmax,
  689.                     $colmin,
  690.                     $colmax
  691.                     );
  692.  
  693.             // (exclusive) either repeatColumns or repeatRows
  694.             else if ($this->_phpExcel->getSheet($i)->getPageSetup()->isColumnsToRepeatAtLeftSet(|| $this->_phpExcel->getSheet($i)->getPageSetup()->isRowsToRepeatAtTopSet()) {
  695.  
  696.                 // Columns to repeat
  697.                 if ($this->_phpExcel->getSheet($i)->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
  698.                     $repeat $this->_phpExcel->getSheet($i)->getPageSetup()->getColumnsToRepeatAtLeft();
  699.                     $colmin PHPExcel_Cell::columnIndexFromString($repeat[0]1;
  700.                     $colmax PHPExcel_Cell::columnIndexFromString($repeat[1]1;
  701.                 else {
  702.                     $colmin 0;
  703.                     $colmax 255;
  704.                 }
  705.  
  706.                 // Rows to repeat
  707.                 if ($this->_phpExcel->getSheet($i)->getPageSetup()->isRowsToRepeatAtTopSet()) {
  708.                     $repeat $this->_phpExcel->getSheet($i)->getPageSetup()->getRowsToRepeatAtTop();
  709.                     $rowmin $repeat[01;
  710.                     $rowmax $repeat[11;
  711.                 else {
  712.                     $rowmin 0;
  713.                     $rowmax 16383;
  714.                 }
  715.  
  716.                 $this->_storeNameShort(
  717.                     $i// sheet index
  718.                     0x07// NAME type
  719.                     $rowmin,
  720.                     $rowmax,
  721.                     $colmin,
  722.                     $colmax
  723.                     );
  724.             }
  725.         }
  726.     }
  727.  
  728.  
  729.     /**
  730.      * Writes all the DEFINEDNAME records (BIFF8).
  731.      * So far this is only used for repeating rows/columns (print titles) and print areas
  732.      */
  733.     private function _writeAllDefinedNamesBiff8()
  734.     {
  735.         $chunk '';
  736.  
  737.         // Named ranges
  738.         if (count($this->_phpExcel->getNamedRanges()) 0{
  739.             // Loop named ranges
  740.             $namedRanges $this->_phpExcel->getNamedRanges();
  741.             foreach ($namedRanges as $namedRange{
  742.             
  743.                 // Create absolute coordinate
  744.                 $range PHPExcel_Cell::splitRange($namedRange->getRange());
  745.                 for ($i 0$i count($range)$i++{
  746.                     $range[$i][0'\'' str_replace("'""''"$namedRange->getWorksheet()->getTitle()) '\'!' PHPExcel_Cell::absoluteCoordinate($range[$i][0]);
  747.                     if (isset($range[$i][1])) {
  748.                         $range[$i][1PHPExcel_Cell::absoluteCoordinate($range[$i][1]);
  749.                     }
  750.                 }
  751.                 $range PHPExcel_Cell::buildRange($range)// e.g. Sheet1!$A$1:$B$2
  752.  
  753.                 // parse formula
  754.                 try {
  755.                     $error $this->_parser->parse($range);
  756.                     $formulaData $this->_parser->toReversePolish();
  757.                     $chunk .= $this->writeData($this->_writeDefinedNameBiff8($namedRange->getName()$formulaData0false));
  758.  
  759.                 catch(Exception $e{
  760.                     // do nothing
  761.                 }
  762.             }
  763.         }
  764.  
  765.         // total number of sheets
  766.         $total_worksheets count($this->_phpExcel->getAllSheets());
  767.  
  768.         // write the print titles (repeating rows, columns), if any
  769.         for ($i 0$i $total_worksheets++$i{
  770.  
  771.             // simultaneous repeatColumns repeatRows
  772.             if ($this->_phpExcel->getSheet($i)->getPageSetup()->isColumnsToRepeatAtLeftSet(&& $this->_phpExcel->getSheet($i)->getPageSetup()->isRowsToRepeatAtTopSet()) {
  773.                 $repeat $this->_phpExcel->getSheet($i)->getPageSetup()->getColumnsToRepeatAtLeft();
  774.                 $colmin PHPExcel_Cell::columnIndexFromString($repeat[0]1;
  775.                 $colmax PHPExcel_Cell::columnIndexFromString($repeat[1]1;
  776.  
  777.                 $repeat $this->_phpExcel->getSheet($i)->getPageSetup()->getRowsToRepeatAtTop();
  778.                 $rowmin $repeat[01;
  779.                 $rowmax $repeat[11;
  780.  
  781.                 // construct formula data manually
  782.                 $formulaData pack('Cv'0x290x17)// tMemFunc
  783.                 $formulaData .= pack('Cvvvvv'0x3B$i065535$colmin$colmax)// tArea3d
  784.                 $formulaData .= pack('Cvvvvv'0x3B$i$rowmin$rowmax0255)// tArea3d
  785.                 $formulaData .= pack('C'0x10)// tList
  786.  
  787.                 // store the DEFINEDNAME record
  788.                 $chunk .= $this->writeData($this->_writeDefinedNameBiff8(pack('C'0x07)$formulaData$i 1true));
  789.  
  790.             // (exclusive) either repeatColumns or repeatRows
  791.             else if ($this->_phpExcel->getSheet($i)->getPageSetup()->isColumnsToRepeatAtLeftSet(|| $this->_phpExcel->getSheet($i)->getPageSetup()->isRowsToRepeatAtTopSet()) {
  792.  
  793.                 // Columns to repeat
  794.                 if ($this->_phpExcel->getSheet($i)->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
  795.                     $repeat $this->_phpExcel->getSheet($i)->getPageSetup()->getColumnsToRepeatAtLeft();
  796.                     $colmin PHPExcel_Cell::columnIndexFromString($repeat[0]1;
  797.                     $colmax PHPExcel_Cell::columnIndexFromString($repeat[1]1;
  798.                 else {
  799.                     $colmin 0;
  800.                     $colmax 255;
  801.                 }
  802.                 // Rows to repeat
  803.                 if ($this->_phpExcel->getSheet($i)->getPageSetup()->isRowsToRepeatAtTopSet()) {
  804.                     $repeat $this->_phpExcel->getSheet($i)->getPageSetup()->getRowsToRepeatAtTop();
  805.                     $rowmin $repeat[01;
  806.                     $rowmax $repeat[11;
  807.                 else {
  808.                     $rowmin 0;
  809.                     $rowmax 65535;
  810.                 }
  811.  
  812.                 // construct formula data manually because parser does not recognize absolute 3d cell references
  813.                 $formulaData pack('Cvvvvv'0x3B$i$rowmin$rowmax$colmin$colmax);
  814.  
  815.                 // store the DEFINEDNAME record
  816.                 $chunk .= $this->writeData($this->_writeDefinedNameBiff8(pack('C'0x07)$formulaData$i 1true));
  817.             }
  818.         }
  819.  
  820.         // write the print areas, if any
  821.         for ($i 0$i $total_worksheets++$i{
  822.             if ($this->_phpExcel->getSheet($i)->getPageSetup()->isPrintAreaSet()) {
  823.                 // Print area, e.g. A3:J6,H1:X20
  824.                 $printArea PHPExcel_Cell::splitRange($this->_phpExcel->getSheet($i)->getPageSetup()->getPrintArea());
  825.                 $countPrintArea count($printArea);
  826.  
  827.                 $formulaData '';
  828.                 for ($j 0$j $countPrintArea++$j{
  829.                     $printAreaRect $printArea[$j]// e.g. A3:J6
  830.                     $printAreaRect[0PHPExcel_Cell::coordinateFromString($printAreaRect[0]);
  831.                     $printAreaRect[1PHPExcel_Cell::coordinateFromString($printAreaRect[1]);
  832.                 
  833.                     $print_rowmin $printAreaRect[0][11;
  834.                     $print_rowmax $printAreaRect[1][11;
  835.                     $print_colmin PHPExcel_Cell::columnIndexFromString($printAreaRect[0][0]1;
  836.                     $print_colmax PHPExcel_Cell::columnIndexFromString($printAreaRect[1][0]1;
  837.  
  838.                     // construct formula data manually because parser does not recognize absolute 3d cell references
  839.                     $formulaData .= pack('Cvvvvv'0x3B$i$print_rowmin$print_rowmax$print_colmin$print_colmax);
  840.                     
  841.                     if ($j 0{
  842.                         $formulaData .= pack('C'0x10)// list operator token ','
  843.                     }
  844.                 }
  845.  
  846.                 // store the DEFINEDNAME record
  847.                 $chunk .= $this->writeData($this->_writeDefinedNameBiff8(pack('C'0x06)$formulaData$i 1true));
  848.             }
  849.         }
  850.  
  851.         return $chunk;
  852.     }
  853.  
  854.     /**
  855.      * Write a DEFINEDNAME record for BIFF8 using explicit binary formula data
  856.      *
  857.      * @param    string        $name            The name in UTF-8
  858.      * @param    string        $formulaData    The binary formula data
  859.      * @param    string        $sheetIndex        1-based sheet index the defined name applies to. 0 = global
  860.      * @param    boolean        $isBuiltIn        Built-in name?
  861.      * @return    string    Complete binary record data
  862.      */
  863.     private function _writeDefinedNameBiff8($name$formulaData$sheetIndex 0$isBuiltIn false)
  864.     {
  865.         $record 0x0018;
  866.  
  867.         // option flags
  868.         $options $isBuiltIn 0x20 0x00;
  869.  
  870.         // length of the name, character count
  871.         $nlen PHPExcel_Shared_String::CountCharacters($name);
  872.  
  873.         // name with stripped length field
  874.         $name substr(PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($name)2);
  875.  
  876.         // size of the formula (in bytes)
  877.         $sz strlen($formulaData);
  878.  
  879.         // combine the parts
  880.         $data pack('vCCvvvCCCC'$options0$nlen$sz0$sheetIndex0000)
  881.             . $name $formulaData;
  882.         $length strlen($data);
  883.  
  884.         $header pack('vv'$record$length);
  885.  
  886.         return $header $data;
  887.     }
  888.  
  889.     /**
  890.      * Stores the CODEPAGE biff record.
  891.      *
  892.      * @access private
  893.      */
  894.     function _storeCodepage()
  895.     {
  896.         $record          0x0042;             // Record identifier
  897.         $length          0x0002;             // Number of bytes to follow
  898.         $cv              $this->_codepage;   // The code page
  899.  
  900.         $header          pack('vv'$record$length);
  901.         $data            pack('v',  $cv);
  902.  
  903.         $this->_append($header $data);
  904.     }
  905.  
  906.     /**
  907.      * Write Excel BIFF WINDOW1 record.
  908.      *
  909.      * @access private
  910.      */
  911.     function _storeWindow1()
  912.     {
  913.         $record    0x003D;                 // Record identifier
  914.         $length    0x0012;                 // Number of bytes to follow
  915.  
  916.         $xWn       0x0000;                 // Horizontal position of window
  917.         $yWn       0x0000;                 // Vertical position of window
  918.         $dxWn      0x25BC;                 // Width of window
  919.         $dyWn      0x1572;                 // Height of window
  920.  
  921.         $grbit     0x0038;                 // Option flags
  922.         
  923.         // not supported by PHPExcel, so there is only one selected sheet, the active
  924.         $ctabsel   1;       // Number of workbook tabs selected
  925.         
  926.         $wTabRatio 0x0258;                 // Tab to scrollbar ratio
  927.  
  928.         // not supported by PHPExcel, set to 0
  929.         $itabFirst 0;     // 1st displayed worksheet
  930.         $itabCur   $this->_phpExcel->getActiveSheetIndex();    // Active worksheet
  931.  
  932.         $header    pack("vv",        $record$length);
  933.         $data      pack("vvvvvvvvv"$xWn$yWn$dxWn$dyWn,
  934.                                        $grbit,
  935.                                        $itabCur$itabFirst,
  936.                                        $ctabsel$wTabRatio);
  937.         $this->_append($header $data);
  938.     }
  939.  
  940.     /**
  941.      * Writes Excel BIFF BOUNDSHEET record.
  942.      *
  943.      * @param PHPExcel_Worksheet  $sheet Worksheet name
  944.      * @param integer $offset    Location of worksheet BOF
  945.      * @access private
  946.      */
  947.     function _storeBoundsheet($sheet$offset)
  948.     {
  949.         $sheetname $sheet->getTitle();
  950.         $record    0x0085;                    // Record identifier
  951.  
  952.         // sheet state
  953.         switch ($sheet->getSheetState()) {
  954.             case PHPExcel_Worksheet::SHEETSTATE_VISIBLE:    $ss 0x00break;
  955.             case PHPExcel_Worksheet::SHEETSTATE_HIDDEN:        $ss 0x01break;
  956.             case PHPExcel_Worksheet::SHEETSTATE_VERYHIDDEN:    $ss 0x02break;
  957.             default$ss 0x00break;
  958.         }
  959.  
  960.         // sheet type
  961.         $st 0x00;
  962.  
  963.         $grbit     0x0000;                    // Visibility and sheet type
  964.  
  965.         if ($this->_BIFF_version == 0x0600{
  966.             $data      pack("VCC"$offset$ss$st);
  967.             $data .= PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($sheetname);
  968.         else {
  969.             $cch       strlen($sheetname);        // Length of sheet name
  970.             $data      pack("VCCC"$offset$ss$st$cch);
  971.             $data .= $sheetname;
  972.         }
  973.  
  974.         $length strlen($data);
  975.         $header pack("vv",  $record$length);
  976.         $this->_append($header $data);
  977.     }
  978.  
  979.     /**
  980.      * Write Internal SUPBOOK record
  981.      */
  982.     private function _writeSupbookInternal()
  983.     {
  984.         $record    0x01AE;   // Record identifier
  985.         $length    0x0004;   // Bytes to follow
  986.  
  987.         $header    pack("vv"$record$length);
  988.         $data      pack("vv"count($this->_phpExcel->getAllSheets())0x0401);
  989.         return $this->writeData($header $data);
  990.     }
  991.  
  992.     /**
  993.      * Writes the Excel BIFF EXTERNSHEET record. These references are used by
  994.      * formulas.
  995.      *
  996.      */
  997.     private function _writeExternsheetBiff8()
  998.     {
  999.         $total_references count($this->_parser->_references);
  1000.         $record   0x0017;                     // Record identifier
  1001.         $length   $total_references;  // Number of bytes to follow
  1002.  
  1003.         $supbook_index 0;           // FIXME: only using internal SUPBOOK record
  1004.         $header           pack("vv",  $record$length);
  1005.         $data             pack('v'$total_references);
  1006.         for ($i 0$i $total_references++$i{
  1007.             $data .= $this->_parser->_references[$i];
  1008.         }
  1009.         return $this->writeData($header $data);
  1010.     }
  1011.  
  1012.     /**
  1013.      * Write Excel BIFF STYLE records.
  1014.      *
  1015.      * @access private
  1016.      */
  1017.     function _storeStyle()
  1018.     {
  1019.         $record    0x0293;   // Record identifier
  1020.         $length    0x0004;   // Bytes to follow
  1021.  
  1022.         $ixfe      0x8000;  // Index to cell style XF
  1023.         $BuiltIn   0x00;     // Built-in style
  1024.         $iLevel    0xff;     // Outline style level
  1025.  
  1026.         $header    pack("vv",  $record$length);
  1027.         $data      pack("vCC"$ixfe$BuiltIn$iLevel);
  1028.         $this->_append($header $data);
  1029.     }
  1030.  
  1031.  
  1032.     /**
  1033.      * Writes Excel FORMAT record for non "built-in" numerical formats.
  1034.      *
  1035.      * @param string  $format Custom format string
  1036.      * @param integer $ifmt   Format index code
  1037.      * @access private
  1038.      */
  1039.     function _storeNumFormat($format$ifmt)
  1040.     {
  1041.         $record    0x041E;                      // Record identifier
  1042.  
  1043.         if ($this->_BIFF_version == 0x0600{
  1044.             $numberFormatString PHPExcel_Shared_String::UTF8toBIFF8UnicodeLong($format);
  1045.             $length    strlen($numberFormatString);      // Number of bytes to follow
  1046.         elseif ($this->_BIFF_version == 0x0500{
  1047.             $length    strlen($format);      // Number of bytes to follow
  1048.         }
  1049.  
  1050.  
  1051.         $header    pack("vv"$record$length);
  1052.         if ($this->_BIFF_version == 0x0600{
  1053.             $data      pack("v"$ifmt.  $numberFormatString;
  1054.             $this->_append($header $data);
  1055.         elseif ($this->_BIFF_version == 0x0500{
  1056.             $cch       strlen($format);             // Length of format string
  1057.             $data      pack("vC"$ifmt$cch);
  1058.             $this->_append($header $data $format);
  1059.         }
  1060.     }
  1061.  
  1062.     /**
  1063.      * Write DATEMODE record to indicate the date system in use (1904 or 1900).
  1064.      *
  1065.      * @access private
  1066.      */
  1067.     function _storeDatemode()
  1068.     {
  1069.         $record    0x0022;         // Record identifier
  1070.         $length    0x0002;         // Bytes to follow
  1071.  
  1072.         $f1904     (PHPExcel_Shared_Date::getExcelCalendar(== PHPExcel_Shared_Date::CALENDAR_MAC_1904?
  1073.             0;   // Flag for 1904 date system
  1074.  
  1075.         $header    pack("vv"$record$length);
  1076.         $data      pack("v"$f1904);
  1077.         $this->_append($header $data);
  1078.     }
  1079.  
  1080.  
  1081.     /**
  1082.      * Write BIFF record EXTERNCOUNT to indicate the number of external sheet
  1083.      * references in the workbook.
  1084.      *
  1085.      * Excel only stores references to external sheets that are used in NAME.
  1086.      * The workbook NAME record is required to define the print area and the repeat
  1087.      * rows and columns.
  1088.      *
  1089.      * A similar method is used in Worksheet.php for a slightly different purpose.
  1090.      *
  1091.      * @param integer $cxals Number of external references
  1092.      * @access private
  1093.      */
  1094.     function _storeExterncount($cxals)
  1095.     {
  1096.         $record   0x0016;          // Record identifier
  1097.         $length   0x0002;          // Number of bytes to follow
  1098.  
  1099.         $header   pack("vv"$record$length);
  1100.         $data     pack("v",  $cxals);
  1101.         $this->_append($header $data);
  1102.     }
  1103.  
  1104.  
  1105.     /**
  1106.      * Writes the Excel BIFF EXTERNSHEET record. These references are used by
  1107.      * formulas. NAME record is required to define the print area and the repeat
  1108.      * rows and columns.
  1109.      *
  1110.      * A similar method is used in Worksheet.php for a slightly different purpose.
  1111.      *
  1112.      * @param string $sheetname Worksheet name
  1113.      * @access private
  1114.      */
  1115.     function _storeExternsheet($sheetname)
  1116.     {
  1117.         $record      0x0017;                     // Record identifier
  1118.         $length      0x02 strlen($sheetname);  // Number of bytes to follow
  1119.  
  1120.         $cch         strlen($sheetname);         // Length of sheet name
  1121.         $rgch        0x03;                       // Filename encoding
  1122.  
  1123.         $header      pack("vv",  $record$length);
  1124.         $data        pack("CC"$cch$rgch);
  1125.         $this->_append($header $data $sheetname);
  1126.     }
  1127.  
  1128.  
  1129.     /**
  1130.      * Store the NAME record in the short format that is used for storing the print
  1131.      * area, repeat rows only and repeat columns only.
  1132.      *
  1133.      * @param integer $index  Sheet index
  1134.      * @param integer $type   Built-in name type
  1135.      * @param integer $rowmin Start row
  1136.      * @param integer $rowmax End row
  1137.      * @param integer $colmin Start colum
  1138.      * @param integer $colmax End column
  1139.      * @access private
  1140.      */
  1141.     function _storeNameShort($index$type$rowmin$rowmax$colmin$colmax)
  1142.     {
  1143.         $record          0x0018;       // Record identifier
  1144.         $length          0x0024;       // Number of bytes to follow
  1145.  
  1146.         $grbit           0x0020;       // Option flags
  1147.         $chKey           0x00;         // Keyboard shortcut
  1148.         $cch             0x01;         // Length of text name
  1149.         $cce             0x0015;       // Length of text definition
  1150.         $ixals           $index 1;   // Sheet index
  1151.         $itab            $ixals;       // Equal to ixals
  1152.         $cchCustMenu     0x00;         // Length of cust menu text
  1153.         $cchDescription  0x00;         // Length of description text
  1154.         $cchHelptopic    0x00;         // Length of help topic text
  1155.         $cchStatustext   0x00;         // Length of status bar text
  1156.         $rgch            $type;        // Built-in name type
  1157.  
  1158.         $unknown03       0x3b;
  1159.         $unknown04       0xffff-$index;
  1160.         $unknown05       0x0000;
  1161.         $unknown06       0x0000;
  1162.         $unknown07       0x1087;
  1163.         $unknown08       0x8005;
  1164.  
  1165.         $header             pack("vv"$record$length);
  1166.         $data               pack("v"$grbit);
  1167.         $data              .= pack("C"$chKey);
  1168.         $data              .= pack("C"$cch);
  1169.         $data              .= pack("v"$cce);
  1170.         $data              .= pack("v"$ixals);
  1171.         $data              .= pack("v"$itab);
  1172.         $data              .= pack("C"$cchCustMenu);
  1173.         $data              .= pack("C"$cchDescription);
  1174.         $data              .= pack("C"$cchHelptopic);
  1175.         $data              .= pack("C"$cchStatustext);
  1176.         $data              .= pack("C"$rgch);
  1177.         $data              .= pack("C"$unknown03);
  1178.         $data              .= pack("v"$unknown04);
  1179.         $data              .= pack("v"$unknown05);
  1180.         $data              .= pack("v"$unknown06);
  1181.         $data              .= pack("v"$unknown07);
  1182.         $data              .= pack("v"$unknown08);
  1183.         $data              .= pack("v"$index);
  1184.         $data              .= pack("v"$index);
  1185.         $data              .= pack("v"$rowmin);
  1186.         $data              .= pack("v"$rowmax);
  1187.         $data              .= pack("C"$colmin);
  1188.         $data              .= pack("C"$colmax);
  1189.         $this->_append($header $data);
  1190.     }
  1191.  
  1192.  
  1193.     /**
  1194.      * Store the NAME record in the long format that is used for storing the repeat
  1195.      * rows and columns when both are specified. This shares a lot of code with
  1196.      * _storeNameShort() but we use a separate method to keep the code clean.
  1197.      * Code abstraction for reuse can be carried too far, and I should know. ;-)
  1198.      *
  1199.      * @param integer $index Sheet index
  1200.      * @param integer $type  Built-in name type
  1201.      * @param integer $rowmin Start row
  1202.      * @param integer $rowmax End row
  1203.      * @param integer $colmin Start colum
  1204.      * @param integer $colmax End column
  1205.      * @access private
  1206.      */
  1207.     function _storeNameLong($index$type$rowmin$rowmax$colmin$colmax)
  1208.     {
  1209.         $record          0x0018;       // Record identifier
  1210.         $length          0x003d;       // Number of bytes to follow
  1211.         $grbit           0x0020;       // Option flags
  1212.         $chKey           0x00;         // Keyboard shortcut
  1213.         $cch             0x01;         // Length of text name
  1214.         $cce             0x002e;       // Length of text definition
  1215.         $ixals           $index 1;   // Sheet index
  1216.         $itab            $ixals;       // Equal to ixals
  1217.         $cchCustMenu     0x00;         // Length of cust menu text
  1218.         $cchDescription  0x00;         // Length of description text
  1219.         $cchHelptopic    0x00;         // Length of help topic text
  1220.         $cchStatustext   0x00;         // Length of status bar text
  1221.         $rgch            $type;        // Built-in name type
  1222.  
  1223.         $unknown01       0x29;
  1224.         $unknown02       0x002b;
  1225.         $unknown03       0x3b;
  1226.         $unknown04       0xffff-$index;
  1227.         $unknown05       0x0000;
  1228.         $unknown06       0x0000;
  1229.         $unknown07       0x1087;
  1230.         $unknown08       0x8008;
  1231.  
  1232.         $header             pack("vv",  $record$length);
  1233.         $data               pack("v"$grbit);
  1234.         $data              .= pack("C"$chKey);
  1235.         $data              .= pack("C"$cch);
  1236.         $data              .= pack("v"$cce);
  1237.         $data              .= pack("v"$ixals);
  1238.         $data              .= pack("v"$itab);
  1239.         $data              .= pack("C"$cchCustMenu);
  1240.         $data              .= pack("C"$cchDescription);
  1241.         $data              .= pack("C"$cchHelptopic);
  1242.         $data              .= pack("C"$cchStatustext);
  1243.         $data              .= pack("C"$rgch);
  1244.         $data              .= pack("C"$unknown01);
  1245.         $data              .= pack("v"$unknown02);
  1246.         // Column definition
  1247.         $data              .= pack("C"$unknown03);
  1248.         $data              .= pack("v"$unknown04);
  1249.         $data              .= pack("v"$unknown05);
  1250.         $data              .= pack("v"$unknown06);
  1251.         $data              .= pack("v"$unknown07);
  1252.         $data              .= pack("v"$unknown08);
  1253.         $data              .= pack("v"$index);
  1254.         $data              .= pack("v"$index);
  1255.         $data              .= pack("v"0x0000);
  1256.         $data              .= pack("v"0x3fff);
  1257.         $data              .= pack("C"$colmin);
  1258.         $data              .= pack("C"$colmax);
  1259.         // Row definition
  1260.         $data              .= pack("C"$unknown03);
  1261.         $data              .= pack("v"$unknown04);
  1262.         $data              .= pack("v"$unknown05);
  1263.         $data              .= pack("v"$unknown06);
  1264.         $data              .= pack("v"$unknown07);
  1265.         $data              .= pack("v"$unknown08);
  1266.         $data              .= pack("v"$index);
  1267.         $data              .= pack("v"$index);
  1268.         $data              .= pack("v"$rowmin);
  1269.         $data              .= pack("v"$rowmax);
  1270.         $data              .= pack("C"0x00);
  1271.         $data              .= pack("C"0xff);
  1272.         // End of data
  1273.         $data              .= pack("C"0x10);
  1274.         $this->_append($header $data);
  1275.     }
  1276.  
  1277.     /**
  1278.      * Stores the COUNTRY record for localization
  1279.      *
  1280.      * @return string 
  1281.      */
  1282.     private function _writeCountry()
  1283.     {
  1284.         $record          0x008C;    // Record identifier
  1285.         $length          4;         // Number of bytes to follow
  1286.  
  1287.         $header pack('vv',  $record$length);
  1288.         /* using the same country code always for simplicity */
  1289.         $data pack('vv'$this->_country_code$this->_country_code);
  1290.         //$this->_append($header . $data);
  1291.         return $this->writeData($header $data);
  1292.     }
  1293.  
  1294.     /**
  1295.      * Write the RECALCID record
  1296.      *
  1297.      * @return string 
  1298.      */
  1299.     private function _writeRecalcId()
  1300.     {
  1301.         $record 0x01C1;    // Record identifier
  1302.         $length 8;         // Number of bytes to follow
  1303.  
  1304.         $header pack('vv',  $record$length);
  1305.  
  1306.         // by inspection of real Excel files, MS Office Excel 2007 writes this
  1307.         $data pack('VV'0x000001C10x00001E667);
  1308.  
  1309.         return $this->writeData($header $data);
  1310.     }
  1311.  
  1312.     /**
  1313.      * Stores the PALETTE biff record.
  1314.      *
  1315.      * @access private
  1316.      */
  1317.     function _storePalette()
  1318.     {
  1319.         $aref            $this->_palette;
  1320.  
  1321.         $record          0x0092;                 // Record identifier
  1322.         $length          count($aref);   // Number of bytes to follow
  1323.         $ccv             =         count($aref);   // Number of RGB values to follow
  1324.         $data '';                                // The RGB data
  1325.  
  1326.         // Pack the RGB data
  1327.         foreach ($aref as $color{
  1328.             foreach ($color as $byte{
  1329.                 $data .= pack("C",$byte);
  1330.             }
  1331.         }
  1332.  
  1333.         $header pack("vvv",  $record$length$ccv);
  1334.         $this->_append($header $data);
  1335.     }
  1336.  
  1337.     /**
  1338.      * Handling of the SST continue blocks is complicated by the need to include an
  1339.      * additional continuation byte depending on whether the string is split between
  1340.      * blocks or whether it starts at the beginning of the block. (There are also
  1341.      * additional complications that will arise later when/if Rich Strings are
  1342.      * supported).
  1343.      *
  1344.      * The Excel documentation says that the SST record should be followed by an
  1345.      * EXTSST record. The EXTSST record is a hash table that is used to optimise
  1346.      * access to SST. However, despite the documentation it doesn't seem to be
  1347.      * required so we will ignore it.
  1348.      *
  1349.      * @return string Binary data
  1350.      */
  1351.     private function _writeSharedStringsTable()
  1352.     {
  1353.         // maximum size of record data (excluding record header)
  1354.         $continue_limit 8224;
  1355.  
  1356.         // initialize array of record data blocks
  1357.         $recordDatas array();
  1358.  
  1359.         // start SST record data block with total number of strings, total number of unique strings
  1360.         $recordData pack("VV"$this->_str_total$this->_str_unique);
  1361.  
  1362.         // loop through all (unique) strings in shared strings table
  1363.         foreach (array_keys($this->_str_tableas $string{
  1364.  
  1365.             // here $string is a BIFF8 encoded string
  1366.  
  1367.             // length = character count
  1368.             $headerinfo unpack("vlength/Cencoding"$string);
  1369.  
  1370.             // currently, this is always 1 = uncompressed
  1371.             $encoding $headerinfo["encoding"];
  1372.  
  1373.             // initialize finished writing current $string
  1374.             $finished false;
  1375.  
  1376.             while ($finished === false{
  1377.  
  1378.                 // normally, there will be only one cycle, but if string cannot immediately be written as is
  1379.                 // there will be need for more than one cylcle, if string longer than one record data block, there
  1380.                 // may be need for even more cycles
  1381.  
  1382.                 if (strlen($recordDatastrlen($string$continue_limit{
  1383.                     // then we can write the string (or remainder of string) without any problems
  1384.                     $recordData .= $string;
  1385.  
  1386.                     // we are finished writing this string
  1387.                     $finished true;
  1388.  
  1389.                 else if (strlen($recordDatastrlen($string== $continue_limit{
  1390.                     // then we can also write the string (or remainder of string)
  1391.                     $recordData .= $string;
  1392.  
  1393.                     // but we close the record data block, and initialize a new one
  1394.                     $recordDatas[$recordData;
  1395.                     $recordData '';
  1396.  
  1397.                     // we are finished writing this string
  1398.                     $finished true;
  1399.  
  1400.                 else {
  1401.                     // special treatment writing the string (or remainder of the string)
  1402.                     // If the string is very long it may need to be written in more than one CONTINUE record.
  1403.  
  1404.                     // check how many bytes more there is room for in the current record
  1405.                     $space_remaining $continue_limit strlen($recordData);
  1406.  
  1407.                     // minimum space needed
  1408.                     // uncompressed: 2 byte string length length field + 1 byte option flags + 2 byte character
  1409.                     // compressed:   2 byte string length length field + 1 byte option flags + 1 byte character
  1410.                     $min_space_needed ($encoding == 14;
  1411.  
  1412.                     // We have two cases
  1413.                     // 1. space remaining is less than minimum space needed
  1414.                     //        here we must waste the space remaining and move to next record data block
  1415.                     // 2. space remaining is greater than or equal to minimum space needed
  1416.                     //        here we write as much as we can in the current block, then move to next record data block
  1417.  
  1418.                     // 1. space remaining is less than minimum space needed
  1419.                     if ($space_remaining $min_space_needed{
  1420.                         // we close the block, store the block data
  1421.                         $recordDatas[$recordData;
  1422.  
  1423.                         // and start new record data block where we start writing the string
  1424.                         $recordData '';
  1425.  
  1426.                     // 2. space remaining is greater than or equal to minimum space needed
  1427.                     else {
  1428.                         // initialize effective remaining space, for Unicode strings this may need to be reduced by 1, see below
  1429.                         $effective_space_remaining $space_remaining;
  1430.  
  1431.                         // for uncompressed strings, sometimes effective space remaining is reduced by 1
  1432.                         if $encoding == && (strlen($string$space_remaining== {
  1433.                             --$effective_space_remaining;
  1434.                         }
  1435.  
  1436.                         // one block fininshed, store the block data
  1437.                         $recordData .= substr($string0$effective_space_remaining);
  1438.  
  1439.                         $string substr($string$effective_space_remaining)// for next cycle in while loop
  1440.                         $recordDatas[$recordData;
  1441.  
  1442.                         // start new record data block with the repeated option flags
  1443.                         $recordData pack('C'$encoding);
  1444.                     }
  1445.                 }
  1446.             }
  1447.         }
  1448.  
  1449.         // Store the last record data block unless it is empty
  1450.         // if there was no need for any continue records, this will be the for SST record data block itself
  1451.         if (strlen($recordData0{
  1452.             $recordDatas[$recordData;
  1453.         }
  1454.  
  1455.         // combine into one chunk with all the blocks SST, CONTINUE,...
  1456.         $chunk '';
  1457.         foreach ($recordDatas as $i => $recordData{
  1458.             // first block should have the SST record header, remaing should have CONTINUE header
  1459.             $record ($i == 00x00FC 0x003C;
  1460.  
  1461.             $header pack("vv"$recordstrlen($recordData));
  1462.             $data $header $recordData;
  1463.  
  1464.             $chunk .= $this->writeData($data);
  1465.         }
  1466.  
  1467.         return $chunk;
  1468.     }
  1469.  
  1470.     /**
  1471.      * Writes the MSODRAWINGGROUP record if needed. Possibly split using CONTINUE records.
  1472.      */
  1473.     private function _writeMsoDrawingGroup()
  1474.     {
  1475.         // any drawings in this workbook?
  1476.         $found false;
  1477.         foreach ($this->_phpExcel->getAllSheets(as $sheet{
  1478.             if (count($sheet->getDrawingCollection()) 0{
  1479.                 $found true;
  1480.             }
  1481.         }
  1482.  
  1483.         // if there are drawings, then we need to write MSODRAWINGGROUP record
  1484.         if ($found{
  1485.  
  1486.             // create intermediate Escher object
  1487.             $escher new PHPExcel_Shared_Escher();
  1488.  
  1489.             // dggContainer
  1490.             $dggContainer new PHPExcel_Shared_Escher_DggContainer();
  1491.             $escher->setDggContainer($dggContainer);
  1492.  
  1493.             // this loop is for determining maximum shape identifier of all drawing
  1494.             $spIdMax 0;
  1495.             $totalCountShapes 0;
  1496.             $countDrawings 0;
  1497.  
  1498.             foreach ($this->_phpExcel->getAllsheets(as $sheet{
  1499.                 $sheetCountShapes 0// count number of shapes (minus group shape), in sheet
  1500.  
  1501.                 if (count($sheet->getDrawingCollection()) 0{
  1502.                     ++$countDrawings;
  1503.  
  1504.                     foreach ($sheet->getDrawingCollection(as $drawing{
  1505.                         ++$sheetCountShapes;
  1506.                         ++$totalCountShapes;
  1507.  
  1508.                         $spId $sheetCountShapes
  1509.                             | ($this->_phpExcel->getIndex($sheet1<< 10;
  1510.                         $spIdMax max($spId$spIdMax);
  1511.                     }
  1512.                 }
  1513.             }
  1514.  
  1515.             $dggContainer->setSpIdMax($spIdMax 1);
  1516.             $dggContainer->setCDgSaved($countDrawings);
  1517.             $dggContainer->setCSpSaved($totalCountShapes $countDrawings)// total number of shapes incl. one group shapes per drawing
  1518.  
  1519.             // bstoreContainer
  1520.             $bstoreContainer new PHPExcel_Shared_Escher_DggContainer_BstoreContainer();
  1521.             $dggContainer->setBstoreContainer($bstoreContainer);
  1522.  
  1523.             // the BSE's (all the images)
  1524.             foreach ($this->_phpExcel->getAllsheets(as $sheet{
  1525.                 foreach ($sheet->getDrawingCollection(as $drawing{
  1526.                     if ($drawing instanceof PHPExcel_Worksheet_Drawing{
  1527.  
  1528.                         $filename $drawing->getPath();
  1529.  
  1530.                         list($imagesx$imagesy$imageFormatgetimagesize($filename);
  1531.  
  1532.                         switch ($imageFormat{
  1533.  
  1534.                         case 1// GIF, not supported by BIFF8, we convert to PNG
  1535.                             $blipType PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
  1536.                             $imageResource imagecreatefromgif($filename);
  1537.                             ob_start();
  1538.                             imagepng($imageResource);
  1539.                             $blipData ob_get_contents();
  1540.                             ob_end_clean();
  1541.                             break;
  1542.  
  1543.                         case 2// JPEG
  1544.                             $blipType PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG;
  1545.                             $blipData file_get_contents($filename);
  1546.                             break;
  1547.  
  1548.                         case 3// PNG
  1549.                             $blipType PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
  1550.                             $blipData file_get_contents($filename);
  1551.                             break;
  1552.  
  1553.                         defaultcontinue 2;
  1554.  
  1555.                         }
  1556.  
  1557.                         $blip new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
  1558.                         $blip->setData($blipData);
  1559.  
  1560.                         $BSE new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
  1561.                         $BSE->setBlipType($blipType);
  1562.                         $BSE->setBlip($blip);
  1563.  
  1564.                         $bstoreContainer->addBSE($BSE);
  1565.  
  1566.                     else if ($drawing instanceof PHPExcel_Worksheet_MemoryDrawing{
  1567.  
  1568.                         switch ($drawing->getRenderingFunction()) {
  1569.  
  1570.                         case PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG:
  1571.                             $blipType PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG;
  1572.                             $renderingFunction 'imagejpeg';
  1573.                             break;
  1574.  
  1575.                         case PHPExcel_Worksheet_MemoryDrawing::RENDERING_GIF:
  1576.                         case PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG:
  1577.                         case PHPExcel_Worksheet_MemoryDrawing::RENDERING_DEFAULT:
  1578.                             $blipType PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
  1579.                             $renderingFunction 'imagepng';
  1580.                             break;
  1581.  
  1582.                         }
  1583.  
  1584.                         ob_start();
  1585.                         call_user_func($renderingFunction$drawing->getImageResource());
  1586.                         $blipData ob_get_contents();
  1587.                         ob_end_clean();
  1588.  
  1589.                         $blip new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
  1590.                         $blip->setData($blipData);
  1591.  
  1592.                         $BSE new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
  1593.                         $BSE->setBlipType($blipType);
  1594.                         $BSE->setBlip($blip);
  1595.  
  1596.                         $bstoreContainer->addBSE($BSE);
  1597.                     }
  1598.                 }
  1599.             }
  1600.  
  1601.             // write the Escher stream from the intermediate Escher object
  1602.             $writer new PHPExcel_Writer_Excel5_Escher($escher);
  1603.             $data $writer->close();
  1604.  
  1605.             $record 0x00EB;
  1606.             $length strlen($data);
  1607.             $header pack("vv",  $record$length);
  1608.  
  1609.             return $this->writeData($header $data);
  1610.         }
  1611.     }
  1612.  
  1613. }

Documentation generated on Mon, 11 Jan 2010 08:18:38 +0100 by phpDocumentor 1.4.1