parent
97540137c5
commit
0a323306f2
@ -0,0 +1,229 @@ |
||||
<?php |
||||
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
||||
|
||||
/** |
||||
* Stream wrapper for reading data stored in an OLE file. |
||||
* |
||||
* PHP versions 4 and 5 |
||||
* |
||||
* LICENSE: This source file is subject to version 3.0 of the PHP license |
||||
* that is available through the world-wide-web at the following URI: |
||||
* http://www.php.net/license/3_0.txt. If you did not receive a copy of |
||||
* the PHP License and are unable to obtain it through the web, please |
||||
* send a note to license@php.net so we can mail you a copy immediately. |
||||
* |
||||
* @category Structures |
||||
* @package OLE |
||||
* @author Christian Schmidt <schmidt@php.net> |
||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||||
* @version CVS: $Id: ChainedBlockStream.php,v 1.1 2007/02/13 21:00:42 schmidt Exp $ |
||||
* @link http://pear.php.net/package/OLE |
||||
* @since File available since Release 0.6.0 |
||||
*/ |
||||
|
||||
require_once 'PEAR.php'; |
||||
require_once 'OLE.php'; |
||||
|
||||
/** |
||||
* Stream wrapper for reading data stored in an OLE file. Implements methods |
||||
* for PHP's stream_wrapper_register(). For creating streams using this |
||||
* wrapper, use OLE_PPS_File::getStream(). |
||||
* |
||||
* @category Structures |
||||
* @package OLE |
||||
* @author Christian Schmidt <schmidt@php.net> |
||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||||
* @version Release: @package_version@ |
||||
* @link http://pear.php.net/package/OLE |
||||
* @since Class available since Release 0.6.0 |
||||
*/ |
||||
class OLE_ChainedBlockStream extends PEAR |
||||
{ |
||||
/** |
||||
* The OLE container of the file that is being read. |
||||
* @var OLE |
||||
*/ |
||||
var $ole; |
||||
|
||||
/** |
||||
* Parameters specified by fopen(). |
||||
* @var array |
||||
*/ |
||||
var $params; |
||||
|
||||
/** |
||||
* The binary data of the file. |
||||
* @var string |
||||
*/ |
||||
var $data; |
||||
|
||||
/** |
||||
* The file pointer. |
||||
* @var int byte offset |
||||
*/ |
||||
var $pos; |
||||
|
||||
/** |
||||
* Implements support for fopen(). |
||||
* For creating streams using this wrapper, use OLE_PPS_File::getStream(). |
||||
* @param string resource name including scheme, e.g. |
||||
* ole-chainedblockstream://oleInstanceId=1 |
||||
* @param string only "r" is supported |
||||
* @param int mask of STREAM_REPORT_ERRORS and STREAM_USE_PATH |
||||
* @param string absolute path of the opened stream (out parameter) |
||||
* @return bool true on success |
||||
*/ |
||||
function stream_open($path, $mode, $options, &$openedPath) |
||||
{ |
||||
if ($mode != 'r') { |
||||
if ($options & STREAM_REPORT_ERRORS) { |
||||
trigger_error('Only reading is supported', E_USER_WARNING); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
// 25 is length of "ole-chainedblockstream://" |
||||
parse_str(substr($path, 25), $this->params); |
||||
if (!isset($this->params['oleInstanceId'], |
||||
$this->params['blockId'], |
||||
$GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) { |
||||
|
||||
if ($options & STREAM_REPORT_ERRORS) { |
||||
trigger_error('OLE stream not found', E_USER_WARNING); |
||||
} |
||||
return false; |
||||
} |
||||
$this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']]; |
||||
|
||||
$blockId = $this->params['blockId']; |
||||
$this->data = ''; |
||||
if (isset($this->params['size']) && |
||||
$this->params['size'] < $this->ole->bigBlockThreshold && |
||||
$blockId != $this->ole->root->_StartBlock) { |
||||
|
||||
// Block id refers to small blocks |
||||
$rootPos = $this->ole->_getBlockOffset($this->ole->root->_StartBlock); |
||||
while ($blockId != -2) { |
||||
$pos = $rootPos + $blockId * $this->ole->bigBlockSize; |
||||
$blockId = $this->ole->sbat[$blockId]; |
||||
fseek($this->ole->_file_handle, $pos); |
||||
$this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize); |
||||
} |
||||
} else { |
||||
// Block id refers to big blocks |
||||
while ($blockId != -2) { |
||||
$pos = $this->ole->_getBlockOffset($blockId); |
||||
fseek($this->ole->_file_handle, $pos); |
||||
$this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize); |
||||
$blockId = $this->ole->bbat[$blockId]; |
||||
} |
||||
} |
||||
if (isset($this->params['size'])) { |
||||
$this->data = substr($this->data, 0, $this->params['size']); |
||||
} |
||||
|
||||
if ($options & STREAM_USE_PATH) { |
||||
$openedPath = $path; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Implements support for fclose(). |
||||
* @return string |
||||
*/ |
||||
function stream_close() |
||||
{ |
||||
$this->ole = null; |
||||
unset($GLOBALS['_OLE_INSTANCES']); |
||||
} |
||||
|
||||
/** |
||||
* Implements support for fread(), fgets() etc. |
||||
* @param int maximum number of bytes to read |
||||
* @return string |
||||
*/ |
||||
function stream_read($count) |
||||
{ |
||||
if ($this->stream_eof()) { |
||||
return false; |
||||
} |
||||
$s = substr($this->data, $this->pos, $count); |
||||
$this->pos += $count; |
||||
return $s; |
||||
} |
||||
|
||||
/** |
||||
* Implements support for feof(). |
||||
* @return bool TRUE if the file pointer is at EOF; otherwise FALSE |
||||
*/ |
||||
function stream_eof() |
||||
{ |
||||
$eof = $this->pos >= strlen($this->data); |
||||
// Workaround for bug in PHP 5.0.x: http://bugs.php.net/27508 |
||||
if (version_compare(PHP_VERSION, '5.0', '>=') && |
||||
version_compare(PHP_VERSION, '5.1', '<')) { |
||||
|
||||
$eof = !$eof; |
||||
} |
||||
return $eof; |
||||
} |
||||
|
||||
/** |
||||
* Returns the position of the file pointer, i.e. its offset into the file |
||||
* stream. Implements support for ftell(). |
||||
* @return int |
||||
*/ |
||||
function stream_tell() |
||||
{ |
||||
return $this->pos; |
||||
} |
||||
|
||||
/** |
||||
* Implements support for fseek(). |
||||
* @param int byte offset |
||||
* @param int SEEK_SET, SEEK_CUR or SEEK_END |
||||
* @return bool |
||||
*/ |
||||
function stream_seek($offset, $whence) |
||||
{ |
||||
if ($whence == SEEK_SET && $offset >= 0) { |
||||
$this->pos = $offset; |
||||
} elseif ($whence == SEEK_CUR && -$offset <= $this->pos) { |
||||
$this->pos += $offset; |
||||
} elseif ($whence == SEEK_END && -$offset <= sizeof($this->data)) { |
||||
$this->pos = strlen($this->data) + $offset; |
||||
} else { |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Implements support for fstat(). Currently the only supported field is |
||||
* "size". |
||||
* @return array |
||||
*/ |
||||
function stream_stat() |
||||
{ |
||||
return array( |
||||
'size' => strlen($this->data), |
||||
); |
||||
} |
||||
|
||||
// Methods used by stream_wrapper_register() that are not implemented: |
||||
// bool stream_flush ( void ) |
||||
// int stream_write ( string data ) |
||||
// bool rename ( string path_from, string path_to ) |
||||
// bool mkdir ( string path, int mode, int options ) |
||||
// bool rmdir ( string path, int options ) |
||||
// bool dir_opendir ( string path, int options ) |
||||
// array url_stat ( string path, int flags ) |
||||
// string dir_readdir ( void ) |
||||
// bool dir_rewinddir ( void ) |
||||
// bool dir_closedir ( void ) |
||||
} |
||||
|
||||
?> |
||||
@ -0,0 +1,570 @@ |
||||
<?php |
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */ |
||||
// +----------------------------------------------------------------------+ |
||||
// | PHP Version 4 | |
||||
// +----------------------------------------------------------------------+ |
||||
// | Copyright (c) 1997-2002 The PHP Group | |
||||
// +----------------------------------------------------------------------+ |
||||
// | This source file is subject to version 2.02 of the PHP license, | |
||||
// | that is bundled with this package in the file LICENSE, and is | |
||||
// | available at through the world-wide-web at | |
||||
// | http://www.php.net/license/2_02.txt. | |
||||
// | If you did not receive a copy of the PHP license and are unable to | |
||||
// | obtain it through the world-wide-web, please send a note to | |
||||
// | license@php.net so we can mail you a copy immediately. | |
||||
// +----------------------------------------------------------------------+ |
||||
// | Author: Xavier Noguer <xnoguer@php.net> | |
||||
// | Based on OLE::Storage_Lite by Kawai, Takanori | |
||||
// +----------------------------------------------------------------------+ |
||||
// |
||||
// $Id: OLE.php,v 1.15 2007/12/18 20:59:11 schmidt Exp $ |
||||
|
||||
|
||||
/** |
||||
* Constants for OLE package |
||||
*/ |
||||
define('OLE_PPS_TYPE_ROOT', 5); |
||||
define('OLE_PPS_TYPE_DIR', 1); |
||||
define('OLE_PPS_TYPE_FILE', 2); |
||||
define('OLE_DATA_SIZE_SMALL', 0x1000); |
||||
define('OLE_LONG_INT_SIZE', 4); |
||||
define('OLE_PPS_SIZE', 0x80); |
||||
|
||||
require_once 'PEAR.php'; |
||||
|
||||
/** |
||||
* Array for storing OLE instances that are accessed from |
||||
* OLE_ChainedBlockStream::stream_open(). |
||||
* @var array |
||||
*/ |
||||
$GLOBALS['_OLE_INSTANCES'] = array(); |
||||
|
||||
/** |
||||
* OLE package base class. |
||||
* |
||||
* @category Structures |
||||
* @package OLE |
||||
* @author Xavier Noguer <xnoguer@php.net> |
||||
* @author Christian Schmidt <schmidt@php.net> |
||||
*/ |
||||
class OLE extends PEAR |
||||
{ |
||||
|
||||
/** |
||||
* The file handle for reading an OLE container |
||||
* @var resource |
||||
*/ |
||||
var $_file_handle; |
||||
|
||||
/** |
||||
* Array of PPS's found on the OLE container |
||||
* @var array |
||||
*/ |
||||
var $_list; |
||||
|
||||
/** |
||||
* Root directory of OLE container |
||||
* @var OLE_PPS_Root |
||||
*/ |
||||
var $root; |
||||
|
||||
/** |
||||
* Big Block Allocation Table |
||||
* @var array (blockId => nextBlockId) |
||||
*/ |
||||
var $bbat; |
||||
|
||||
/** |
||||
* Short Block Allocation Table |
||||
* @var array (blockId => nextBlockId) |
||||
*/ |
||||
var $sbat; |
||||
|
||||
/** |
||||
* Size of big blocks. This is usually 512. |
||||
* @var int number of octets per block. |
||||
*/ |
||||
var $bigBlockSize; |
||||
|
||||
/** |
||||
* Size of small blocks. This is usually 64. |
||||
* @var int number of octets per block |
||||
*/ |
||||
var $smallBlockSize; |
||||
|
||||
/** |
||||
* Creates a new OLE object |
||||
* @access public |
||||
*/ |
||||
function OLE() |
||||
{ |
||||
$this->_list = array(); |
||||
} |
||||
|
||||
/** |
||||
* Destructor (using PEAR) |
||||
* Just closes the file handle on the OLE file. |
||||
* |
||||
* @access private |
||||
*/ |
||||
function _OLE() |
||||
{ |
||||
fclose($this->_file_handle); |
||||
} |
||||
|
||||
/** |
||||
* Reads an OLE container from the contents of the file given. |
||||
* |
||||
* @access public |
||||
* @param string $file |
||||
* @return mixed true on success, PEAR_Error on failure |
||||
*/ |
||||
function read($file) |
||||
{ |
||||
$fh = @fopen($file, "r"); |
||||
if (!$fh) { |
||||
return $this->raiseError("Can't open file $file"); |
||||
} |
||||
$this->_file_handle = $fh; |
||||
|
||||
$signature = fread($fh, 8); |
||||
if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) { |
||||
return $this->raiseError("File doesn't seem to be an OLE container."); |
||||
} |
||||
fseek($fh, 28); |
||||
if (fread($fh, 2) != "\xFE\xFF") { |
||||
// This shouldn't be a problem in practice |
||||
return $this->raiseError("Only Little-Endian encoding is supported."); |
||||
} |
||||
// Size of blocks and short blocks in bytes |
||||
$this->bigBlockSize = pow(2, $this->_readInt2($fh)); |
||||
$this->smallBlockSize = pow(2, $this->_readInt2($fh)); |
||||
|
||||
// Skip UID, revision number and version number |
||||
fseek($fh, 44); |
||||
// Number of blocks in Big Block Allocation Table |
||||
$bbatBlockCount = $this->_readInt4($fh); |
||||
|
||||
// Root chain 1st block |
||||
$directoryFirstBlockId = $this->_readInt4($fh); |
||||
|
||||
// Skip unused bytes |
||||
fseek($fh, 56); |
||||
// Streams shorter than this are stored using small blocks |
||||
$this->bigBlockThreshold = $this->_readInt4($fh); |
||||
// Block id of first sector in Short Block Allocation Table |
||||
$sbatFirstBlockId = $this->_readInt4($fh); |
||||
// Number of blocks in Short Block Allocation Table |
||||
$sbbatBlockCount = $this->_readInt4($fh); |
||||
// Block id of first sector in Master Block Allocation Table |
||||
$mbatFirstBlockId = $this->_readInt4($fh); |
||||
// Number of blocks in Master Block Allocation Table |
||||
$mbbatBlockCount = $this->_readInt4($fh); |
||||
$this->bbat = array(); |
||||
|
||||
// Remaining 4 * 109 bytes of current block is beginning of Master |
||||
// Block Allocation Table |
||||
$mbatBlocks = array(); |
||||
for ($i = 0; $i < 109; $i++) { |
||||
$mbatBlocks[] = $this->_readInt4($fh); |
||||
} |
||||
|
||||
// Read rest of Master Block Allocation Table (if any is left) |
||||
$pos = $this->_getBlockOffset($mbatFirstBlockId); |
||||
for ($i = 0; $i < $mbbatBlockCount; $i++) { |
||||
fseek($fh, $pos); |
||||
for ($j = 0; $j < $this->bigBlockSize / 4 - 1; $j++) { |
||||
$mbatBlocks[] = $this->_readInt4($fh); |
||||
} |
||||
// Last block id in each block points to next block |
||||
$pos = $this->_getBlockOffset($this->_readInt4($fh)); |
||||
} |
||||
|
||||
// Read Big Block Allocation Table according to chain specified by |
||||
// $mbatBlocks |
||||
for ($i = 0; $i < $bbatBlockCount; $i++) { |
||||
$pos = $this->_getBlockOffset($mbatBlocks[$i]); |
||||
fseek($fh, $pos); |
||||
for ($j = 0 ; $j < $this->bigBlockSize / 4; $j++) { |
||||
$this->bbat[] = $this->_readInt4($fh); |
||||
} |
||||
} |
||||
|
||||
// Read short block allocation table (SBAT) |
||||
$this->sbat = array(); |
||||
$shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4; |
||||
$sbatFh = $this->getStream($sbatFirstBlockId); |
||||
for ($blockId = 0; $blockId < $shortBlockCount; $blockId++) { |
||||
$this->sbat[$blockId] = $this->_readInt4($sbatFh); |
||||
} |
||||
fclose($sbatFh); |
||||
|
||||
$this->_readPpsWks($directoryFirstBlockId); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* @param int $blockId block id |
||||
* @return int byte offset from beginning of file |
||||
* @access private |
||||
*/ |
||||
function _getBlockOffset($blockId) |
||||
{ |
||||
return 512 + $blockId * $this->bigBlockSize; |
||||
} |
||||
|
||||
/** |
||||
* Returns a stream for use with fread() etc. External callers should |
||||
* use OLE_PPS_File::getStream(). |
||||
* @param int|PPS $blockIdOrPps block id or PPS |
||||
* @return resource read-only stream |
||||
*/ |
||||
function getStream($blockIdOrPps) |
||||
{ |
||||
include_once 'OLE/ChainedBlockStream.php'; |
||||
static $isRegistered = false; |
||||
if (!$isRegistered) { |
||||
stream_wrapper_register('ole-chainedblockstream', |
||||
'OLE_ChainedBlockStream'); |
||||
$isRegistered = true; |
||||
} |
||||
|
||||
// Store current instance in global array, so that it can be accessed |
||||
// in OLE_ChainedBlockStream::stream_open(). |
||||
// Object is removed from self::$instances in OLE_Stream::close(). |
||||
$GLOBALS['_OLE_INSTANCES'][] = $this; |
||||
$instanceId = end(array_keys($GLOBALS['_OLE_INSTANCES'])); |
||||
|
||||
$path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId; |
||||
if (is_a($blockIdOrPps, 'OLE_PPS')) { |
||||
$path .= '&blockId=' . $blockIdOrPps->_StartBlock; |
||||
$path .= '&size=' . $blockIdOrPps->Size; |
||||
} else { |
||||
$path .= '&blockId=' . $blockIdOrPps; |
||||
} |
||||
return fopen($path, 'r'); |
||||
} |
||||
|
||||
/** |
||||
* Reads a signed char. |
||||
* @param resource $fh file handle |
||||
* @return int |
||||
* @access private |
||||
*/ |
||||
function _readInt1($fh) |
||||
{ |
||||
list(, $tmp) = unpack("c", fread($fh, 1)); |
||||
return $tmp; |
||||
} |
||||
|
||||
/** |
||||
* Reads an unsigned short (2 octets). |
||||
* @param resource $fh file handle |
||||
* @return int |
||||
* @access private |
||||
*/ |
||||
function _readInt2($fh) |
||||
{ |
||||
list(, $tmp) = unpack("v", fread($fh, 2)); |
||||
return $tmp; |
||||
} |
||||
|
||||
/** |
||||
* Reads an unsigned long (4 octets). |
||||
* @param resource file handle |
||||
* @return int |
||||
* @access private |
||||
*/ |
||||
function _readInt4($fh) |
||||
{ |
||||
list(, $tmp) = unpack("V", fread($fh, 4)); |
||||
return $tmp; |
||||
} |
||||
|
||||
/** |
||||
* Gets information about all PPS's on the OLE container from the PPS WK's |
||||
* creates an OLE_PPS object for each one. |
||||
* |
||||
* @access private |
||||
* @param integer $blockId the block id of the first block |
||||
* @return mixed true on success, PEAR_Error on failure |
||||
*/ |
||||
function _readPpsWks($blockId) |
||||
{ |
||||
$fh = $this->getStream($blockId); |
||||
for ($pos = 0; ; $pos += 128) { |
||||
fseek($fh, $pos, SEEK_SET); |
||||
$nameUtf16 = fread($fh, 64); |
||||
$nameLength = $this->_readInt2($fh); |
||||
$nameUtf16 = substr($nameUtf16, 0, $nameLength - 2); |
||||
// Simple conversion from UTF-16LE to ISO-8859-1 |
||||
$name = str_replace("\x00", "", $nameUtf16); |
||||
$type = $this->_readInt1($fh); |
||||
switch ($type) { |
||||
case OLE_PPS_TYPE_ROOT: |
||||
require_once 'OLE/PPS/Root.php'; |
||||
$pps = new OLE_PPS_Root(null, null, array()); |
||||
$this->root = $pps; |
||||
break; |
||||
case OLE_PPS_TYPE_DIR: |
||||
$pps = new OLE_PPS(null, null, null, null, null, |
||||
null, null, null, null, array()); |
||||
break; |
||||
case OLE_PPS_TYPE_FILE: |
||||
require_once 'OLE/PPS/File.php'; |
||||
$pps = new OLE_PPS_File($name); |
||||
break; |
||||
default: |
||||
continue; |
||||
} |
||||
fseek($fh, 1, SEEK_CUR); |
||||
$pps->Type = $type; |
||||
$pps->Name = $name; |
||||
$pps->PrevPps = $this->_readInt4($fh); |
||||
$pps->NextPps = $this->_readInt4($fh); |
||||
$pps->DirPps = $this->_readInt4($fh); |
||||
fseek($fh, 20, SEEK_CUR); |
||||
$pps->Time1st = OLE::OLE2LocalDate(fread($fh, 8)); |
||||
$pps->Time2nd = OLE::OLE2LocalDate(fread($fh, 8)); |
||||
$pps->_StartBlock = $this->_readInt4($fh); |
||||
$pps->Size = $this->_readInt4($fh); |
||||
$pps->No = count($this->_list); |
||||
$this->_list[] = $pps; |
||||
|
||||
// check if the PPS tree (starting from root) is complete |
||||
if (isset($this->root) && |
||||
$this->_ppsTreeComplete($this->root->No)) { |
||||
|
||||
break; |
||||
} |
||||
} |
||||
fclose($fh); |
||||
|
||||
// Initialize $pps->children on directories |
||||
foreach ($this->_list as $pps) { |
||||
if ($pps->Type == OLE_PPS_TYPE_DIR || $pps->Type == OLE_PPS_TYPE_ROOT) { |
||||
$nos = array($pps->DirPps); |
||||
$pps->children = array(); |
||||
while ($nos) { |
||||
$no = array_pop($nos); |
||||
if ($no != -1) { |
||||
$childPps = $this->_list[$no]; |
||||
$nos[] = $childPps->PrevPps; |
||||
$nos[] = $childPps->NextPps; |
||||
$pps->children[] = $childPps; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* It checks whether the PPS tree is complete (all PPS's read) |
||||
* starting with the given PPS (not necessarily root) |
||||
* |
||||
* @access private |
||||
* @param integer $index The index of the PPS from which we are checking |
||||
* @return boolean Whether the PPS tree for the given PPS is complete |
||||
*/ |
||||
function _ppsTreeComplete($index) |
||||
{ |
||||
return isset($this->_list[$index]) && |
||||
($pps = $this->_list[$index]) && |
||||
($pps->PrevPps == -1 || |
||||
$this->_ppsTreeComplete($pps->PrevPps)) && |
||||
($pps->NextPps == -1 || |
||||
$this->_ppsTreeComplete($pps->NextPps)) && |
||||
($pps->DirPps == -1 || |
||||
$this->_ppsTreeComplete($pps->DirPps)); |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a PPS is a File PPS or not. |
||||
* If there is no PPS for the index given, it will return false. |
||||
* @param integer $index The index for the PPS |
||||
* @return bool true if it's a File PPS, false otherwise |
||||
* @access public |
||||
*/ |
||||
function isFile($index) |
||||
{ |
||||
if (isset($this->_list[$index])) { |
||||
return ($this->_list[$index]->Type == OLE_PPS_TYPE_FILE); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Checks whether a PPS is a Root PPS or not. |
||||
* If there is no PPS for the index given, it will return false. |
||||
* @param integer $index The index for the PPS. |
||||
* @return bool true if it's a Root PPS, false otherwise |
||||
* @access public |
||||
*/ |
||||
function isRoot($index) |
||||
{ |
||||
if (isset($this->_list[$index])) { |
||||
return ($this->_list[$index]->Type == OLE_PPS_TYPE_ROOT); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Gives the total number of PPS's found in the OLE container. |
||||
* @return integer The total number of PPS's found in the OLE container |
||||
* @access public |
||||
*/ |
||||
function ppsTotal() |
||||
{ |
||||
return count($this->_list); |
||||
} |
||||
|
||||
/** |
||||
* Gets data from a PPS |
||||
* If there is no PPS for the index given, it will return an empty string. |
||||
* @param integer $index The index for the PPS |
||||
* @param integer $position The position from which to start reading |
||||
* (relative to the PPS) |
||||
* @param integer $length The amount of bytes to read (at most) |
||||
* @return string The binary string containing the data requested |
||||
* @access public |
||||
* @see OLE_PPS_File::getStream() |
||||
*/ |
||||
function getData($index, $position, $length) |
||||
{ |
||||
// if position is not valid return empty string |
||||
if (!isset($this->_list[$index]) || |
||||
$position >= $this->_list[$index]->Size || |
||||
$position < 0) { |
||||
|
||||
return ''; |
||||
} |
||||
$fh = $this->getStream($this->_list[$index]); |
||||
$data = stream_get_contents($fh, $length, $position); |
||||
fclose($fh); |
||||
return $data; |
||||
} |
||||
|
||||
/** |
||||
* Gets the data length from a PPS |
||||
* If there is no PPS for the index given, it will return 0. |
||||
* @param integer $index The index for the PPS |
||||
* @return integer The amount of bytes in data the PPS has |
||||
* @access public |
||||
*/ |
||||
function getDataLength($index) |
||||
{ |
||||
if (isset($this->_list[$index])) { |
||||
return $this->_list[$index]->Size; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Utility function to transform ASCII text to Unicode |
||||
* |
||||
* @access public |
||||
* @static |
||||
* @param string $ascii The ASCII string to transform |
||||
* @return string The string in Unicode |
||||
*/ |
||||
function Asc2Ucs($ascii) |
||||
{ |
||||
$rawname = ''; |
||||
for ($i = 0; $i < strlen($ascii); $i++) { |
||||
$rawname .= $ascii{$i} . "\x00"; |
||||
} |
||||
return $rawname; |
||||
} |
||||
|
||||
/** |
||||
* Utility function |
||||
* Returns a string for the OLE container with the date given |
||||
* |
||||
* @access public |
||||
* @static |
||||
* @param integer $date A timestamp |
||||
* @return string The string for the OLE container |
||||
*/ |
||||
function LocalDate2OLE($date = null) |
||||
{ |
||||
if (!isset($date)) { |
||||
return "\x00\x00\x00\x00\x00\x00\x00\x00"; |
||||
} |
||||
|
||||
// factor used for separating numbers into 4 bytes parts |
||||
$factor = pow(2, 32); |
||||
|
||||
// days from 1-1-1601 until the beggining of UNIX era |
||||
$days = 134774; |
||||
// calculate seconds |
||||
$big_date = $days * 24 * 3600 + |
||||
gmmktime(date("H",$date),date("i",$date),date("s",$date), |
||||
date("m",$date),date("d",$date),date("Y",$date)); |
||||
// multiply just to make MS happy |
||||
$big_date *= 10000000; |
||||
|
||||
$high_part = floor($big_date / $factor); |
||||
// lower 4 bytes |
||||
$low_part = floor((($big_date / $factor) - $high_part) * $factor); |
||||
|
||||
// Make HEX string |
||||
$res = ''; |
||||
|
||||
for ($i = 0; $i < 4; $i++) { |
||||
$hex = $low_part % 0x100; |
||||
$res .= pack('c', $hex); |
||||
$low_part /= 0x100; |
||||
} |
||||
for ($i = 0; $i < 4; $i++) { |
||||
$hex = $high_part % 0x100; |
||||
$res .= pack('c', $hex); |
||||
$high_part /= 0x100; |
||||
} |
||||
return $res; |
||||
} |
||||
|
||||
/** |
||||
* Returns a timestamp from an OLE container's date |
||||
* @param integer $string A binary string with the encoded date |
||||
* @return string The timestamp corresponding to the string |
||||
* @access public |
||||
* @static |
||||
*/ |
||||
function OLE2LocalDate($string) |
||||
{ |
||||
if (strlen($string) != 8) { |
||||
return new PEAR_Error("Expecting 8 byte string"); |
||||
} |
||||
|
||||
// factor used for separating numbers into 4 bytes parts |
||||
$factor = pow(2,32); |
||||
$high_part = 0; |
||||
for ($i = 0; $i < 4; $i++) { |
||||
list(, $high_part) = unpack('C', $string{(7 - $i)}); |
||||
if ($i < 3) { |
||||
$high_part *= 0x100; |
||||
} |
||||
} |
||||
$low_part = 0; |
||||
for ($i = 4; $i < 8; $i++) { |
||||
list(, $low_part) = unpack('C', $string{(7 - $i)}); |
||||
if ($i < 7) { |
||||
$low_part *= 0x100; |
||||
} |
||||
} |
||||
$big_date = ($high_part * $factor) + $low_part; |
||||
// translate to seconds |
||||
$big_date /= 10000000; |
||||
|
||||
// days from 1-1-1601 until the beggining of UNIX era |
||||
$days = 134774; |
||||
|
||||
// translate to seconds from beggining of UNIX era |
||||
$big_date -= $days * 24 * 3600; |
||||
return floor($big_date); |
||||
} |
||||
} |
||||
?> |
||||
@ -0,0 +1,222 @@ |
||||
<?php |
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */ |
||||
// +----------------------------------------------------------------------+ |
||||
// | PHP Version 4 | |
||||
// +----------------------------------------------------------------------+ |
||||
// | Copyright (c) 1997-2002 The PHP Group | |
||||
// +----------------------------------------------------------------------+ |
||||
// | This source file is subject to version 2.02 of the PHP license, | |
||||
// | that is bundled with this package in the file LICENSE, and is | |
||||
// | available at through the world-wide-web at | |
||||
// | http://www.php.net/license/2_02.txt. | |
||||
// | If you did not receive a copy of the PHP license and are unable to | |
||||
// | obtain it through the world-wide-web, please send a note to | |
||||
// | license@php.net so we can mail you a copy immediately. | |
||||
// +----------------------------------------------------------------------+ |
||||
// | Author: Xavier Noguer <xnoguer@php.net> | |
||||
// | Based on OLE::Storage_Lite by Kawai, Takanori | |
||||
// +----------------------------------------------------------------------+ |
||||
// |
||||
// $Id: PPS.php,v 1.7 2007/02/13 21:00:42 schmidt Exp $ |
||||
|
||||
|
||||
require_once 'PEAR.php'; |
||||
require_once 'OLE.php'; |
||||
|
||||
/** |
||||
* Class for creating PPS's for OLE containers |
||||
* |
||||
* @author Xavier Noguer <xnoguer@php.net> |
||||
* @category Structures |
||||
* @package OLE |
||||
*/ |
||||
class OLE_PPS extends PEAR |
||||
{ |
||||
/** |
||||
* The PPS index |
||||
* @var integer |
||||
*/ |
||||
var $No; |
||||
|
||||
/** |
||||
* The PPS name (in Unicode) |
||||
* @var string |
||||
*/ |
||||
var $Name; |
||||
|
||||
/** |
||||
* The PPS type. Dir, Root or File |
||||
* @var integer |
||||
*/ |
||||
var $Type; |
||||
|
||||
/** |
||||
* The index of the previous PPS |
||||
* @var integer |
||||
*/ |
||||
var $PrevPps; |
||||
|
||||
/** |
||||
* The index of the next PPS |
||||
* @var integer |
||||
*/ |
||||
var $NextPps; |
||||
|
||||
/** |
||||
* The index of it's first child if this is a Dir or Root PPS |
||||
* @var integer |
||||
*/ |
||||
var $DirPps; |
||||
|
||||
/** |
||||
* A timestamp |
||||
* @var integer |
||||
*/ |
||||
var $Time1st; |
||||
|
||||
/** |
||||
* A timestamp |
||||
* @var integer |
||||
*/ |
||||
var $Time2nd; |
||||
|
||||
/** |
||||
* Starting block (small or big) for this PPS's data inside the container |
||||
* @var integer |
||||
*/ |
||||
var $_StartBlock; |
||||
|
||||
/** |
||||
* The size of the PPS's data (in bytes) |
||||
* @var integer |
||||
*/ |
||||
var $Size; |
||||
|
||||
/** |
||||
* The PPS's data (only used if it's not using a temporary file) |
||||
* @var string |
||||
*/ |
||||
var $_data; |
||||
|
||||
/** |
||||
* Array of child PPS's (only used by Root and Dir PPS's) |
||||
* @var array |
||||
*/ |
||||
var $children = array(); |
||||
|
||||
/** |
||||
* Pointer to OLE container |
||||
* @var OLE |
||||
*/ |
||||
var $ole; |
||||
|
||||
/** |
||||
* The constructor |
||||
* |
||||
* @access public |
||||
* @param integer $No The PPS index |
||||
* @param string $name The PPS name |
||||
* @param integer $type The PPS type. Dir, Root or File |
||||
* @param integer $prev The index of the previous PPS |
||||
* @param integer $next The index of the next PPS |
||||
* @param integer $dir The index of it's first child if this is a Dir or Root PPS |
||||
* @param integer $time_1st A timestamp |
||||
* @param integer $time_2nd A timestamp |
||||
* @param string $data The (usually binary) source data of the PPS |
||||
* @param array $children Array containing children PPS for this PPS |
||||
*/ |
||||
function OLE_PPS($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children) |
||||
{ |
||||
$this->No = $No; |
||||
$this->Name = $name; |
||||
$this->Type = $type; |
||||
$this->PrevPps = $prev; |
||||
$this->NextPps = $next; |
||||
$this->DirPps = $dir; |
||||
$this->Time1st = $time_1st; |
||||
$this->Time2nd = $time_2nd; |
||||
$this->_data = $data; |
||||
$this->children = $children; |
||||
if ($data != '') { |
||||
$this->Size = strlen($data); |
||||
} else { |
||||
$this->Size = 0; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the amount of data saved for this PPS |
||||
* |
||||
* @access private |
||||
* @return integer The amount of data (in bytes) |
||||
*/ |
||||
function _DataLen() |
||||
{ |
||||
if (!isset($this->_data)) { |
||||
return 0; |
||||
} |
||||
if (isset($this->_PPS_FILE)) { |
||||
fseek($this->_PPS_FILE, 0); |
||||
$stats = fstat($this->_PPS_FILE); |
||||
return $stats[7]; |
||||
} else { |
||||
return strlen($this->_data); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns a string with the PPS's WK (What is a WK?) |
||||
* |
||||
* @access private |
||||
* @return string The binary string |
||||
*/ |
||||
function _getPpsWk() |
||||
{ |
||||
$ret = $this->Name; |
||||
for ($i = 0; $i < (64 - strlen($this->Name)); $i++) { |
||||
$ret .= "\x00"; |
||||
} |
||||
$ret .= 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 // Seems to be ok only for Root |
||||
. "\x00\x00\x00\x00" // 100 |
||||
. OLE::LocalDate2OLE($this->Time1st) // 108 |
||||
. OLE::LocalDate2OLE($this->Time2nd) // 116 |
||||
. pack("V", isset($this->_StartBlock)? |
||||
$this->_StartBlock:0) // 120 |
||||
. pack("V", $this->Size) // 124 |
||||
. pack("V", 0); // 128 |
||||
return $ret; |
||||
} |
||||
|
||||
/** |
||||
* Updates index and pointers to previous, next and children PPS's for this |
||||
* PPS. I don't think it'll work with Dir PPS's. |
||||
* |
||||
* @access private |
||||
* @param array &$pps_array Reference to the array of PPS's for the whole OLE |
||||
* container |
||||
* @return integer The index for this PPS |
||||
*/ |
||||
function _savePpsSetPnt(&$pps_array) |
||||
{ |
||||
$pps_array[count($pps_array)] = &$this; |
||||
$this->No = count($pps_array) - 1; |
||||
$this->PrevPps = 0xFFFFFFFF; |
||||
$this->NextPps = 0xFFFFFFFF; |
||||
if (count($this->children) > 0) { |
||||
$this->DirPps = $this->children[0]->_savePpsSetPnt($pps_array); |
||||
} else { |
||||
$this->DirPps = 0xFFFFFFFF; |
||||
} |
||||
return $this->No; |
||||
} |
||||
} |
||||
?> |
||||
@ -0,0 +1,124 @@ |
||||
<?php |
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */ |
||||
// +----------------------------------------------------------------------+ |
||||
// | PHP Version 4 | |
||||
// +----------------------------------------------------------------------+ |
||||
// | Copyright (c) 1997-2002 The PHP Group | |
||||
// +----------------------------------------------------------------------+ |
||||
// | This source file is subject to version 2.02 of the PHP license, | |
||||
// | that is bundled with this package in the file LICENSE, and is | |
||||
// | available at through the world-wide-web at | |
||||
// | http://www.php.net/license/2_02.txt. | |
||||
// | If you did not receive a copy of the PHP license and are unable to | |
||||
// | obtain it through the world-wide-web, please send a note to | |
||||
// | license@php.net so we can mail you a copy immediately. | |
||||
// +----------------------------------------------------------------------+ |
||||
// | Author: Xavier Noguer <xnoguer@php.net> | |
||||
// | Based on OLE::Storage_Lite by Kawai, Takanori | |
||||
// +----------------------------------------------------------------------+ |
||||
// |
||||
// $Id: File.php,v 1.11 2007/02/13 21:00:42 schmidt Exp $ |
||||
|
||||
|
||||
require_once api_get_path(LIBRARY_PATH).'pear/OLE/PPS.php'; |
||||
|
||||
/** |
||||
* Class for creating File PPS's for OLE containers |
||||
* |
||||
* @author Xavier Noguer <xnoguer@php.net> |
||||
* @category Structures |
||||
* @package OLE |
||||
*/ |
||||
class OLE_PPS_File extends OLE_PPS |
||||
{ |
||||
/** |
||||
* The temporary dir for storing the OLE file |
||||
* @var string |
||||
*/ |
||||
var $_tmp_dir; |
||||
|
||||
/** |
||||
* The constructor |
||||
* |
||||
* @access public |
||||
* @param string $name The name of the file (in Unicode) |
||||
* @see OLE::Asc2Ucs() |
||||
*/ |
||||
function OLE_PPS_File($name) |
||||
{ |
||||
$this->_tmp_dir = ''; |
||||
$this->OLE_PPS( |
||||
null, |
||||
$name, |
||||
OLE_PPS_TYPE_FILE, |
||||
null, |
||||
null, |
||||
null, |
||||
null, |
||||
null, |
||||
'', |
||||
array()); |
||||
} |
||||
|
||||
/** |
||||
* Sets the temp dir used for storing the OLE file |
||||
* |
||||
* @access public |
||||
* @param string $dir The dir to be used as temp dir |
||||
* @return true if given dir is valid, false otherwise |
||||
*/ |
||||
function setTempDir($dir) |
||||
{ |
||||
if (is_dir($dir)) { |
||||
$this->_tmp_dir = $dir; |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Initialization method. Has to be called right after OLE_PPS_File(). |
||||
* |
||||
* @access public |
||||
* @return mixed true on success. PEAR_Error on failure |
||||
*/ |
||||
function init() |
||||
{ |
||||
$this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_File"); |
||||
$fh = @fopen($this->_tmp_filename, "w+b"); |
||||
if ($fh == false) { |
||||
return $this->raiseError("Can't create temporary file"); |
||||
} |
||||
$this->_PPS_FILE = $fh; |
||||
if ($this->_PPS_FILE) { |
||||
fseek($this->_PPS_FILE, 0); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Append data to PPS |
||||
* |
||||
* @access public |
||||
* @param string $data The data to append |
||||
*/ |
||||
function append($data) |
||||
{ |
||||
if ($this->_PPS_FILE) { |
||||
fwrite($this->_PPS_FILE, $data); |
||||
} else { |
||||
$this->_data .= $data; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns a stream for reading this file using fread() etc. |
||||
* @return resource a read-only stream |
||||
*/ |
||||
function getStream() |
||||
{ |
||||
$this->ole->getStream($this); |
||||
} |
||||
} |
||||
?> |
||||
@ -0,0 +1,485 @@ |
||||
<?php |
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */ |
||||
// +----------------------------------------------------------------------+ |
||||
// | PHP Version 4 | |
||||
// +----------------------------------------------------------------------+ |
||||
// | Copyright (c) 1997-2002 The PHP Group | |
||||
// +----------------------------------------------------------------------+ |
||||
// | This source file is subject to version 2.02 of the PHP license, | |
||||
// | that is bundled with this package in the file LICENSE, and is | |
||||
// | available at through the world-wide-web at | |
||||
// | http://www.php.net/license/2_02.txt. | |
||||
// | If you did not receive a copy of the PHP license and are unable to | |
||||
// | obtain it through the world-wide-web, please send a note to | |
||||
// | license@php.net so we can mail you a copy immediately. | |
||||
// +----------------------------------------------------------------------+ |
||||
// | Author: Xavier Noguer <xnoguer@php.net> | |
||||
// | Based on OLE::Storage_Lite by Kawai, Takanori | |
||||
// +----------------------------------------------------------------------+ |
||||
// |
||||
// $Id: Root.php,v 1.9 2005/04/23 21:53:49 dufuz Exp $ |
||||
|
||||
|
||||
require_once 'OLE/PPS.php'; |
||||
|
||||
/** |
||||
* Class for creating Root PPS's for OLE containers |
||||
* |
||||
* @author Xavier Noguer <xnoguer@php.net> |
||||
* @category Structures |
||||
* @package OLE |
||||
*/ |
||||
class OLE_PPS_Root extends OLE_PPS |
||||
{ |
||||
/** |
||||
* The temporary dir for storing the OLE file |
||||
* @var string |
||||
*/ |
||||
var $_tmp_dir; |
||||
|
||||
/** |
||||
* Constructor |
||||
* |
||||
* @access public |
||||
* @param integer $time_1st A timestamp |
||||
* @param integer $time_2nd A timestamp |
||||
*/ |
||||
function OLE_PPS_Root($time_1st, $time_2nd, $raChild) |
||||
{ |
||||
$this->_tmp_dir = ''; |
||||
$this->OLE_PPS( |
||||
null, |
||||
OLE::Asc2Ucs('Root Entry'), |
||||
OLE_PPS_TYPE_ROOT, |
||||
null, |
||||
null, |
||||
null, |
||||
$time_1st, |
||||
$time_2nd, |
||||
null, |
||||
$raChild); |
||||
} |
||||
|
||||
/** |
||||
* Sets the temp dir used for storing the OLE file |
||||
* |
||||
* @access public |
||||
* @param string $dir The dir to be used as temp dir |
||||
* @return true if given dir is valid, false otherwise |
||||
*/ |
||||
function setTempDir($dir) |
||||
{ |
||||
if (is_dir($dir)) { |
||||
$this->_tmp_dir = $dir; |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Method for saving the whole OLE container (including files). |
||||
* In fact, if called with an empty argument (or '-'), it saves to a |
||||
* temporary file and then outputs it's contents to stdout. |
||||
* |
||||
* @param string $filename The name of the file where to save the OLE container |
||||
* @access public |
||||
* @return mixed true on success, PEAR_Error on failure |
||||
*/ |
||||
function save($filename) |
||||
{ |
||||
// Initial Setting for saving |
||||
$this->_BIG_BLOCK_SIZE = pow(2, |
||||
((isset($this->_BIG_BLOCK_SIZE))? $this->_adjust2($this->_BIG_BLOCK_SIZE) : 9)); |
||||
$this->_SMALL_BLOCK_SIZE= pow(2, |
||||
((isset($this->_SMALL_BLOCK_SIZE))? $this->_adjust2($this->_SMALL_BLOCK_SIZE): 6)); |
||||
|
||||
// Open temp file if we are sending output to stdout |
||||
if (($filename == '-') || ($filename == '')) { |
||||
$this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root"); |
||||
$this->_FILEH_ = @fopen($this->_tmp_filename,"w+b"); |
||||
if ($this->_FILEH_ == false) { |
||||
return $this->raiseError("Can't create temporary file."); |
||||
} |
||||
} else { |
||||
$this->_FILEH_ = @fopen($filename, "wb"); |
||||
if ($this->_FILEH_ == false) { |
||||
return $this->raiseError("Can't open $filename. It may be in use or protected."); |
||||
} |
||||
} |
||||
// Make an array of PPS's (for Save) |
||||
$aList = array(); |
||||
$this->_savePpsSetPnt($aList); |
||||
// calculate values for header |
||||
list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo); |
||||
// Save Header |
||||
$this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt); |
||||
|
||||
// Make Small Data string (write SBD) |
||||
$this->_data = $this->_makeSmallData($aList); |
||||
|
||||
// Write BB |
||||
$this->_saveBigData($iSBDcnt, $aList); |
||||
// Write PPS |
||||
$this->_savePps($aList); |
||||
// Write Big Block Depot and BDList and Adding Header informations |
||||
$this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt); |
||||
// Close File, send it to stdout if necessary |
||||
if (($filename == '-') || ($filename == '')) { |
||||
fseek($this->_FILEH_, 0); |
||||
fpassthru($this->_FILEH_); |
||||
@fclose($this->_FILEH_); |
||||
// Delete the temporary file. |
||||
@unlink($this->_tmp_filename); |
||||
} else { |
||||
@fclose($this->_FILEH_); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Calculate some numbers |
||||
* |
||||
* @access private |
||||
* @param array $raList Reference to an array of PPS's |
||||
* @return array The array of numbers |
||||
*/ |
||||
function _calcSize(&$raList) |
||||
{ |
||||
// Calculate Basic Setting |
||||
list($iSBDcnt, $iBBcnt, $iPPScnt) = array(0,0,0); |
||||
$iSmallLen = 0; |
||||
$iSBcnt = 0; |
||||
for ($i = 0; $i < count($raList); $i++) { |
||||
if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) { |
||||
$raList[$i]->Size = $raList[$i]->_DataLen(); |
||||
if ($raList[$i]->Size < OLE_DATA_SIZE_SMALL) { |
||||
$iSBcnt += floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) |
||||
+ (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); |
||||
} else { |
||||
$iBBcnt += (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + |
||||
(($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); |
||||
} |
||||
} |
||||
} |
||||
$iSmallLen = $iSBcnt * $this->_SMALL_BLOCK_SIZE; |
||||
$iSlCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE); |
||||
$iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt)? 1:0); |
||||
$iBBcnt += (floor($iSmallLen / $this->_BIG_BLOCK_SIZE) + |
||||
(( $iSmallLen % $this->_BIG_BLOCK_SIZE)? 1: 0)); |
||||
$iCnt = count($raList); |
||||
$iBdCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE; |
||||
$iPPScnt = (floor($iCnt/$iBdCnt) + (($iCnt % $iBdCnt)? 1: 0)); |
||||
|
||||
return array($iSBDcnt, $iBBcnt, $iPPScnt); |
||||
} |
||||
|
||||
/** |
||||
* Helper function for caculating a magic value for block sizes |
||||
* |
||||
* @access private |
||||
* @param integer $i2 The argument |
||||
* @see save() |
||||
* @return integer |
||||
*/ |
||||
function _adjust2($i2) |
||||
{ |
||||
$iWk = log($i2)/log(2); |
||||
return ($iWk > floor($iWk))? floor($iWk)+1:$iWk; |
||||
} |
||||
|
||||
/** |
||||
* Save OLE header |
||||
* |
||||
* @access private |
||||
* @param integer $iSBDcnt |
||||
* @param integer $iBBcnt |
||||
* @param integer $iPPScnt |
||||
*/ |
||||
function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt) |
||||
{ |
||||
$FILE = $this->_FILEH_; |
||||
|
||||
// Calculate Basic Setting |
||||
$iBlCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; |
||||
$i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE; |
||||
|
||||
$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); |
||||
|
||||
// Calculate BD count |
||||
if ($iBdCnt > $i1stBdL) { |
||||
while (1) { |
||||
$iBdExL++; |
||||
$iAllW++; |
||||
$iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt)? 1: 0); |
||||
$iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW+$iBdCntW) % $iBlCnt)? 1: 0); |
||||
if ($iBdCnt <= ($iBdExL*$iBlCnt+ $i1stBdL)) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Save Header |
||||
fwrite($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) |
||||
); |
||||
// Extra BDList Start, Count |
||||
if ($iBdCnt < $i1stBdL) { |
||||
fwrite($FILE, |
||||
pack("V", -2). // Extra BDList Start |
||||
pack("V", 0) // Extra BDList Count |
||||
); |
||||
} else { |
||||
fwrite($FILE, pack("V", $iAll+$iBdCnt) . pack("V", $iBdExL)); |
||||
} |
||||
|
||||
// BDList |
||||
for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; $i++) { |
||||
fwrite($FILE, pack("V", $iAll+$i)); |
||||
} |
||||
if ($i < $i1stBdL) { |
||||
for ($j = 0; $j < ($i1stBdL-$i); $j++) { |
||||
fwrite($FILE, (pack("V", -1))); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Saving big data (PPS's with data bigger than OLE_DATA_SIZE_SMALL) |
||||
* |
||||
* @access private |
||||
* @param integer $iStBlk |
||||
* @param array &$raList Reference to array of PPS's |
||||
*/ |
||||
function _saveBigData($iStBlk, &$raList) |
||||
{ |
||||
$FILE = $this->_FILEH_; |
||||
|
||||
// cycle through PPS's |
||||
for ($i = 0; $i < count($raList); $i++) { |
||||
if ($raList[$i]->Type != OLE_PPS_TYPE_DIR) { |
||||
$raList[$i]->Size = $raList[$i]->_DataLen(); |
||||
if (($raList[$i]->Size >= OLE_DATA_SIZE_SMALL) || |
||||
(($raList[$i]->Type == OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) |
||||
{ |
||||
// Write Data |
||||
if (isset($raList[$i]->_PPS_FILE)) { |
||||
$iLen = 0; |
||||
fseek($raList[$i]->_PPS_FILE, 0); // To The Top |
||||
while($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) { |
||||
$iLen += strlen($sBuff); |
||||
fwrite($FILE, $sBuff); |
||||
} |
||||
} else { |
||||
fwrite($FILE, $raList[$i]->_data); |
||||
} |
||||
|
||||
if ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE) { |
||||
for ($j = 0; $j < ($this->_BIG_BLOCK_SIZE - ($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)); $j++) { |
||||
fwrite($FILE, "\x00"); |
||||
} |
||||
} |
||||
// Set For PPS |
||||
$raList[$i]->_StartBlock = $iStBlk; |
||||
$iStBlk += |
||||
(floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + |
||||
(($raList[$i]->Size % $this->_BIG_BLOCK_SIZE)? 1: 0)); |
||||
} |
||||
// Close file for each PPS, and unlink it |
||||
if (isset($raList[$i]->_PPS_FILE)) { |
||||
@fclose($raList[$i]->_PPS_FILE); |
||||
$raList[$i]->_PPS_FILE = null; |
||||
@unlink($raList[$i]->_tmp_filename); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* get small data (PPS's with data smaller than OLE_DATA_SIZE_SMALL) |
||||
* |
||||
* @access private |
||||
* @param array &$raList Reference to array of PPS's |
||||
*/ |
||||
function _makeSmallData(&$raList) |
||||
{ |
||||
$sRes = ''; |
||||
$FILE = $this->_FILEH_; |
||||
$iSmBlk = 0; |
||||
|
||||
for ($i = 0; $i < count($raList); $i++) { |
||||
// Make SBD, small data string |
||||
if ($raList[$i]->Type == OLE_PPS_TYPE_FILE) { |
||||
if ($raList[$i]->Size <= 0) { |
||||
continue; |
||||
} |
||||
if ($raList[$i]->Size < OLE_DATA_SIZE_SMALL) { |
||||
$iSmbCnt = floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) |
||||
+ (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)? 1: 0); |
||||
// Add to SBD |
||||
for ($j = 0; $j < ($iSmbCnt-1); $j++) { |
||||
fwrite($FILE, pack("V", $j+$iSmBlk+1)); |
||||
} |
||||
fwrite($FILE, pack("V", -2)); |
||||
|
||||
// Add to Data String(this will be written for RootEntry) |
||||
if ($raList[$i]->_PPS_FILE) { |
||||
fseek($raList[$i]->_PPS_FILE, 0); // To The Top |
||||
while ($sBuff = fread($raList[$i]->_PPS_FILE, 4096)) { |
||||
$sRes .= $sBuff; |
||||
} |
||||
} else { |
||||
$sRes .= $raList[$i]->_data; |
||||
} |
||||
if ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE) { |
||||
for ($j = 0; $j < ($this->_SMALL_BLOCK_SIZE - ($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE)); $j++) { |
||||
$sRes .= "\x00"; |
||||
} |
||||
} |
||||
// Set for PPS |
||||
$raList[$i]->_StartBlock = $iSmBlk; |
||||
$iSmBlk += $iSmbCnt; |
||||
} |
||||
} |
||||
} |
||||
$iSbCnt = floor($this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE); |
||||
if ($iSmBlk % $iSbCnt) { |
||||
for ($i = 0; $i < ($iSbCnt - ($iSmBlk % $iSbCnt)); $i++) { |
||||
fwrite($FILE, pack("V", -1)); |
||||
} |
||||
} |
||||
return $sRes; |
||||
} |
||||
|
||||
/** |
||||
* Saves all the PPS's WKs |
||||
* |
||||
* @access private |
||||
* @param array $raList Reference to an array with all PPS's |
||||
*/ |
||||
function _savePps(&$raList) |
||||
{ |
||||
// Save each PPS WK |
||||
for ($i = 0; $i < count($raList); $i++) { |
||||
fwrite($this->_FILEH_, $raList[$i]->_getPpsWk()); |
||||
} |
||||
// Adjust for Block |
||||
$iCnt = count($raList); |
||||
$iBCnt = $this->_BIG_BLOCK_SIZE / OLE_PPS_SIZE; |
||||
if ($iCnt % $iBCnt) { |
||||
for ($i = 0; $i < (($iBCnt - ($iCnt % $iBCnt)) * OLE_PPS_SIZE); $i++) { |
||||
fwrite($this->_FILEH_, "\x00"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Saving Big Block Depot |
||||
* |
||||
* @access private |
||||
* @param integer $iSbdSize |
||||
* @param integer $iBsize |
||||
* @param integer $iPpsCnt |
||||
*/ |
||||
function _saveBbd($iSbdSize, $iBsize, $iPpsCnt) |
||||
{ |
||||
$FILE = $this->_FILEH_; |
||||
// Calculate Basic Setting |
||||
$iBbCnt = $this->_BIG_BLOCK_SIZE / OLE_LONG_INT_SIZE; |
||||
$i1stBdL = ($this->_BIG_BLOCK_SIZE - 0x4C) / OLE_LONG_INT_SIZE; |
||||
|
||||
$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); |
||||
// Calculate BD count |
||||
if ($iBdCnt >$i1stBdL) { |
||||
while (1) { |
||||
$iBdExL++; |
||||
$iAllW++; |
||||
$iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt)? 1: 0); |
||||
$iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW+$iBdCntW) % $iBbCnt)? 1: 0); |
||||
if ($iBdCnt <= ($iBdExL*$iBbCnt+ $i1stBdL)) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Making BD |
||||
// Set for SBD |
||||
if ($iSbdSize > 0) { |
||||
for ($i = 0; $i < ($iSbdSize - 1); $i++) { |
||||
fwrite($FILE, pack("V", $i+1)); |
||||
} |
||||
fwrite($FILE, pack("V", -2)); |
||||
} |
||||
// Set for B |
||||
for ($i = 0; $i < ($iBsize - 1); $i++) { |
||||
fwrite($FILE, pack("V", $i+$iSbdSize+1)); |
||||
} |
||||
fwrite($FILE, pack("V", -2)); |
||||
|
||||
// Set for PPS |
||||
for ($i = 0; $i < ($iPpsCnt - 1); $i++) { |
||||
fwrite($FILE, pack("V", $i+$iSbdSize+$iBsize+1)); |
||||
} |
||||
fwrite($FILE, pack("V", -2)); |
||||
// Set for BBD itself ( 0xFFFFFFFD : BBD) |
||||
for ($i = 0; $i < $iBdCnt; $i++) { |
||||
fwrite($FILE, pack("V", 0xFFFFFFFD)); |
||||
} |
||||
// Set for ExtraBDList |
||||
for ($i = 0; $i < $iBdExL; $i++) { |
||||
fwrite($FILE, pack("V", 0xFFFFFFFC)); |
||||
} |
||||
// Adjust for Block |
||||
if (($iAllW + $iBdCnt) % $iBbCnt) { |
||||
for ($i = 0; $i < ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt)); $i++) { |
||||
fwrite($FILE, pack("V", -1)); |
||||
} |
||||
} |
||||
// Extra BDList |
||||
if ($iBdCnt > $i1stBdL) { |
||||
$iN=0; |
||||
$iNb=0; |
||||
for ($i = $i1stBdL;$i < $iBdCnt; $i++, $iN++) { |
||||
if ($iN >= ($iBbCnt - 1)) { |
||||
$iN = 0; |
||||
$iNb++; |
||||
fwrite($FILE, pack("V", $iAll+$iBdCnt+$iNb)); |
||||
} |
||||
fwrite($FILE, pack("V", $iBsize+$iSbdSize+$iPpsCnt+$i)); |
||||
} |
||||
if (($iBdCnt-$i1stBdL) % ($iBbCnt-1)) { |
||||
for ($i = 0; $i < (($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1))); $i++) { |
||||
fwrite($FILE, pack("V", -1)); |
||||
} |
||||
} |
||||
fwrite($FILE, pack("V", -2)); |
||||
} |
||||
} |
||||
} |
||||
?> |
||||
@ -0,0 +1,104 @@ |
||||
<?php |
||||
/* |
||||
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com> |
||||
* |
||||
* PERL Spreadsheet::WriteExcel module. |
||||
* |
||||
* The author of the Spreadsheet::WriteExcel module is John McNamara |
||||
* <jmcnamara@cpan.org> |
||||
* |
||||
* I _DO_ maintain this code, and John McNamara has nothing to do with the |
||||
* porting of this code to PHP. Any questions directly related to this |
||||
* class library should be directed to me. |
||||
* |
||||
* License Information: |
||||
* |
||||
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets |
||||
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
*/ |
||||
|
||||
//require_once(api_get_path(LIBRARY_PATH).'pear/PEAR.php'); |
||||
require_once('Writer/Workbook.php'); |
||||
|
||||
/** |
||||
* Class for writing Excel Spreadsheets. This class should change COMPLETELY. |
||||
* |
||||
* @author Xavier Noguer <xnoguer@rezebra.com> |
||||
* @category FileFormats |
||||
* @package Spreadsheet_Excel_Writer |
||||
*/ |
||||
|
||||
class Spreadsheet_Excel_Writer extends Spreadsheet_Excel_Writer_Workbook |
||||
{ |
||||
/** |
||||
* The constructor. It just creates a Workbook |
||||
* |
||||
* @param string $filename The optional filename for the Workbook. |
||||
* @return Spreadsheet_Excel_Writer_Workbook The Workbook created |
||||
*/ |
||||
function Spreadsheet_Excel_Writer($filename = '') |
||||
{ |
||||
$this->_filename = $filename; |
||||
$this->Spreadsheet_Excel_Writer_Workbook($filename); |
||||
} |
||||
|
||||
/** |
||||
* Send HTTP headers for the Excel file. |
||||
* |
||||
* @param string $filename The filename to use for HTTP headers |
||||
* @access public |
||||
*/ |
||||
function send($filename) |
||||
{ |
||||
header("Content-type: application/vnd.ms-excel"); |
||||
header("Content-Disposition: attachment; filename=\"$filename\""); |
||||
header("Expires: 0"); |
||||
header("Cache-Control: must-revalidate, post-check=0,pre-check=0"); |
||||
header("Pragma: public"); |
||||
} |
||||
|
||||
/** |
||||
* Utility function for writing formulas |
||||
* Converts a cell's coordinates to the A1 format. |
||||
* |
||||
* @access public |
||||
* @static |
||||
* @param integer $row Row for the cell to convert (0-indexed). |
||||
* @param integer $col Column for the cell to convert (0-indexed). |
||||
* @return string The cell identifier in A1 format |
||||
*/ |
||||
function rowcolToCell($row, $col) |
||||
{ |
||||
if ($col > 255) { //maximum column value exceeded |
||||
return new PEAR_Error("Maximum column value exceeded: $col"); |
||||
} |
||||
|
||||
$int = (int)($col / 26); |
||||
$frac = $col % 26; |
||||
$chr1 = ''; |
||||
|
||||
if ($int > 0) { |
||||
$chr1 = chr(ord('A') + $int - 1); |
||||
} |
||||
|
||||
$chr2 = chr(ord('A') + $frac); |
||||
$row++; |
||||
|
||||
return $chr1 . $chr2 . $row; |
||||
} |
||||
} |
||||
?> |
||||
@ -0,0 +1,238 @@ |
||||
<?php |
||||
/* |
||||
* Module written/ported by Xavier Noguer <xnoguer@php.net> |
||||
* |
||||
* The majority of this is _NOT_ my code. I simply ported it from the |
||||
* PERL Spreadsheet::WriteExcel module. |
||||
* |
||||
* The author of the Spreadsheet::WriteExcel module is John McNamara |
||||
* <jmcnamara@cpan.org> |
||||
* |
||||
* I _DO_ maintain this code, and John McNamara has nothing to do with the |
||||
* porting of this code to PHP. Any questions directly related to this |
||||
* class library should be directed to me. |
||||
* |
||||
* License Information: |
||||
* |
||||
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets |
||||
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@php.net |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
*/ |
||||
|
||||
require_once 'PEAR.php'; |
||||
|
||||
/** |
||||
* Class for writing Excel BIFF records. |
||||
* |
||||
* From "MICROSOFT EXCEL BINARY FILE FORMAT" by Mark O'Brien (Microsoft Corporation): |
||||
* |
||||
* BIFF (BInary File Format) is the file format in which Excel documents are |
||||
* saved on disk. A BIFF file is a complete description of an Excel document. |
||||
* BIFF files consist of sequences of variable-length records. There are many |
||||
* different types of BIFF records. For example, one record type describes a |
||||
* formula entered into a cell; one describes the size and location of a |
||||
* window into a document; another describes a picture format. |
||||
* |
||||
* @author Xavier Noguer <xnoguer@php.net> |
||||
* @category FileFormats |
||||
* @package Spreadsheet_Excel_Writer |
||||
*/ |
||||
|
||||
class Spreadsheet_Excel_Writer_BIFFwriter extends PEAR |
||||
{ |
||||
/** |
||||
* The BIFF/Excel version (5). |
||||
* @var integer |
||||
*/ |
||||
var $_BIFF_version = 0x0500; |
||||
|
||||
/** |
||||
* The byte order of this architecture. 0 => little endian, 1 => big endian |
||||
* @var integer |
||||
*/ |
||||
var $_byte_order; |
||||
|
||||
/** |
||||
* The string containing the data of the BIFF stream |
||||
* @var string |
||||
*/ |
||||
var $_data; |
||||
|
||||
/** |
||||
* The size of the data in bytes. Should be the same as strlen($this->_data) |
||||
* @var integer |
||||
*/ |
||||
var $_datasize; |
||||
|
||||
/** |
||||
* The maximun length for a BIFF record. See _addContinue() |
||||
* @var integer |
||||
* @see _addContinue() |
||||
*/ |
||||
var $_limit; |
||||
|
||||
/** |
||||
* Constructor |
||||
* |
||||
* @access public |
||||
*/ |
||||
function Spreadsheet_Excel_Writer_BIFFwriter() |
||||
{ |
||||
$this->_byte_order = ''; |
||||
$this->_data = ''; |
||||
$this->_datasize = 0; |
||||
$this->_limit = 2080; |
||||
// Set the byte order |
||||
$this->_setByteOrder(); |
||||
} |
||||
|
||||
/** |
||||
* Determine the byte order and store it as class data to avoid |
||||
* recalculating it for each call to new(). |
||||
* |
||||
* @access private |
||||
*/ |
||||
function _setByteOrder() |
||||
{ |
||||
// Check if "pack" gives the required IEEE 64bit float |
||||
$teststr = pack("d", 1.2345); |
||||
$number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); |
||||
if ($number == $teststr) { |
||||
$byte_order = 0; // Little Endian |
||||
} elseif ($number == strrev($teststr)){ |
||||
$byte_order = 1; // Big Endian |
||||
} else { |
||||
// Give up. I'll fix this in a later version. |
||||
return $this->raiseError("Required floating point format ". |
||||
"not supported on this platform."); |
||||
} |
||||
$this->_byte_order = $byte_order; |
||||
} |
||||
|
||||
/** |
||||
* General storage function |
||||
* |
||||
* @param string $data binary data to prepend |
||||
* @access private |
||||
*/ |
||||
function _prepend($data) |
||||
{ |
||||
if (strlen($data) > $this->_limit) { |
||||
$data = $this->_addContinue($data); |
||||
} |
||||
$this->_data = $data.$this->_data; |
||||
$this->_datasize += strlen($data); |
||||
} |
||||
|
||||
/** |
||||
* General storage function |
||||
* |
||||
* @param string $data binary data to append |
||||
* @access private |
||||
*/ |
||||
function _append($data) |
||||
{ |
||||
if (strlen($data) > $this->_limit) { |
||||
$data = $this->_addContinue($data); |
||||
} |
||||
$this->_data = $this->_data.$data; |
||||
$this->_datasize += strlen($data); |
||||
} |
||||
|
||||
/** |
||||
* Writes Excel BOF record to indicate the beginning of a stream or |
||||
* sub-stream in the BIFF file. |
||||
* |
||||
* @param integer $type Type of BIFF file to write: 0x0005 Workbook, |
||||
* 0x0010 Worksheet. |
||||
* @access private |
||||
*/ |
||||
function _storeBof($type) |
||||
{ |
||||
$record = 0x0809; // Record identifier |
||||
|
||||
// According to the SDK $build and $year should be set to zero. |
||||
// However, this throws a warning in Excel 5. So, use magic numbers. |
||||
if ($this->_BIFF_version == 0x0500) { |
||||
$length = 0x0008; |
||||
$unknown = ''; |
||||
$build = 0x096C; |
||||
$year = 0x07C9; |
||||
} elseif ($this->_BIFF_version == 0x0600) { |
||||
$length = 0x0010; |
||||
$unknown = pack("VV", 0x00000041, 0x00000006); //unknown last 8 bytes for BIFF8 |
||||
$build = 0x0DBB; |
||||
$year = 0x07CC; |
||||
} |
||||
$version = $this->_BIFF_version; |
||||
|
||||
$header = pack("vv", $record, $length); |
||||
$data = pack("vvvv", $version, $type, $build, $year); |
||||
$this->_prepend($header . $data . $unknown); |
||||
} |
||||
|
||||
/** |
||||
* Writes Excel EOF record to indicate the end of a BIFF stream. |
||||
* |
||||
* @access private |
||||
*/ |
||||
function _storeEof() |
||||
{ |
||||
$record = 0x000A; // Record identifier |
||||
$length = 0x0000; // Number of bytes to follow |
||||
$header = pack("vv", $record, $length); |
||||
$this->_append($header); |
||||
} |
||||
|
||||
/** |
||||
* Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In |
||||
* Excel 97 the limit is 8228 bytes. Records that are longer than these limits |
||||
* must be split up into CONTINUE blocks. |
||||
* |
||||
* This function takes a long BIFF record and inserts CONTINUE records as |
||||
* necessary. |
||||
* |
||||
* @param string $data The original binary data to be written |
||||
* @return string A very convenient string of continue blocks |
||||
* @access private |
||||
*/ |
||||
function _addContinue($data) |
||||
{ |
||||
$limit = $this->_limit; |
||||
$record = 0x003C; // Record identifier |
||||
|
||||
// The first 2080/8224 bytes remain intact. However, we have to change |
||||
// the length field of the record. |
||||
$tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4); |
||||
|
||||
$header = pack("vv", $record, $limit); // Headers for continue records |
||||
|
||||
// Retrieve chunks of 2080/8224 bytes +4 for the header. |
||||
$data_length = strlen($data); |
||||
for ($i = $limit; $i < ($data_length - $limit); $i += $limit) { |
||||
$tmp .= $header; |
||||
$tmp .= substr($data, $i, $limit); |
||||
} |
||||
|
||||
// Retrieve the last chunk of data |
||||
$header = pack("vv", $record, strlen($data) - $i); |
||||
$tmp .= $header; |
||||
$tmp .= substr($data, $i, strlen($data) - $i); |
||||
|
||||
return $tmp; |
||||
} |
||||
} |
||||
?> |
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,230 @@ |
||||
<?php |
||||
/* |
||||
* Module written by Herman Kuiper <herman@ozuzo.net> |
||||
* |
||||
* License Information: |
||||
* |
||||
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets |
||||
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with this library; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
*/ |
||||
|
||||
//require_once('PEAR.php'); |
||||
|
||||
// Possible operator types |
||||
|
||||
/* |
||||
FIXME: change prefixes |
||||
*/ |
||||
define("OP_BETWEEN", 0x00); |
||||
define("OP_NOTBETWEEN", 0x01); |
||||
define("OP_EQUAL", 0x02); |
||||
define("OP_NOTEQUAL", 0x03); |
||||
define("OP_GT", 0x04); |
||||
define("OP_LT", 0x05); |
||||
define("OP_GTE", 0x06); |
||||
define("OP_LTE", 0x07); |
||||
|
||||
/** |
||||
* Baseclass for generating Excel DV records (validations) |
||||
* |
||||
* @author Herman Kuiper |
||||
* @category FileFormats |
||||
* @package Spreadsheet_Excel_Writer |
||||
*/ |
||||
class Spreadsheet_Excel_Writer_Validator |
||||
{ |
||||
var $_type; |
||||
var $_style; |
||||
var $_fixedList; |
||||
var $_blank; |
||||
var $_incell; |
||||
var $_showprompt; |
||||
var $_showerror; |
||||
var $_title_prompt; |
||||
var $_descr_prompt; |
||||
var $_title_error; |
||||
var $_descr_error; |
||||
var $_operator; |
||||
var $_formula1; |
||||
var $_formula2; |
||||
/** |
||||
* The parser from the workbook. Used to parse validation formulas also |
||||
* @var Spreadsheet_Excel_Writer_Parser |
||||
*/ |
||||
var $_parser; |
||||
|
||||
function Spreadsheet_Excel_Writer_Validator(&$parser) |
||||
{ |
||||
$this->_parser = $parser; |
||||
$this->_type = 0x01; // FIXME: add method for setting datatype |
||||
$this->_style = 0x00; |
||||
$this->_fixedList = false; |
||||
$this->_blank = false; |
||||
$this->_incell = false; |
||||
$this->_showprompt = false; |
||||
$this->_showerror = true; |
||||
$this->_title_prompt = "\x00"; |
||||
$this->_descr_prompt = "\x00"; |
||||
$this->_title_error = "\x00"; |
||||
$this->_descr_error = "\x00"; |
||||
$this->_operator = 0x00; // default is equal |
||||
$this->_formula1 = ''; |
||||
$this->_formula2 = ''; |
||||
} |
||||
|
||||
function setPrompt($promptTitle = "\x00", $promptDescription = "\x00", $showPrompt = true) |
||||
{ |
||||
$this->_showprompt = $showPrompt; |
||||
$this->_title_prompt = $promptTitle; |
||||
$this->_descr_prompt = $promptDescription; |
||||
} |
||||
|
||||
function setError($errorTitle = "\x00", $errorDescription = "\x00", $showError = true) |
||||
{ |
||||
$this->_showerror = $showError; |
||||
$this->_title_error = $errorTitle; |
||||
$this->_descr_error = $errorDescription; |
||||
} |
||||
|
||||
function allowBlank() |
||||
{ |
||||
$this->_blank = true; |
||||
} |
||||
|
||||
function onInvalidStop() |
||||
{ |
||||
$this->_style = 0x00; |
||||
} |
||||
|
||||
function onInvalidWarn() |
||||
{ |
||||
$this->_style = 0x01; |
||||
} |
||||
|
||||
function onInvalidInfo() |
||||
{ |
||||
$this->_style = 0x02; |
||||
} |
||||
|
||||
function setFormula1($formula) |
||||
{ |
||||
// Parse the formula using the parser in Parser.php |
||||
$error = $this->_parser->parse($formula); |
||||
if (PEAR::isError($error)) { |
||||
return $this->_formula1; |
||||
} |
||||
|
||||
$this->_formula1 = $this->_parser->toReversePolish(); |
||||
if (PEAR::isError($this->_formula1)) { |
||||
return $this->_formula1; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
function setFormula2($formula) |
||||
{ |
||||
// Parse the formula using the parser in Parser.php |
||||
$error = $this->_parser->parse($formula); |
||||
if (PEAR::isError($error)) { |
||||
return $this->_formula2; |
||||
} |
||||
|
||||
$this->_formula2 = $this->_parser->toReversePolish(); |
||||
if (PEAR::isError($this->_formula2)) { |
||||
return $this->_formula2; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
function _getOptions() |
||||
{ |
||||
$options = $this->_type; |
||||
$options |= $this->_style << 3; |
||||
if ($this->_fixedList) { |
||||
$options |= 0x80; |
||||
} |
||||
if ($this->_blank) { |
||||
$options |= 0x100; |
||||
} |
||||
if (!$this->_incell) { |
||||
$options |= 0x200; |
||||
} |
||||
if ($this->_showprompt) { |
||||
$options |= 0x40000; |
||||
} |
||||
if ($this->_showerror) { |
||||
$options |= 0x80000; |
||||
} |
||||
$options |= $this->_operator << 20; |
||||
|
||||
return $options; |
||||
} |
||||
|
||||
function _getData() |
||||
{ |
||||
$title_prompt_len = strlen($this->_title_prompt); |
||||
$descr_prompt_len = strlen($this->_descr_prompt); |
||||
$title_error_len = strlen($this->_title_error); |
||||
$descr_error_len = strlen($this->_descr_error); |
||||
|
||||
$formula1_size = strlen($this->_formula1); |
||||
$formula2_size = strlen($this->_formula2); |
||||
|
||||
$data = pack("V", $this->_getOptions()); |
||||
$data .= pack("vC", $title_prompt_len, 0x00) . $this->_title_prompt; |
||||
$data .= pack("vC", $title_error_len, 0x00) . $this->_title_error; |
||||
$data .= pack("vC", $descr_prompt_len, 0x00) . $this->_descr_prompt; |
||||
$data .= pack("vC", $descr_error_len, 0x00) . $this->_descr_error; |
||||
|
||||
$data .= pack("vv", $formula1_size, 0x0000) . $this->_formula1; |
||||
$data .= pack("vv", $formula2_size, 0x0000) . $this->_formula2; |
||||
|
||||
return $data; |
||||
} |
||||
} |
||||
|
||||
/*class Spreadsheet_Excel_Writer_Validation_List extends Spreadsheet_Excel_Writer_Validation |
||||
{ |
||||
function Spreadsheet_Excel_Writer_Validation_list() |
||||
{ |
||||
parent::Spreadsheet_Excel_Writer_Validation(); |
||||
$this->_type = 0x03; |
||||
} |
||||
|
||||
function setList($source, $incell = true) |
||||
{ |
||||
$this->_incell = $incell; |
||||
$this->_fixedList = true; |
||||
|
||||
$source = implode("\x00", $source); |
||||
$this->_formula1 = pack("CCC", 0x17, strlen($source), 0x0c) . $source; |
||||
} |
||||
|
||||
function setRow($row, $col1, $col2, $incell = true) |
||||
{ |
||||
$this->_incell = $incell; |
||||
//$this->_formula1 = ...; |
||||
} |
||||
|
||||
function setCol($col, $row1, $row2, $incell = true) |
||||
{ |
||||
$this->_incell = $incell; |
||||
//$this->_formula1 = ...; |
||||
} |
||||
}*/ |
||||
|
||||
?> |
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue