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

Documentation generated on Mon, 10 Aug 2009 08:11:34 +0200 by phpDocumentor 1.4.1