git-svn-id: https://svn.php.net/repository/pear/packages/Spreadsheet_Excel_Writer/trunk@160425 c90b9560-bf6c-de11-be94-00142212c4b1
This commit is contained in:
Xavier Noguer Gallego 2004-06-02 20:30:18 +00:00
parent c9e9efad49
commit c80071d1fa
1 changed files with 213 additions and 148 deletions

View File

@ -1304,47 +1304,119 @@ class Spreadsheet_Excel_Writer_Workbook extends Spreadsheet_Excel_Writer_BIFFwri
} }
/** /**
* new method for calculating blocksizes of SST-record. * Calculate
* Handling of the SST continue blocks is complicated by the need to include an * Handling of the SST continue blocks is complicated by the need to include an
* additional continuation byte depending on whether the string is split between * additional continuation byte depending on whether the string is split between
* blocks or whether it starts at the beginning of the block. (There are also * blocks or whether it starts at the beginning of the block. (There are also
* additional complications that will arise later when/if Rich Strings are * additional complications that will arise later when/if Rich Strings are
* supported). * supported).
* *
* @author <jaenichen@globalpark.de>
* @access private * @access private
*/ */
function _calculateSharedStringsSizes() function _calculateSharedStringsSizes()
{ {
$total_offset = 0; /* Iterate through the strings to calculate the CONTINUE block sizes.
$continue_limit = 8228; For simplicity we use the same size for the SST and CONTINUE records:
$header_size = 4; 8228 : Maximum Excel97 block size
-4 : Length of block header
-8 : Length of additional SST header information
= 8216
*/
$continue_limit = 8216;
$block_length = 0;
$written = 0;
$this->_block_sizes = array(); $this->_block_sizes = array();
$continue = 0;
// set the SST block header information foreach (array_keys($this->_str_table) as $string) {
$buffer = pack("vv", 0x00fc, 0); $string_length = strlen($string);
$buffer .= pack("VV", 0, 0);
foreach (array_keys($this->_str_table) as $string) // Block length is the total length of the strings that will be
{ // written out in a single SST or CONTINUE block.
// block is full $block_length += $string_length;
if ((strlen($buffer) + strlen($string)) > $continue_limit) {
$this->_calculateSSTContinueBlock($string, &$buffer, &$total_offset, $header_size, $continue_limit); // We can write the string if it doesn't cross a CONTINUE boundary
if ($block_length < $continue_limit) {
$written += $string_length;
continue;
}
// Deal with the cases where the next string to be written will exceed
// the CONTINUE boundary. If the string is very long it may need to be
// written in more than one CONTINUE record.
while ($block_length >= $continue_limit) {
// We need to avoid the case where a string is continued in the first
// n bytes that contain the string header information.
$header_length = 3; // Min string + header size -1
$space_remaining = $continue_limit - $written - $continue;
/* TODO: Unicode data should only be split on char (2 byte)
boundaries. Therefore, in some cases we need to reduce the
amount of available
*/
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;
// Store the max size for this block
$this->_block_sizes[] = $continue_limit;
// If the current string was split then the next CONTINUE block
// should have the string continue flag (grbit) set unless the
// split string fits exactly into the remaining space.
if ($block_length > 0) {
$continue = 1;
}
else {
$continue = 0;
}
}
else {
// Store the max size for this block
$this->_block_sizes[] = $written + $continue;
// Not enough space to start the string in the current block
$block_length -= $continue_limit - $space_remaining - $continue;
$continue = 0;
}
// If the string (or substr) is small enough we can write it in the
// new CONTINUE block. Else, go through the loop again to write it in
// one or more CONTINUE blocks
if ($block_length < $continue_limit) {
$written = $block_length;
}
else {
$written = 0;
} }
else
{
// add string to block
$buffer .= $string;
} }
} }
// save last block // Store the max size for the last block unless it is empty
if (strlen($buffer) > 0) if ($written + $continue) {
{ $this->_block_sizes[] = $written + $continue;
$this->_block_sizes[] = (strlen($buffer) - $header_size);
$total_offset += strlen($buffer);
} }
/* Calculate the total length of the SST and associated CONTINUEs (if any).
The SST record will have a length even if it contains no strings.
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
}
return $total_offset; return $total_offset;
} }
@ -1357,122 +1429,115 @@ class Spreadsheet_Excel_Writer_Workbook extends Spreadsheet_Excel_Writer_BIFFwri
* access to SST. However, despite the documentation it doesn't seem to be * access to SST. However, despite the documentation it doesn't seem to be
* required so we will ignore it. * required so we will ignore it.
* *
* @author <jaenichen@globalpark.de>
* @access private * @access private
*/ */
function _storeSharedStringsTable() function _storeSharedStringsTable()
{ {
$continue_limit = 8228;
$record = 0x00fc; // Record identifier $record = 0x00fc; // Record identifier
$length = array_shift($this->_block_sizes); // 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 // Write the SST block header information
$buffer = pack("vv", $record, $length); $header = pack("vv", $record, $length);
$buffer .= pack("VV", $this->_str_total, $this->_str_unique); $data = pack("VV", $this->_str_total, $this->_str_unique);
$this->_append($header.$data);
foreach (array_keys($this->_str_table) as $string)
{ // Iterate through the strings to calculate the CONTINUE block sizes
// block is full $continue_limit = 8216;
if ((strlen($buffer) + strlen($string)) > $continue_limit) { $block_length = 0;
$this->_storeSSTContinueBlock($string, &$buffer, $continue_limit); $written = 0;
} $continue = 0;
else
{
// add string to block /* TODO: not good for performance */
$buffer .= $string; foreach (array_keys($this->_str_table) as $string) {
}
$string_length = strlen($string);
$encoding = 0; // assume there are no Unicode strings
$split_string = 0;
// Block length is the total length of the strings that will be
// written out in a single SST or CONTINUE block.
//
$block_length += $string_length;
// We can write the string if it doesn't cross a CONTINUE boundary
if ($block_length < $continue_limit) {
$this->_append($string);
$written += $string_length;
continue;
} }
// save last block // Deal with the cases where the next string to be written will exceed
if (strlen($buffer) > 0) // the CONTINUE boundary. If the string is very long it may need to be
{ // written in more than one CONTINUE record.
$this->_append($buffer); //
} while ($block_length >= $continue_limit) {
}
/** // We need to avoid the case where a string is continued in the first
* method for calculation of SST-record Continue-Blocks. // n bytes that contain the string header information.
* //
* @author <jaenichen@globalpark.de> $header_length = 3; // Min string + header size -1
* @access private $space_remaining = $continue_limit - $written - $continue;
*/
function _calculateSSTContinueBlock($string, &$buffer, &$total_offset, $header_size, $continue_limit)
{
// calculate remaining space in block
$space = $continue_limit - strlen($buffer);
// if there's space left in block to store at least the sizeinfo of actual string
if ($space > $this->_string_sizeinfo)
{
// split string
$tmp = substr($string, 0, $space);
$buffer .= $tmp;
// save rest of string
$string = substr($string, $space);
}
// save blocksize decremented by headersize // Unicode data should only be split on char (2 byte) boundaries.
$this->_block_sizes[] = (strlen($buffer) - $header_size); // Therefore, in some cases we need to reduce the amount of available
// save full blocksize
$total_offset += strlen($buffer);
// set CONTINUE header if ($space_remaining > $header_length) {
$buffer = pack("vv", 0x003c, 0); // Write as much as possible of the string in the current block
// set optional flagbyte of CONTINUE record for splittet strings $tmp = substr($string, 0, $space_remaining);
if ($space > $this->_string_sizeinfo) { $this->_append($tmp);
$buffer .= pack('C', 0);
}
// block is full again // The remainder will be written in the next block(s)
if ((strlen($buffer) + strlen($string)) > $continue_limit) { $string = substr($string, $space_remaining);
$this->_calculateSSTContinueBlock($string, &$buffer, &$total_offset, $header_size, $continue_limit);
// Reduce the current block length by the amount written
$block_length -= $continue_limit - $continue;
// If the current string was split then the next CONTINUE block
// should have the string continue flag (grbit) set unless the
// split string fits exactly into the remaining space.
//
if ($block_length > 0) {
$continue = 1;
} }
else { else {
$buffer .= $string; $continue = 0;
} }
} }
else {
/** // Not enough space to start the string in the current block
* method for storing of SST-record Continue-Blocks. $block_length -= $continue_limit - $space_remaining - $continue;
* $continue = 0;
* @author <jaenichen@globalpark.de>
* @access private
*/
function _storeSSTContinueBlock($string, &$buffer, $continue_limit)
{
// calculate remaining space in block
$space = $continue_limit - strlen($buffer);
// if there's space left in block to store at least the sizeinfo of actual string
if ($space > $this->_string_sizeinfo)
{
// split string
$tmp = substr($string, 0, $space);
$buffer .= $tmp;
// save rest of string including optional flagbyte of CONTINUE record
$string = substr($string, $space);
} }
// save block // Write the CONTINUE block header
$this->_append($buffer); if (!empty($this->_block_sizes)) {
// save CONTINUE header
$record = 0x003C; $record = 0x003C;
$length = array_shift($this->_block_sizes); $length = array_pop($this->_block_sizes);
$buffer = pack('vv', $record, $length); $header = pack('vv', $record, $length);
// set optional flagbyte of CONTINUE record for splittet strings if ($continue) {
if ($space > $this->_string_sizeinfo) { $header .= pack('C', $encoding);
$buffer .= pack('C', 0); }
$this->_append($header);
} }
// block is full again // If the string (or substr) is small enough we can write it in the
if ((strlen($buffer) + strlen($string)) > $continue_limit) { // new CONTINUE block. Else, go through the loop again to write it in
$this->_storeSSTContinueBlock($string, &$buffer, $continue_limit); // one or more CONTINUE blocks
//
if ($block_length < $continue_limit) {
$this->_append($string);
$written = $block_length;
} }
else { else {
$buffer .= $string; $written = 0;
}
}
} }
} }
} }