📄 workbook.php
字号:
$data .= pack("v", $itab); $data .= pack("C", $cchCustMenu); $data .= pack("C", $cchDescription); $data .= pack("C", $cchHelptopic); $data .= pack("C", $cchStatustext); $data .= pack("C", $rgch); $data .= pack("C", $unknown03); $data .= pack("v", $unknown04); $data .= pack("v", $unknown05); $data .= pack("v", $unknown06); $data .= pack("v", $unknown07); $data .= pack("v", $unknown08); $data .= pack("v", $index); $data .= pack("v", $index); $data .= pack("v", $rowmin); $data .= pack("v", $rowmax); $data .= pack("C", $colmin); $data .= pack("C", $colmax); $this->_append($header . $data); } /** * Store the NAME record in the long format that is used for storing the repeat * rows and columns when both are specified. This shares a lot of code with * _storeNameShort() but we use a separate method to keep the code clean. * Code abstraction for reuse can be carried too far, and I should know. ;-) * * @param integer $index Sheet index * @param integer $type Built-in name type * @param integer $rowmin Start row * @param integer $rowmax End row * @param integer $colmin Start colum * @param integer $colmax End column * @access private */ function _storeNameLong($index, $type, $rowmin, $rowmax, $colmin, $colmax) { $record = 0x0018; // Record identifier $length = 0x003d; // Number of bytes to follow $grbit = 0x0020; // Option flags $chKey = 0x00; // Keyboard shortcut $cch = 0x01; // Length of text name $cce = 0x002e; // Length of text definition $ixals = $index + 1; // Sheet index $itab = $ixals; // Equal to ixals $cchCustMenu = 0x00; // Length of cust menu text $cchDescription = 0x00; // Length of description text $cchHelptopic = 0x00; // Length of help topic text $cchStatustext = 0x00; // Length of status bar text $rgch = $type; // Built-in name type $unknown01 = 0x29; $unknown02 = 0x002b; $unknown03 = 0x3b; $unknown04 = 0xffff-$index; $unknown05 = 0x0000; $unknown06 = 0x0000; $unknown07 = 0x1087; $unknown08 = 0x8008; $header = pack("vv", $record, $length); $data = pack("v", $grbit); $data .= pack("C", $chKey); $data .= pack("C", $cch); $data .= pack("v", $cce); $data .= pack("v", $ixals); $data .= pack("v", $itab); $data .= pack("C", $cchCustMenu); $data .= pack("C", $cchDescription); $data .= pack("C", $cchHelptopic); $data .= pack("C", $cchStatustext); $data .= pack("C", $rgch); $data .= pack("C", $unknown01); $data .= pack("v", $unknown02); // Column definition $data .= pack("C", $unknown03); $data .= pack("v", $unknown04); $data .= pack("v", $unknown05); $data .= pack("v", $unknown06); $data .= pack("v", $unknown07); $data .= pack("v", $unknown08); $data .= pack("v", $index); $data .= pack("v", $index); $data .= pack("v", 0x0000); $data .= pack("v", 0x3fff); $data .= pack("C", $colmin); $data .= pack("C", $colmax); // Row definition $data .= pack("C", $unknown03); $data .= pack("v", $unknown04); $data .= pack("v", $unknown05); $data .= pack("v", $unknown06); $data .= pack("v", $unknown07); $data .= pack("v", $unknown08); $data .= pack("v", $index); $data .= pack("v", $index); $data .= pack("v", $rowmin); $data .= pack("v", $rowmax); $data .= pack("C", 0x00); $data .= pack("C", 0xff); // End of data $data .= pack("C", 0x10); $this->_append($header . $data); } /** * Stores the COUNTRY record for localization * * @access private */ function _storeCountry() { $record = 0x008C; // Record identifier $length = 4; // Number of bytes to follow $header = pack('vv', $record, $length); /* using the same country code always for simplicity */ $data = pack('vv', $this->_country_code, $this->_country_code); $this->_append($header . $data); } /** * Stores the PALETTE biff record. * * @access private */ function _storePalette() { $aref = $this->_palette; $record = 0x0092; // Record identifier $length = 2 + 4 * count($aref); // Number of bytes to follow $ccv = count($aref); // Number of RGB values to follow $data = ''; // The RGB data // Pack the RGB data foreach ($aref as $color) { foreach ($color as $byte) { $data .= pack("C",$byte); } } $header = pack("vvv", $record, $length, $ccv); $this->_append($header . $data); } /** * 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 = 8216 */ $continue_limit = 8216; $block_length = 0; $written = 0; $this->_block_sizes = array(); $continue = 0; foreach (array_keys($this->_str_table) as $string) { $string_length = strlen($string); // 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 */ 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; } } } // 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 */ $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; } /** * 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 // 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); // Iterate through the strings to calculate the CONTINUE block sizes $continue_limit = 8216; $block_length = 0; $written = 0; $continue = 0; /* TODO: not good for performance */ 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; } // 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 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; // 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_pop($this->_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; } } } }}?>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -