| 
					
						 讓 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;                 }             }         }     } 
					
  
						
					 |