*/
define('SPREADSHEET_EXCEL_WRITER_NE', "<>");
-$encoding_string='';
-/**
-* Class for creating OLE streams for Excel Spreadsheets
-*
-* @author Xavier Noguer <xnoguer@rezebra.com>
-* @package Spreadsheet_WriteExcel
-*/
-class OLEwriter
+define('PpsType_Root', 5);
+define('PpsType_Dir', 1);
+define('PpsType_File', 2);
+define('DataSizeSmall', 0x1000);
+define('LongIntSize', 4);
+define('PpsSize', 0x80);
+
+function Asc2Ucs($sAsc)
{
- /**
- * Filename for the OLE stream
- * @var string
- * @see _initialize()
- */
- var $_OLEfilename;
+ return implode("\x00", (preg_split('//', $sAsc, -1, PREG_SPLIT_NO_EMPTY)))."\x00";
+}
- /**
- * Filehandle for the OLE stream
- * @var resource
- */
- var $_filehandle;
+function Ucs2Asc($sUcs)
+{
+ $chars=explode("\x00", $sUcs);
+ array_pop($chars);
+ return implode("", $chars);
+}
- /**
- * Name of the temporal file in case OLE stream goes to stdout
- * @var string
- */
- var $_tmp_filename;
+function OLEDate2Local($sDateTime)
+{
+}
- /**
- * Variable for preventing closing two times
- * @var integer
- */
- var $_fileclosed;
+#------------------------------------------------------------------------------
+# Localtime->OLE Date
+#------------------------------------------------------------------------------
+function LocalDate2OLE($raDate)
+{
+}
- /**
- * Size of the data to be written to the OLE stream
- * @var integer
- */
- var $_biffsize;
+function _leapYear($iYear)
+{
+ return ((($iYear % 4)==0) && (($iYear % 100) || ($iYear % 400)==0)) ? 1 : 0;
+}
- /**
- * Real data size to be written to the OLE stream
- * @var integer
- */
- var $_booksize;
+function _yearDays($iYear)
+{
+ return _leapYear($iYear) ? 366 : 365;
+}
- /**
- * Number of big blocks in the OLE stream
- * @var integer
- */
- var $_big_blocks;
+function _monthDays($iMon, $iYear)
+{
+ if ($iMon == 1 || $iMon == 3 || $iMon == 5 || $iMon == 7 ||
+ $iMon == 8 || $iMon == 10 || $iMon == 12)
+ return 31;
+ elseif ($iMon == 4 || $iMon == 6 || $iMon == 9 || $iMon == 11)
+ return 30;
+ elseif ($iMon == 2)
+ return _leapYear($iYear) ? 29 : 28;
+}
- /**
- * Number of list blocks in the OLE stream
- * @var integer
- */
- var $_list_blocks;
+/*
+ * This is the OLE::Storage_Lite Perl package ported to PHP
+ * OLE::Storage_Lite was written by Kawai Takanori, kwitknr@cpan.org
+ */
- /**
- * Number of big blocks in the OLE stream
- * @var integer
- */
- var $_root_start;
+class ole_pps
+{
+ var $No;
+ var $Name;
+ var $Type;
+ var $PrevPps;
+ var $NextPps;
+ var $DirPps;
+ var $Time1st;
+ var $Time2nd;
+ var $StartBlock;
+ var $Size;
+ var $Data;
+ var $Child;
+ var $_PPS_FILE;
+
+ #------------------------------------------------------------------------------
+ # _new (OLE::Storage_Lite::PPS)
+ # for OLE::Storage_Lite
+ #------------------------------------------------------------------------------
+ function __construct($iNo, $sNm, $iType, $iPrev, $iNext, $iDir,
+ $raTime1st, $raTime2nd, $iStart, $iSize,
+ $sData=false, $raChild=false)
+ {
+ #1. Constructor for OLE::Storage_Lite
+
+ $this->No = $iNo;
+ $this->Name = $sNm;
+ $this->Type = $iType;
+ $this->PrevPps = $iPrev;
+ $this->NextPps = $iNext;
+ $this->DirPps = $iDir;
+ $this->Time1st = $raTime1st;
+ $this->Time2nd = $raTime2nd;
+ $this->StartBlock = $iStart;
+ $this->Size = $iSize;
+ $this->Data = $sData;
+ $this->Child = $raChild;
+ $this->_PPS_FILE = NULL;
+ }
+
+ #------------------------------------------------------------------------------
+ # _DataLen (OLE::Storage_Lite::PPS)
+ # Check for update
+ #------------------------------------------------------------------------------
+ function _DataLen()
+ {
+ if ($this->Data===false)
+ return 0;
+
+ if ($this->_PPS_FILE)
+ return filesize($this->_PPS_FILE);
+
+ return strlen($this->Data);
+ }
+
+ #------------------------------------------------------------------------------
+ # _makeSmallData (OLE::Storage_Lite::PPS)
+ #------------------------------------------------------------------------------
+ function _makeSmallData(&$aList, $rhInfo)
+ {
+ //my ($sRes);
+ $FILE = $rhInfo->_FILEH_;
+ $iSmBlk = 0;
+ $sRes = '';
+
+ for ($c=0;$c<sizeof($aList);$c++)
+ {
+ $oPps=&$aList[$c];
- /**
- * Constructor for the OLEwriter class
- *
- * @param string $OLEfilename the name of the file for the OLE stream
- */
- function OLEwriter($OLEfilename)
- {
- $this->_OLEfilename = $OLEfilename;
- $this->_filehandle = '';
- $this->_tmp_filename = '';
- $this->_fileclosed = 0;
- $this->_biff_only = 0;
- //$this->_size_allowed = 0;
- $this->_biffsize = 0;
- $this->_booksize = 0;
- $this->_big_blocks = 0;
- $this->_list_blocks = 0;
- $this->_root_start = 0;
- //$this->_block_count = 4;
- $this->_initialize();
- }
+ #1. Make SBD, small data string
- /**
- * Check for a valid filename and store the filehandle.
- * Filehandle "-" writes to STDOUT
- *
- * @access private
- */
- function _initialize()
- {
- $OLEfile = $this->_OLEfilename;
+ if ($oPps->Type==PpsType_File)
+ {
+ if ($oPps->Size<=0)
+ continue;
- if (($OLEfile == '-') or ($OLEfile == '')) {
- $this->_tmp_filename = tempnam("/tmp", "OLEwriter");
- $fh = fopen($this->_tmp_filename, "wb");
- if ($fh == false) {
- die("Can't create temporary file.");
- }
- } else {
- // Create a new file, open for writing (in binmode)
- $fh = fopen($OLEfile, "wb");
- if ($fh == false) {
- die("Can't open $OLEfile. It may be in use or protected.");
+ if($oPps->Size < $rhInfo->_SMALL_SIZE)
+ {
+ $iSmbCnt = floor($oPps->Size / $rhInfo->_SMALL_BLOCK_SIZE) +
+ (($oPps->Size % $rhInfo->_SMALL_BLOCK_SIZE) ? 1 : 0);
+ #1.1 Add to SBD
+ for ($i = 0; $i<($iSmbCnt-1); $i++)
+ fputs($FILE, pack("V", $i+$iSmBlk+1));
+ fputs($FILE, pack("V", -2));
+
+ #1.2 Add to Data String(this will be written for RootEntry)
+ #Check for update
+ if ($oPps->_PPS_FILE)
+ {
+ //my $sBuff;
+ fseek($oPps->_PPS_FILE, 0, SEEK_SET); #To The Top
+ while ($sBuff=fread($oPps->_PPS_FILE, 4096))
+ $sRes .= $sBuff;
+ }
+ else
+ $sRes .= $oPps->Data;
+ if($oPps->Size % $rhInfo->_SMALL_BLOCK_SIZE)
+ {
+ $sRes .= (str_repeat("\x00",
+ ($rhInfo->_SMALL_BLOCK_SIZE -
+ ($oPps->Size % $rhInfo->_SMALL_BLOCK_SIZE))));
+ }
+ #1.3 Set for PPS
+ $oPps->StartBlock = $iSmBlk;
+ $iSmBlk += $iSmbCnt;
+ }
}
- }
-
- // Store filehandle
- $this->_filehandle = $fh;
- }
-
-
- /**
- * Set the size of the data to be written to the OLE stream.
- * The maximun size comes from this:
- * $big_blocks = (109 depot block x (128 -1 marker word)
- * - (1 x end words)) = 13842
- * $maxsize = $big_blocks * 512 bytes = 7087104
- *
- * @access public
- * @see Spreadsheet_Excel_Writer_Workbook::store_OLE_file()
- * @param integer $biffsize The size of the data to be written to the OLE stream
- * @return integer 1 for success
- */
- function setSize($biffsize)
- {
- $maxsize = 7087104; // TODO: extend max size
- if ($biffsize > $maxsize) {
- die("Maximum file size, $maxsize, exceeded.");
}
-
- $this->_biffsize = $biffsize;
- // Set the min file size to 4k to avoid having to use small blocks
- if ($biffsize > 4096) {
- $this->_booksize = $biffsize;
- } else {
- $this->_booksize = 4096;
- }
- //$this->_size_allowed = 1;
- return(1);
+ $iSbCnt = floor($rhInfo->_BIG_BLOCK_SIZE / LongIntSize);
+ if ($iSmBlk % $iSbCnt)
+ fputs($FILE, str_repeat(pack("V", -1), $iSbCnt - ($iSmBlk % $iSbCnt)));
+ #2. Write SBD with adjusting length for block
+ return $sRes;
+ }
+
+ #------------------------------------------------------------------------------
+ # _savePpsWk (OLE::Storage_Lite::PPS)
+ #------------------------------------------------------------------------------
+ function _savePpsWk($rhInfo)
+ {
+ #1. Write PPS
+ $FILE=$rhInfo->_FILEH_;
+ fputs($FILE,
+ $this->Name.
+ str_repeat("\x00", 64 - strlen($this->Name)). # 64
+ pack("v", strlen($this->Name) + 2). # 66
+ pack("c", $this->Type). # 67
+ pack("c", 0x00). #UK # 68
+ pack("V", $this->PrevPps). #Prev # 72
+ pack("V", $this->NextPps). #Next # 76
+ pack("V", $this->DirPps). #Dir # 80
+ "\x00\x09\x02\x00". # 84
+ "\x00\x00\x00\x00". # 88
+ "\xc0\x00\x00\x00". # 92
+ "\x00\x00\x00\x46". # 96
+ "\x00\x00\x00\x00". # 100
+ "\x00\x00\x00\x00\x00\x00\x00\x00".
+ "\x00\x00\x00\x00\x00\x00\x00\x00".
+ pack("V", ($this->StartBlock!==false) ?
+ $this->StartBlock : 0). # 120
+ pack("V", ($this->Size!==false) ?
+ $this->Size : 0). # 124
+ pack("V", 0) # 128
+ );
}
+}
- /**
- * Calculate various sizes needed for the OLE stream
- *
- * @access private
- */
- function _calculateSizes()
- {
- $datasize = $this->_booksize;
- if ($datasize % 512 == 0) {
- $this->_big_blocks = $datasize/512;
- } else {
- $this->_big_blocks = floor($datasize/512) + 1;
- }
- // There are 127 list blocks and 1 marker blocks for each big block
- // depot + 1 end of chain block
- $this->_list_blocks = floor(($this->_big_blocks)/127) + 1;
- $this->_root_start = $this->_big_blocks;
- }
+/*
+ * This is the OLE::Storage_Lite Perl package ported to PHP
+ * OLE::Storage_Lite was written by Kawai Takanori, kwitknr@cpan.org
+ */
- /**
- * Write root entry, big block list and close the filehandle.
- * This routine is used to explicitly close the open filehandle without
- * having to wait for DESTROY.
- *
- * @access public
- * @see Spreadsheet_Excel_Writer_Workbook::store_OLE_file()
- */
- function close()
- {
- //return if not $this->{_size_allowed};
- $this->_writePadding();
- $this->_writePropertyStorage();
- $this->_writeBigBlockDepot();
- // Close the filehandle
- fclose($this->_filehandle);
- if (($this->_OLEfilename == '-') or ($this->_OLEfilename == '')) {
- $fh = fopen($this->_tmp_filename, "rb");
- if ($fh == false) {
- die("Can't read temporary file.");
+class ole_pps_file extends ole_pps
+{
+ function __construct($sNm, $sData=false, $sFile=false)
+ {
+ $this->No = false;
+ $this->Name = $sNm;
+ $this->Type = PpsType_File;
+ $this->PrevPps = false;
+ $this->NextPps = false;
+ $this->DirPps = false;
+ $this->Time1st = false;
+ $this->Time2nd = false;
+ $this->StartBlock = false;
+ $this->Size = false;
+ $this->Data = ($sFile===false) ? $sData : '';
+ $this->Child = false;
+
+ if ($sFile!==false)
+ {
+ if (is_ressource($sFile))
+ $this->_PPS_FILE=$sFile;
+ elseif ($sFile=="")
+ {
+ $fname=tempnam("php_ole");
+ $this->_PPS_FILE=fopen($fname, "r+b");
+ }
+ else
+ {
+ $fname=$sFile;
+ $this->_PPS_FILE=fopen($fname, "r+b");
}
- fpassthru($fh);
- @unlink($this->_tmp_filename);
- }
- $this->_fileclosed = 1;
- }
-
- /**
- * Write BIFF data to OLE file.
- *
- * @param string $data string of bytes to be written
- */
- function write($data)
- {
- fwrite($this->_filehandle, $data, strlen($data));
- }
-
-
- /**
- * Write OLE header block.
- */
- function writeHeader()
- {
- $this->_calculateSizes();
- $root_start = $this->_root_start;
- $num_lists = $this->_list_blocks;
- $id = pack("nnnn", 0xD0CF, 0x11E0, 0xA1B1, 0x1AE1);
- $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
- $unknown2 = pack("vv", 0x3E, 0x03);
- $unknown3 = pack("v", -2);
- $unknown4 = pack("v", 0x09);
- $unknown5 = pack("VVV", 0x06, 0x00, 0x00);
- $num_bbd_blocks = pack("V", $num_lists);
- $root_startblock = pack("V", $root_start);
- $unknown6 = pack("VV", 0x00, 0x1000);
- $sbd_startblock = pack("V", -2);
- $unknown7 = pack("VVV", 0x00, -2 ,0x00);
- $unused = pack("V", -1);
-
- fwrite($this->_filehandle, $id);
- fwrite($this->_filehandle, $unknown1);
- fwrite($this->_filehandle, $unknown2);
- fwrite($this->_filehandle, $unknown3);
- fwrite($this->_filehandle, $unknown4);
- fwrite($this->_filehandle, $unknown5);
- fwrite($this->_filehandle, $num_bbd_blocks);
- fwrite($this->_filehandle, $root_startblock);
- fwrite($this->_filehandle, $unknown6);
- fwrite($this->_filehandle, $sbd_startblock);
- fwrite($this->_filehandle, $unknown7);
-
- for ($i=1; $i <= $num_lists; $i++) {
- $root_start++;
- fwrite($this->_filehandle, pack("V",$root_start));
- }
- for ($i = $num_lists; $i <=108; $i++) {
- fwrite($this->_filehandle, $unused);
- }
- }
-
-
- /**
- * Write big block depot.
- *
- * @access private
- */
- function _writeBigBlockDepot()
- {
- $num_blocks = $this->_big_blocks;
- $num_lists = $this->_list_blocks;
- $total_blocks = $num_lists *128;
- $used_blocks = $num_blocks + $num_lists +2;
-
- $marker = pack("V", -3);
- $end_of_chain = pack("V", -2);
- $unused = pack("V", -1);
-
- for ($i = 1; $i < $num_blocks; $i++) {
- fwrite($this->_filehandle, pack("V",$i));
- }
-
- fwrite($this->_filehandle, $end_of_chain);
- fwrite($this->_filehandle, $end_of_chain);
- for ($i = 0; $i < $num_lists; $i++) {
- fwrite($this->_filehandle, $marker);
- }
-
- for ($i = $used_blocks; $i <= $total_blocks; $i++) {
- fwrite($this->_filehandle, $unused);
+ if ($sData!==false)
+ fputs($this->_PPS_FILE, $sData);
}
}
- /**
- * Write property storage. TODO: add summary sheets
- *
- * @access private
- */
- function _writePropertyStorage()
+ function append ($sData)
{
- //$rootsize = -2;
- /*************** name type dir start size */
- $this->_writePps("Root Entry", 0x05, 1, -2, 0x00);
- $this->_writePps("Book", 0x02, -1, 0x00, $this->_booksize);
- $this->_writePps('', 0x00, -1, 0x00, 0x0000);
- $this->_writePps('', 0x00, -1, 0x00, 0x0000);
+ if ($this->_PPS_FILE)
+ fputs($this->_PPS_FILE, $sData);
+ else
+ $this->Data.=$sData;
}
+}
-/**
-* Write property sheet in property storage
-*
-* @param string $name name of the property storage.
-* @param integer $type type of the property storage.
-* @param integer $dir dir of the property storage.
-* @param integer $start start of the property storage.
-* @param integer $size size of the property storage.
-* @access private
-*/
- function _writePps($name, $type, $dir, $start, $size)
- {
- $length = 0;
- $rawname = '';
-
- if ($name != '') {
- $name = $name . "\0";
- $name_length = strlen($name);
- for ($i = 0; $i < $name_length; $i++) {
- // Simulate a Unicode string
- $rawname .= pack("H*",dechex(ord($name{$i}))).pack("C",0);
- }
- $length = strlen($name) * 2;
- }
-
- $zero = pack("C", 0);
- $pps_sizeofname = pack("v", $length); // 0x40
- $pps_type = pack("v", $type); // 0x42
- $pps_prev = pack("V", -1); // 0x44
- $pps_next = pack("V", -1); // 0x48
- $pps_dir = pack("V", $dir); // 0x4c
-
- $unknown1 = pack("V", 0);
-
- $pps_ts1s = pack("V", 0); // 0x64
- $pps_ts1d = pack("V", 0); // 0x68
- $pps_ts2s = pack("V", 0); // 0x6c
- $pps_ts2d = pack("V", 0); // 0x70
- $pps_sb = pack("V", $start); // 0x74
- $pps_size = pack("V", $size); // 0x78
-
-
- fwrite($this->_filehandle, $rawname);
- for ($i = 0; $i < (64 -$length); $i++) {
- fwrite($this->_filehandle, $zero);
- }
- fwrite($this->_filehandle, $pps_sizeofname);
- fwrite($this->_filehandle, $pps_type);
- fwrite($this->_filehandle, $pps_prev);
- fwrite($this->_filehandle, $pps_next);
- fwrite($this->_filehandle, $pps_dir);
- for ($i = 0; $i < 5; $i++) {
- fwrite($this->_filehandle, $unknown1);
- }
- fwrite($this->_filehandle, $pps_ts1s);
- fwrite($this->_filehandle, $pps_ts1d);
- fwrite($this->_filehandle, $pps_ts2d);
- fwrite($this->_filehandle, $pps_ts2d);
- fwrite($this->_filehandle, $pps_sb);
- fwrite($this->_filehandle, $pps_size);
- fwrite($this->_filehandle, $unknown1);
- }
+/*
+ * This is the OLE::Storage_Lite Perl package ported to PHP
+ * OLE::Storage_Lite was written by Kawai Takanori, kwitknr@cpan.org
+ */
- /**
- * Pad the end of the file
- *
- * @access private
- */
- function _writePadding()
- {
- $biffsize = $this->_biffsize;
- if ($biffsize < 4096) {
- $min_size = 4096;
- } else {
- $min_size = 512;
- }
- if ($biffsize % $min_size != 0) {
- $padding = $min_size - ($biffsize % $min_size);
- for ($i = 0; $i < $padding; $i++) {
- fwrite($this->_filehandle, "\0");
- }
- }
- }
+class ole_pps_root extends ole_pps
+{
+ function __construct($raTime1st=false, $raTime2nd=false, $raChild=false)
+ {
+ $this->No = false;
+ $this->Name = Asc2Ucs('Root Entry');
+ $this->Type = PpsType_Root;
+ $this->PrevPps = false;
+ $this->NextPps = false;
+ $this->DirPps = false;
+ $this->Time1st = $raTime1st;
+ $this->Time2nd = $raTime2nd;
+ $this->StartBlock = false;
+ $this->Size = false;
+ $this->Data = false;
+ $this->Child = $raChild;
+ }
+
+ #------------------------------------------------------------------------------
+ # save (OLE::Storage_Lite::PPS::Root)
+ #------------------------------------------------------------------------------
+ function save($sFile, $bNoAs=false, $rhInfo=false)
+ {
+ #0.Initial Setting for saving
+
+ if (!$rhInfo)
+ $rhInfo=new stdClass();
+
+ $rhInfo->_BIG_BLOCK_SIZE = $rhInfo->_SMALL_BLOCK_SIZE = 0;
+ $rhInfo->_BIG_BLOCK_SIZE=pow(2, (($rhInfo->_BIG_BLOCK_SIZE) ?
+ _adjust2($rhInfo->_BIG_BLOCK_SIZE) : 9));
+ $rhInfo->_SMALL_BLOCK_SIZE=pow(2, (($rhInfo->_SMALL_BLOCK_SIZE) ?
+ _adjust2($rhInfo->_SMALL_BLOCK_SIZE) : 6));
+ $rhInfo->_SMALL_SIZE = 0x1000;
+ $rhInfo->_PPS_SIZE = 0x80;
+
+ #1.Open File
+ #1.1 $sFile is Ref of scalar
+ if(is_resource($sFile))
+ {
+ $oIo=$sFile;
+ $rhInfo->_FILEH_ = $oIo;
+ }
+ #1.2 $sFile is a simple filename string
+ else
+ {
+ $oIo=fopen("$sFile", "wb");
+ $rhInfo->_FILEH_ = $oIo;
+ }
+
+ $iBlk = 0;
+ #1. Make an array of PPS (for Save)
+ $aList=array();
+ $list=array(&$this);
+ if($bNoAs)
+ $this->_savePpsSetPnt2($list, $aList, $rhInfo);
+ else
+ $this->_savePpsSetPnt($list, $aList, $rhInfo);
+ list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList, $rhInfo);
+ #2.Save Header
+ $this->_saveHeader($rhInfo, $iSBDcnt, $iBBcnt, $iPPScnt);
+
+ #3.Make Small Data string (write SBD)
+ $sSmWk = $this->_makeSmallData($aList, $rhInfo);
+ $this->Data = $sSmWk; #Small Datas become RootEntry Data
+
+ #4. Write BB
+ $iBBlk = $iSBDcnt;
+ $this->_saveBigData($iBBlk, $aList, $rhInfo);
+ #5. Write PPS
+ $this->_savePps($aList, $rhInfo);
+ #6. Write BD and BDList and Adding Header informations
+ $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt, $rhInfo);
+ #7.Close File
+ fclose($rhInfo->_FILEH_);
+ }
+
+ #------------------------------------------------------------------------------
+ # _calcSize (OLE::Storage_Lite::PPS)
+ #------------------------------------------------------------------------------
+ function _calcSize(&$raList, $rhInfo)
+ {
+ #0. Calculate Basic Setting
+ $iSBDcnt=0;
+ $iBBcnt=0;
+ $iPPScnt = 0;
+ $iSmallLen = 0;
+ $iSBcnt = 0;
+
+ for ($c=0;$c<sizeof($raList);$c++)
+ {
+ $oPps=&$raList[$c];
+
+ if($oPps->Type==PpsType_File)
+ {
+ $oPps->Size = $oPps->_DataLen(); #Mod
+ if($oPps->Size < $rhInfo->_SMALL_SIZE)
+ {
+ $iSBcnt += floor($oPps->Size / $rhInfo->_SMALL_BLOCK_SIZE) +
+ (($oPps->Size % $rhInfo->_SMALL_BLOCK_SIZE) ? 1 : 0);
+ }
+ else
+ {
+ $iBBcnt +=
+ (floor($oPps->Size/ $rhInfo->_BIG_BLOCK_SIZE) +
+ (($oPps->Size % $rhInfo->_BIG_BLOCK_SIZE)? 1: 0));
+ }
+ }
+ }
+ $iSmallLen = $iSBcnt * $rhInfo->_SMALL_BLOCK_SIZE;
+ $iSlCnt = floor($rhInfo->_BIG_BLOCK_SIZE / LongIntSize);
+ $iSBDcnt = floor($iSBcnt / $iSlCnt)+ (($iSBcnt % $iSlCnt) ? 1 : 0);
+ $iBBcnt += (floor($iSmallLen/ $rhInfo->_BIG_BLOCK_SIZE) +
+ (( $iSmallLen% $rhInfo->_BIG_BLOCK_SIZE) ? 1 : 0));
+ $iCnt = sizeof($raList);
+ $iBdCnt = $rhInfo->_BIG_BLOCK_SIZE/PpsSize;
+ $iPPScnt = (floor($iCnt/$iBdCnt) + (($iCnt % $iBdCnt) ? 1 : 0));
+
+ return array($iSBDcnt, $iBBcnt, $iPPScnt);
+ }
+
+ #------------------------------------------------------------------------------
+ # _adjust2 (OLE::Storage_Lite::PPS::Root)
+ #------------------------------------------------------------------------------
+ function _adjust2($i2)
+ {
+ $iWk = log($i2)/log(2);
+ return ($iWk > int($iWk)) ? floor($iWk)+1 : $iWk;
+ }
+
+ #------------------------------------------------------------------------------
+ # _saveHeader (OLE::Storage_Lite::PPS::Root)
+ #------------------------------------------------------------------------------
+ function _saveHeader($rhInfo, $iSBDcnt, $iBBcnt, $iPPScnt)
+ {
+ $FILE = $rhInfo->_FILEH_;
+
+ #0. Calculate Basic Setting
+ $iBlCnt = $rhInfo->_BIG_BLOCK_SIZE / LongIntSize;
+ $i1stBdL = ($rhInfo->_BIG_BLOCK_SIZE - 0x4C) / LongIntSize;
+
+ $iBdExL = 0;
+ $iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
+ $iAllW = $iAll;
+ $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
+ $iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt) ? 1 : 0);
+ //my $i;
+
+ #0.1 Calculate BD count
+ if ($iBdCnt > $i1stBdL)
+ {
+ // TODO: is do-while correct here?
+ do
+ {
+ $iBdExL++;
+ $iAllW++;
+ $iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
+ $iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt) ? 1 : 0);
+ }
+ while($iBdCnt > ($iBdExL*$iBlCnt+ $i1stBdL));
+ }
+
+ #1.Save Header
+ fputs($FILE,
+ "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1".
+ "\x00\x00\x00\x00".
+ "\x00\x00\x00\x00".
+ "\x00\x00\x00\x00".
+ "\x00\x00\x00\x00".
+ pack("v", 0x3b).
+ pack("v", 0x03).
+ pack("v", -2).
+ pack("v", 9).
+ pack("v", 6).
+ pack("v", 0).
+ "\x00\x00\x00\x00".
+ "\x00\x00\x00\x00".
+ pack("V", $iBdCnt).
+ pack("V", $iBBcnt+$iSBDcnt). #ROOT START
+ pack("V", 0).
+ pack("V", 0x1000).
+ pack("V", 0). #Small Block Depot
+ pack("V", 1)
+ );
+ #2. Extra BDList Start, Count
+ if($iBdCnt < $i1stBdL)
+ {
+ fputs($FILE,
+ pack("V", -2). #Extra BDList Start
+ pack("V", 0) #Extra BDList Count
+ );
+ }
+ else
+ {
+ fputs($FILE,
+ pack("V", $iAll+$iBdCnt).
+ pack("V", $iBdExL)
+ );
+ }
+
+ #3. BDList
+ for ($i=0;($i<$i1stBdL) && ($i < $iBdCnt); $i++)
+ fputs($FILE, pack("V", $iAll+$i));
+ if ($i<$i1stBdL)
+ {
+ // TODO: Check, if str_repeat is binary safe
+ fputs($FILE, str_repeat((pack("V", -1)), ($i1stBdL-$i)));
+ }
+ }
+
+ #------------------------------------------------------------------------------
+ # _saveBigData (OLE::Storage_Lite::PPS)
+ #------------------------------------------------------------------------------
+ function _saveBigData(&$iStBlk, &$raList, $rhInfo)
+ {
+
+ //return;//!!!
+
+ $iRes = 0;
+ $FILE = $rhInfo->_FILEH_;
+
+ #1.Write Big (ge 0x1000) Data into Block
+ for ($c=0;$c<sizeof($raList);$c++)
+ {
+ $oPps=&$raList[$c];
+ if($oPps->Type!=PpsType_Dir)
+ {
+ #print "PPS: $oPps DEF:", defined($oPps->{Data}), "\n";
+ $oPps->Size = $oPps->_DataLen(); #Mod
+ if(($oPps->Size >= $rhInfo->_SMALL_SIZE) ||
+ (($oPps->Type == PpsType_Root) && $oPps->Data!==false))
+ {
+ #1.1 Write Data
+ #Check for update
+ if($oPps->_PPS_FILE)
+ {
+ //my $sBuff;
+ $iLen = 0;
+ fseek($oPps->_PPS_FILE, 0, SEEK_SET); #To The Top
+ while ($sBuff=fread($oPps->_PPS_FILE, 4096))
+ {
+ $iLen += length($sBuff);
+ fputs($FILE, $sBuff); #Check for update
+ }
+ }
+ else
+ fputs($FILE, $oPps->Data);
+ if ($oPps->Size % $rhInfo->_BIG_BLOCK_SIZE)
+ {
+ // TODO: Check, if str_repeat() is binary safe
+ fputs($FILE, str_repeat("\x00",
+ ($rhInfo->_BIG_BLOCK_SIZE -
+ ($oPps->Size % $rhInfo->_BIG_BLOCK_SIZE)))
+ );
+ }
+ #1.2 Set For PPS
+ $oPps->StartBlock = $iStBlk;
+ $iStBlk +=
+ (floor($oPps->Size/ $rhInfo->_BIG_BLOCK_SIZE) +
+ (($oPps->Size % $rhInfo->_BIG_BLOCK_SIZE) ? 1 : 0));
+ }
+ }
+ }
+ }
+
+ #------------------------------------------------------------------------------
+ # _savePps (OLE::Storage_Lite::PPS::Root)
+ #------------------------------------------------------------------------------
+ function _savePps(&$raList, $rhInfo)
+ {
+ #0. Initial
+ $FILE = $rhInfo->_FILEH_;
+ #2. Save PPS
+ for ($c=0;$c<sizeof($raList);$c++)
+ {
+ $oItem=&$raList[$c];
+ $oItem->_savePpsWk($rhInfo);
+ }
+ #3. Adjust for Block
+ $iCnt = sizeof($raList);
+ $iBCnt = $rhInfo->_BIG_BLOCK_SIZE / $rhInfo->_PPS_SIZE;
+ if($iCnt % $iBCnt)
+ fputs($FILE, str_repeat("\x00", (($iBCnt - ($iCnt % $iBCnt)) * $rhInfo->_PPS_SIZE)));
+ return (floor($iCnt / $iBCnt) + (($iCnt % $iBCnt) ? 1 : 0));
+ }
+
+ #------------------------------------------------------------------------------
+ # _savePpsSetPnt2 (OLE::Storage_Lite::PPS::Root)
+ # For Test
+ #------------------------------------------------------------------------------
+ function _savePpsSetPnt2(&$aThis, &$raList, $rhInfo)
+ {
+ #1. make Array as Children-Relations
+ #1.1 if No Children
+ if (!is_array($aThis) || sizeof($aThis)==0)
+ return 0xFFFFFFFF;
+ elseif (sizeof($aThis)==1)
+ {
+ #1.2 Just Only one
+ array_push($raList, $aThis[0]);
+ $aThis[0]->No = sizeof($raList)-1;
+ $aThis[0]->PrevPps = 0xFFFFFFFF;
+ $aThis[0]->NextPps = 0xFFFFFFFF;
+ $aThis[0]->DirPps = $this->_savePpsSetPnt2($aThis[0]->Child, $raList, $rhInfo);
+ return $aThis[0]->No;
+ }
+ else
+ {
+ #1.3 Array
+ $iCnt = sizeof($aThis);
+ #1.3.1 Define Center
+ $iPos = 0; #int($iCnt/ 2); #$iCnt
+
+ $aWk = $aThis;
+ $aPrev = (sizeof($aThis) > 2) ? array_splice($aWk, 1, 1) : array(); #$iPos);
+ $aNext = array_splice($aWk, 1); #, $iCnt - $iPos -1);
+ $aThis[$iPos]->PrevPps = $this->_savePpsSetPnt2($aPrev, $raList, $rhInfo);
+ array_push($raList, $aThis[$iPos]);
+ $aThis[$iPos]->No = sizeof($raList)-1;
+
+ #1.3.2 Devide a array into Previous,Next
+ $aThis[$iPos]->NextPps = $this->_savePpsSetPnt2($aNext, $raList, $rhInfo);
+ $aThis[$iPos]->DirPps = $this->_savePpsSetPnt2($aThis[$iPos]->Child, $raList, $rhInfo);
+ return $aThis[$iPos]->No;
+ }
+ }
+
+ #------------------------------------------------------------------------------
+ # _savePpsSetPnt2 (OLE::Storage_Lite::PPS::Root)
+ # For Test
+ #------------------------------------------------------------------------------
+ function _savePpsSetPnt2s(&$aThis, &$raList, $rhInfo)
+ {
+ #1. make Array as Children-Relations
+ #1.1 if No Children
+ if (!is_array($aThis) || sizeof($aThis)==0)
+ return 0xFFFFFFFF;
+ elseif (sizeof($aThis)==1)
+ {
+ #1.2 Just Only one
+ array_push($raList, $aThis[0]);
+ $aThis[0]->No = sizeof($raList)-1;
+ $aThis[0]->PrevPps = 0xFFFFFFFF;
+ $aThis[0]->NextPps = 0xFFFFFFFF;
+ $aThis[0]->DirPps = $this->_savePpsSetPnt2($aThis[0]->Child, $raList, $rhInfo);
+ return $aThis[0]->No;
+ }
+ else
+ {
+ #1.3 Array
+ $iCnt = sizeof($aThis);
+ #1.3.1 Define Center
+ $iPos = 0; #int($iCnt/ 2); #$iCnt
+ array_push($raList, $aThis[$iPos]);
+ $aThis[$iPos]->No = sizeof($raList)-1;
+ $aWk = $aThis;
+ #1.3.2 Devide a array into Previous,Next
+ $aPrev = array_splice($aWk, 0, $iPos);
+ $aNext = array_splice($aWk, 1, $iCnt - $iPos - 1);
+ $aThis[$iPos]->PrevPps = $this->_savePpsSetPnt2($aPrev, $raList, $rhInfo);
+ $aThis[$iPos]->NextPps = $this->_savePpsSetPnt2($aNext, $raList, $rhInfo);
+ $aThis[$iPos]->DirPps = $this->_savePpsSetPnt2($aThis[$iPos]->Child, $raList, $rhInfo);
+ return $aThis[$iPos]->No;
+ }
+ }
+
+ #------------------------------------------------------------------------------
+ # _savePpsSetPnt (OLE::Storage_Lite::PPS::Root)
+ #------------------------------------------------------------------------------
+ function _savePpsSetPnt(&$aThis, &$raList, $rhInfo)
+ {
+
+ //print "yyy type: ".gettype($aThis)."<br>\n";
+ //print "yyy name: ".$aThis[0]->Name."<br>\n";
+
+ #1. make Array as Children-Relations
+ #1.1 if No Children
+ if (!is_array($aThis) || sizeof($aThis)==0)
+ {
+ return 0xFFFFFFFF;
+ }
+ elseif (sizeof($aThis)==1)
+ {
+ #1.2 Just Only one
+ array_push($raList, $aThis[0]);
+ $aThis[0]->No = sizeof($raList)-1;
+ $aThis[0]->PrevPps = 0xFFFFFFFF;
+ $aThis[0]->NextPps = 0xFFFFFFFF;
+ $aThis[0]->DirPps = $this->_savePpsSetPnt($aThis[0]->Child, $raList, $rhInfo);
+ return $aThis[0]->No;
+ }
+ else
+ {
+ #1.3 Array
+ $iCnt = sizeof($aThis);
+ #1.3.1 Define Center
+ $iPos = floor($iCnt/2); #$iCnt
+ array_push($raList, $aThis[$iPos]);
+ $aThis[$iPos]->No = sizeof($raList)-1;
+ $aWk = $aThis;
+ #1.3.2 Devide a array into Previous,Next
+ $aPrev = splice($aWk, 0, $iPos);
+ $aNext = splice($aWk, 1, $iCnt - $iPos - 1);
+ $aThis[$iPos]->PrevPps = $this->_savePpsSetPnt($aPrev, $raList, $rhInfo);
+ $aThis[$iPos]->NextPps = $this->_savePpsSetPnt($aNext, $raList, $rhInfo);
+ $aThis[$iPos]->DirPps = $this->_savePpsSetPnt($aThis[$iPos]->Child, $raList, $rhInfo);
+ return $aThis[$iPos]->No;
+ }
+ }
+
+ #------------------------------------------------------------------------------
+ # _savePpsSetPnt (OLE::Storage_Lite::PPS::Root)
+ #------------------------------------------------------------------------------
+ function _savePpsSetPnt1(&$aThis, &$raList, $rhInfo)
+ {
+ #1. make Array as Children-Relations
+ #1.1 if No Children
+ if (!is_array($aThis) || sizeof($aThis)==0)
+ {
+ return 0xFFFFFFFF;
+ }
+ elseif (sizeof($aThis)==1)
+ {
+ #1.2 Just Only one
+ array_push($raList, $aThis[0]);
+ $aThis[0]->No = sizeof($raList)-1;
+ $aThis[0]->PrevPps = 0xFFFFFFFF;
+ $aThis[0]->NextPps = 0xFFFFFFFF;
+ $aThis[0]->DirPps = $this->_savePpsSetPnt($aThis[0]->Child, $raList, $rhInfo);
+ return $aThis[0]->No;
+ }
+ else
+ {
+ #1.3 Array
+ $iCnt = sizeof($aThis);
+ #1.3.1 Define Center
+ $iPos = floor($iCnt / 2); #$iCnt
+ array_push($raList, $aThis[$iPos]);
+ $aThis[$iPos]->No = sizeof($raList)-1;
+ $aWk = $aThis;
+ #1.3.2 Devide a array into Previous,Next
+ $aPrev = splice($aWk, 0, $iPos);
+ $aNext = splice($aWk, 1, $iCnt - $iPos - 1);
+ $aThis[$iPos]->PrevPps = $this->_savePpsSetPnt($aPrev, $raList, $rhInfo);
+ $aThis[$iPos]->NextPps = $this->_savePpsSetPnt($aNext, $raList, $rhInfo);
+ $aThis[$iPos]->DirPps = $this->_savePpsSetPnt($aThis[$iPos]->Child, $raList, $rhInfo);
+ return $aThis[$iPos]->No;
+ }
+ }
+
+ #------------------------------------------------------------------------------
+ # _saveBbd (OLE::Storage_Lite)
+ #------------------------------------------------------------------------------
+ function _saveBbd($iSbdSize, $iBsize, $iPpsCnt, $rhInfo)
+ {
+ $FILE = $rhInfo->_FILEH_;
+ #0. Calculate Basic Setting
+ $iBbCnt = $rhInfo->_BIG_BLOCK_SIZE / LongIntSize;
+ $i1stBdL = ($rhInfo->_BIG_BLOCK_SIZE - 0x4C) / LongIntSize;
+
+ $iBdExL = 0;
+ $iAll = $iBsize + $iPpsCnt + $iSbdSize;
+ $iAllW = $iAll;
+ $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
+ $iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0);
+ //my $i;
+ #0.1 Calculate BD count
+ if ($iBdCnt >$i1stBdL)
+ {
+ // TODO: do-while correct here?
+ do
+ {
+ $iBdExL++;
+ $iAllW++;
+ $iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
+ $iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt) ? 1 : 0);
+ }
+ while ($iBdCnt > ($iBdExL*$iBbCnt+$i1stBdL));
+ }
+
+ #1. Making BD
+ #1.1 Set for SBD
+ if($iSbdSize > 0)
+ {
+ for ($i = 0; $i<($iSbdSize-1); $i++)
+ fputs($FILE, pack("V", $i+1));
+ fputs($FILE, pack("V", -2));
+ }
+ #1.2 Set for B
+ for ($i = 0; $i<($iBsize-1); $i++)
+ fputs($FILE, pack("V", $i+$iSbdSize+1));
+ fputs($FILE, pack("V", -2));
+
+ #1.3 Set for PPS
+ for ($i = 0; $i<($iPpsCnt-1); $i++)
+ fputs($FILE, pack("V", $i+$iSbdSize+$iBsize+1));
+ fputs($FILE, pack("V", -2));
+ #1.4 Set for BBD itself ( 0xFFFFFFFD : BBD)
+ for ($i=0; $i<$iBdCnt;$i++)
+ fputs($FILE, pack("V", 0xFFFFFFFD));
+ #1.5 Set for ExtraBDList
+ for ($i=0; $i<$iBdExL;$i++)
+ fputs($FILE, pack("V", 0xFFFFFFFC));
+ #1.6 Adjust for Block
+ if(($iAllW + $iBdCnt) % $iBbCnt)
+ fputs($FILE, str_repeat(pack("V", -1), ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt))));
+
+ #2.Extra BDList
+ if($iBdCnt > $i1stBdL)
+ {
+ $iN=0;
+ $iNb=0;
+ for ($i=$i1stBdL;$i<$iBdCnt; $i++, $iN++)
+ {
+ if($iN>=($iBbCnt-1))
+ {
+ $iN = 0;
+ $iNb++;
+ fputs($FILE, pack("V", $iAll+$iBdCnt+$iNb));
+ }
+ fputs($FILE, pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i));
+ }
+ if(($iBdCnt-$i1stBdL) % ($iBbCnt-1))
+ fputs($FILE, str_repeat(pack("V", -1), (($iBbCnt-1) - (($iBdCnt-$i1stBdL) % ($iBbCnt-1)))));
+ fputs($FILE, pack("V", -2));
+ }
+ }
}
/**
*
* @access public
*/
- function Spreadsheet_Excel_Writer_BIFFwriter()
+ function __construct()
{
$this->_byte_order = '';
$this->_data = '';
*/
var $_parser;
- function Spreadsheet_Excel_Writer_Validator(&$parser)
+ function __construct(&$parser)
{
$this->_parser = $parser;
$this->_type = 0x01; // FIXME: add method for setting datatype
* @param integer $index the XF index for the format.
* @param array $properties array with properties to be set on initialization.
*/
- function Spreadsheet_Excel_Writer_Format($BIFF_version, $index = 0, $properties = array())
+ function __construct($BIFF_version, $index = 0, $properties = array())
{
$this->_xf_index = $index;
$this->_BIFF_version = $BIFF_version;
* @param integer $byte_order The byte order (Little endian or Big endian) of the architecture
(optional). 1 => big endian, 0 (default) little endian.
*/
- function Spreadsheet_Excel_Writer_Parser($byte_order, $biff_version)
+ function __construct($byte_order, $biff_version)
{
$this->_current_char = 0;
$this->_BIFF_version = $biff_version;
function _expression()
{
// If it's a string return a string node
- if (ereg("^\"[^\"]{0,255}\"$", $this->_current_token)) {
+ if (preg_match("/^\"[^\"]{0,255}\"$/", $this->_current_token)) {
$result = $this->_createTree($this->_current_token, '', '');
$this->_advance();
return $result;
* @param mixed &$parser The formula parser created for the Workbook
* @access private
*/
- function Spreadsheet_Excel_Writer_Worksheet($BIFF_version, $name,
+ function __construct($BIFF_version, $name,
$index, &$activesheet,
&$firstsheet, &$str_total,
&$str_unique, &$str_table,
&$url_format, &$parser)
{
// It needs to call its parent's constructor explicitly
- $this->Spreadsheet_Excel_Writer_BIFFwriter();
+ parent::__construct();
$this->_BIFF_version = $BIFF_version;
$rowmax = 65536; // 16384 in Excel 5
$colmax = 256;
* @access public
* @param string $encoding The encoding. Ex: 'UTF-16LE', 'utf-8', 'ISO-859-7'
*/
- function
- setInputEncoding($encoding)
+ function setInputEncoding($encoding)
{
- global $encoding_string;
if ($encoding != 'UTF-16LE' && !function_exists('iconv')) {
die("Using an input encoding other than UTF-16LE requires PHP support for iconv");
}
- $this->_input_encoding = $encoding_string = $encoding;
+ $this->_input_encoding = $encoding;
}
/** added 2009-03-05 by Joe Hunt, FA for arabic languages */
}
elseif ($this->_input_encoding != '')
{
- $x = $str;
$str = iconv($this->_input_encoding, 'UTF-16LE', $str);
$strlen = function_exists('mb_strlen') ? mb_strlen($str, 'UTF-16LE') : (strlen($str) / 2);
$encoding = 0x1;
* @param string filename for storing the workbook. "-" for writing to stdout.
* @access public
*/
- function Spreadsheet_Excel_Writer_Workbook($filename)
+ function __construct($filename)
{
// It needs to call its parent's constructor explicitly
- $this->Spreadsheet_Excel_Writer_BIFFwriter();
+ parent::__construct();
$this->_filename = $filename;
$this->_parser = new Spreadsheet_Excel_Writer_Parser($this->_byte_order, $this->_BIFF_version);
* @access public
* @param integer $version The BIFF version
*/
- function
- setVersion($version)
+ function setVersion($version)
{
if ($version == 8) { // only accept version 8
$version = 0x0600;
*/
function _storeOLEFile()
{
- $OLE = new OLEwriter($this->_filename);
- // Write Worksheet data if data <~ 7MB
- if ($OLE->setSize($this->_biffsize))
- {
- $OLE->writeHeader();
- $OLE->write($this->_data);
- foreach($this->_worksheets as $sheet)
- {
- while ($tmp = $sheet->getData()) {
- $OLE->write($tmp);
- }
+ if($this->_BIFF_version == 0x0600) {
+ $OLE = new ole_pps_file(Asc2Ucs('Workbook'));
+ } else {
+ $OLE = new ole_pps_file(Asc2Ucs('Book'));
+ }
+
+ $OLE->append($this->_data);
+
+ $total_worksheets = count($this->_worksheets);
+ for ($i = 0; $i < $total_worksheets; $i++) {
+ while ($tmp = $this->_worksheets[$i]->getData()) {
+ $OLE->append($tmp);
}
}
- $OLE->close();
- return true;
+
+ $root = new ole_pps_root(false, false, array($OLE));
+
+ $root->save($this->_filename);
}
/**
/*
if ($this->_BIFF_version == 0x0600) // Tried to fix the correct handling here, with the
{ // corrected specification from M$ - Joe Hunt 2009-03-08
- global $encoding_string;
+ $encoding_string = $this->_input_encoding;
if ($encoding_string == 'UTF-16LE')
{
$strlen = function_exists('mb_strlen') ? mb_strlen($sheetname, 'UTF-16LE') : (strlen($sheetname) / 2);
8228 : Maximum Excel97 block size
-4 : Length of block header
-8 : Length of additional SST header information
- = 8216
+ -8 : Arbitrary number to keep within _add_continue() limit = 8208
*/
- $continue_limit = 8216;
+ $continue_limit = 8208;
$block_length = 0;
$written = 0;
$this->_block_sizes = array();
foreach (array_keys($this->_str_table) as $string) {
$string_length = strlen($string);
+ $headerinfo = unpack("vlength/Cencoding", $string);
+ $encoding = $headerinfo["encoding"];
+ $split_string = 0;
// Block length is the total length of the strings that will be
// written out in a single SST or CONTINUE block.
boundaries. Therefore, in some cases we need to reduce the
amount of available
*/
+ $align = 0;
+
+ // Only applies to Unicode strings
+ if ($encoding == 1) {
+ // Min string + header size -1
+ $header_length = 4;
+
+ if ($space_remaining > $header_length) {
+ // String contains 3 byte header => split on odd boundary
+ if (!$split_string && $space_remaining % 2 != 1) {
+ $space_remaining--;
+ $align = 1;
+ }
+ // Split section without header => split on even boundary
+ else if ($split_string && $space_remaining % 2 == 1) {
+ $space_remaining--;
+ $align = 1;
+ }
+
+ $split_string = 1;
+ }
+ }
+
if ($space_remaining > $header_length) {
// Write as much as possible of the string in the current block
$written += $space_remaining;
// Reduce the current block length by the amount written
- $block_length -= $continue_limit - $continue;
+ $block_length -= $continue_limit - $continue - $align;
// Store the max size for this block
- $this->_block_sizes[] = $continue_limit;
+ $this->_block_sizes[] = $continue_limit - $align;
// If the current string was split then the next CONTINUE block
// should have the string continue flag (grbit) set unless the
This length is required to set the offsets in the BOUNDSHEET records since
they must be written before the SST records
*/
- $total_offset = array_sum($this->_block_sizes);
- // SST information
- $total_offset += 8;
- if (!empty($this->_block_sizes)) {
- $total_offset += (count($this->_block_sizes)) * 4; // add CONTINUE headers
+
+ $tmp_block_sizes = array();
+ $tmp_block_sizes = $this->_block_sizes;
+
+ $length = 12;
+ if (!empty($tmp_block_sizes)) {
+ $length += array_shift($tmp_block_sizes); // SST
+ }
+ while (!empty($tmp_block_sizes)) {
+ $length += 4 + array_shift($tmp_block_sizes); // CONTINUEs
}
- return $total_offset;
+
+ return $length;
}
/**
*/
function _storeSharedStringsTable()
{
- global $encoding_string;
-
$record = 0x00fc; // Record identifier
- // sizes are upside down
- $this->_block_sizes = array_reverse($this->_block_sizes);
- $length = array_pop($this->_block_sizes) + 8; // First block size plus SST information
-
- // Write the SST block header information
- $header = pack("vv", $record, $length);
- $data = pack("VV", $this->_str_total, $this->_str_unique);
- $this->_append($header . $data);
-
+ $length = 0x0008; // Number of bytes to follow
+ $total = 0x0000;
// Iterate through the strings to calculate the CONTINUE block sizes
- $continue_limit = 8216;
+ $continue_limit = 8208;
$block_length = 0;
$written = 0;
$continue = 0;
+ // sizes are upside down
+ $tmp_block_sizes = $this->_block_sizes;
+ // $tmp_block_sizes = array_reverse($this->_block_sizes);
+
+ // The SST record is required even if it contains no strings. Thus we will
+ // always have a length
+ //
+ if (!empty($tmp_block_sizes)) {
+ $length = 8 + array_shift($tmp_block_sizes);
+ }
+ else {
+ // No strings
+ $length = 8;
+ }
+
+ // Write the SST block header information
+ $header = pack("vv", $record, $length);
+ $data = pack("VV", $this->_str_total, $this->_str_unique);
+ $this->_append($header . $data);
/* TODO: not good for performance */
foreach (array_keys($this->_str_table) as $string) {
$string_length = strlen($string);
- $encoding = $encoding_string ? 1:0; // this is FA specific assumption
+ $headerinfo = unpack("vlength/Cencoding", $string);
+ $encoding = $headerinfo["encoding"];
$split_string = 0;
// Block length is the total length of the strings that will be
// Unicode data should only be split on char (2 byte) boundaries.
// Therefore, in some cases we need to reduce the amount of available
+ // space by 1 byte to ensure the correct alignment.
+ $align = 0;
+
+ // Only applies to Unicode strings
+ if ($encoding == 1) {
+ // Min string + header size -1
+ $header_length = 4;
+
+ if ($space_remaining > $header_length) {
+ // String contains 3 byte header => split on odd boundary
+ if (!$split_string && $space_remaining % 2 != 1) {
+ $space_remaining--;
+ $align = 1;
+ }
+ // Split section without header => split on even boundary
+ else if ($split_string && $space_remaining % 2 == 1) {
+ $space_remaining--;
+ $align = 1;
+ }
+
+ $split_string = 1;
+ }
+ }
+
if ($space_remaining > $header_length) {
// Write as much as possible of the string in the current block
$string = substr($string, $space_remaining);
// Reduce the current block length by the amount written
- $block_length -= $continue_limit - $continue;
+ $block_length -= $continue_limit - $continue - $align;
// If the current string was split then the next CONTINUE block
// should have the string continue flag (grbit) set unless the
// Write the CONTINUE block header
if (!empty($this->_block_sizes)) {
$record = 0x003C;
- $length = array_pop($this->_block_sizes);
+ $length = array_shift($tmp_block_sizes);
+
$header = pack('vv', $record, $length);
if ($continue) {
$header .= pack('C', $encoding);
}
}
}
-?>