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

Documentation generated on Wed, 22 Apr 2009 09:06:27 +0200 by phpDocumentor 1.4.1