華府江山 http://203.72.56.8/phpBB3/ |
|
讓 PHP 產生 Excel 檔案 http://203.72.56.8/phpBB3/viewtopic.php?f=42&t=1035 |
第 1 頁 (共 1 頁) |
發表人: | alex [ 2013年 6月 10日, 08:34 ] |
文章主題 : | 讓 PHP 產生 Excel 檔案 |
讓 PHP 產生 Excel 檔案 /** * Calculate * 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 * 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 * supported). * * @access private */ function _calculateSharedStringsSizes() { /* Iterate through the strings to calculate the CONTINUE block sizes. For simplicity we use the same size for the SST and CONTINUE records: 8228 : Maximum Excel97 block size -4 : Length of block header -8 : Length of additional SST header information -8 : Arbitrary number to keep within _add_continue() limit = 8208 */ $continue_limit = 8208; $block_length = 0; $written = 0; $this->_block_sizes = array(); $continue = 0; 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. $block_length += $string_length; // 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 */ $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 - $align; // Store the max size for this block $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 // 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; } } } // Store the max size for the last block unless it is empty if ($written + $continue) { $this->_block_sizes[] = $written + $continue; } /* 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 */ $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 $length; } /** * Write all of the workbooks strings into an indexed array. * See the comments in _calculate_shared_string_sizes() for more information. * * The Excel documentation says that the SST record should be followed by an * EXTSST record. The EXTSST record is a hash table that is used to optimise * access to SST. However, despite the documentation it doesn't seem to be * required so we will ignore it. * * @access private */ function _storeSharedStringsTable() { $record = 0x00fc; // Record identifier $length = 0x0008; // Number of bytes to follow $total = 0x0000; // Iterate through the strings to calculate the CONTINUE block sizes $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); $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. // $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; } // 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; // 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 $tmp = substr($string, 0, $space_remaining); $this->_append($tmp); // The remainder will be written in the next block(s) $string = substr($string, $space_remaining); // Reduce the current block length by the amount written $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 // split string fits exactly into the remaining space. // if ($block_length > 0) { $continue = 1; } else { $continue = 0; } } else { // Not enough space to start the string in the current block $block_length -= $continue_limit - $space_remaining - $continue; $continue = 0; } // Write the CONTINUE block header if (!empty($this->_block_sizes)) { $record = 0x003C; $length = array_shift($tmp_block_sizes); $header = pack('vv', $record, $length); if ($continue) { $header .= pack('C', $encoding); } $this->_append($header); } // 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) { $this->_append($string); $written = $block_length; } else { $written = 0; } } } } |
第 1 頁 (共 1 頁) | 所有顯示的時間為 UTC + 8 小時 |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |