From 278a926ddb10736ba831e8ee26aa39fa0fa8a402 Mon Sep 17 00:00:00 2001
From: schrom01
+ * References:
+ *
';
+ $stack->push('Brace', '(');
+ ++$index;
+
+ } elseif ($isOperandOrFunction && !$expectingOperator) { // do we now have a function/variable/number?
+ $expectingOperator = true;
+ $expectingOperand = false;
+ $val = $match[1];
+ $length = strlen($val);
+// echo 'Element with value '.$val.' is an Operand, Variable, Constant, String, Number, Cell Reference or Function
';
+
+ if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $val, $matches)) {
+ $val = preg_replace('/\s/u', '', $val);
+// echo 'Element '.$val.' is a Function
';
+ if (isset(self::$PHPExcelFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function
+ $stack->push('Function', strtoupper($val));
+ $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index+$length), $amatch);
+ if ($ax) {
+ $stack->push('Operand Count for Function '.strtoupper($val).')', 0);
+ $expectingOperator = true;
+ } else {
+ $stack->push('Operand Count for Function '.strtoupper($val).')', 1);
+ $expectingOperator = false;
+ }
+ $stack->push('Brace', '(');
+ } else { // it's a var w/ implicit multiplication
+ $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => null);
+ }
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $val, $matches)) {
+// echo 'Element '.$val.' is a Cell reference
';
+ // Watch for this case-change when modifying to allow cell references in different worksheets...
+ // Should only be applied to the actual cell column, not the worksheet name
+
+ // If the last entry on the stack was a : operator, then we have a cell range reference
+ $testPrevOp = $stack->last(1);
+ if ($testPrevOp['value'] == ':') {
+ // If we have a worksheet reference, then we're playing with a 3D reference
+ if ($matches[2] == '') {
+ // Otherwise, we 'inherit' the worksheet reference from the start cell reference
+ // The start of the cell range reference should be the last entry in $output
+ $startCellRef = $output[count($output)-1]['value'];
+ preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $startCellRef, $startMatches);
+ if ($startMatches[2] > '') {
+ $val = $startMatches[2].'!'.$val;
+ }
+ } else {
+ return $this->raiseFormulaError("3D Range references are not yet supported");
+ }
+ }
+
+ $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $val);
+// $expectingOperator = FALSE;
+ } else { // it's a variable, constant, string, number or boolean
+// echo 'Element is a Variable, Constant, String, Number or Boolean
';
+ // If the last entry on the stack was a : operator, then we may have a row or column range reference
+ $testPrevOp = $stack->last(1);
+ if ($testPrevOp['value'] == ':') {
+ $startRowColRef = $output[count($output)-1]['value'];
+ $rangeWS1 = '';
+ if (strpos('!', $startRowColRef) !== false) {
+ list($rangeWS1, $startRowColRef) = explode('!', $startRowColRef);
+ }
+ if ($rangeWS1 != '') {
+ $rangeWS1 .= '!';
+ }
+ $rangeWS2 = $rangeWS1;
+ if (strpos('!', $val) !== false) {
+ list($rangeWS2, $val) = explode('!', $val);
+ }
+ if ($rangeWS2 != '') {
+ $rangeWS2 .= '!';
+ }
+ if ((is_integer($startRowColRef)) && (ctype_digit($val)) &&
+ ($startRowColRef <= 1048576) && ($val <= 1048576)) {
+ // Row range
+ $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestColumn() : 'XFD'; // Max 16,384 columns for Excel2007
+ $output[count($output)-1]['value'] = $rangeWS1.'A'.$startRowColRef;
+ $val = $rangeWS2.$endRowColRef.$val;
+ } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
+ (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) {
+ // Column range
+ $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestRow() : 1048576; // Max 1,048,576 rows for Excel2007
+ $output[count($output)-1]['value'] = $rangeWS1.strtoupper($startRowColRef).'1';
+ $val = $rangeWS2.$val.$endRowColRef;
+ }
+ }
+
+ $localeConstant = false;
+ if ($opCharacter == '"') {
+// echo 'Element is a String
';
+ // UnEscape any quotes within the string
+ $val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val)));
+ } elseif (is_numeric($val)) {
+// echo 'Element is a Number
';
+ if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
+// echo 'Casting '.$val.' to float
';
+ $val = (float) $val;
+ } else {
+// echo 'Casting '.$val.' to integer
';
+ $val = (integer) $val;
+ }
+ } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
+ $excelConstant = trim(strtoupper($val));
+// echo 'Element '.$excelConstant.' is an Excel Constant
';
+ $val = self::$excelConstants[$excelConstant];
+ } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
+// echo 'Element '.$localeConstant.' is an Excel Constant
';
+ $val = self::$excelConstants[$localeConstant];
+ }
+ $details = array('type' => 'Value', 'value' => $val, 'reference' => null);
+ if ($localeConstant) {
+ $details['localeValue'] = $localeConstant;
+ }
+ $output[] = $details;
+ }
+ $index += $length;
+
+ } elseif ($opCharacter == '$') { // absolute row or column range
+ ++$index;
+ } elseif ($opCharacter == ')') { // miscellaneous error checking
+ if ($expectingOperand) {
+ $output[] = array('type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null);
+ $expectingOperand = false;
+ $expectingOperator = true;
+ } else {
+ return $this->raiseFormulaError("Formula Error: Unexpected ')'");
+ }
+ } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) {
+ return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
+ } else { // I don't even want to know what you did to get here
+ return $this->raiseFormulaError("Formula Error: An unexpected error occured");
+ }
+ // Test for end of formula string
+ if ($index == strlen($formula)) {
+ // Did we end with an operator?.
+ // Only valid for the % unary operator
+ if ((isset(self::$operators[$opCharacter])) && ($opCharacter != '%')) {
+ return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
+ } else {
+ break;
+ }
+ }
+ // Ignore white space
+ while (($formula{$index} == "\n") || ($formula{$index} == "\r")) {
+ ++$index;
+ }
+ if ($formula{$index} == ' ') {
+ while ($formula{$index} == ' ') {
+ ++$index;
+ }
+ // If we're expecting an operator, but only have a space between the previous and next operands (and both are
+ // Cell References) then we have an INTERSECTION operator
+// echo 'Possible Intersect Operator
';
+ if (($expectingOperator) && (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'.*/Ui', substr($formula, $index), $match)) &&
+ ($output[count($output)-1]['type'] == 'Cell Reference')) {
+// echo 'Element is an Intersect Operator
';
+ while ($stack->count() > 0 &&
+ ($o2 = $stack->last()) &&
+ isset(self::$operators[$o2['value']]) &&
+ @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
+ $output[] = $stack->pop(); // Swap operands and higher precedence operators from the stack to the output
+ }
+ $stack->push('Binary Operator', '|'); // Put an Intersect Operator on the stack
+ $expectingOperator = false;
+ }
+ }
+ }
+
+ while (($op = $stack->pop()) !== null) { // pop everything off the stack and push onto output
+ if ((is_array($op) && $op['value'] == '(') || ($op === '(')) {
+ return $this->raiseFormulaError("Formula Error: Expecting ')'"); // if there are any opening braces on the stack, then braces were unbalanced
+ }
+ $output[] = $op;
+ }
+ return $output;
+ }
+
+
+ private static function dataTestReference(&$operandData)
+ {
+ $operand = $operandData['value'];
+ if (($operandData['reference'] === null) && (is_array($operand))) {
+ $rKeys = array_keys($operand);
+ $rowKey = array_shift($rKeys);
+ $cKeys = array_keys(array_keys($operand[$rowKey]));
+ $colKey = array_shift($cKeys);
+ if (ctype_upper($colKey)) {
+ $operandData['reference'] = $colKey.$rowKey;
+ }
+ }
+ return $operand;
+ }
+
+ // evaluate postfix notation
+ private function processTokenStack($tokens, $cellID = null, PHPExcel_Cell $pCell = null)
+ {
+ if ($tokens == false) {
+ return false;
+ }
+
+ // If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
+ // so we store the parent cell collection so that we can re-attach it when necessary
+ $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null;
+ $pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
+ $stack = new PHPExcel_Calculation_Token_Stack;
+
+ // Loop through each token in turn
+ foreach ($tokens as $tokenData) {
+// print_r($tokenData);
+// echo '
';
+ $token = $tokenData['value'];
+// echo 'Token is '.$token.'
';
+ // if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
+ if (isset(self::$binaryOperators[$token])) {
+// echo 'Token is a binary operator
';
+ // We must have two operands, error if we don't
+ if (($operand2Data = $stack->pop()) === null) {
+ return $this->raiseFormulaError('Internal error - Operand value missing from stack');
+ }
+ if (($operand1Data = $stack->pop()) === null) {
+ return $this->raiseFormulaError('Internal error - Operand value missing from stack');
+ }
+
+ $operand1 = self::dataTestReference($operand1Data);
+ $operand2 = self::dataTestReference($operand2Data);
+
+ // Log what we're doing
+ if ($token == ':') {
+ $this->_debugLog->writeDebugLog('Evaluating Range ', $this->showValue($operand1Data['reference']), ' ', $token, ' ', $this->showValue($operand2Data['reference']));
+ } else {
+ $this->_debugLog->writeDebugLog('Evaluating ', $this->showValue($operand1), ' ', $token, ' ', $this->showValue($operand2));
+ }
+
+ // Process the operation in the appropriate manner
+ switch ($token) {
+ // Comparison (Boolean) Operators
+ case '>': // Greater than
+ case '<': // Less than
+ case '>=': // Greater than or Equal to
+ case '<=': // Less than or Equal to
+ case '=': // Equality
+ case '<>': // Inequality
+ $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
+ break;
+ // Binary Operators
+ case ':': // Range
+ $sheet1 = $sheet2 = '';
+ if (strpos($operand1Data['reference'], '!') !== false) {
+ list($sheet1, $operand1Data['reference']) = explode('!', $operand1Data['reference']);
+ } else {
+ $sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : '';
+ }
+ if (strpos($operand2Data['reference'], '!') !== false) {
+ list($sheet2, $operand2Data['reference']) = explode('!', $operand2Data['reference']);
+ } else {
+ $sheet2 = $sheet1;
+ }
+ if ($sheet1 == $sheet2) {
+ if ($operand1Data['reference'] === null) {
+ if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
+ $operand1Data['reference'] = $pCell->getColumn().$operand1Data['value'];
+ } elseif (trim($operand1Data['reference']) == '') {
+ $operand1Data['reference'] = $pCell->getCoordinate();
+ } else {
+ $operand1Data['reference'] = $operand1Data['value'].$pCell->getRow();
+ }
+ }
+ if ($operand2Data['reference'] === null) {
+ if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
+ $operand2Data['reference'] = $pCell->getColumn().$operand2Data['value'];
+ } elseif (trim($operand2Data['reference']) == '') {
+ $operand2Data['reference'] = $pCell->getCoordinate();
+ } else {
+ $operand2Data['reference'] = $operand2Data['value'].$pCell->getRow();
+ }
+ }
+
+ $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference']));
+ $oCol = $oRow = array();
+ foreach ($oData as $oDatum) {
+ $oCR = PHPExcel_Cell::coordinateFromString($oDatum);
+ $oCol[] = PHPExcel_Cell::columnIndexFromString($oCR[0]) - 1;
+ $oRow[] = $oCR[1];
+ }
+ $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
+ if ($pCellParent !== null) {
+ $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($sheet1), false);
+ } else {
+ return $this->raiseFormulaError('Unable to access Cell Reference');
+ }
+ $stack->push('Cell Reference', $cellValue, $cellRef);
+ } else {
+ $stack->push('Error', PHPExcel_Calculation_Functions::REF(), null);
+ }
+ break;
+ case '+': // Addition
+ $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'plusEquals', $stack);
+ break;
+ case '-': // Subtraction
+ $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'minusEquals', $stack);
+ break;
+ case '*': // Multiplication
+ $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayTimesEquals', $stack);
+ break;
+ case '/': // Division
+ $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayRightDivide', $stack);
+ break;
+ case '^': // Exponential
+ $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'power', $stack);
+ break;
+ case '&': // Concatenation
+ // If either of the operands is a matrix, we need to treat them both as matrices
+ // (converting the other operand to a matrix if need be); then perform the required
+ // matrix operation
+ if (is_bool($operand1)) {
+ $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
+ }
+ if (is_bool($operand2)) {
+ $operand2 = ($operand2) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
+ }
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ // Ensure that both operands are arrays/matrices
+ self::checkMatrixOperands($operand1, $operand2, 2);
+ try {
+ // Convert operand 1 from a PHP array to a matrix
+ $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
+ // Perform the required operation against the operand 1 matrix, passing in operand 2
+ $matrixResult = $matrix->concat($operand2);
+ $result = $matrixResult->getArray();
+ } catch (PHPExcel_Exception $ex) {
+ $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
+ $result = '#VALUE!';
+ }
+ } else {
+ $result = '"'.str_replace('""', '"', self::unwrapResult($operand1, '"').self::unwrapResult($operand2, '"')).'"';
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
+ $stack->push('Value', $result);
+ break;
+ case '|': // Intersect
+ $rowIntersect = array_intersect_key($operand1, $operand2);
+ $cellIntersect = $oCol = $oRow = array();
+ foreach (array_keys($rowIntersect) as $row) {
+ $oRow[] = $row;
+ foreach ($rowIntersect[$row] as $col => $data) {
+ $oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1;
+ $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
+ }
+ }
+ $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect));
+ $stack->push('Value', $cellIntersect, $cellRef);
+ break;
+ }
+
+ // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
+ } elseif (($token === '~') || ($token === '%')) {
+// echo 'Token is a unary operator
';
+ if (($arg = $stack->pop()) === null) {
+ return $this->raiseFormulaError('Internal error - Operand value missing from stack');
+ }
+ $arg = $arg['value'];
+ if ($token === '~') {
+// echo 'Token is a negation operator
';
+ $this->_debugLog->writeDebugLog('Evaluating Negation of ', $this->showValue($arg));
+ $multiplier = -1;
+ } else {
+// echo 'Token is a percentile operator
';
+ $this->_debugLog->writeDebugLog('Evaluating Percentile of ', $this->showValue($arg));
+ $multiplier = 0.01;
+ }
+ if (is_array($arg)) {
+ self::checkMatrixOperands($arg, $multiplier, 2);
+ try {
+ $matrix1 = new PHPExcel_Shared_JAMA_Matrix($arg);
+ $matrixResult = $matrix1->arrayTimesEquals($multiplier);
+ $result = $matrixResult->getArray();
+ } catch (PHPExcel_Exception $ex) {
+ $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
+ $result = '#VALUE!';
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
+ $stack->push('Value', $result);
+ } else {
+ $this->executeNumericBinaryOperation($cellID, $multiplier, $arg, '*', 'arrayTimesEquals', $stack);
+ }
+
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) {
+ $cellRef = null;
+// echo 'Element '.$token.' is a Cell reference
';
+ if (isset($matches[8])) {
+// echo 'Reference is a Range of cells
';
+ if ($pCell === null) {
+// We can't access the range, so return a REF error
+ $cellValue = PHPExcel_Calculation_Functions::REF();
+ } else {
+ $cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10];
+ if ($matches[2] > '') {
+ $matches[2] = trim($matches[2], "\"'");
+ if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
+ // It's a Reference to an external workbook (not currently supported)
+ return $this->raiseFormulaError('Unable to access External Workbook');
+ }
+ $matches[2] = trim($matches[2], "\"'");
+// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
';
+ $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
+ if ($pCellParent !== null) {
+ $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($matches[2]), false);
+ } else {
+ return $this->raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
+// $cellRef = $matches[2].'!'.$cellRef;
+ } else {
+// echo '$cellRef='.$cellRef.' in current worksheet
';
+ $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet');
+ if ($pCellParent !== null) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
+ } else {
+ return $this->raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
+ }
+ }
+ } else {
+// echo 'Reference is a single Cell
';
+ if ($pCell === null) {
+// We can't access the cell, so return a REF error
+ $cellValue = PHPExcel_Calculation_Functions::REF();
+ } else {
+ $cellRef = $matches[6].$matches[7];
+ if ($matches[2] > '') {
+ $matches[2] = trim($matches[2], "\"'");
+ if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
+ // It's a Reference to an external workbook (not currently supported)
+ return $this->raiseFormulaError('Unable to access External Workbook');
+ }
+// echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'
';
+ $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
+ if ($pCellParent !== null) {
+ $cellSheet = $this->workbook->getSheetByName($matches[2]);
+ if ($cellSheet && $cellSheet->cellExists($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($matches[2]), false);
+ $pCell->attach($pCellParent);
+ } else {
+ $cellValue = null;
+ }
+ } else {
+ return $this->raiseFormulaError('Unable to access Cell Reference');
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
+// $cellRef = $matches[2].'!'.$cellRef;
+ } else {
+// echo '$cellRef='.$cellRef.' in current worksheet
';
+ $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
+ if ($pCellParent->isDataSet($cellRef)) {
+ $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
+ $pCell->attach($pCellParent);
+ } else {
+ $cellValue = null;
+ }
+ $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
+ }
+ }
+ }
+ $stack->push('Value', $cellValue, $cellRef);
+
+ // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $token, $matches)) {
+// echo 'Token is a function
';
+ $functionName = $matches[1];
+ $argCount = $stack->pop();
+ $argCount = $argCount['value'];
+ if ($functionName != 'MKMATRIX') {
+ $this->_debugLog->writeDebugLog('Evaluating Function ', self::localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's'));
+ }
+ if ((isset(self::$PHPExcelFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) { // function
+ if (isset(self::$PHPExcelFunctions[$functionName])) {
+ $functionCall = self::$PHPExcelFunctions[$functionName]['functionCall'];
+ $passByReference = isset(self::$PHPExcelFunctions[$functionName]['passByReference']);
+ $passCellReference = isset(self::$PHPExcelFunctions[$functionName]['passCellReference']);
+ } elseif (isset(self::$controlFunctions[$functionName])) {
+ $functionCall = self::$controlFunctions[$functionName]['functionCall'];
+ $passByReference = isset(self::$controlFunctions[$functionName]['passByReference']);
+ $passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']);
+ }
+ // get the arguments for this function
+// echo 'Function '.$functionName.' expects '.$argCount.' arguments
';
+ $args = $argArrayVals = array();
+ for ($i = 0; $i < $argCount; ++$i) {
+ $arg = $stack->pop();
+ $a = $argCount - $i - 1;
+ if (($passByReference) &&
+ (isset(self::$PHPExcelFunctions[$functionName]['passByReference'][$a])) &&
+ (self::$PHPExcelFunctions[$functionName]['passByReference'][$a])) {
+ if ($arg['reference'] === null) {
+ $args[] = $cellID;
+ if ($functionName != 'MKMATRIX') {
+ $argArrayVals[] = $this->showValue($cellID);
+ }
+ } else {
+ $args[] = $arg['reference'];
+ if ($functionName != 'MKMATRIX') {
+ $argArrayVals[] = $this->showValue($arg['reference']);
+ }
+ }
+ } else {
+ $args[] = self::unwrapResult($arg['value']);
+ if ($functionName != 'MKMATRIX') {
+ $argArrayVals[] = $this->showValue($arg['value']);
+ }
+ }
+ }
+ // Reverse the order of the arguments
+ krsort($args);
+ if (($passByReference) && ($argCount == 0)) {
+ $args[] = $cellID;
+ $argArrayVals[] = $this->showValue($cellID);
+ }
+// echo 'Arguments are: ';
+// print_r($args);
+// echo '
';
+ if ($functionName != 'MKMATRIX') {
+ if ($this->_debugLog->getWriteDebugLog()) {
+ krsort($argArrayVals);
+ $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', implode(self::$localeArgumentSeparator.' ', PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )');
+ }
+ }
+ // Process each argument in turn, building the return value as an array
+// if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) {
+// $operand1 = $args[1];
+// $this->_debugLog->writeDebugLog('Argument is a matrix: ', $this->showValue($operand1));
+// $result = array();
+// $row = 0;
+// foreach($operand1 as $args) {
+// if (is_array($args)) {
+// foreach($args as $arg) {
+// $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($arg), ' )');
+// $r = call_user_func_array($functionCall, $arg);
+// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r));
+// $result[$row][] = $r;
+// }
+// ++$row;
+// } else {
+// $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($args), ' )');
+// $r = call_user_func_array($functionCall, $args);
+// $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r));
+// $result[] = $r;
+// }
+// }
+// } else {
+ // Process the argument with the appropriate function call
+ if ($passCellReference) {
+ $args[] = $pCell;
+ }
+ if (strpos($functionCall, '::') !== false) {
+ $result = call_user_func_array(explode('::', $functionCall), $args);
+ } else {
+ foreach ($args as &$arg) {
+ $arg = PHPExcel_Calculation_Functions::flattenSingleValue($arg);
+ }
+ unset($arg);
+ $result = call_user_func_array($functionCall, $args);
+ }
+ if ($functionName != 'MKMATRIX') {
+ $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result));
+ }
+ $stack->push('Value', self::wrapResult($result));
+ }
+
+ } else {
+ // if the token is a number, boolean, string or an Excel error, push it onto the stack
+ if (isset(self::$excelConstants[strtoupper($token)])) {
+ $excelConstant = strtoupper($token);
+// echo 'Token is a PHPExcel constant: '.$excelConstant.'
';
+ $stack->push('Constant Value', self::$excelConstants[$excelConstant]);
+ $this->_debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
+ } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) {
+// echo 'Token is a number, boolean, string, null or an Excel error
';
+ $stack->push('Value', $token);
+ // if the token is a named range, push the named range name onto the stack
+ } elseif (preg_match('/^'.self::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $token, $matches)) {
+// echo 'Token is a named range
';
+ $namedRange = $matches[6];
+// echo 'Named Range is '.$namedRange.'
';
+ $this->_debugLog->writeDebugLog('Evaluating Named Range ', $namedRange);
+ $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false);
+ $pCell->attach($pCellParent);
+ $this->_debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue));
+ $stack->push('Named Range', $cellValue, $namedRange);
+ } else {
+ return $this->raiseFormulaError("undefined variable '$token'");
+ }
+ }
+ }
+ // when we're out of tokens, the stack should have a single element, the final result
+ if ($stack->count() != 1) {
+ return $this->raiseFormulaError("internal error");
+ }
+ $output = $stack->pop();
+ $output = $output['value'];
+
+// if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
+// return array_shift(PHPExcel_Calculation_Functions::flattenArray($output));
+// }
+ return $output;
+ }
+
+
+ private function validateBinaryOperand($cellID, &$operand, &$stack)
+ {
+ if (is_array($operand)) {
+ if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) {
+ do {
+ $operand = array_pop($operand);
+ } while (is_array($operand));
+ }
+ }
+ // Numbers, matrices and booleans can pass straight through, as they're already valid
+ if (is_string($operand)) {
+ // We only need special validations for the operand if it is a string
+ // Start by stripping off the quotation marks we use to identify true excel string values internally
+ if ($operand > '' && $operand{0} == '"') {
+ $operand = self::unwrapResult($operand);
+ }
+ // If the string is a numeric value, we treat it as a numeric, so no further testing
+ if (!is_numeric($operand)) {
+ // If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations
+ if ($operand > '' && $operand{0} == '#') {
+ $stack->push('Value', $operand);
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($operand));
+ return false;
+ } elseif (!PHPExcel_Shared_String::convertToNumberIfFraction($operand)) {
+ // If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations
+ $stack->push('Value', '#VALUE!');
+ $this->_debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!'));
+ return false;
+ }
+ }
+ }
+
+ // return a true if the value of the operand is one that we can use in normal binary operations
+ return true;
+ }
+
+
+ private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, &$stack, $recursingArrays = false)
+ {
+ // If we're dealing with matrix operations, we want a matrix result
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ $result = array();
+ if ((is_array($operand1)) && (!is_array($operand2))) {
+ foreach ($operand1 as $x => $operandData) {
+ $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2));
+ $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2, $operation, $stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } elseif ((!is_array($operand1)) && (is_array($operand2))) {
+ foreach ($operand2 as $x => $operandData) {
+ $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operand1), ' ', $operation, ' ', $this->showValue($operandData));
+ $this->executeBinaryComparisonOperation($cellID, $operand1, $operandData, $operation, $stack);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ } else {
+ if (!$recursingArrays) {
+ self::checkMatrixOperands($operand1, $operand2, 2);
+ }
+ foreach ($operand1 as $x => $operandData) {
+ $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2[$x]));
+ $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true);
+ $r = $stack->pop();
+ $result[$x] = $r['value'];
+ }
+ }
+ // Log the result details
+ $this->_debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Array', $result);
+ return true;
+ }
+
+ // Simple validate the two operands if they are string values
+ if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') {
+ $operand1 = self::unwrapResult($operand1);
+ }
+ if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') {
+ $operand2 = self::unwrapResult($operand2);
+ }
+
+ // Use case insensitive comparaison if not OpenOffice mode
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ if (is_string($operand1)) {
+ $operand1 = strtoupper($operand1);
+ }
+ if (is_string($operand2)) {
+ $operand2 = strtoupper($operand2);
+ }
+ }
+
+ $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE;
+
+ // execute the necessary operation
+ switch ($operation) {
+ // Greater than
+ case '>':
+ if ($useLowercaseFirstComparison) {
+ $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0;
+ } else {
+ $result = ($operand1 > $operand2);
+ }
+ break;
+ // Less than
+ case '<':
+ if ($useLowercaseFirstComparison) {
+ $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0;
+ } else {
+ $result = ($operand1 < $operand2);
+ }
+ break;
+ // Equality
+ case '=':
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = (abs($operand1 - $operand2) < $this->delta);
+ } else {
+ $result = strcmp($operand1, $operand2) == 0;
+ }
+ break;
+ // Greater than or equal
+ case '>=':
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 > $operand2));
+ } elseif ($useLowercaseFirstComparison) {
+ $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0;
+ } else {
+ $result = strcmp($operand1, $operand2) >= 0;
+ }
+ break;
+ // Less than or equal
+ case '<=':
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 < $operand2));
+ } elseif ($useLowercaseFirstComparison) {
+ $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0;
+ } else {
+ $result = strcmp($operand1, $operand2) <= 0;
+ }
+ break;
+ // Inequality
+ case '<>':
+ if (is_numeric($operand1) && is_numeric($operand2)) {
+ $result = (abs($operand1 - $operand2) > 1E-14);
+ } else {
+ $result = strcmp($operand1, $operand2) != 0;
+ }
+ break;
+ }
+
+ // Log the result details
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value', $result);
+ return true;
+ }
+
+ /**
+ * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters
+ * @param string $str1 First string value for the comparison
+ * @param string $str2 Second string value for the comparison
+ * @return integer
+ */
+ private function strcmpLowercaseFirst($str1, $str2)
+ {
+ $inversedStr1 = PHPExcel_Shared_String::StrCaseReverse($str1);
+ $inversedStr2 = PHPExcel_Shared_String::StrCaseReverse($str2);
+
+ return strcmp($inversedStr1, $inversedStr2);
+ }
+
+ private function executeNumericBinaryOperation($cellID, $operand1, $operand2, $operation, $matrixFunction, &$stack)
+ {
+ // Validate the two operands
+ if (!$this->validateBinaryOperand($cellID, $operand1, $stack)) {
+ return false;
+ }
+ if (!$this->validateBinaryOperand($cellID, $operand2, $stack)) {
+ return false;
+ }
+
+ // If either of the operands is a matrix, we need to treat them both as matrices
+ // (converting the other operand to a matrix if need be); then perform the required
+ // matrix operation
+ if ((is_array($operand1)) || (is_array($operand2))) {
+ // Ensure that both operands are arrays/matrices of the same size
+ self::checkMatrixOperands($operand1, $operand2, 2);
+
+ try {
+ // Convert operand 1 from a PHP array to a matrix
+ $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
+ // Perform the required operation against the operand 1 matrix, passing in operand 2
+ $matrixResult = $matrix->$matrixFunction($operand2);
+ $result = $matrixResult->getArray();
+ } catch (PHPExcel_Exception $ex) {
+ $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
+ $result = '#VALUE!';
+ }
+ } else {
+ if ((PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) &&
+ ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1)>0) ||
+ (is_string($operand2) && !is_numeric($operand2) && strlen($operand2)>0))) {
+ $result = PHPExcel_Calculation_Functions::VALUE();
+ } else {
+ // If we're dealing with non-matrix operations, execute the necessary operation
+ switch ($operation) {
+ // Addition
+ case '+':
+ $result = $operand1 + $operand2;
+ break;
+ // Subtraction
+ case '-':
+ $result = $operand1 - $operand2;
+ break;
+ // Multiplication
+ case '*':
+ $result = $operand1 * $operand2;
+ break;
+ // Division
+ case '/':
+ if ($operand2 == 0) {
+ // Trap for Divide by Zero error
+ $stack->push('Value', '#DIV/0!');
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!'));
+ return false;
+ } else {
+ $result = $operand1 / $operand2;
+ }
+ break;
+ // Power
+ case '^':
+ $result = pow($operand1, $operand2);
+ break;
+ }
+ }
+ }
+
+ // Log the result details
+ $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
+ // And push the result onto the stack
+ $stack->push('Value', $result);
+ return true;
+ }
+
+
+ // trigger an error, but nicely, if need be
+ protected function raiseFormulaError($errorMessage)
+ {
+ $this->formulaError = $errorMessage;
+ $this->cyclicReferenceStack->clear();
+ if (!$this->suppressFormulaErrors) {
+ throw new PHPExcel_Calculation_Exception($errorMessage);
+ }
+ trigger_error($errorMessage, E_USER_ERROR);
+ }
+
+
+ /**
+ * Extract range values
+ *
+ * @param string &$pRange String based range representation
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @param boolean $resetLog Flag indicating whether calculation log should be reset or not
+ * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog = true)
+ {
+ // Return value
+ $returnValue = array ();
+
+// echo 'extractCellRange('.$pRange.')', PHP_EOL;
+ if ($pSheet !== null) {
+ $pSheetName = $pSheet->getTitle();
+// echo 'Passed sheet name is '.$pSheetName.PHP_EOL;
+// echo 'Range reference is '.$pRange.PHP_EOL;
+ if (strpos($pRange, '!') !== false) {
+// echo '$pRange reference includes sheet reference', PHP_EOL;
+ list($pSheetName, $pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
+// echo 'New sheet name is '.$pSheetName, PHP_EOL;
+// echo 'Adjusted Range reference is '.$pRange, PHP_EOL;
+ $pSheet = $this->workbook->getSheetByName($pSheetName);
+ }
+
+ // Extract range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
+ $pRange = $pSheetName.'!'.$pRange;
+ if (!isset($aReferences[1])) {
+ // Single cell in range
+ sscanf($aReferences[0], '%[A-Z]%d', $currentCol, $currentRow);
+ $cellValue = null;
+ if ($pSheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = null;
+ }
+ } else {
+ // Extract cell data for all cells in the range
+ foreach ($aReferences as $reference) {
+ // Extract range
+ sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow);
+ $cellValue = null;
+ if ($pSheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = null;
+ }
+ }
+ }
+ }
+
+ return $returnValue;
+ }
+
+
+ /**
+ * Extract range values
+ *
+ * @param string &$pRange String based range representation
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @return mixed Array of values in range if range contains more than one element. Otherwise, a single value is returned.
+ * @param boolean $resetLog Flag indicating whether calculation log should be reset or not
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog = true)
+ {
+ // Return value
+ $returnValue = array ();
+
+// echo 'extractNamedRange('.$pRange.')
';
+ if ($pSheet !== null) {
+ $pSheetName = $pSheet->getTitle();
+// echo 'Current sheet name is '.$pSheetName.'
';
+// echo 'Range reference is '.$pRange.'
';
+ if (strpos($pRange, '!') !== false) {
+// echo '$pRange reference includes sheet reference', PHP_EOL;
+ list($pSheetName, $pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
+// echo 'New sheet name is '.$pSheetName, PHP_EOL;
+// echo 'Adjusted Range reference is '.$pRange, PHP_EOL;
+ $pSheet = $this->workbook->getSheetByName($pSheetName);
+ }
+
+ // Named range?
+ $namedRange = PHPExcel_NamedRange::resolveRange($pRange, $pSheet);
+ if ($namedRange !== null) {
+ $pSheet = $namedRange->getWorksheet();
+// echo 'Named Range '.$pRange.' (';
+ $pRange = $namedRange->getRange();
+ $splitRange = PHPExcel_Cell::splitRange($pRange);
+ // Convert row and column references
+ if (ctype_alpha($splitRange[0][0])) {
+ $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
+ } elseif (ctype_digit($splitRange[0][0])) {
+ $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
+ }
+// echo $pRange.') is in sheet '.$namedRange->getWorksheet()->getTitle().'
';
+
+// if ($pSheet->getTitle() != $namedRange->getWorksheet()->getTitle()) {
+// if (!$namedRange->getLocalOnly()) {
+// $pSheet = $namedRange->getWorksheet();
+// } else {
+// return $returnValue;
+// }
+// }
+ } else {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+
+ // Extract range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
+// var_dump($aReferences);
+ if (!isset($aReferences[1])) {
+ // Single cell (or single column or row) in range
+ list($currentCol, $currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]);
+ $cellValue = null;
+ if ($pSheet->cellExists($aReferences[0])) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = null;
+ }
+ } else {
+ // Extract cell data for all cells in the range
+ foreach ($aReferences as $reference) {
+ // Extract range
+ list($currentCol, $currentRow) = PHPExcel_Cell::coordinateFromString($reference);
+// echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'
';
+ $cellValue = null;
+ if ($pSheet->cellExists($reference)) {
+ $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
+ } else {
+ $returnValue[$currentRow][$currentCol] = null;
+ }
+ }
+ }
+// print_r($returnValue);
+// echo '
';
+ }
+
+ return $returnValue;
+ }
+
+
+ /**
+ * Is a specific function implemented?
+ *
+ * @param string $pFunction Function Name
+ * @return boolean
+ */
+ public function isImplemented($pFunction = '')
+ {
+ $pFunction = strtoupper($pFunction);
+ if (isset(self::$PHPExcelFunctions[$pFunction])) {
+ return (self::$PHPExcelFunctions[$pFunction]['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY');
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Get a list of all implemented functions as an array of function objects
+ *
+ * @return array of PHPExcel_Calculation_Function
+ */
+ public function listFunctions()
+ {
+ $returnValue = array();
+
+ foreach (self::$PHPExcelFunctions as $functionName => $function) {
+ if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') {
+ $returnValue[$functionName] = new PHPExcel_Calculation_Function(
+ $function['category'],
+ $functionName,
+ $function['functionCall']
+ );
+ }
+ }
+
+ return $returnValue;
+ }
+
+
+ /**
+ * Get a list of all Excel function names
+ *
+ * @return array
+ */
+ public function listAllFunctionNames()
+ {
+ return array_keys(self::$PHPExcelFunctions);
+ }
+
+ /**
+ * Get a list of implemented Excel function names
+ *
+ * @return array
+ */
+ public function listFunctionNames()
+ {
+ $returnValue = array();
+ foreach (self::$PHPExcelFunctions as $functionName => $function) {
+ if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') {
+ $returnValue[] = $functionName;
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/PHPExcel/Calculation/Database.php b/PHPExcel/Calculation/Database.php
new file mode 100644
index 0000000..b8d91cb
--- /dev/null
+++ b/PHPExcel/Calculation/Database.php
@@ -0,0 +1,676 @@
+ $criteriaName) {
+ $testCondition = array();
+ $testConditionCount = 0;
+ foreach ($criteria as $row => $criterion) {
+ if ($criterion[$key] > '') {
+ $testCondition[] = '[:'.$criteriaName.']'.PHPExcel_Calculation_Functions::ifCondition($criterion[$key]);
+ $testConditionCount++;
+ }
+ }
+ if ($testConditionCount > 1) {
+ $testConditions[] = 'OR(' . implode(',', $testCondition) . ')';
+ $testConditionsCount++;
+ } elseif ($testConditionCount == 1) {
+ $testConditions[] = $testCondition[0];
+ $testConditionsCount++;
+ }
+ }
+
+ if ($testConditionsCount > 1) {
+ $testConditionSet = 'AND(' . implode(',', $testConditions) . ')';
+ } elseif ($testConditionsCount == 1) {
+ $testConditionSet = $testConditions[0];
+ }
+
+ // Loop through each row of the database
+ foreach ($database as $dataRow => $dataValues) {
+ // Substitute actual values from the database row for our [:placeholders]
+ $testConditionList = $testConditionSet;
+ foreach ($criteriaNames as $key => $criteriaName) {
+ $k = array_search($criteriaName, $fieldNames);
+ if (isset($dataValues[$k])) {
+ $dataValue = $dataValues[$k];
+ $dataValue = (is_string($dataValue)) ? PHPExcel_Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
+ $testConditionList = str_replace('[:' . $criteriaName . ']', $dataValue, $testConditionList);
+ }
+ }
+ // evaluate the criteria against the row data
+ $result = PHPExcel_Calculation::getInstance()->_calculateFormulaValue('='.$testConditionList);
+ // If the row failed to meet the criteria, remove it from the database
+ if (!$result) {
+ unset($database[$dataRow]);
+ }
+ }
+
+ return $database;
+ }
+
+
+ private static function getFilteredColumn($database, $field, $criteria)
+ {
+ // reduce the database to a set of rows that match all the criteria
+ $database = self::filter($database, $criteria);
+ // extract an array of values for the requested column
+ $colData = array();
+ foreach ($database as $row) {
+ $colData[] = $row[$field];
+ }
+
+ return $colData;
+ }
+
+ /**
+ * DAVERAGE
+ *
+ * Averages the values in a column of a list or database that match conditions you specify.
+ *
+ * Excel Function:
+ * DAVERAGE(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return float
+ *
+ */
+ public static function DAVERAGE($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_Statistical::AVERAGE(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DCOUNT
+ *
+ * Counts the cells that contain numbers in a column of a list or database that match conditions
+ * that you specify.
+ *
+ * Excel Function:
+ * DCOUNT(database,[field],criteria)
+ *
+ * Excel Function:
+ * DAVERAGE(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return integer
+ *
+ * @TODO The field argument is optional. If field is omitted, DCOUNT counts all records in the
+ * database that match the criteria.
+ *
+ */
+ public static function DCOUNT($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_Statistical::COUNT(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DCOUNTA
+ *
+ * Counts the nonblank cells in a column of a list or database that match conditions that you specify.
+ *
+ * Excel Function:
+ * DCOUNTA(database,[field],criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return integer
+ *
+ * @TODO The field argument is optional. If field is omitted, DCOUNTA counts all records in the
+ * database that match the criteria.
+ *
+ */
+ public static function DCOUNTA($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // reduce the database to a set of rows that match all the criteria
+ $database = self::filter($database, $criteria);
+ // extract an array of values for the requested column
+ $colData = array();
+ foreach ($database as $row) {
+ $colData[] = $row[$field];
+ }
+
+ // Return
+ return PHPExcel_Calculation_Statistical::COUNTA(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DGET
+ *
+ * Extracts a single value from a column of a list or database that matches conditions that you
+ * specify.
+ *
+ * Excel Function:
+ * DGET(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return mixed
+ *
+ */
+ public static function DGET($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ $colData = self::getFilteredColumn($database, $field, $criteria);
+ if (count($colData) > 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ return $colData[0];
+ }
+
+
+ /**
+ * DMAX
+ *
+ * Returns the largest number in a column of a list or database that matches conditions you that
+ * specify.
+ *
+ * Excel Function:
+ * DMAX(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return float
+ *
+ */
+ public static function DMAX($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_Statistical::MAX(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DMIN
+ *
+ * Returns the smallest number in a column of a list or database that matches conditions you that
+ * specify.
+ *
+ * Excel Function:
+ * DMIN(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return float
+ *
+ */
+ public static function DMIN($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_Statistical::MIN(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DPRODUCT
+ *
+ * Multiplies the values in a column of a list or database that match conditions that you specify.
+ *
+ * Excel Function:
+ * DPRODUCT(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return float
+ *
+ */
+ public static function DPRODUCT($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_MathTrig::PRODUCT(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DSTDEV
+ *
+ * Estimates the standard deviation of a population based on a sample by using the numbers in a
+ * column of a list or database that match conditions that you specify.
+ *
+ * Excel Function:
+ * DSTDEV(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return float
+ *
+ */
+ public static function DSTDEV($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_Statistical::STDEV(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DSTDEVP
+ *
+ * Calculates the standard deviation of a population based on the entire population by using the
+ * numbers in a column of a list or database that match conditions that you specify.
+ *
+ * Excel Function:
+ * DSTDEVP(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return float
+ *
+ */
+ public static function DSTDEVP($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_Statistical::STDEVP(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DSUM
+ *
+ * Adds the numbers in a column of a list or database that match conditions that you specify.
+ *
+ * Excel Function:
+ * DSUM(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return float
+ *
+ */
+ public static function DSUM($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_MathTrig::SUM(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DVAR
+ *
+ * Estimates the variance of a population based on a sample by using the numbers in a column
+ * of a list or database that match conditions that you specify.
+ *
+ * Excel Function:
+ * DVAR(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return float
+ *
+ */
+ public static function DVAR($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_Statistical::VARFunc(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+
+
+ /**
+ * DVARP
+ *
+ * Calculates the variance of a population based on the entire population by using the numbers
+ * in a column of a list or database that match conditions that you specify.
+ *
+ * Excel Function:
+ * DVARP(database,field,criteria)
+ *
+ * @access public
+ * @category Database Functions
+ * @param mixed[] $database The range of cells that makes up the list or database.
+ * A database is a list of related data in which rows of related
+ * information are records, and columns of data are fields. The
+ * first row of the list contains labels for each column.
+ * @param string|integer $field Indicates which column is used in the function. Enter the
+ * column label enclosed between double quotation marks, such as
+ * "Age" or "Yield," or a number (without quotation marks) that
+ * represents the position of the column within the list: 1 for
+ * the first column, 2 for the second column, and so on.
+ * @param mixed[] $criteria The range of cells that contains the conditions you specify.
+ * You can use any range for the criteria argument, as long as it
+ * includes at least one column label and at least one cell below
+ * the column label in which you specify a condition for the
+ * column.
+ * @return float
+ *
+ */
+ public static function DVARP($database, $field, $criteria)
+ {
+ $field = self::fieldExtract($database, $field);
+ if (is_null($field)) {
+ return null;
+ }
+
+ // Return
+ return PHPExcel_Calculation_Statistical::VARP(
+ self::getFilteredColumn($database, $field, $criteria)
+ );
+ }
+}
diff --git a/PHPExcel/Calculation/DateTime.php b/PHPExcel/Calculation/DateTime.php
new file mode 100644
index 0000000..72f4c7a
--- /dev/null
+++ b/PHPExcel/Calculation/DateTime.php
@@ -0,0 +1,1553 @@
+format('m');
+ $oYear = (int) $PHPDateObject->format('Y');
+
+ $adjustmentMonthsString = (string) $adjustmentMonths;
+ if ($adjustmentMonths > 0) {
+ $adjustmentMonthsString = '+'.$adjustmentMonths;
+ }
+ if ($adjustmentMonths != 0) {
+ $PHPDateObject->modify($adjustmentMonthsString.' months');
+ }
+ $nMonth = (int) $PHPDateObject->format('m');
+ $nYear = (int) $PHPDateObject->format('Y');
+
+ $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
+ if ($monthDiff != $adjustmentMonths) {
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-'.$adjustDays.' days';
+ $PHPDateObject->modify($adjustDaysString);
+ }
+ return $PHPDateObject;
+ }
+
+
+ /**
+ * DATETIMENOW
+ *
+ * Returns the current date and time.
+ * The NOW function is useful when you need to display the current date and time on a worksheet or
+ * calculate a value based on the current date and time, and have that value updated each time you
+ * open the worksheet.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * and time format of your regional settings. PHPExcel does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * NOW()
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATETIMENOW()
+ {
+ $saveTimeZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+ $retValue = false;
+ switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
+ case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
+ $retValue = (float) PHPExcel_Shared_Date::PHPToExcel(time());
+ break;
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
+ $retValue = (integer) time();
+ break;
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
+ $retValue = new DateTime();
+ break;
+ }
+ date_default_timezone_set($saveTimeZone);
+
+ return $retValue;
+ }
+
+
+ /**
+ * DATENOW
+ *
+ * Returns the current date.
+ * The NOW function is useful when you need to display the current date and time on a worksheet or
+ * calculate a value based on the current date and time, and have that value updated each time you
+ * open the worksheet.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * and time format of your regional settings. PHPExcel does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * TODAY()
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATENOW()
+ {
+ $saveTimeZone = date_default_timezone_get();
+ date_default_timezone_set('UTC');
+ $retValue = false;
+ $excelDateTime = floor(PHPExcel_Shared_Date::PHPToExcel(time()));
+ switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
+ case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
+ $retValue = (float) $excelDateTime;
+ break;
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
+ $retValue = (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateTime);
+ break;
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
+ $retValue = PHPExcel_Shared_Date::ExcelToPHPObject($excelDateTime);
+ break;
+ }
+ date_default_timezone_set($saveTimeZone);
+
+ return $retValue;
+ }
+
+
+ /**
+ * DATE
+ *
+ * The DATE function returns a value that represents a particular date.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * format of your regional settings. PHPExcel does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * DATE(year,month,day)
+ *
+ * PHPExcel is a lot more forgiving than MS Excel when passing non numeric values to this function.
+ * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
+ * as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @param integer $year The value of the year argument can include one to four digits.
+ * Excel interprets the year argument according to the configured
+ * date system: 1900 or 1904.
+ * If year is between 0 (zero) and 1899 (inclusive), Excel adds that
+ * value to 1900 to calculate the year. For example, DATE(108,1,2)
+ * returns January 2, 2008 (1900+108).
+ * If year is between 1900 and 9999 (inclusive), Excel uses that
+ * value as the year. For example, DATE(2008,1,2) returns January 2,
+ * 2008.
+ * If year is less than 0 or is 10000 or greater, Excel returns the
+ * #NUM! error value.
+ * @param integer $month A positive or negative integer representing the month of the year
+ * from 1 to 12 (January to December).
+ * If month is greater than 12, month adds that number of months to
+ * the first month in the year specified. For example, DATE(2008,14,2)
+ * returns the serial number representing February 2, 2009.
+ * If month is less than 1, month subtracts the magnitude of that
+ * number of months, plus 1, from the first month in the year
+ * specified. For example, DATE(2008,-3,2) returns the serial number
+ * representing September 2, 2007.
+ * @param integer $day A positive or negative integer representing the day of the month
+ * from 1 to 31.
+ * If day is greater than the number of days in the month specified,
+ * day adds that number of days to the first day in the month. For
+ * example, DATE(2008,1,35) returns the serial number representing
+ * February 4, 2008.
+ * If day is less than 1, day subtracts the magnitude that number of
+ * days, plus one, from the first day of the month specified. For
+ * example, DATE(2008,1,-15) returns the serial number representing
+ * December 16, 2007.
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATE($year = 0, $month = 1, $day = 1)
+ {
+ $year = PHPExcel_Calculation_Functions::flattenSingleValue($year);
+ $month = PHPExcel_Calculation_Functions::flattenSingleValue($month);
+ $day = PHPExcel_Calculation_Functions::flattenSingleValue($day);
+
+ if (($month !== null) && (!is_numeric($month))) {
+ $month = PHPExcel_Shared_Date::monthStringToNumber($month);
+ }
+
+ if (($day !== null) && (!is_numeric($day))) {
+ $day = PHPExcel_Shared_Date::dayStringToNumber($day);
+ }
+
+ $year = ($year !== null) ? PHPExcel_Shared_String::testStringAsNumeric($year) : 0;
+ $month = ($month !== null) ? PHPExcel_Shared_String::testStringAsNumeric($month) : 0;
+ $day = ($day !== null) ? PHPExcel_Shared_String::testStringAsNumeric($day) : 0;
+ if ((!is_numeric($year)) ||
+ (!is_numeric($month)) ||
+ (!is_numeric($day))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $year = (integer) $year;
+ $month = (integer) $month;
+ $day = (integer) $day;
+
+ $baseYear = PHPExcel_Shared_Date::getExcelCalendar();
+ // Validate parameters
+ if ($year < ($baseYear-1900)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if ((($baseYear-1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ if (($year < $baseYear) && ($year >= ($baseYear-1900))) {
+ $year += 1900;
+ }
+
+ if ($month < 1) {
+ // Handle year/month adjustment if month < 1
+ --$month;
+ $year += ceil($month / 12) - 1;
+ $month = 13 - abs($month % 12);
+ } elseif ($month > 12) {
+ // Handle year/month adjustment if month > 12
+ $year += floor($month / 12);
+ $month = ($month % 12);
+ }
+
+ // Re-validate the year parameter after adjustments
+ if (($year < $baseYear) || ($year >= 10000)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Execute function
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($year, $month, $day);
+ switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
+ case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
+ return (float) $excelDateValue;
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
+ return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
+ return PHPExcel_Shared_Date::ExcelToPHPObject($excelDateValue);
+ }
+ }
+
+
+ /**
+ * TIME
+ *
+ * The TIME function returns a value that represents a particular time.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
+ * format of your regional settings. PHPExcel does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * TIME(hour,minute,second)
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @param integer $hour A number from 0 (zero) to 32767 representing the hour.
+ * Any value greater than 23 will be divided by 24 and the remainder
+ * will be treated as the hour value. For example, TIME(27,0,0) =
+ * TIME(3,0,0) = .125 or 3:00 AM.
+ * @param integer $minute A number from 0 to 32767 representing the minute.
+ * Any value greater than 59 will be converted to hours and minutes.
+ * For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
+ * @param integer $second A number from 0 to 32767 representing the second.
+ * Any value greater than 59 will be converted to hours, minutes,
+ * and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
+ * or 12:33:20 AM
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function TIME($hour = 0, $minute = 0, $second = 0)
+ {
+ $hour = PHPExcel_Calculation_Functions::flattenSingleValue($hour);
+ $minute = PHPExcel_Calculation_Functions::flattenSingleValue($minute);
+ $second = PHPExcel_Calculation_Functions::flattenSingleValue($second);
+
+ if ($hour == '') {
+ $hour = 0;
+ }
+ if ($minute == '') {
+ $minute = 0;
+ }
+ if ($second == '') {
+ $second = 0;
+ }
+
+ if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $hour = (integer) $hour;
+ $minute = (integer) $minute;
+ $second = (integer) $second;
+
+ if ($second < 0) {
+ $minute += floor($second / 60);
+ $second = 60 - abs($second % 60);
+ if ($second == 60) {
+ $second = 0;
+ }
+ } elseif ($second >= 60) {
+ $minute += floor($second / 60);
+ $second = $second % 60;
+ }
+ if ($minute < 0) {
+ $hour += floor($minute / 60);
+ $minute = 60 - abs($minute % 60);
+ if ($minute == 60) {
+ $minute = 0;
+ }
+ } elseif ($minute >= 60) {
+ $hour += floor($minute / 60);
+ $minute = $minute % 60;
+ }
+
+ if ($hour > 23) {
+ $hour = $hour % 24;
+ } elseif ($hour < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Execute function
+ switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
+ case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
+ $date = 0;
+ $calendar = PHPExcel_Shared_Date::getExcelCalendar();
+ if ($calendar != PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900) {
+ $date = 1;
+ }
+ return (float) PHPExcel_Shared_Date::FormattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
+ return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::FormattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
+ $dayAdjust = 0;
+ if ($hour < 0) {
+ $dayAdjust = floor($hour / 24);
+ $hour = 24 - abs($hour % 24);
+ if ($hour == 24) {
+ $hour = 0;
+ }
+ } elseif ($hour >= 24) {
+ $dayAdjust = floor($hour / 24);
+ $hour = $hour % 24;
+ }
+ $phpDateObject = new DateTime('1900-01-01 '.$hour.':'.$minute.':'.$second);
+ if ($dayAdjust != 0) {
+ $phpDateObject->modify($dayAdjust.' days');
+ }
+ return $phpDateObject;
+ }
+ }
+
+
+ /**
+ * DATEVALUE
+ *
+ * Returns a value that represents a particular date.
+ * Use DATEVALUE to convert a date represented by a text string to an Excel or PHP date/time stamp
+ * value.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
+ * format of your regional settings. PHPExcel does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * DATEVALUE(dateValue)
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @param string $dateValue Text that represents a date in a Microsoft Excel date format.
+ * For example, "1/30/2008" or "30-Jan-2008" are text strings within
+ * quotation marks that represent dates. Using the default date
+ * system in Excel for Windows, date_text must represent a date from
+ * January 1, 1900, to December 31, 9999. Using the default date
+ * system in Excel for the Macintosh, date_text must represent a date
+ * from January 1, 1904, to December 31, 9999. DATEVALUE returns the
+ * #VALUE! error value if date_text is out of this range.
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function DATEVALUE($dateValue = 1)
+ {
+ $dateValue = trim(PHPExcel_Calculation_Functions::flattenSingleValue($dateValue), '"');
+ // Strip any ordinals because they're allowed in Excel (English only)
+ $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
+ // Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
+ $dateValue = str_replace(array('/', '.', '-', ' '), array(' ', ' ', ' ', ' '), $dateValue);
+
+ $yearFound = false;
+ $t1 = explode(' ', $dateValue);
+ foreach ($t1 as &$t) {
+ if ((is_numeric($t)) && ($t > 31)) {
+ if ($yearFound) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } else {
+ if ($t < 100) {
+ $t += 1900;
+ }
+ $yearFound = true;
+ }
+ }
+ }
+ if ((count($t1) == 1) && (strpos($t, ':') != false)) {
+ // We've been fed a time value without any date
+ return 0.0;
+ } elseif (count($t1) == 2) {
+ // We only have two parts of the date: either day/month or month/year
+ if ($yearFound) {
+ array_unshift($t1, 1);
+ } else {
+ array_push($t1, date('Y'));
+ }
+ }
+ unset($t);
+ $dateValue = implode(' ', $t1);
+
+ $PHPDateArray = date_parse($dateValue);
+ if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
+ $testVal1 = strtok($dateValue, '- ');
+ if ($testVal1 !== false) {
+ $testVal2 = strtok('- ');
+ if ($testVal2 !== false) {
+ $testVal3 = strtok('- ');
+ if ($testVal3 === false) {
+ $testVal3 = strftime('%Y');
+ }
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $PHPDateArray = date_parse($testVal1.'-'.$testVal2.'-'.$testVal3);
+ if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
+ $PHPDateArray = date_parse($testVal2.'-'.$testVal1.'-'.$testVal3);
+ if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ }
+
+ if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
+ // Execute function
+ if ($PHPDateArray['year'] == '') {
+ $PHPDateArray['year'] = strftime('%Y');
+ }
+ if ($PHPDateArray['year'] < 1900) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if ($PHPDateArray['month'] == '') {
+ $PHPDateArray['month'] = strftime('%m');
+ }
+ if ($PHPDateArray['day'] == '') {
+ $PHPDateArray['day'] = strftime('%d');
+ }
+ $excelDateValue = floor(
+ PHPExcel_Shared_Date::FormattedPHPToExcel(
+ $PHPDateArray['year'],
+ $PHPDateArray['month'],
+ $PHPDateArray['day'],
+ $PHPDateArray['hour'],
+ $PHPDateArray['minute'],
+ $PHPDateArray['second']
+ )
+ );
+
+ switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
+ case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
+ return (float) $excelDateValue;
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
+ return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
+ return new DateTime($PHPDateArray['year'].'-'.$PHPDateArray['month'].'-'.$PHPDateArray['day'].' 00:00:00');
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * TIMEVALUE
+ *
+ * Returns a value that represents a particular time.
+ * Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
+ * value.
+ *
+ * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
+ * format of your regional settings. PHPExcel does not change cell formatting in this way.
+ *
+ * Excel Function:
+ * TIMEVALUE(timeValue)
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @param string $timeValue A text string that represents a time in any one of the Microsoft
+ * Excel time formats; for example, "6:45 PM" and "18:45" text strings
+ * within quotation marks that represent time.
+ * Date information in time_text is ignored.
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function TIMEVALUE($timeValue)
+ {
+ $timeValue = trim(PHPExcel_Calculation_Functions::flattenSingleValue($timeValue), '"');
+ $timeValue = str_replace(array('/', '.'), array('-', '-'), $timeValue);
+
+ $PHPDateArray = date_parse($timeValue);
+ if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel(
+ $PHPDateArray['year'],
+ $PHPDateArray['month'],
+ $PHPDateArray['day'],
+ $PHPDateArray['hour'],
+ $PHPDateArray['minute'],
+ $PHPDateArray['second']
+ );
+ } else {
+ $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
+ }
+
+ switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
+ case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
+ return (float) $excelDateValue;
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
+ return (integer) $phpDateValue = PHPExcel_Shared_Date::ExcelToPHP($excelDateValue+25569) - 3600;
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
+ return new DateTime('1900-01-01 '.$PHPDateArray['hour'].':'.$PHPDateArray['minute'].':'.$PHPDateArray['second']);
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * DATEDIF
+ *
+ * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
+ * or a standard date string
+ * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
+ * or a standard date string
+ * @param string $unit
+ * @return integer Interval between the dates
+ */
+ public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
+ {
+ $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
+ $endDate = PHPExcel_Calculation_Functions::flattenSingleValue($endDate);
+ $unit = strtoupper(PHPExcel_Calculation_Functions::flattenSingleValue($unit));
+
+ if (is_string($startDate = self::getDateValue($startDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($endDate = self::getDateValue($endDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // Validate parameters
+ if ($startDate >= $endDate) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Execute function
+ $difference = $endDate - $startDate;
+
+ $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
+ $startDays = $PHPStartDateObject->format('j');
+ $startMonths = $PHPStartDateObject->format('n');
+ $startYears = $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
+ $endDays = $PHPEndDateObject->format('j');
+ $endMonths = $PHPEndDateObject->format('n');
+ $endYears = $PHPEndDateObject->format('Y');
+
+ $retVal = PHPExcel_Calculation_Functions::NaN();
+ switch ($unit) {
+ case 'D':
+ $retVal = intval($difference);
+ break;
+ case 'M':
+ $retVal = intval($endMonths - $startMonths) + (intval($endYears - $startYears) * 12);
+ // We're only interested in full months
+ if ($endDays < $startDays) {
+ --$retVal;
+ }
+ break;
+ case 'Y':
+ $retVal = intval($endYears - $startYears);
+ // We're only interested in full months
+ if ($endMonths < $startMonths) {
+ --$retVal;
+ } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
+ --$retVal;
+ }
+ break;
+ case 'MD':
+ if ($endDays < $startDays) {
+ $retVal = $endDays;
+ $PHPEndDateObject->modify('-'.$endDays.' days');
+ $adjustDays = $PHPEndDateObject->format('j');
+ if ($adjustDays > $startDays) {
+ $retVal += ($adjustDays - $startDays);
+ }
+ } else {
+ $retVal = $endDays - $startDays;
+ }
+ break;
+ case 'YM':
+ $retVal = intval($endMonths - $startMonths);
+ if ($retVal < 0) {
+ $retVal += 12;
+ }
+ // We're only interested in full months
+ if ($endDays < $startDays) {
+ --$retVal;
+ }
+ break;
+ case 'YD':
+ $retVal = intval($difference);
+ if ($endYears > $startYears) {
+ while ($endYears > $startYears) {
+ $PHPEndDateObject->modify('-1 year');
+ $endYears = $PHPEndDateObject->format('Y');
+ }
+ $retVal = $PHPEndDateObject->format('z') - $PHPStartDateObject->format('z');
+ if ($retVal < 0) {
+ $retVal += 365;
+ }
+ }
+ break;
+ default:
+ $retVal = PHPExcel_Calculation_Functions::NaN();
+ }
+ return $retVal;
+ }
+
+
+ /**
+ * DAYS360
+ *
+ * Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
+ * which is used in some accounting calculations. Use this function to help compute payments if
+ * your accounting system is based on twelve 30-day months.
+ *
+ * Excel Function:
+ * DAYS360(startDate,endDate[,method])
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param boolean $method US or European Method
+ * FALSE or omitted: U.S. (NASD) method. If the starting date is
+ * the last day of a month, it becomes equal to the 30th of the
+ * same month. If the ending date is the last day of a month and
+ * the starting date is earlier than the 30th of a month, the
+ * ending date becomes equal to the 1st of the next month;
+ * otherwise the ending date becomes equal to the 30th of the
+ * same month.
+ * TRUE: European method. Starting dates and ending dates that
+ * occur on the 31st of a month become equal to the 30th of the
+ * same month.
+ * @return integer Number of days between start date and end date
+ */
+ public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
+ {
+ $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
+ $endDate = PHPExcel_Calculation_Functions::flattenSingleValue($endDate);
+
+ if (is_string($startDate = self::getDateValue($startDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($endDate = self::getDateValue($endDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (!is_bool($method)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // Execute function
+ $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
+ $startDay = $PHPStartDateObject->format('j');
+ $startMonth = $PHPStartDateObject->format('n');
+ $startYear = $PHPStartDateObject->format('Y');
+
+ $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
+ $endDay = $PHPEndDateObject->format('j');
+ $endMonth = $PHPEndDateObject->format('n');
+ $endYear = $PHPEndDateObject->format('Y');
+
+ return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
+ }
+
+
+ /**
+ * YEARFRAC
+ *
+ * Calculates the fraction of the year represented by the number of whole days between two dates
+ * (the start_date and the end_date).
+ * Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or
+ * obligations to assign to a specific term.
+ *
+ * Excel Function:
+ * YEARFRAC(startDate,endDate[,method])
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param integer $method Method used for the calculation
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float fraction of the year
+ */
+ public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
+ {
+ $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
+ $endDate = PHPExcel_Calculation_Functions::flattenSingleValue($endDate);
+ $method = PHPExcel_Calculation_Functions::flattenSingleValue($method);
+
+ if (is_string($startDate = self::getDateValue($startDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($endDate = self::getDateValue($endDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
+ switch ($method) {
+ case 0:
+ return self::DAYS360($startDate, $endDate) / 360;
+ case 1:
+ $days = self::DATEDIF($startDate, $endDate);
+ $startYear = self::YEAR($startDate);
+ $endYear = self::YEAR($endDate);
+ $years = $endYear - $startYear + 1;
+ $leapDays = 0;
+ if ($years == 1) {
+ if (self::isLeapYear($endYear)) {
+ $startMonth = self::MONTHOFYEAR($startDate);
+ $endMonth = self::MONTHOFYEAR($endDate);
+ $endDay = self::DAYOFMONTH($endDate);
+ if (($startMonth < 3) ||
+ (($endMonth * 100 + $endDay) >= (2 * 100 + 29))) {
+ $leapDays += 1;
+ }
+ }
+ } else {
+ for ($year = $startYear; $year <= $endYear; ++$year) {
+ if ($year == $startYear) {
+ $startMonth = self::MONTHOFYEAR($startDate);
+ $startDay = self::DAYOFMONTH($startDate);
+ if ($startMonth < 3) {
+ $leapDays += (self::isLeapYear($year)) ? 1 : 0;
+ }
+ } elseif ($year == $endYear) {
+ $endMonth = self::MONTHOFYEAR($endDate);
+ $endDay = self::DAYOFMONTH($endDate);
+ if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) {
+ $leapDays += (self::isLeapYear($year)) ? 1 : 0;
+ }
+ } else {
+ $leapDays += (self::isLeapYear($year)) ? 1 : 0;
+ }
+ }
+ if ($years == 2) {
+ if (($leapDays == 0) && (self::isLeapYear($startYear)) && ($days > 365)) {
+ $leapDays = 1;
+ } elseif ($days < 366) {
+ $years = 1;
+ }
+ }
+ $leapDays /= $years;
+ }
+ return $days / (365 + $leapDays);
+ case 2:
+ return self::DATEDIF($startDate, $endDate) / 360;
+ case 3:
+ return self::DATEDIF($startDate, $endDate) / 365;
+ case 4:
+ return self::DAYS360($startDate, $endDate, true) / 360;
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * NETWORKDAYS
+ *
+ * Returns the number of whole working days between start_date and end_date. Working days
+ * exclude weekends and any dates identified in holidays.
+ * Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
+ * worked during a specific term.
+ *
+ * Excel Function:
+ * NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param mixed $holidays,... Optional series of Excel date serial value (float), PHP date
+ * timestamp (integer), PHP DateTime object, or a standard date
+ * strings that will be excluded from the working calendar, such
+ * as state and federal holidays and floating holidays.
+ * @return integer Interval between the dates
+ */
+ public static function NETWORKDAYS($startDate, $endDate)
+ {
+ // Retrieve the mandatory start and end date that are referenced in the function definition
+ $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
+ $endDate = PHPExcel_Calculation_Functions::flattenSingleValue($endDate);
+ // Flush the mandatory start and end date that are referenced in the function definition, and get the optional days
+ $dateArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
+ array_shift($dateArgs);
+ array_shift($dateArgs);
+
+ // Validate the start and end dates
+ if (is_string($startDate = $sDate = self::getDateValue($startDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $startDate = (float) floor($startDate);
+ if (is_string($endDate = $eDate = self::getDateValue($endDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $endDate = (float) floor($endDate);
+
+ if ($sDate > $eDate) {
+ $startDate = $eDate;
+ $endDate = $sDate;
+ }
+
+ // Execute function
+ $startDoW = 6 - self::DAYOFWEEK($startDate, 2);
+ if ($startDoW < 0) {
+ $startDoW = 0;
+ }
+ $endDoW = self::DAYOFWEEK($endDate, 2);
+ if ($endDoW >= 6) {
+ $endDoW = 0;
+ }
+
+ $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
+ $partWeekDays = $endDoW + $startDoW;
+ if ($partWeekDays > 5) {
+ $partWeekDays -= 5;
+ }
+
+ // Test any extra holiday parameters
+ $holidayCountedArray = array();
+ foreach ($dateArgs as $holidayDate) {
+ if (is_string($holidayDate = self::getDateValue($holidayDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if ((self::DAYOFWEEK($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
+ --$partWeekDays;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ }
+
+ if ($sDate > $eDate) {
+ return 0 - ($wholeWeekDays + $partWeekDays);
+ }
+ return $wholeWeekDays + $partWeekDays;
+ }
+
+
+ /**
+ * WORKDAY
+ *
+ * Returns the date that is the indicated number of working days before or after a date (the
+ * starting date). Working days exclude weekends and any dates identified as holidays.
+ * Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
+ * delivery times, or the number of days of work performed.
+ *
+ * Excel Function:
+ * WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
+ *
+ * @access public
+ * @category Date/Time Functions
+ * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param integer $endDays The number of nonweekend and nonholiday days before or after
+ * startDate. A positive value for days yields a future date; a
+ * negative value yields a past date.
+ * @param mixed $holidays,... Optional series of Excel date serial value (float), PHP date
+ * timestamp (integer), PHP DateTime object, or a standard date
+ * strings that will be excluded from the working calendar, such
+ * as state and federal holidays and floating holidays.
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function WORKDAY($startDate, $endDays)
+ {
+ // Retrieve the mandatory start date and days that are referenced in the function definition
+ $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
+ $endDays = PHPExcel_Calculation_Functions::flattenSingleValue($endDays);
+ // Flush the mandatory start date and days that are referenced in the function definition, and get the optional days
+ $dateArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
+ array_shift($dateArgs);
+ array_shift($dateArgs);
+
+ if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $startDate = (float) floor($startDate);
+ $endDays = (int) floor($endDays);
+ // If endDays is 0, we always return startDate
+ if ($endDays == 0) {
+ return $startDate;
+ }
+
+ $decrementing = ($endDays < 0) ? true : false;
+
+ // Adjust the start date if it falls over a weekend
+
+ $startDoW = self::DAYOFWEEK($startDate, 3);
+ if (self::DAYOFWEEK($startDate, 3) >= 5) {
+ $startDate += ($decrementing) ? -$startDoW + 4: 7 - $startDoW;
+ ($decrementing) ? $endDays++ : $endDays--;
+ }
+
+ // Add endDays
+ $endDate = (float) $startDate + (intval($endDays / 5) * 7) + ($endDays % 5);
+
+ // Adjust the calculated end date if it falls over a weekend
+ $endDoW = self::DAYOFWEEK($endDate, 3);
+ if ($endDoW >= 5) {
+ $endDate += ($decrementing) ? -$endDoW + 4: 7 - $endDoW;
+ }
+
+ // Test any extra holiday parameters
+ if (!empty($dateArgs)) {
+ $holidayCountedArray = $holidayDates = array();
+ foreach ($dateArgs as $holidayDate) {
+ if (($holidayDate !== null) && (trim($holidayDate) > '')) {
+ if (is_string($holidayDate = self::getDateValue($holidayDate))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (self::DAYOFWEEK($holidayDate, 3) < 5) {
+ $holidayDates[] = $holidayDate;
+ }
+ }
+ }
+ if ($decrementing) {
+ rsort($holidayDates, SORT_NUMERIC);
+ } else {
+ sort($holidayDates, SORT_NUMERIC);
+ }
+ foreach ($holidayDates as $holidayDate) {
+ if ($decrementing) {
+ if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
+ if (!in_array($holidayDate, $holidayCountedArray)) {
+ --$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ } else {
+ if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
+ if (!in_array($holidayDate, $holidayCountedArray)) {
+ ++$endDate;
+ $holidayCountedArray[] = $holidayDate;
+ }
+ }
+ }
+ // Adjust the calculated end date if it falls over a weekend
+ $endDoW = self::DAYOFWEEK($endDate, 3);
+ if ($endDoW >= 5) {
+ $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
+ }
+ }
+ }
+
+ switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
+ case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
+ return (float) $endDate;
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
+ return (integer) PHPExcel_Shared_Date::ExcelToPHP($endDate);
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
+ return PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
+ }
+ }
+
+
+ /**
+ * DAYOFMONTH
+ *
+ * Returns the day of the month, for a specified date. The day is given as an integer
+ * ranging from 1 to 31.
+ *
+ * Excel Function:
+ * DAY(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @return int Day of the month
+ */
+ public static function DAYOFMONTH($dateValue = 1)
+ {
+ $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
+
+ if ($dateValue === null) {
+ $dateValue = 1;
+ } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif ($dateValue == 0.0) {
+ return 0;
+ } elseif ($dateValue < 0.0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+
+ return (int) $PHPDateObject->format('j');
+ }
+
+
+ /**
+ * DAYOFWEEK
+ *
+ * Returns the day of the week for a specified date. The day is given as an integer
+ * ranging from 0 to 7 (dependent on the requested style).
+ *
+ * Excel Function:
+ * WEEKDAY(dateValue[,style])
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param int $style A number that determines the type of return value
+ * 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
+ * 2 Numbers 1 (Monday) through 7 (Sunday).
+ * 3 Numbers 0 (Monday) through 6 (Sunday).
+ * @return int Day of the week value
+ */
+ public static function DAYOFWEEK($dateValue = 1, $style = 1)
+ {
+ $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
+ $style = PHPExcel_Calculation_Functions::flattenSingleValue($style);
+
+ if (!is_numeric($style)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif (($style < 1) || ($style > 3)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $style = floor($style);
+
+ if ($dateValue === null) {
+ $dateValue = 1;
+ } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif ($dateValue < 0.0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+ $DoW = $PHPDateObject->format('w');
+
+ $firstDay = 1;
+ switch ($style) {
+ case 1:
+ ++$DoW;
+ break;
+ case 2:
+ if ($DoW == 0) {
+ $DoW = 7;
+ }
+ break;
+ case 3:
+ if ($DoW == 0) {
+ $DoW = 7;
+ }
+ $firstDay = 0;
+ --$DoW;
+ break;
+ }
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL) {
+ // Test for Excel's 1900 leap year, and introduce the error as required
+ if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
+ --$DoW;
+ if ($DoW < $firstDay) {
+ $DoW += 7;
+ }
+ }
+ }
+
+ return (int) $DoW;
+ }
+
+
+ /**
+ * WEEKOFYEAR
+ *
+ * Returns the week of the year for a specified date.
+ * The WEEKNUM function considers the week containing January 1 to be the first week of the year.
+ * However, there is a European standard that defines the first week as the one with the majority
+ * of days (four or more) falling in the new year. This means that for years in which there are
+ * three days or less in the first week of January, the WEEKNUM function returns week numbers
+ * that are incorrect according to the European standard.
+ *
+ * Excel Function:
+ * WEEKNUM(dateValue[,style])
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param boolean $method Week begins on Sunday or Monday
+ * 1 or omitted Week begins on Sunday.
+ * 2 Week begins on Monday.
+ * @return int Week Number
+ */
+ public static function WEEKOFYEAR($dateValue = 1, $method = 1)
+ {
+ $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
+ $method = PHPExcel_Calculation_Functions::flattenSingleValue($method);
+
+ if (!is_numeric($method)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif (($method < 1) || ($method > 2)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $method = floor($method);
+
+ if ($dateValue === null) {
+ $dateValue = 1;
+ } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif ($dateValue < 0.0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+ $dayOfYear = $PHPDateObject->format('z');
+ $dow = $PHPDateObject->format('w');
+ $PHPDateObject->modify('-' . $dayOfYear . ' days');
+ $dow = $PHPDateObject->format('w');
+ $daysInFirstWeek = 7 - (($dow + (2 - $method)) % 7);
+ $dayOfYear -= $daysInFirstWeek;
+ $weekOfYear = ceil($dayOfYear / 7) + 1;
+
+ return (int) $weekOfYear;
+ }
+
+
+ /**
+ * MONTHOFYEAR
+ *
+ * Returns the month of a date represented by a serial number.
+ * The month is given as an integer, ranging from 1 (January) to 12 (December).
+ *
+ * Excel Function:
+ * MONTH(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @return int Month of the year
+ */
+ public static function MONTHOFYEAR($dateValue = 1)
+ {
+ $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
+
+ if ($dateValue === null) {
+ $dateValue = 1;
+ } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif ($dateValue < 0.0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+
+ return (int) $PHPDateObject->format('n');
+ }
+
+
+ /**
+ * YEAR
+ *
+ * Returns the year corresponding to a date.
+ * The year is returned as an integer in the range 1900-9999.
+ *
+ * Excel Function:
+ * YEAR(dateValue)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @return int Year
+ */
+ public static function YEAR($dateValue = 1)
+ {
+ $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
+
+ if ($dateValue === null) {
+ $dateValue = 1;
+ } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif ($dateValue < 0.0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Execute function
+ $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
+
+ return (int) $PHPDateObject->format('Y');
+ }
+
+
+ /**
+ * HOUROFDAY
+ *
+ * Returns the hour of a time value.
+ * The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
+ *
+ * Excel Function:
+ * HOUR(timeValue)
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ * @return int Hour
+ */
+ public static function HOUROFDAY($timeValue = 0)
+ {
+ $timeValue = PHPExcel_Calculation_Functions::flattenSingleValue($timeValue);
+
+ if (!is_numeric($timeValue)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
+ $testVal = strtok($timeValue, '/-: ');
+ if (strlen($testVal) < strlen($timeValue)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ $timeValue = self::getTimeValue($timeValue);
+ if (is_string($timeValue)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ // Execute function
+ if ($timeValue >= 1) {
+ $timeValue = fmod($timeValue, 1);
+ } elseif ($timeValue < 0.0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
+
+ return (int) gmdate('G', $timeValue);
+ }
+
+
+ /**
+ * MINUTEOFHOUR
+ *
+ * Returns the minutes of a time value.
+ * The minute is given as an integer, ranging from 0 to 59.
+ *
+ * Excel Function:
+ * MINUTE(timeValue)
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ * @return int Minute
+ */
+ public static function MINUTEOFHOUR($timeValue = 0)
+ {
+ $timeValue = $timeTester = PHPExcel_Calculation_Functions::flattenSingleValue($timeValue);
+
+ if (!is_numeric($timeValue)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
+ $testVal = strtok($timeValue, '/-: ');
+ if (strlen($testVal) < strlen($timeValue)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ $timeValue = self::getTimeValue($timeValue);
+ if (is_string($timeValue)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ // Execute function
+ if ($timeValue >= 1) {
+ $timeValue = fmod($timeValue, 1);
+ } elseif ($timeValue < 0.0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
+
+ return (int) gmdate('i', $timeValue);
+ }
+
+
+ /**
+ * SECONDOFMINUTE
+ *
+ * Returns the seconds of a time value.
+ * The second is given as an integer in the range 0 (zero) to 59.
+ *
+ * Excel Function:
+ * SECOND(timeValue)
+ *
+ * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard time string
+ * @return int Second
+ */
+ public static function SECONDOFMINUTE($timeValue = 0)
+ {
+ $timeValue = PHPExcel_Calculation_Functions::flattenSingleValue($timeValue);
+
+ if (!is_numeric($timeValue)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
+ $testVal = strtok($timeValue, '/-: ');
+ if (strlen($testVal) < strlen($timeValue)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ $timeValue = self::getTimeValue($timeValue);
+ if (is_string($timeValue)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ // Execute function
+ if ($timeValue >= 1) {
+ $timeValue = fmod($timeValue, 1);
+ } elseif ($timeValue < 0.0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
+
+ return (int) gmdate('s', $timeValue);
+ }
+
+
+ /**
+ * EDATE
+ *
+ * Returns the serial number that represents the date that is the indicated number of months
+ * before or after a specified date (the start_date).
+ * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
+ * as the date of issue.
+ *
+ * Excel Function:
+ * EDATE(dateValue,adjustmentMonths)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param int $adjustmentMonths The number of months before or after start_date.
+ * A positive value for months yields a future date;
+ * a negative value yields a past date.
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
+ {
+ $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
+ $adjustmentMonths = PHPExcel_Calculation_Functions::flattenSingleValue($adjustmentMonths);
+
+ if (!is_numeric($adjustmentMonths)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $adjustmentMonths = floor($adjustmentMonths);
+
+ if (is_string($dateValue = self::getDateValue($dateValue))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // Execute function
+ $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths);
+
+ switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
+ case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
+ return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
+ return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
+ return $PHPDateObject;
+ }
+ }
+
+
+ /**
+ * EOMONTH
+ *
+ * Returns the date value for the last day of the month that is the indicated number of months
+ * before or after start_date.
+ * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
+ *
+ * Excel Function:
+ * EOMONTH(dateValue,adjustmentMonths)
+ *
+ * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
+ * PHP DateTime object, or a standard date string
+ * @param int $adjustmentMonths The number of months before or after start_date.
+ * A positive value for months yields a future date;
+ * a negative value yields a past date.
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
+ {
+ $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
+ $adjustmentMonths = PHPExcel_Calculation_Functions::flattenSingleValue($adjustmentMonths);
+
+ if (!is_numeric($adjustmentMonths)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $adjustmentMonths = floor($adjustmentMonths);
+
+ if (is_string($dateValue = self::getDateValue($dateValue))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // Execute function
+ $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths+1);
+ $adjustDays = (int) $PHPDateObject->format('d');
+ $adjustDaysString = '-' . $adjustDays . ' days';
+ $PHPDateObject->modify($adjustDaysString);
+
+ switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
+ case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
+ return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
+ return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
+ case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
+ return $PHPDateObject;
+ }
+ }
+}
diff --git a/PHPExcel/Calculation/Engineering.php b/PHPExcel/Calculation/Engineering.php
new file mode 100644
index 0000000..75e2784
--- /dev/null
+++ b/PHPExcel/Calculation/Engineering.php
@@ -0,0 +1,2650 @@
+ array('Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => true),
+ 'sg' => array('Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => false),
+ 'lbm' => array('Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false),
+ 'u' => array('Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true),
+ 'ozm' => array('Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false),
+ 'm' => array('Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => true),
+ 'mi' => array('Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => false),
+ 'Nmi' => array('Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false),
+ 'in' => array('Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => false),
+ 'ft' => array('Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => false),
+ 'yd' => array('Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => false),
+ 'ang' => array('Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => true),
+ 'Pica' => array('Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false),
+ 'yr' => array('Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => false),
+ 'day' => array('Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => false),
+ 'hr' => array('Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => false),
+ 'mn' => array('Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => false),
+ 'sec' => array('Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => true),
+ 'Pa' => array('Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true),
+ 'p' => array('Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true),
+ 'atm' => array('Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true),
+ 'at' => array('Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true),
+ 'mmHg' => array('Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true),
+ 'N' => array('Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => true),
+ 'dyn' => array('Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true),
+ 'dy' => array('Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true),
+ 'lbf' => array('Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => false),
+ 'J' => array('Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => true),
+ 'e' => array('Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => true),
+ 'c' => array('Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true),
+ 'cal' => array('Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => true),
+ 'eV' => array('Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true),
+ 'ev' => array('Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true),
+ 'HPh' => array('Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false),
+ 'hh' => array('Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false),
+ 'Wh' => array('Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true),
+ 'wh' => array('Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true),
+ 'flb' => array('Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false),
+ 'BTU' => array('Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false),
+ 'btu' => array('Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false),
+ 'HP' => array('Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false),
+ 'h' => array('Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false),
+ 'W' => array('Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true),
+ 'w' => array('Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true),
+ 'T' => array('Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => true),
+ 'ga' => array('Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => true),
+ 'C' => array('Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false),
+ 'cel' => array('Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false),
+ 'F' => array('Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false),
+ 'fah' => array('Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false),
+ 'K' => array('Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false),
+ 'kel' => array('Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false),
+ 'tsp' => array('Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false),
+ 'tbs' => array('Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false),
+ 'oz' => array('Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false),
+ 'cup' => array('Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => false),
+ 'pt' => array('Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false),
+ 'us_pt' => array('Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false),
+ 'uk_pt' => array('Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false),
+ 'qt' => array('Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => false),
+ 'gal' => array('Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => false),
+ 'l' => array('Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true),
+ 'lt' => array('Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true),
+ );
+
+ /**
+ * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM()
+ *
+ * @var mixed[]
+ */
+ private static $conversionMultipliers = array(
+ 'Y' => array('multiplier' => 1E24, 'name' => 'yotta'),
+ 'Z' => array('multiplier' => 1E21, 'name' => 'zetta'),
+ 'E' => array('multiplier' => 1E18, 'name' => 'exa'),
+ 'P' => array('multiplier' => 1E15, 'name' => 'peta'),
+ 'T' => array('multiplier' => 1E12, 'name' => 'tera'),
+ 'G' => array('multiplier' => 1E9, 'name' => 'giga'),
+ 'M' => array('multiplier' => 1E6, 'name' => 'mega'),
+ 'k' => array('multiplier' => 1E3, 'name' => 'kilo'),
+ 'h' => array('multiplier' => 1E2, 'name' => 'hecto'),
+ 'e' => array('multiplier' => 1E1, 'name' => 'deka'),
+ 'd' => array('multiplier' => 1E-1, 'name' => 'deci'),
+ 'c' => array('multiplier' => 1E-2, 'name' => 'centi'),
+ 'm' => array('multiplier' => 1E-3, 'name' => 'milli'),
+ 'u' => array('multiplier' => 1E-6, 'name' => 'micro'),
+ 'n' => array('multiplier' => 1E-9, 'name' => 'nano'),
+ 'p' => array('multiplier' => 1E-12, 'name' => 'pico'),
+ 'f' => array('multiplier' => 1E-15, 'name' => 'femto'),
+ 'a' => array('multiplier' => 1E-18, 'name' => 'atto'),
+ 'z' => array('multiplier' => 1E-21, 'name' => 'zepto'),
+ 'y' => array('multiplier' => 1E-24, 'name' => 'yocto'),
+ );
+
+ /**
+ * Details of the Units of measure conversion factors, organised by group
+ *
+ * @var mixed[]
+ */
+ private static $unitConversions = array(
+ 'Mass' => array(
+ 'g' => array(
+ 'g' => 1.0,
+ 'sg' => 6.85220500053478E-05,
+ 'lbm' => 2.20462291469134E-03,
+ 'u' => 6.02217000000000E+23,
+ 'ozm' => 3.52739718003627E-02,
+ ),
+ 'sg' => array(
+ 'g' => 1.45938424189287E+04,
+ 'sg' => 1.0,
+ 'lbm' => 3.21739194101647E+01,
+ 'u' => 8.78866000000000E+27,
+ 'ozm' => 5.14782785944229E+02,
+ ),
+ 'lbm' => array(
+ 'g' => 4.5359230974881148E+02,
+ 'sg' => 3.10810749306493E-02,
+ 'lbm' => 1.0,
+ 'u' => 2.73161000000000E+26,
+ 'ozm' => 1.60000023429410E+01,
+ ),
+ 'u' => array(
+ 'g' => 1.66053100460465E-24,
+ 'sg' => 1.13782988532950E-28,
+ 'lbm' => 3.66084470330684E-27,
+ 'u' => 1.0,
+ 'ozm' => 5.85735238300524E-26,
+ ),
+ 'ozm' => array(
+ 'g' => 2.83495152079732E+01,
+ 'sg' => 1.94256689870811E-03,
+ 'lbm' => 6.24999908478882E-02,
+ 'u' => 1.70725600000000E+25,
+ 'ozm' => 1.0,
+ ),
+ ),
+ 'Distance' => array(
+ 'm' => array(
+ 'm' => 1.0,
+ 'mi' => 6.21371192237334E-04,
+ 'Nmi' => 5.39956803455724E-04,
+ 'in' => 3.93700787401575E+01,
+ 'ft' => 3.28083989501312E+00,
+ 'yd' => 1.09361329797891E+00,
+ 'ang' => 1.00000000000000E+10,
+ 'Pica' => 2.83464566929116E+03,
+ ),
+ 'mi' => array(
+ 'm' => 1.60934400000000E+03,
+ 'mi' => 1.0,
+ 'Nmi' => 8.68976241900648E-01,
+ 'in' => 6.33600000000000E+04,
+ 'ft' => 5.28000000000000E+03,
+ 'yd' => 1.76000000000000E+03,
+ 'ang' => 1.60934400000000E+13,
+ 'Pica' => 4.56191999999971E+06,
+ ),
+ 'Nmi' => array(
+ 'm' => 1.85200000000000E+03,
+ 'mi' => 1.15077944802354E+00,
+ 'Nmi' => 1.0,
+ 'in' => 7.29133858267717E+04,
+ 'ft' => 6.07611548556430E+03,
+ 'yd' => 2.02537182785694E+03,
+ 'ang' => 1.85200000000000E+13,
+ 'Pica' => 5.24976377952723E+06,
+ ),
+ 'in' => array(
+ 'm' => 2.54000000000000E-02,
+ 'mi' => 1.57828282828283E-05,
+ 'Nmi' => 1.37149028077754E-05,
+ 'in' => 1.0,
+ 'ft' => 8.33333333333333E-02,
+ 'yd' => 2.77777777686643E-02,
+ 'ang' => 2.54000000000000E+08,
+ 'Pica' => 7.19999999999955E+01,
+ ),
+ 'ft' => array(
+ 'm' => 3.04800000000000E-01,
+ 'mi' => 1.89393939393939E-04,
+ 'Nmi' => 1.64578833693305E-04,
+ 'in' => 1.20000000000000E+01,
+ 'ft' => 1.0,
+ 'yd' => 3.33333333223972E-01,
+ 'ang' => 3.04800000000000E+09,
+ 'Pica' => 8.63999999999946E+02,
+ ),
+ 'yd' => array(
+ 'm' => 9.14400000300000E-01,
+ 'mi' => 5.68181818368230E-04,
+ 'Nmi' => 4.93736501241901E-04,
+ 'in' => 3.60000000118110E+01,
+ 'ft' => 3.00000000000000E+00,
+ 'yd' => 1.0,
+ 'ang' => 9.14400000300000E+09,
+ 'Pica' => 2.59200000085023E+03,
+ ),
+ 'ang' => array(
+ 'm' => 1.00000000000000E-10,
+ 'mi' => 6.21371192237334E-14,
+ 'Nmi' => 5.39956803455724E-14,
+ 'in' => 3.93700787401575E-09,
+ 'ft' => 3.28083989501312E-10,
+ 'yd' => 1.09361329797891E-10,
+ 'ang' => 1.0,
+ 'Pica' => 2.83464566929116E-07,
+ ),
+ 'Pica' => array(
+ 'm' => 3.52777777777800E-04,
+ 'mi' => 2.19205948372629E-07,
+ 'Nmi' => 1.90484761219114E-07,
+ 'in' => 1.38888888888898E-02,
+ 'ft' => 1.15740740740748E-03,
+ 'yd' => 3.85802469009251E-04,
+ 'ang' => 3.52777777777800E+06,
+ 'Pica' => 1.0,
+ ),
+ ),
+ 'Time' => array(
+ 'yr' => array(
+ 'yr' => 1.0,
+ 'day' => 365.25,
+ 'hr' => 8766.0,
+ 'mn' => 525960.0,
+ 'sec' => 31557600.0,
+ ),
+ 'day' => array(
+ 'yr' => 2.73785078713210E-03,
+ 'day' => 1.0,
+ 'hr' => 24.0,
+ 'mn' => 1440.0,
+ 'sec' => 86400.0,
+ ),
+ 'hr' => array(
+ 'yr' => 1.14077116130504E-04,
+ 'day' => 4.16666666666667E-02,
+ 'hr' => 1.0,
+ 'mn' => 60.0,
+ 'sec' => 3600.0,
+ ),
+ 'mn' => array(
+ 'yr' => 1.90128526884174E-06,
+ 'day' => 6.94444444444444E-04,
+ 'hr' => 1.66666666666667E-02,
+ 'mn' => 1.0,
+ 'sec' => 60.0,
+ ),
+ 'sec' => array(
+ 'yr' => 3.16880878140289E-08,
+ 'day' => 1.15740740740741E-05,
+ 'hr' => 2.77777777777778E-04,
+ 'mn' => 1.66666666666667E-02,
+ 'sec' => 1.0,
+ ),
+ ),
+ 'Pressure' => array(
+ 'Pa' => array(
+ 'Pa' => 1.0,
+ 'p' => 1.0,
+ 'atm' => 9.86923299998193E-06,
+ 'at' => 9.86923299998193E-06,
+ 'mmHg' => 7.50061707998627E-03,
+ ),
+ 'p' => array(
+ 'Pa' => 1.0,
+ 'p' => 1.0,
+ 'atm' => 9.86923299998193E-06,
+ 'at' => 9.86923299998193E-06,
+ 'mmHg' => 7.50061707998627E-03,
+ ),
+ 'atm' => array(
+ 'Pa' => 1.01324996583000E+05,
+ 'p' => 1.01324996583000E+05,
+ 'atm' => 1.0,
+ 'at' => 1.0,
+ 'mmHg' => 760.0,
+ ),
+ 'at' => array(
+ 'Pa' => 1.01324996583000E+05,
+ 'p' => 1.01324996583000E+05,
+ 'atm' => 1.0,
+ 'at' => 1.0,
+ 'mmHg' => 760.0,
+ ),
+ 'mmHg' => array(
+ 'Pa' => 1.33322363925000E+02,
+ 'p' => 1.33322363925000E+02,
+ 'atm' => 1.31578947368421E-03,
+ 'at' => 1.31578947368421E-03,
+ 'mmHg' => 1.0,
+ ),
+ ),
+ 'Force' => array(
+ 'N' => array(
+ 'N' => 1.0,
+ 'dyn' => 1.0E+5,
+ 'dy' => 1.0E+5,
+ 'lbf' => 2.24808923655339E-01,
+ ),
+ 'dyn' => array(
+ 'N' => 1.0E-5,
+ 'dyn' => 1.0,
+ 'dy' => 1.0,
+ 'lbf' => 2.24808923655339E-06,
+ ),
+ 'dy' => array(
+ 'N' => 1.0E-5,
+ 'dyn' => 1.0,
+ 'dy' => 1.0,
+ 'lbf' => 2.24808923655339E-06,
+ ),
+ 'lbf' => array(
+ 'N' => 4.448222,
+ 'dyn' => 4.448222E+5,
+ 'dy' => 4.448222E+5,
+ 'lbf' => 1.0,
+ ),
+ ),
+ 'Energy' => array(
+ 'J' => array(
+ 'J' => 1.0,
+ 'e' => 9.99999519343231E+06,
+ 'c' => 2.39006249473467E-01,
+ 'cal' => 2.38846190642017E-01,
+ 'eV' => 6.24145700000000E+18,
+ 'ev' => 6.24145700000000E+18,
+ 'HPh' => 3.72506430801000E-07,
+ 'hh' => 3.72506430801000E-07,
+ 'Wh' => 2.77777916238711E-04,
+ 'wh' => 2.77777916238711E-04,
+ 'flb' => 2.37304222192651E+01,
+ 'BTU' => 9.47815067349015E-04,
+ 'btu' => 9.47815067349015E-04,
+ ),
+ 'e' => array(
+ 'J' => 1.00000048065700E-07,
+ 'e' => 1.0,
+ 'c' => 2.39006364353494E-08,
+ 'cal' => 2.38846305445111E-08,
+ 'eV' => 6.24146000000000E+11,
+ 'ev' => 6.24146000000000E+11,
+ 'HPh' => 3.72506609848824E-14,
+ 'hh' => 3.72506609848824E-14,
+ 'Wh' => 2.77778049754611E-11,
+ 'wh' => 2.77778049754611E-11,
+ 'flb' => 2.37304336254586E-06,
+ 'BTU' => 9.47815522922962E-11,
+ 'btu' => 9.47815522922962E-11,
+ ),
+ 'c' => array(
+ 'J' => 4.18399101363672E+00,
+ 'e' => 4.18398900257312E+07,
+ 'c' => 1.0,
+ 'cal' => 9.99330315287563E-01,
+ 'eV' => 2.61142000000000E+19,
+ 'ev' => 2.61142000000000E+19,
+ 'HPh' => 1.55856355899327E-06,
+ 'hh' => 1.55856355899327E-06,
+ 'Wh' => 1.16222030532950E-03,
+ 'wh' => 1.16222030532950E-03,
+ 'flb' => 9.92878733152102E+01,
+ 'BTU' => 3.96564972437776E-03,
+ 'btu' => 3.96564972437776E-03,
+ ),
+ 'cal' => array(
+ 'J' => 4.18679484613929E+00,
+ 'e' => 4.18679283372801E+07,
+ 'c' => 1.00067013349059E+00,
+ 'cal' => 1.0,
+ 'eV' => 2.61317000000000E+19,
+ 'ev' => 2.61317000000000E+19,
+ 'HPh' => 1.55960800463137E-06,
+ 'hh' => 1.55960800463137E-06,
+ 'Wh' => 1.16299914807955E-03,
+ 'wh' => 1.16299914807955E-03,
+ 'flb' => 9.93544094443283E+01,
+ 'BTU' => 3.96830723907002E-03,
+ 'btu' => 3.96830723907002E-03,
+ ),
+ 'eV' => array(
+ 'J' => 1.60219000146921E-19,
+ 'e' => 1.60218923136574E-12,
+ 'c' => 3.82933423195043E-20,
+ 'cal' => 3.82676978535648E-20,
+ 'eV' => 1.0,
+ 'ev' => 1.0,
+ 'HPh' => 5.96826078912344E-26,
+ 'hh' => 5.96826078912344E-26,
+ 'Wh' => 4.45053000026614E-23,
+ 'wh' => 4.45053000026614E-23,
+ 'flb' => 3.80206452103492E-18,
+ 'BTU' => 1.51857982414846E-22,
+ 'btu' => 1.51857982414846E-22,
+ ),
+ 'ev' => array(
+ 'J' => 1.60219000146921E-19,
+ 'e' => 1.60218923136574E-12,
+ 'c' => 3.82933423195043E-20,
+ 'cal' => 3.82676978535648E-20,
+ 'eV' => 1.0,
+ 'ev' => 1.0,
+ 'HPh' => 5.96826078912344E-26,
+ 'hh' => 5.96826078912344E-26,
+ 'Wh' => 4.45053000026614E-23,
+ 'wh' => 4.45053000026614E-23,
+ 'flb' => 3.80206452103492E-18,
+ 'BTU' => 1.51857982414846E-22,
+ 'btu' => 1.51857982414846E-22,
+ ),
+ 'HPh' => array(
+ 'J' => 2.68451741316170E+06,
+ 'e' => 2.68451612283024E+13,
+ 'c' => 6.41616438565991E+05,
+ 'cal' => 6.41186757845835E+05,
+ 'eV' => 1.67553000000000E+25,
+ 'ev' => 1.67553000000000E+25,
+ 'HPh' => 1.0,
+ 'hh' => 1.0,
+ 'Wh' => 7.45699653134593E+02,
+ 'wh' => 7.45699653134593E+02,
+ 'flb' => 6.37047316692964E+07,
+ 'BTU' => 2.54442605275546E+03,
+ 'btu' => 2.54442605275546E+03,
+ ),
+ 'hh' => array(
+ 'J' => 2.68451741316170E+06,
+ 'e' => 2.68451612283024E+13,
+ 'c' => 6.41616438565991E+05,
+ 'cal' => 6.41186757845835E+05,
+ 'eV' => 1.67553000000000E+25,
+ 'ev' => 1.67553000000000E+25,
+ 'HPh' => 1.0,
+ 'hh' => 1.0,
+ 'Wh' => 7.45699653134593E+02,
+ 'wh' => 7.45699653134593E+02,
+ 'flb' => 6.37047316692964E+07,
+ 'BTU' => 2.54442605275546E+03,
+ 'btu' => 2.54442605275546E+03,
+ ),
+ 'Wh' => array(
+ 'J' => 3.59999820554720E+03,
+ 'e' => 3.59999647518369E+10,
+ 'c' => 8.60422069219046E+02,
+ 'cal' => 8.59845857713046E+02,
+ 'eV' => 2.24692340000000E+22,
+ 'ev' => 2.24692340000000E+22,
+ 'HPh' => 1.34102248243839E-03,
+ 'hh' => 1.34102248243839E-03,
+ 'Wh' => 1.0,
+ 'wh' => 1.0,
+ 'flb' => 8.54294774062316E+04,
+ 'BTU' => 3.41213254164705E+00,
+ 'btu' => 3.41213254164705E+00,
+ ),
+ 'wh' => array(
+ 'J' => 3.59999820554720E+03,
+ 'e' => 3.59999647518369E+10,
+ 'c' => 8.60422069219046E+02,
+ 'cal' => 8.59845857713046E+02,
+ 'eV' => 2.24692340000000E+22,
+ 'ev' => 2.24692340000000E+22,
+ 'HPh' => 1.34102248243839E-03,
+ 'hh' => 1.34102248243839E-03,
+ 'Wh' => 1.0,
+ 'wh' => 1.0,
+ 'flb' => 8.54294774062316E+04,
+ 'BTU' => 3.41213254164705E+00,
+ 'btu' => 3.41213254164705E+00,
+ ),
+ 'flb' => array(
+ 'J' => 4.21400003236424E-02,
+ 'e' => 4.21399800687660E+05,
+ 'c' => 1.00717234301644E-02,
+ 'cal' => 1.00649785509554E-02,
+ 'eV' => 2.63015000000000E+17,
+ 'ev' => 2.63015000000000E+17,
+ 'HPh' => 1.56974211145130E-08,
+ 'hh' => 1.56974211145130E-08,
+ 'Wh' => 1.17055614802000E-05,
+ 'wh' => 1.17055614802000E-05,
+ 'flb' => 1.0,
+ 'BTU' => 3.99409272448406E-05,
+ 'btu' => 3.99409272448406E-05,
+ ),
+ 'BTU' => array(
+ 'J' => 1.05505813786749E+03,
+ 'e' => 1.05505763074665E+10,
+ 'c' => 2.52165488508168E+02,
+ 'cal' => 2.51996617135510E+02,
+ 'eV' => 6.58510000000000E+21,
+ 'ev' => 6.58510000000000E+21,
+ 'HPh' => 3.93015941224568E-04,
+ 'hh' => 3.93015941224568E-04,
+ 'Wh' => 2.93071851047526E-01,
+ 'wh' => 2.93071851047526E-01,
+ 'flb' => 2.50369750774671E+04,
+ 'BTU' => 1.0,
+ 'btu' => 1.0,
+ ),
+ 'btu' => array(
+ 'J' => 1.05505813786749E+03,
+ 'e' => 1.05505763074665E+10,
+ 'c' => 2.52165488508168E+02,
+ 'cal' => 2.51996617135510E+02,
+ 'eV' => 6.58510000000000E+21,
+ 'ev' => 6.58510000000000E+21,
+ 'HPh' => 3.93015941224568E-04,
+ 'hh' => 3.93015941224568E-04,
+ 'Wh' => 2.93071851047526E-01,
+ 'wh' => 2.93071851047526E-01,
+ 'flb' => 2.50369750774671E+04,
+ 'BTU' => 1.0,
+ 'btu' => 1.0,
+ ),
+ ),
+ 'Power' => array(
+ 'HP' => array(
+ 'HP' => 1.0,
+ 'h' => 1.0,
+ 'W' => 7.45701000000000E+02,
+ 'w' => 7.45701000000000E+02,
+ ),
+ 'h' => array(
+ 'HP' => 1.0,
+ 'h' => 1.0,
+ 'W' => 7.45701000000000E+02,
+ 'w' => 7.45701000000000E+02,
+ ),
+ 'W' => array(
+ 'HP' => 1.34102006031908E-03,
+ 'h' => 1.34102006031908E-03,
+ 'W' => 1.0,
+ 'w' => 1.0,
+ ),
+ 'w' => array(
+ 'HP' => 1.34102006031908E-03,
+ 'h' => 1.34102006031908E-03,
+ 'W' => 1.0,
+ 'w' => 1.0,
+ ),
+ ),
+ 'Magnetism' => array(
+ 'T' => array(
+ 'T' => 1.0,
+ 'ga' => 10000.0,
+ ),
+ 'ga' => array(
+ 'T' => 0.0001,
+ 'ga' => 1.0,
+ ),
+ ),
+ 'Liquid' => array(
+ 'tsp' => array(
+ 'tsp' => 1.0,
+ 'tbs' => 3.33333333333333E-01,
+ 'oz' => 1.66666666666667E-01,
+ 'cup' => 2.08333333333333E-02,
+ 'pt' => 1.04166666666667E-02,
+ 'us_pt' => 1.04166666666667E-02,
+ 'uk_pt' => 8.67558516821960E-03,
+ 'qt' => 5.20833333333333E-03,
+ 'gal' => 1.30208333333333E-03,
+ 'l' => 4.92999408400710E-03,
+ 'lt' => 4.92999408400710E-03,
+ ),
+ 'tbs' => array(
+ 'tsp' => 3.00000000000000E+00,
+ 'tbs' => 1.0,
+ 'oz' => 5.00000000000000E-01,
+ 'cup' => 6.25000000000000E-02,
+ 'pt' => 3.12500000000000E-02,
+ 'us_pt' => 3.12500000000000E-02,
+ 'uk_pt' => 2.60267555046588E-02,
+ 'qt' => 1.56250000000000E-02,
+ 'gal' => 3.90625000000000E-03,
+ 'l' => 1.47899822520213E-02,
+ 'lt' => 1.47899822520213E-02,
+ ),
+ 'oz' => array(
+ 'tsp' => 6.00000000000000E+00,
+ 'tbs' => 2.00000000000000E+00,
+ 'oz' => 1.0,
+ 'cup' => 1.25000000000000E-01,
+ 'pt' => 6.25000000000000E-02,
+ 'us_pt' => 6.25000000000000E-02,
+ 'uk_pt' => 5.20535110093176E-02,
+ 'qt' => 3.12500000000000E-02,
+ 'gal' => 7.81250000000000E-03,
+ 'l' => 2.95799645040426E-02,
+ 'lt' => 2.95799645040426E-02,
+ ),
+ 'cup' => array(
+ 'tsp' => 4.80000000000000E+01,
+ 'tbs' => 1.60000000000000E+01,
+ 'oz' => 8.00000000000000E+00,
+ 'cup' => 1.0,
+ 'pt' => 5.00000000000000E-01,
+ 'us_pt' => 5.00000000000000E-01,
+ 'uk_pt' => 4.16428088074541E-01,
+ 'qt' => 2.50000000000000E-01,
+ 'gal' => 6.25000000000000E-02,
+ 'l' => 2.36639716032341E-01,
+ 'lt' => 2.36639716032341E-01,
+ ),
+ 'pt' => array(
+ 'tsp' => 9.60000000000000E+01,
+ 'tbs' => 3.20000000000000E+01,
+ 'oz' => 1.60000000000000E+01,
+ 'cup' => 2.00000000000000E+00,
+ 'pt' => 1.0,
+ 'us_pt' => 1.0,
+ 'uk_pt' => 8.32856176149081E-01,
+ 'qt' => 5.00000000000000E-01,
+ 'gal' => 1.25000000000000E-01,
+ 'l' => 4.73279432064682E-01,
+ 'lt' => 4.73279432064682E-01,
+ ),
+ 'us_pt' => array(
+ 'tsp' => 9.60000000000000E+01,
+ 'tbs' => 3.20000000000000E+01,
+ 'oz' => 1.60000000000000E+01,
+ 'cup' => 2.00000000000000E+00,
+ 'pt' => 1.0,
+ 'us_pt' => 1.0,
+ 'uk_pt' => 8.32856176149081E-01,
+ 'qt' => 5.00000000000000E-01,
+ 'gal' => 1.25000000000000E-01,
+ 'l' => 4.73279432064682E-01,
+ 'lt' => 4.73279432064682E-01,
+ ),
+ 'uk_pt' => array(
+ 'tsp' => 1.15266000000000E+02,
+ 'tbs' => 3.84220000000000E+01,
+ 'oz' => 1.92110000000000E+01,
+ 'cup' => 2.40137500000000E+00,
+ 'pt' => 1.20068750000000E+00,
+ 'us_pt' => 1.20068750000000E+00,
+ 'uk_pt' => 1.0,
+ 'qt' => 6.00343750000000E-01,
+ 'gal' => 1.50085937500000E-01,
+ 'l' => 5.68260698087162E-01,
+ 'lt' => 5.68260698087162E-01,
+ ),
+ 'qt' => array(
+ 'tsp' => 1.92000000000000E+02,
+ 'tbs' => 6.40000000000000E+01,
+ 'oz' => 3.20000000000000E+01,
+ 'cup' => 4.00000000000000E+00,
+ 'pt' => 2.00000000000000E+00,
+ 'us_pt' => 2.00000000000000E+00,
+ 'uk_pt' => 1.66571235229816E+00,
+ 'qt' => 1.0,
+ 'gal' => 2.50000000000000E-01,
+ 'l' => 9.46558864129363E-01,
+ 'lt' => 9.46558864129363E-01,
+ ),
+ 'gal' => array(
+ 'tsp' => 7.68000000000000E+02,
+ 'tbs' => 2.56000000000000E+02,
+ 'oz' => 1.28000000000000E+02,
+ 'cup' => 1.60000000000000E+01,
+ 'pt' => 8.00000000000000E+00,
+ 'us_pt' => 8.00000000000000E+00,
+ 'uk_pt' => 6.66284940919265E+00,
+ 'qt' => 4.00000000000000E+00,
+ 'gal' => 1.0,
+ 'l' => 3.78623545651745E+00,
+ 'lt' => 3.78623545651745E+00,
+ ),
+ 'l' => array(
+ 'tsp' => 2.02840000000000E+02,
+ 'tbs' => 6.76133333333333E+01,
+ 'oz' => 3.38066666666667E+01,
+ 'cup' => 4.22583333333333E+00,
+ 'pt' => 2.11291666666667E+00,
+ 'us_pt' => 2.11291666666667E+00,
+ 'uk_pt' => 1.75975569552166E+00,
+ 'qt' => 1.05645833333333E+00,
+ 'gal' => 2.64114583333333E-01,
+ 'l' => 1.0,
+ 'lt' => 1.0,
+ ),
+ 'lt' => array(
+ 'tsp' => 2.02840000000000E+02,
+ 'tbs' => 6.76133333333333E+01,
+ 'oz' => 3.38066666666667E+01,
+ 'cup' => 4.22583333333333E+00,
+ 'pt' => 2.11291666666667E+00,
+ 'us_pt' => 2.11291666666667E+00,
+ 'uk_pt' => 1.75975569552166E+00,
+ 'qt' => 1.05645833333333E+00,
+ 'gal' => 2.64114583333333E-01,
+ 'l' => 1.0,
+ 'lt' => 1.0,
+ ),
+ ),
+ );
+
+
+ /**
+ * parseComplex
+ *
+ * Parses a complex number into its real and imaginary parts, and an I or J suffix
+ *
+ * @param string $complexNumber The complex number
+ * @return string[] Indexed on "real", "imaginary" and "suffix"
+ */
+ public static function parseComplex($complexNumber)
+ {
+ $workString = (string) $complexNumber;
+
+ $realNumber = $imaginary = 0;
+ // Extract the suffix, if there is one
+ $suffix = substr($workString, -1);
+ if (!is_numeric($suffix)) {
+ $workString = substr($workString, 0, -1);
+ } else {
+ $suffix = '';
+ }
+
+ // Split the input into its Real and Imaginary components
+ $leadingSign = 0;
+ if (strlen($workString) > 0) {
+ $leadingSign = (($workString{0} == '+') || ($workString{0} == '-')) ? 1 : 0;
+ }
+ $power = '';
+ $realNumber = strtok($workString, '+-');
+ if (strtoupper(substr($realNumber, -1)) == 'E') {
+ $power = strtok('+-');
+ ++$leadingSign;
+ }
+
+ $realNumber = substr($workString, 0, strlen($realNumber)+strlen($power)+$leadingSign);
+
+ if ($suffix != '') {
+ $imaginary = substr($workString, strlen($realNumber));
+
+ if (($imaginary == '') && (($realNumber == '') || ($realNumber == '+') || ($realNumber == '-'))) {
+ $imaginary = $realNumber.'1';
+ $realNumber = '0';
+ } elseif ($imaginary == '') {
+ $imaginary = $realNumber;
+ $realNumber = '0';
+ } elseif (($imaginary == '+') || ($imaginary == '-')) {
+ $imaginary .= '1';
+ }
+ }
+
+ return array(
+ 'real' => $realNumber,
+ 'imaginary' => $imaginary,
+ 'suffix' => $suffix
+ );
+ }
+
+
+ /**
+ * Cleans the leading characters in a complex number string
+ *
+ * @param string $complexNumber The complex number to clean
+ * @return string The "cleaned" complex number
+ */
+ private static function cleanComplex($complexNumber)
+ {
+ if ($complexNumber{0} == '+') {
+ $complexNumber = substr($complexNumber, 1);
+ }
+ if ($complexNumber{0} == '0') {
+ $complexNumber = substr($complexNumber, 1);
+ }
+ if ($complexNumber{0} == '.') {
+ $complexNumber = '0'.$complexNumber;
+ }
+ if ($complexNumber{0} == '+') {
+ $complexNumber = substr($complexNumber, 1);
+ }
+ return $complexNumber;
+ }
+
+ /**
+ * Formats a number base string value with leading zeroes
+ *
+ * @param string $xVal The "number" to pad
+ * @param integer $places The length that we want to pad this value
+ * @return string The padded "number"
+ */
+ private static function nbrConversionFormat($xVal, $places)
+ {
+ if (!is_null($places)) {
+ if (strlen($xVal) <= $places) {
+ return substr(str_pad($xVal, $places, '0', STR_PAD_LEFT), -10);
+ } else {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ }
+
+ return substr($xVal, -10);
+ }
+
+ /**
+ * BESSELI
+ *
+ * Returns the modified Bessel function In(x), which is equivalent to the Bessel function evaluated
+ * for purely imaginary arguments
+ *
+ * Excel Function:
+ * BESSELI(x,ord)
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param float $x The value at which to evaluate the function.
+ * If x is nonnumeric, BESSELI returns the #VALUE! error value.
+ * @param integer $ord The order of the Bessel function.
+ * If ord is not an integer, it is truncated.
+ * If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
+ * If $ord < 0, BESSELI returns the #NUM! error value.
+ * @return float
+ *
+ */
+ public static function BESSELI($x, $ord)
+ {
+ $x = (is_null($x)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $ord = (is_null($ord)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($ord);
+
+ if ((is_numeric($x)) && (is_numeric($ord))) {
+ $ord = floor($ord);
+ if ($ord < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ if (abs($x) <= 30) {
+ $fResult = $fTerm = pow($x / 2, $ord) / PHPExcel_Calculation_MathTrig::FACT($ord);
+ $ordK = 1;
+ $fSqrX = ($x * $x) / 4;
+ do {
+ $fTerm *= $fSqrX;
+ $fTerm /= ($ordK * ($ordK + $ord));
+ $fResult += $fTerm;
+ } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
+ } else {
+ $f_2_PI = 2 * M_PI;
+
+ $fXAbs = abs($x);
+ $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
+ if (($ord & 1) && ($x < 0)) {
+ $fResult = -$fResult;
+ }
+ }
+ return (is_nan($fResult)) ? PHPExcel_Calculation_Functions::NaN() : $fResult;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * BESSELJ
+ *
+ * Returns the Bessel function
+ *
+ * Excel Function:
+ * BESSELJ(x,ord)
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param float $x The value at which to evaluate the function.
+ * If x is nonnumeric, BESSELJ returns the #VALUE! error value.
+ * @param integer $ord The order of the Bessel function. If n is not an integer, it is truncated.
+ * If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
+ * If $ord < 0, BESSELJ returns the #NUM! error value.
+ * @return float
+ *
+ */
+ public static function BESSELJ($x, $ord)
+ {
+ $x = (is_null($x)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $ord = (is_null($ord)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($ord);
+
+ if ((is_numeric($x)) && (is_numeric($ord))) {
+ $ord = floor($ord);
+ if ($ord < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ $fResult = 0;
+ if (abs($x) <= 30) {
+ $fResult = $fTerm = pow($x / 2, $ord) / PHPExcel_Calculation_MathTrig::FACT($ord);
+ $ordK = 1;
+ $fSqrX = ($x * $x) / -4;
+ do {
+ $fTerm *= $fSqrX;
+ $fTerm /= ($ordK * ($ordK + $ord));
+ $fResult += $fTerm;
+ } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
+ } else {
+ $f_PI_DIV_2 = M_PI / 2;
+ $f_PI_DIV_4 = M_PI / 4;
+
+ $fXAbs = abs($x);
+ $fResult = sqrt(M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4);
+ if (($ord & 1) && ($x < 0)) {
+ $fResult = -$fResult;
+ }
+ }
+ return (is_nan($fResult)) ? PHPExcel_Calculation_Functions::NaN() : $fResult;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ private static function besselK0($fNum)
+ {
+ if ($fNum <= 2) {
+ $fNum2 = $fNum * 0.5;
+ $y = ($fNum2 * $fNum2);
+ $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
+ (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
+ (0.10750e-3 + $y * 0.74e-5))))));
+ } else {
+ $y = 2 / $fNum;
+ $fRet = exp(-$fNum) / sqrt($fNum) *
+ (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
+ (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
+ }
+ return $fRet;
+ }
+
+
+ private static function besselK1($fNum)
+ {
+ if ($fNum <= 2) {
+ $fNum2 = $fNum * 0.5;
+ $y = ($fNum2 * $fNum2);
+ $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
+ (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
+ (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
+ } else {
+ $y = 2 / $fNum;
+ $fRet = exp(-$fNum) / sqrt($fNum) *
+ (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
+ (0.325614e-2 + $y * (-0.68245e-3)))))));
+ }
+ return $fRet;
+ }
+
+
+ /**
+ * BESSELK
+ *
+ * Returns the modified Bessel function Kn(x), which is equivalent to the Bessel functions evaluated
+ * for purely imaginary arguments.
+ *
+ * Excel Function:
+ * BESSELK(x,ord)
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param float $x The value at which to evaluate the function.
+ * If x is nonnumeric, BESSELK returns the #VALUE! error value.
+ * @param integer $ord The order of the Bessel function. If n is not an integer, it is truncated.
+ * If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
+ * If $ord < 0, BESSELK returns the #NUM! error value.
+ * @return float
+ *
+ */
+ public static function BESSELK($x, $ord)
+ {
+ $x = (is_null($x)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $ord = (is_null($ord)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($ord);
+
+ if ((is_numeric($x)) && (is_numeric($ord))) {
+ if (($ord < 0) || ($x == 0.0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ switch (floor($ord)) {
+ case 0:
+ return self::besselK0($x);
+ case 1:
+ return self::besselK1($x);
+ default:
+ $fTox = 2 / $x;
+ $fBkm = self::besselK0($x);
+ $fBk = self::besselK1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fBkp = $fBkm + $n * $fTox * $fBk;
+ $fBkm = $fBk;
+ $fBk = $fBkp;
+ }
+ }
+ return (is_nan($fBk)) ? PHPExcel_Calculation_Functions::NaN() : $fBk;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ private static function besselY0($fNum)
+ {
+ if ($fNum < 8.0) {
+ $y = ($fNum * $fNum);
+ $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
+ $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
+ $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum);
+ } else {
+ $z = 8.0 / $fNum;
+ $y = ($z * $z);
+ $xx = $fNum - 0.785398164;
+ $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
+ $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
+ $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
+ }
+ return $fRet;
+ }
+
+
+ private static function besselY1($fNum)
+ {
+ if ($fNum < 8.0) {
+ $y = ($fNum * $fNum);
+ $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
+ (-0.4237922726e7 + $y * 0.8511937935e4)))));
+ $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
+ (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
+ $fRet = $f1 / $f2 + 0.636619772 * ( self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
+ } else {
+ $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
+ }
+ return $fRet;
+ }
+
+
+ /**
+ * BESSELY
+ *
+ * Returns the Bessel function, which is also called the Weber function or the Neumann function.
+ *
+ * Excel Function:
+ * BESSELY(x,ord)
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param float $x The value at which to evaluate the function.
+ * If x is nonnumeric, BESSELK returns the #VALUE! error value.
+ * @param integer $ord The order of the Bessel function. If n is not an integer, it is truncated.
+ * If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
+ * If $ord < 0, BESSELK returns the #NUM! error value.
+ *
+ * @return float
+ */
+ public static function BESSELY($x, $ord)
+ {
+ $x = (is_null($x)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $ord = (is_null($ord)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($ord);
+
+ if ((is_numeric($x)) && (is_numeric($ord))) {
+ if (($ord < 0) || ($x == 0.0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ switch (floor($ord)) {
+ case 0:
+ return self::besselY0($x);
+ case 1:
+ return self::besselY1($x);
+ default:
+ $fTox = 2 / $x;
+ $fBym = self::besselY0($x);
+ $fBy = self::besselY1($x);
+ for ($n = 1; $n < $ord; ++$n) {
+ $fByp = $n * $fTox * $fBy - $fBym;
+ $fBym = $fBy;
+ $fBy = $fByp;
+ }
+ }
+ return (is_nan($fBy)) ? PHPExcel_Calculation_Functions::NaN() : $fBy;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * BINTODEC
+ *
+ * Return a binary value as decimal.
+ *
+ * Excel Function:
+ * BIN2DEC(x)
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The binary number (as a string) that you want to convert. The number
+ * cannot contain more than 10 characters (10 bits). The most significant
+ * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is not a valid binary number, or if number contains more than
+ * 10 characters (10 bits), BIN2DEC returns the #NUM! error value.
+ * @return string
+ */
+ public static function BINTODEC($x)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+
+ if (is_bool($x)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
+ $x = floor($x);
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if (strlen($x) > 10) {
+ return PHPExcel_Calculation_Functions::NaN();
+ } elseif (strlen($x) == 10) {
+ // Two's Complement
+ $x = substr($x, -9);
+ return '-'.(512-bindec($x));
+ }
+ return bindec($x);
+ }
+
+
+ /**
+ * BINTOHEX
+ *
+ * Return a binary value as hex.
+ *
+ * Excel Function:
+ * BIN2HEX(x[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The binary number (as a string) that you want to convert. The number
+ * cannot contain more than 10 characters (10 bits). The most significant
+ * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is not a valid binary number, or if number contains more than
+ * 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
+ * @param integer $places The number of characters to use. If places is omitted, BIN2HEX uses the
+ * minimum number of characters necessary. Places is useful for padding the
+ * return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
+ * If places is negative, BIN2HEX returns the #NUM! error value.
+ * @return string
+ */
+ public static function BINTOHEX($x, $places = null)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $places = PHPExcel_Calculation_Functions::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
+ $x = floor($x);
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if (strlen($x) > 10) {
+ return PHPExcel_Calculation_Functions::NaN();
+ } elseif (strlen($x) == 10) {
+ // Two's Complement
+ return str_repeat('F', 8).substr(strtoupper(dechex(bindec(substr($x, -9)))), -2);
+ }
+ $hexVal = (string) strtoupper(dechex(bindec($x)));
+
+ return self::nbrConversionFormat($hexVal, $places);
+ }
+
+
+ /**
+ * BINTOOCT
+ *
+ * Return a binary value as octal.
+ *
+ * Excel Function:
+ * BIN2OCT(x[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The binary number (as a string) that you want to convert. The number
+ * cannot contain more than 10 characters (10 bits). The most significant
+ * bit of number is the sign bit. The remaining 9 bits are magnitude bits.
+ * Negative numbers are represented using two's-complement notation.
+ * If number is not a valid binary number, or if number contains more than
+ * 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
+ * @param integer $places The number of characters to use. If places is omitted, BIN2OCT uses the
+ * minimum number of characters necessary. Places is useful for padding the
+ * return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
+ * If places is negative, BIN2OCT returns the #NUM! error value.
+ * @return string
+ */
+ public static function BINTOOCT($x, $places = null)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $places = PHPExcel_Calculation_Functions::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
+ $x = floor($x);
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if (strlen($x) > 10) {
+ return PHPExcel_Calculation_Functions::NaN();
+ } elseif (strlen($x) == 10) {
+ // Two's Complement
+ return str_repeat('7', 7).substr(strtoupper(decoct(bindec(substr($x, -9)))), -3);
+ }
+ $octVal = (string) decoct(bindec($x));
+
+ return self::nbrConversionFormat($octVal, $places);
+ }
+
+
+ /**
+ * DECTOBIN
+ *
+ * Return a decimal value as binary.
+ *
+ * Excel Function:
+ * DEC2BIN(x[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The decimal integer you want to convert. If number is negative,
+ * valid place values are ignored and DEC2BIN returns a 10-character
+ * (10-bit) binary number in which the most significant bit is the sign
+ * bit. The remaining 9 bits are magnitude bits. Negative numbers are
+ * represented using two's-complement notation.
+ * If number < -512 or if number > 511, DEC2BIN returns the #NUM! error
+ * value.
+ * If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If DEC2BIN requires more than places characters, it returns the #NUM!
+ * error value.
+ * @param integer $places The number of characters to use. If places is omitted, DEC2BIN uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
+ * If places is zero or negative, DEC2BIN returns the #NUM! error value.
+ * @return string
+ */
+ public static function DECTOBIN($x, $places = null)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $places = PHPExcel_Calculation_Functions::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $x = (string) floor($x);
+ $r = decbin($x);
+ if (strlen($r) == 32) {
+ // Two's Complement
+ $r = substr($r, -10);
+ } elseif (strlen($r) > 11) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+
+ /**
+ * DECTOHEX
+ *
+ * Return a decimal value as hex.
+ *
+ * Excel Function:
+ * DEC2HEX(x[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2HEX returns a 10-character (40-bit)
+ * hexadecimal number in which the most significant bit is the sign
+ * bit. The remaining 39 bits are magnitude bits. Negative numbers
+ * are represented using two's-complement notation.
+ * If number < -549,755,813,888 or if number > 549,755,813,887,
+ * DEC2HEX returns the #NUM! error value.
+ * If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If DEC2HEX requires more than places characters, it returns the
+ * #NUM! error value.
+ * @param integer $places The number of characters to use. If places is omitted, DEC2HEX uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
+ * If places is zero or negative, DEC2HEX returns the #NUM! error value.
+ * @return string
+ */
+ public static function DECTOHEX($x, $places = null)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $places = PHPExcel_Calculation_Functions::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $x = (string) floor($x);
+ $r = strtoupper(dechex($x));
+ if (strlen($r) == 8) {
+ // Two's Complement
+ $r = 'FF'.$r;
+ }
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+
+ /**
+ * DECTOOCT
+ *
+ * Return an decimal value as octal.
+ *
+ * Excel Function:
+ * DEC2OCT(x[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The decimal integer you want to convert. If number is negative,
+ * places is ignored and DEC2OCT returns a 10-character (30-bit)
+ * octal number in which the most significant bit is the sign bit.
+ * The remaining 29 bits are magnitude bits. Negative numbers are
+ * represented using two's-complement notation.
+ * If number < -536,870,912 or if number > 536,870,911, DEC2OCT
+ * returns the #NUM! error value.
+ * If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If DEC2OCT requires more than places characters, it returns the
+ * #NUM! error value.
+ * @param integer $places The number of characters to use. If places is omitted, DEC2OCT uses
+ * the minimum number of characters necessary. Places is useful for
+ * padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
+ * If places is zero or negative, DEC2OCT returns the #NUM! error value.
+ * @return string
+ */
+ public static function DECTOOCT($x, $places = null)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $places = PHPExcel_Calculation_Functions::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ $x = (int) $x;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $x = (string) floor($x);
+ $r = decoct($x);
+ if (strlen($r) == 11) {
+ // Two's Complement
+ $r = substr($r, -10);
+ }
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+
+ /**
+ * HEXTOBIN
+ *
+ * Return a hex value as binary.
+ *
+ * Excel Function:
+ * HEX2BIN(x[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x the hexadecimal number you want to convert. Number cannot
+ * contain more than 10 characters. The most significant bit of
+ * number is the sign bit (40th bit from the right). The remaining
+ * 9 bits are magnitude bits. Negative numbers are represented
+ * using two's-complement notation.
+ * If number is negative, HEX2BIN ignores places and returns a
+ * 10-character binary number.
+ * If number is negative, it cannot be less than FFFFFFFE00, and
+ * if number is positive, it cannot be greater than 1FF.
+ * If number is not a valid hexadecimal number, HEX2BIN returns
+ * the #NUM! error value.
+ * If HEX2BIN requires more than places characters, it returns
+ * the #NUM! error value.
+ * @param integer $places The number of characters to use. If places is omitted,
+ * HEX2BIN uses the minimum number of characters necessary. Places
+ * is useful for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
+ * If places is negative, HEX2BIN returns the #NUM! error value.
+ * @return string
+ */
+ public static function HEXTOBIN($x, $places = null)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $places = PHPExcel_Calculation_Functions::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $binVal = decbin(hexdec($x));
+
+ return substr(self::nbrConversionFormat($binVal, $places), -10);
+ }
+
+
+ /**
+ * HEXTODEC
+ *
+ * Return a hex value as decimal.
+ *
+ * Excel Function:
+ * HEX2DEC(x)
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The hexadecimal number you want to convert. This number cannot
+ * contain more than 10 characters (40 bits). The most significant
+ * bit of number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is not a valid hexadecimal number, HEX2DEC returns the
+ * #NUM! error value.
+ * @return string
+ */
+ public static function HEXTODEC($x)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+
+ if (is_bool($x)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return hexdec($x);
+ }
+
+
+ /**
+ * HEXTOOCT
+ *
+ * Return a hex value as octal.
+ *
+ * Excel Function:
+ * HEX2OCT(x[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The hexadecimal number you want to convert. Number cannot
+ * contain more than 10 characters. The most significant bit of
+ * number is the sign bit. The remaining 39 bits are magnitude
+ * bits. Negative numbers are represented using two's-complement
+ * notation.
+ * If number is negative, HEX2OCT ignores places and returns a
+ * 10-character octal number.
+ * If number is negative, it cannot be less than FFE0000000, and
+ * if number is positive, it cannot be greater than 1FFFFFFF.
+ * If number is not a valid hexadecimal number, HEX2OCT returns
+ * the #NUM! error value.
+ * If HEX2OCT requires more than places characters, it returns
+ * the #NUM! error value.
+ * @param integer $places The number of characters to use. If places is omitted, HEX2OCT
+ * uses the minimum number of characters necessary. Places is
+ * useful for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, HEX2OCT returns the #VALUE! error
+ * value.
+ * If places is negative, HEX2OCT returns the #NUM! error value.
+ * @return string
+ */
+ public static function HEXTOOCT($x, $places = null)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $places = PHPExcel_Calculation_Functions::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $x = (string) $x;
+ if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $octVal = decoct(hexdec($x));
+
+ return self::nbrConversionFormat($octVal, $places);
+ } // function HEXTOOCT()
+
+
+ /**
+ * OCTTOBIN
+ *
+ * Return an octal value as binary.
+ *
+ * Excel Function:
+ * OCT2BIN(x[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The octal number you want to convert. Number may not
+ * contain more than 10 characters. The most significant
+ * bit of number is the sign bit. The remaining 29 bits
+ * are magnitude bits. Negative numbers are represented
+ * using two's-complement notation.
+ * If number is negative, OCT2BIN ignores places and returns
+ * a 10-character binary number.
+ * If number is negative, it cannot be less than 7777777000,
+ * and if number is positive, it cannot be greater than 777.
+ * If number is not a valid octal number, OCT2BIN returns
+ * the #NUM! error value.
+ * If OCT2BIN requires more than places characters, it
+ * returns the #NUM! error value.
+ * @param integer $places The number of characters to use. If places is omitted,
+ * OCT2BIN uses the minimum number of characters necessary.
+ * Places is useful for padding the return value with
+ * leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, OCT2BIN returns the #VALUE!
+ * error value.
+ * If places is negative, OCT2BIN returns the #NUM! error
+ * value.
+ * @return string
+ */
+ public static function OCTTOBIN($x, $places = null)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $places = PHPExcel_Calculation_Functions::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $x = (string) $x;
+ if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $r = decbin(octdec($x));
+
+ return self::nbrConversionFormat($r, $places);
+ }
+
+
+ /**
+ * OCTTODEC
+ *
+ * Return an octal value as decimal.
+ *
+ * Excel Function:
+ * OCT2DEC(x)
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is not a valid octal number, OCT2DEC returns the
+ * #NUM! error value.
+ * @return string
+ */
+ public static function OCTTODEC($x)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+
+ if (is_bool($x)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $x = (string) $x;
+ if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return octdec($x);
+ }
+
+
+ /**
+ * OCTTOHEX
+ *
+ * Return an octal value as hex.
+ *
+ * Excel Function:
+ * OCT2HEX(x[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $x The octal number you want to convert. Number may not contain
+ * more than 10 octal characters (30 bits). The most significant
+ * bit of number is the sign bit. The remaining 29 bits are
+ * magnitude bits. Negative numbers are represented using
+ * two's-complement notation.
+ * If number is negative, OCT2HEX ignores places and returns a
+ * 10-character hexadecimal number.
+ * If number is not a valid octal number, OCT2HEX returns the
+ * #NUM! error value.
+ * If OCT2HEX requires more than places characters, it returns
+ * the #NUM! error value.
+ * @param integer $places The number of characters to use. If places is omitted, OCT2HEX
+ * uses the minimum number of characters necessary. Places is useful
+ * for padding the return value with leading 0s (zeros).
+ * If places is not an integer, it is truncated.
+ * If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
+ * If places is negative, OCT2HEX returns the #NUM! error value.
+ * @return string
+ */
+ public static function OCTTOHEX($x, $places = null)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $places = PHPExcel_Calculation_Functions::flattenSingleValue($places);
+
+ if (is_bool($x)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $x = (string) $x;
+ if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $hexVal = strtoupper(dechex(octdec($x)));
+
+ return self::nbrConversionFormat($hexVal, $places);
+ }
+
+
+ /**
+ * COMPLEX
+ *
+ * Converts real and imaginary coefficients into a complex number of the form x + yi or x + yj.
+ *
+ * Excel Function:
+ * COMPLEX(realNumber,imaginary[,places])
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param float $realNumber The real coefficient of the complex number.
+ * @param float $imaginary The imaginary coefficient of the complex number.
+ * @param string $suffix The suffix for the imaginary component of the complex number.
+ * If omitted, the suffix is assumed to be "i".
+ * @return string
+ */
+ public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
+ {
+ $realNumber = (is_null($realNumber)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($realNumber);
+ $imaginary = (is_null($imaginary)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($imaginary);
+ $suffix = (is_null($suffix)) ? 'i' : PHPExcel_Calculation_Functions::flattenSingleValue($suffix);
+
+ if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
+ (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))) {
+ $realNumber = (float) $realNumber;
+ $imaginary = (float) $imaginary;
+
+ if ($suffix == '') {
+ $suffix = 'i';
+ }
+ if ($realNumber == 0.0) {
+ if ($imaginary == 0.0) {
+ return (string) '0';
+ } elseif ($imaginary == 1.0) {
+ return (string) $suffix;
+ } elseif ($imaginary == -1.0) {
+ return (string) '-'.$suffix;
+ }
+ return (string) $imaginary.$suffix;
+ } elseif ($imaginary == 0.0) {
+ return (string) $realNumber;
+ } elseif ($imaginary == 1.0) {
+ return (string) $realNumber.'+'.$suffix;
+ } elseif ($imaginary == -1.0) {
+ return (string) $realNumber.'-'.$suffix;
+ }
+ if ($imaginary > 0) {
+ $imaginary = (string) '+'.$imaginary;
+ }
+ return (string) $realNumber.$imaginary.$suffix;
+ }
+
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * IMAGINARY
+ *
+ * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMAGINARY(complexNumber)
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $complexNumber The complex number for which you want the imaginary
+ * coefficient.
+ * @return float
+ */
+ public static function IMAGINARY($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+ return $parsedComplex['imaginary'];
+ }
+
+
+ /**
+ * IMREAL
+ *
+ * Returns the real coefficient of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMREAL(complexNumber)
+ *
+ * @access public
+ * @category Engineering Functions
+ * @param string $complexNumber The complex number for which you want the real coefficient.
+ * @return float
+ */
+ public static function IMREAL($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+ return $parsedComplex['real'];
+ }
+
+
+ /**
+ * IMABS
+ *
+ * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMABS(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the absolute value.
+ * @return float
+ */
+ public static function IMABS($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ return sqrt(
+ ($parsedComplex['real'] * $parsedComplex['real']) +
+ ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])
+ );
+ }
+
+
+ /**
+ * IMARGUMENT
+ *
+ * Returns the argument theta of a complex number, i.e. the angle in radians from the real
+ * axis to the representation of the number in polar coordinates.
+ *
+ * Excel Function:
+ * IMARGUMENT(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the argument theta.
+ * @return float
+ */
+ public static function IMARGUMENT($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ if ($parsedComplex['real'] == 0.0) {
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return 0.0;
+ } elseif ($parsedComplex['imaginary'] < 0.0) {
+ return M_PI / -2;
+ } else {
+ return M_PI / 2;
+ }
+ } elseif ($parsedComplex['real'] > 0.0) {
+ return atan($parsedComplex['imaginary'] / $parsedComplex['real']);
+ } elseif ($parsedComplex['imaginary'] < 0.0) {
+ return 0 - (M_PI - atan(abs($parsedComplex['imaginary']) / abs($parsedComplex['real'])));
+ } else {
+ return M_PI - atan($parsedComplex['imaginary'] / abs($parsedComplex['real']));
+ }
+ }
+
+
+ /**
+ * IMCONJUGATE
+ *
+ * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCONJUGATE(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the conjugate.
+ * @return string
+ */
+ public static function IMCONJUGATE($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return $parsedComplex['real'];
+ } else {
+ return self::cleanComplex(
+ self::COMPLEX(
+ $parsedComplex['real'],
+ 0 - $parsedComplex['imaginary'],
+ $parsedComplex['suffix']
+ )
+ );
+ }
+ }
+
+
+ /**
+ * IMCOS
+ *
+ * Returns the cosine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMCOS(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the cosine.
+ * @return string|float
+ */
+ public static function IMCOS($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return cos($parsedComplex['real']);
+ } else {
+ return self::IMCONJUGATE(
+ self::COMPLEX(
+ cos($parsedComplex['real']) * cosh($parsedComplex['imaginary']),
+ sin($parsedComplex['real']) * sinh($parsedComplex['imaginary']),
+ $parsedComplex['suffix']
+ )
+ );
+ }
+ }
+
+
+ /**
+ * IMSIN
+ *
+ * Returns the sine of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSIN(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the sine.
+ * @return string|float
+ */
+ public static function IMSIN($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ if ($parsedComplex['imaginary'] == 0.0) {
+ return sin($parsedComplex['real']);
+ } else {
+ return self::COMPLEX(
+ sin($parsedComplex['real']) * cosh($parsedComplex['imaginary']),
+ cos($parsedComplex['real']) * sinh($parsedComplex['imaginary']),
+ $parsedComplex['suffix']
+ );
+ }
+ }
+
+
+ /**
+ * IMSQRT
+ *
+ * Returns the square root of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSQRT(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the square root.
+ * @return string
+ */
+ public static function IMSQRT($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ $theta = self::IMARGUMENT($complexNumber);
+ $d1 = cos($theta / 2);
+ $d2 = sin($theta / 2);
+ $r = sqrt(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
+
+ if ($parsedComplex['suffix'] == '') {
+ return self::COMPLEX($d1 * $r, $d2 * $r);
+ } else {
+ return self::COMPLEX($d1 * $r, $d2 * $r, $parsedComplex['suffix']);
+ }
+ }
+
+
+ /**
+ * IMLN
+ *
+ * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLN(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the natural logarithm.
+ * @return string
+ */
+ public static function IMLN($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ $logR = log(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
+ $t = self::IMARGUMENT($complexNumber);
+
+ if ($parsedComplex['suffix'] == '') {
+ return self::COMPLEX($logR, $t);
+ } else {
+ return self::COMPLEX($logR, $t, $parsedComplex['suffix']);
+ }
+ }
+
+
+ /**
+ * IMLOG10
+ *
+ * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG10(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the common logarithm.
+ * @return string
+ */
+ public static function IMLOG10($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return log10($parsedComplex['real']);
+ }
+
+ return self::IMPRODUCT(log10(EULER), self::IMLN($complexNumber));
+ }
+
+
+ /**
+ * IMLOG2
+ *
+ * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMLOG2(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the base-2 logarithm.
+ * @return string
+ */
+ public static function IMLOG2($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return log($parsedComplex['real'], 2);
+ }
+
+ return self::IMPRODUCT(log(EULER, 2), self::IMLN($complexNumber));
+ }
+
+
+ /**
+ * IMEXP
+ *
+ * Returns the exponential of a complex number in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMEXP(complexNumber)
+ *
+ * @param string $complexNumber The complex number for which you want the exponential.
+ * @return string
+ */
+ public static function IMEXP($complexNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
+ return '1';
+ }
+
+ $e = exp($parsedComplex['real']);
+ $eX = $e * cos($parsedComplex['imaginary']);
+ $eY = $e * sin($parsedComplex['imaginary']);
+
+ if ($parsedComplex['suffix'] == '') {
+ return self::COMPLEX($eX, $eY);
+ } else {
+ return self::COMPLEX($eX, $eY, $parsedComplex['suffix']);
+ }
+ }
+
+
+ /**
+ * IMPOWER
+ *
+ * Returns a complex number in x + yi or x + yj text format raised to a power.
+ *
+ * Excel Function:
+ * IMPOWER(complexNumber,realNumber)
+ *
+ * @param string $complexNumber The complex number you want to raise to a power.
+ * @param float $realNumber The power to which you want to raise the complex number.
+ * @return string
+ */
+ public static function IMPOWER($complexNumber, $realNumber)
+ {
+ $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
+ $realNumber = PHPExcel_Calculation_Functions::flattenSingleValue($realNumber);
+
+ if (!is_numeric($realNumber)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ $parsedComplex = self::parseComplex($complexNumber);
+
+ $r = sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
+ $rPower = pow($r, $realNumber);
+ $theta = self::IMARGUMENT($complexNumber) * $realNumber;
+ if ($theta == 0) {
+ return 1;
+ } elseif ($parsedComplex['imaginary'] == 0.0) {
+ return self::COMPLEX($rPower * cos($theta), $rPower * sin($theta), $parsedComplex['suffix']);
+ } else {
+ return self::COMPLEX($rPower * cos($theta), $rPower * sin($theta), $parsedComplex['suffix']);
+ }
+ }
+
+
+ /**
+ * IMDIV
+ *
+ * Returns the quotient of two complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMDIV(complexDividend,complexDivisor)
+ *
+ * @param string $complexDividend The complex numerator or dividend.
+ * @param string $complexDivisor The complex denominator or divisor.
+ * @return string
+ */
+ public static function IMDIV($complexDividend, $complexDivisor)
+ {
+ $complexDividend = PHPExcel_Calculation_Functions::flattenSingleValue($complexDividend);
+ $complexDivisor = PHPExcel_Calculation_Functions::flattenSingleValue($complexDivisor);
+
+ $parsedComplexDividend = self::parseComplex($complexDividend);
+ $parsedComplexDivisor = self::parseComplex($complexDivisor);
+
+ if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] != '') &&
+ ($parsedComplexDividend['suffix'] != $parsedComplexDivisor['suffix'])) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] == '')) {
+ $parsedComplexDivisor['suffix'] = $parsedComplexDividend['suffix'];
+ }
+
+ $d1 = ($parsedComplexDividend['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['imaginary']);
+ $d2 = ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['real']) - ($parsedComplexDividend['real'] * $parsedComplexDivisor['imaginary']);
+ $d3 = ($parsedComplexDivisor['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDivisor['imaginary'] * $parsedComplexDivisor['imaginary']);
+
+ $r = $d1 / $d3;
+ $i = $d2 / $d3;
+
+ if ($i > 0.0) {
+ return self::cleanComplex($r.'+'.$i.$parsedComplexDivisor['suffix']);
+ } elseif ($i < 0.0) {
+ return self::cleanComplex($r.$i.$parsedComplexDivisor['suffix']);
+ } else {
+ return $r;
+ }
+ }
+
+
+ /**
+ * IMSUB
+ *
+ * Returns the difference of two complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUB(complexNumber1,complexNumber2)
+ *
+ * @param string $complexNumber1 The complex number from which to subtract complexNumber2.
+ * @param string $complexNumber2 The complex number to subtract from complexNumber1.
+ * @return string
+ */
+ public static function IMSUB($complexNumber1, $complexNumber2)
+ {
+ $complexNumber1 = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber1);
+ $complexNumber2 = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber2);
+
+ $parsedComplex1 = self::parseComplex($complexNumber1);
+ $parsedComplex2 = self::parseComplex($complexNumber2);
+
+ if ((($parsedComplex1['suffix'] != '') && ($parsedComplex2['suffix'] != '')) &&
+ ($parsedComplex1['suffix'] != $parsedComplex2['suffix'])) {
+ return PHPExcel_Calculation_Functions::NaN();
+ } elseif (($parsedComplex1['suffix'] == '') && ($parsedComplex2['suffix'] != '')) {
+ $parsedComplex1['suffix'] = $parsedComplex2['suffix'];
+ }
+
+ $d1 = $parsedComplex1['real'] - $parsedComplex2['real'];
+ $d2 = $parsedComplex1['imaginary'] - $parsedComplex2['imaginary'];
+
+ return self::COMPLEX($d1, $d2, $parsedComplex1['suffix']);
+ }
+
+
+ /**
+ * IMSUM
+ *
+ * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMSUM(complexNumber[,complexNumber[,...]])
+ *
+ * @param string $complexNumber,... Series of complex numbers to add
+ * @return string
+ */
+ public static function IMSUM()
+ {
+ // Return value
+ $returnValue = self::parseComplex('0');
+ $activeSuffix = '';
+
+ // Loop through the arguments
+ $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ $parsedComplex = self::parseComplex($arg);
+
+ if ($activeSuffix == '') {
+ $activeSuffix = $parsedComplex['suffix'];
+ } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ $returnValue['real'] += $parsedComplex['real'];
+ $returnValue['imaginary'] += $parsedComplex['imaginary'];
+ }
+
+ if ($returnValue['imaginary'] == 0.0) {
+ $activeSuffix = '';
+ }
+ return self::COMPLEX($returnValue['real'], $returnValue['imaginary'], $activeSuffix);
+ }
+
+
+ /**
+ * IMPRODUCT
+ *
+ * Returns the product of two or more complex numbers in x + yi or x + yj text format.
+ *
+ * Excel Function:
+ * IMPRODUCT(complexNumber[,complexNumber[,...]])
+ *
+ * @param string $complexNumber,... Series of complex numbers to multiply
+ * @return string
+ */
+ public static function IMPRODUCT()
+ {
+ // Return value
+ $returnValue = self::parseComplex('1');
+ $activeSuffix = '';
+
+ // Loop through the arguments
+ $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
+ foreach ($aArgs as $arg) {
+ $parsedComplex = self::parseComplex($arg);
+
+ $workValue = $returnValue;
+ if (($parsedComplex['suffix'] != '') && ($activeSuffix == '')) {
+ $activeSuffix = $parsedComplex['suffix'];
+ } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $returnValue['real'] = ($workValue['real'] * $parsedComplex['real']) - ($workValue['imaginary'] * $parsedComplex['imaginary']);
+ $returnValue['imaginary'] = ($workValue['real'] * $parsedComplex['imaginary']) + ($workValue['imaginary'] * $parsedComplex['real']);
+ }
+
+ if ($returnValue['imaginary'] == 0.0) {
+ $activeSuffix = '';
+ }
+ return self::COMPLEX($returnValue['real'], $returnValue['imaginary'], $activeSuffix);
+ }
+
+
+ /**
+ * DELTA
+ *
+ * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
+ * Use this function to filter a set of values. For example, by summing several DELTA
+ * functions you calculate the count of equal pairs. This function is also known as the
+ * Kronecker Delta function.
+ *
+ * Excel Function:
+ * DELTA(a[,b])
+ *
+ * @param float $a The first number.
+ * @param float $b The second number. If omitted, b is assumed to be zero.
+ * @return int
+ */
+ public static function DELTA($a, $b = 0)
+ {
+ $a = PHPExcel_Calculation_Functions::flattenSingleValue($a);
+ $b = PHPExcel_Calculation_Functions::flattenSingleValue($b);
+
+ return (int) ($a == $b);
+ }
+
+
+ /**
+ * GESTEP
+ *
+ * Excel Function:
+ * GESTEP(number[,step])
+ *
+ * Returns 1 if number >= step; returns 0 (zero) otherwise
+ * Use this function to filter a set of values. For example, by summing several GESTEP
+ * functions you calculate the count of values that exceed a threshold.
+ *
+ * @param float $number The value to test against step.
+ * @param float $step The threshold value.
+ * If you omit a value for step, GESTEP uses zero.
+ * @return int
+ */
+ public static function GESTEP($number, $step = 0)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+ $step = PHPExcel_Calculation_Functions::flattenSingleValue($step);
+
+ return (int) ($number >= $step);
+ }
+
+
+ //
+ // Private method to calculate the erf value
+ //
+ private static $twoSqrtPi = 1.128379167095512574;
+
+ public static function erfVal($x)
+ {
+ if (abs($x) > 2.2) {
+ return 1 - self::erfcVal($x);
+ }
+ $sum = $term = $x;
+ $xsqr = ($x * $x);
+ $j = 1;
+ do {
+ $term *= $xsqr / $j;
+ $sum -= $term / (2 * $j + 1);
+ ++$j;
+ $term *= $xsqr / $j;
+ $sum += $term / (2 * $j + 1);
+ ++$j;
+ if ($sum == 0.0) {
+ break;
+ }
+ } while (abs($term / $sum) > PRECISION);
+ return self::$twoSqrtPi * $sum;
+ }
+
+
+ /**
+ * ERF
+ *
+ * Returns the error function integrated between the lower and upper bound arguments.
+ *
+ * Note: In Excel 2007 or earlier, if you input a negative value for the upper or lower bound arguments,
+ * the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
+ * improved, so that it can now calculate the function for both positive and negative ranges.
+ * PHPExcel follows Excel 2010 behaviour, and accepts nagative arguments.
+ *
+ * Excel Function:
+ * ERF(lower[,upper])
+ *
+ * @param float $lower lower bound for integrating ERF
+ * @param float $upper upper bound for integrating ERF.
+ * If omitted, ERF integrates between zero and lower_limit
+ * @return float
+ */
+ public static function ERF($lower, $upper = null)
+ {
+ $lower = PHPExcel_Calculation_Functions::flattenSingleValue($lower);
+ $upper = PHPExcel_Calculation_Functions::flattenSingleValue($upper);
+
+ if (is_numeric($lower)) {
+ if (is_null($upper)) {
+ return self::erfVal($lower);
+ }
+ if (is_numeric($upper)) {
+ return self::erfVal($upper) - self::erfVal($lower);
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ //
+ // Private method to calculate the erfc value
+ //
+ private static $oneSqrtPi = 0.564189583547756287;
+
+ private static function erfcVal($x)
+ {
+ if (abs($x) < 2.2) {
+ return 1 - self::erfVal($x);
+ }
+ if ($x < 0) {
+ return 2 - self::ERFC(-$x);
+ }
+ $a = $n = 1;
+ $b = $c = $x;
+ $d = ($x * $x) + 0.5;
+ $q1 = $q2 = $b / $d;
+ $t = 0;
+ do {
+ $t = $a * $n + $b * $x;
+ $a = $b;
+ $b = $t;
+ $t = $c * $n + $d * $x;
+ $c = $d;
+ $d = $t;
+ $n += 0.5;
+ $q1 = $q2;
+ $q2 = $b / $d;
+ } while ((abs($q1 - $q2) / $q2) > PRECISION);
+ return self::$oneSqrtPi * exp(-$x * $x) * $q2;
+ }
+
+
+ /**
+ * ERFC
+ *
+ * Returns the complementary ERF function integrated between x and infinity
+ *
+ * Note: In Excel 2007 or earlier, if you input a negative value for the lower bound argument,
+ * the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
+ * improved, so that it can now calculate the function for both positive and negative x values.
+ * PHPExcel follows Excel 2010 behaviour, and accepts nagative arguments.
+ *
+ * Excel Function:
+ * ERFC(x)
+ *
+ * @param float $x The lower bound for integrating ERFC
+ * @return float
+ */
+ public static function ERFC($x)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+
+ if (is_numeric($x)) {
+ return self::erfcVal($x);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * getConversionGroups
+ * Returns a list of the different conversion groups for UOM conversions
+ *
+ * @return array
+ */
+ public static function getConversionGroups()
+ {
+ $conversionGroups = array();
+ foreach (self::$conversionUnits as $conversionUnit) {
+ $conversionGroups[] = $conversionUnit['Group'];
+ }
+ return array_merge(array_unique($conversionGroups));
+ }
+
+
+ /**
+ * getConversionGroupUnits
+ * Returns an array of units of measure, for a specified conversion group, or for all groups
+ *
+ * @param string $group The group whose units of measure you want to retrieve
+ * @return array
+ */
+ public static function getConversionGroupUnits($group = null)
+ {
+ $conversionGroups = array();
+ foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
+ if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
+ $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
+ }
+ }
+ return $conversionGroups;
+ }
+
+
+ /**
+ * getConversionGroupUnitDetails
+ *
+ * @param string $group The group whose units of measure you want to retrieve
+ * @return array
+ */
+ public static function getConversionGroupUnitDetails($group = null)
+ {
+ $conversionGroups = array();
+ foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
+ if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
+ $conversionGroups[$conversionGroup['Group']][] = array(
+ 'unit' => $conversionUnit,
+ 'description' => $conversionGroup['Unit Name']
+ );
+ }
+ }
+ return $conversionGroups;
+ }
+
+
+ /**
+ * getConversionMultipliers
+ * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM()
+ *
+ * @return array of mixed
+ */
+ public static function getConversionMultipliers()
+ {
+ return self::$conversionMultipliers;
+ }
+
+
+ /**
+ * CONVERTUOM
+ *
+ * Converts a number from one measurement system to another.
+ * For example, CONVERT can translate a table of distances in miles to a table of distances
+ * in kilometers.
+ *
+ * Excel Function:
+ * CONVERT(value,fromUOM,toUOM)
+ *
+ * @param float $value The value in fromUOM to convert.
+ * @param string $fromUOM The units for value.
+ * @param string $toUOM The units for the result.
+ *
+ * @return float
+ */
+ public static function CONVERTUOM($value, $fromUOM, $toUOM)
+ {
+ $value = PHPExcel_Calculation_Functions::flattenSingleValue($value);
+ $fromUOM = PHPExcel_Calculation_Functions::flattenSingleValue($fromUOM);
+ $toUOM = PHPExcel_Calculation_Functions::flattenSingleValue($toUOM);
+
+ if (!is_numeric($value)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $fromMultiplier = 1.0;
+ if (isset(self::$conversionUnits[$fromUOM])) {
+ $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
+ } else {
+ $fromMultiplier = substr($fromUOM, 0, 1);
+ $fromUOM = substr($fromUOM, 1);
+ if (isset(self::$conversionMultipliers[$fromMultiplier])) {
+ $fromMultiplier = self::$conversionMultipliers[$fromMultiplier]['multiplier'];
+ } else {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+ if ((isset(self::$conversionUnits[$fromUOM])) && (self::$conversionUnits[$fromUOM]['AllowPrefix'])) {
+ $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
+ } else {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+ }
+ $value *= $fromMultiplier;
+
+ $toMultiplier = 1.0;
+ if (isset(self::$conversionUnits[$toUOM])) {
+ $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
+ } else {
+ $toMultiplier = substr($toUOM, 0, 1);
+ $toUOM = substr($toUOM, 1);
+ if (isset(self::$conversionMultipliers[$toMultiplier])) {
+ $toMultiplier = self::$conversionMultipliers[$toMultiplier]['multiplier'];
+ } else {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+ if ((isset(self::$conversionUnits[$toUOM])) && (self::$conversionUnits[$toUOM]['AllowPrefix'])) {
+ $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
+ } else {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+ }
+ if ($unitGroup1 != $unitGroup2) {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+
+ if (($fromUOM == $toUOM) && ($fromMultiplier == $toMultiplier)) {
+ // We've already factored $fromMultiplier into the value, so we need
+ // to reverse it again
+ return $value / $fromMultiplier;
+ } elseif ($unitGroup1 == 'Temperature') {
+ if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
+ if (($toUOM == 'F') || ($toUOM == 'fah')) {
+ return $value;
+ } else {
+ $value = (($value - 32) / 1.8);
+ if (($toUOM == 'K') || ($toUOM == 'kel')) {
+ $value += 273.15;
+ }
+ return $value;
+ }
+ } elseif ((($fromUOM == 'K') || ($fromUOM == 'kel')) &&
+ (($toUOM == 'K') || ($toUOM == 'kel'))) {
+ return $value;
+ } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) &&
+ (($toUOM == 'C') || ($toUOM == 'cel'))) {
+ return $value;
+ }
+ if (($toUOM == 'F') || ($toUOM == 'fah')) {
+ if (($fromUOM == 'K') || ($fromUOM == 'kel')) {
+ $value -= 273.15;
+ }
+ return ($value * 1.8) + 32;
+ }
+ if (($toUOM == 'C') || ($toUOM == 'cel')) {
+ return $value - 273.15;
+ }
+ return $value + 273.15;
+ }
+ return ($value * self::$unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier;
+ }
+}
diff --git a/PHPExcel/Calculation/Exception.php b/PHPExcel/Calculation/Exception.php
new file mode 100644
index 0000000..52d73fc
--- /dev/null
+++ b/PHPExcel/Calculation/Exception.php
@@ -0,0 +1,46 @@
+line = $line;
+ $e->file = $file;
+ throw $e;
+ }
+}
diff --git a/PHPExcel/Calculation/ExceptionHandler.php b/PHPExcel/Calculation/ExceptionHandler.php
new file mode 100644
index 0000000..4cb0a68
--- /dev/null
+++ b/PHPExcel/Calculation/ExceptionHandler.php
@@ -0,0 +1,45 @@
+format('d') == $testDate->format('t'));
+ }
+
+
+ /**
+ * isFirstDayOfMonth
+ *
+ * Returns a boolean TRUE/FALSE indicating if this date is the first date of the month
+ *
+ * @param DateTime $testDate The date for testing
+ * @return boolean
+ */
+ private static function isFirstDayOfMonth($testDate)
+ {
+ return ($testDate->format('d') == 1);
+ }
+
+
+ private static function couponFirstPeriodDate($settlement, $maturity, $frequency, $next)
+ {
+ $months = 12 / $frequency;
+
+ $result = PHPExcel_Shared_Date::ExcelToPHPObject($maturity);
+ $eom = self::isLastDayOfMonth($result);
+
+ while ($settlement < PHPExcel_Shared_Date::PHPToExcel($result)) {
+ $result->modify('-'.$months.' months');
+ }
+ if ($next) {
+ $result->modify('+'.$months.' months');
+ }
+
+ if ($eom) {
+ $result->modify('-1 day');
+ }
+
+ return PHPExcel_Shared_Date::PHPToExcel($result);
+ }
+
+
+ private static function isValidFrequency($frequency)
+ {
+ if (($frequency == 1) || ($frequency == 2) || ($frequency == 4)) {
+ return true;
+ }
+ if ((PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) &&
+ (($frequency == 6) || ($frequency == 12))) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * daysPerYear
+ *
+ * Returns the number of days in a specified year, as defined by the "basis" value
+ *
+ * @param integer $year The year against which we're testing
+ * @param integer $basis The type of day count:
+ * 0 or omitted US (NASD) 360
+ * 1 Actual (365 or 366 in a leap year)
+ * 2 360
+ * 3 365
+ * 4 European 360
+ * @return integer
+ */
+ private static function daysPerYear($year, $basis = 0)
+ {
+ switch ($basis) {
+ case 0:
+ case 2:
+ case 4:
+ $daysPerYear = 360;
+ break;
+ case 3:
+ $daysPerYear = 365;
+ break;
+ case 1:
+ $daysPerYear = (PHPExcel_Calculation_DateTime::isLeapYear($year)) ? 366 : 365;
+ break;
+ default:
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return $daysPerYear;
+ }
+
+
+ private static function interestAndPrincipal($rate = 0, $per = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
+ {
+ $pmt = self::PMT($rate, $nper, $pv, $fv, $type);
+ $capital = $pv;
+ for ($i = 1; $i<= $per; ++$i) {
+ $interest = ($type && $i == 1) ? 0 : -$capital * $rate;
+ $principal = $pmt - $interest;
+ $capital += $principal;
+ }
+ return array($interest, $principal);
+ }
+
+
+ /**
+ * ACCRINT
+ *
+ * Returns the accrued interest for a security that pays periodic interest.
+ *
+ * Excel Function:
+ * ACCRINT(issue,firstinterest,settlement,rate,par,frequency[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param mixed $issue The security's issue date.
+ * @param mixed $firstinterest The security's first interest date.
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue date
+ * when the security is traded to the buyer.
+ * @param float $rate The security's annual coupon rate.
+ * @param float $par The security's par value.
+ * If you omit par, ACCRINT uses $1,000.
+ * @param integer $frequency the number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * If working in Gnumeric Mode, the following frequency options are
+ * also available
+ * 6 Bimonthly
+ * 12 Monthly
+ * @param integer $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function ACCRINT($issue, $firstinterest, $settlement, $rate, $par = 1000, $frequency = 1, $basis = 0)
+ {
+ $issue = PHPExcel_Calculation_Functions::flattenSingleValue($issue);
+ $firstinterest = PHPExcel_Calculation_Functions::flattenSingleValue($firstinterest);
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $par = (is_null($par)) ? 1000 : PHPExcel_Calculation_Functions::flattenSingleValue($par);
+ $frequency = (is_null($frequency)) ? 1 : PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($rate)) && (is_numeric($par))) {
+ $rate = (float) $rate;
+ $par = (float) $par;
+ if (($rate <= 0) || ($par <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $daysBetweenIssueAndSettlement = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+
+ return $par * $rate * $daysBetweenIssueAndSettlement;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * ACCRINTM
+ *
+ * Returns the accrued interest for a security that pays interest at maturity.
+ *
+ * Excel Function:
+ * ACCRINTM(issue,settlement,rate[,par[,basis]])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param mixed issue The security's issue date.
+ * @param mixed settlement The security's settlement (or maturity) date.
+ * @param float rate The security's annual coupon rate.
+ * @param float par The security's par value.
+ * If you omit par, ACCRINT uses $1,000.
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function ACCRINTM($issue, $settlement, $rate, $par = 1000, $basis = 0)
+ {
+ $issue = PHPExcel_Calculation_Functions::flattenSingleValue($issue);
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $par = (is_null($par)) ? 1000 : PHPExcel_Calculation_Functions::flattenSingleValue($par);
+ $basis = (is_null($basis)) ? 0 : PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($rate)) && (is_numeric($par))) {
+ $rate = (float) $rate;
+ $par = (float) $par;
+ if (($rate <= 0) || ($par <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $daysBetweenIssueAndSettlement = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ return $par * $rate * $daysBetweenIssueAndSettlement;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * AMORDEGRC
+ *
+ * Returns the depreciation for each accounting period.
+ * This function is provided for the French accounting system. If an asset is purchased in
+ * the middle of the accounting period, the prorated depreciation is taken into account.
+ * The function is similar to AMORLINC, except that a depreciation coefficient is applied in
+ * the calculation depending on the life of the assets.
+ * This function will return the depreciation until the last period of the life of the assets
+ * or until the cumulated value of depreciation is greater than the cost of the assets minus
+ * the salvage value.
+ *
+ * Excel Function:
+ * AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float cost The cost of the asset.
+ * @param mixed purchased Date of the purchase of the asset.
+ * @param mixed firstPeriod Date of the end of the first period.
+ * @param mixed salvage The salvage value at the end of the life of the asset.
+ * @param float period The period.
+ * @param float rate Rate of depreciation.
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
+ {
+ $cost = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
+ $purchased = PHPExcel_Calculation_Functions::flattenSingleValue($purchased);
+ $firstPeriod = PHPExcel_Calculation_Functions::flattenSingleValue($firstPeriod);
+ $salvage = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
+ $period = floor(PHPExcel_Calculation_Functions::flattenSingleValue($period));
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $basis = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // The depreciation coefficients are:
+ // Life of assets (1/rate) Depreciation coefficient
+ // Less than 3 years 1
+ // Between 3 and 4 years 1.5
+ // Between 5 and 6 years 2
+ // More than 6 years 2.5
+ $fUsePer = 1.0 / $rate;
+ if ($fUsePer < 3.0) {
+ $amortiseCoeff = 1.0;
+ } elseif ($fUsePer < 5.0) {
+ $amortiseCoeff = 1.5;
+ } elseif ($fUsePer <= 6.0) {
+ $amortiseCoeff = 2.0;
+ } else {
+ $amortiseCoeff = 2.5;
+ }
+
+ $rate *= $amortiseCoeff;
+ $fNRate = round(PHPExcel_Calculation_DateTime::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost, 0);
+ $cost -= $fNRate;
+ $fRest = $cost - $salvage;
+
+ for ($n = 0; $n < $period; ++$n) {
+ $fNRate = round($rate * $cost, 0);
+ $fRest -= $fNRate;
+
+ if ($fRest < 0.0) {
+ switch ($period - $n) {
+ case 0:
+ case 1:
+ return round($cost * 0.5, 0);
+ default:
+ return 0.0;
+ }
+ }
+ $cost -= $fNRate;
+ }
+ return $fNRate;
+ }
+
+
+ /**
+ * AMORLINC
+ *
+ * Returns the depreciation for each accounting period.
+ * This function is provided for the French accounting system. If an asset is purchased in
+ * the middle of the accounting period, the prorated depreciation is taken into account.
+ *
+ * Excel Function:
+ * AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float cost The cost of the asset.
+ * @param mixed purchased Date of the purchase of the asset.
+ * @param mixed firstPeriod Date of the end of the first period.
+ * @param mixed salvage The salvage value at the end of the life of the asset.
+ * @param float period The period.
+ * @param float rate Rate of depreciation.
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis = 0)
+ {
+ $cost = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
+ $purchased = PHPExcel_Calculation_Functions::flattenSingleValue($purchased);
+ $firstPeriod = PHPExcel_Calculation_Functions::flattenSingleValue($firstPeriod);
+ $salvage = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
+ $period = PHPExcel_Calculation_Functions::flattenSingleValue($period);
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $basis = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ $fOneRate = $cost * $rate;
+ $fCostDelta = $cost - $salvage;
+ // Note, quirky variation for leap years on the YEARFRAC for this function
+ $purchasedYear = PHPExcel_Calculation_DateTime::YEAR($purchased);
+ $yearFrac = PHPExcel_Calculation_DateTime::YEARFRAC($purchased, $firstPeriod, $basis);
+
+ if (($basis == 1) && ($yearFrac < 1) && (PHPExcel_Calculation_DateTime::isLeapYear($purchasedYear))) {
+ $yearFrac *= 365 / 366;
+ }
+
+ $f0Rate = $yearFrac * $rate * $cost;
+ $nNumOfFullPeriods = intval(($cost - $salvage - $f0Rate) / $fOneRate);
+
+ if ($period == 0) {
+ return $f0Rate;
+ } elseif ($period <= $nNumOfFullPeriods) {
+ return $fOneRate;
+ } elseif ($period == ($nNumOfFullPeriods + 1)) {
+ return ($fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate);
+ } else {
+ return 0.0;
+ }
+ }
+
+
+ /**
+ * COUPDAYBS
+ *
+ * Returns the number of days from the beginning of the coupon period to the settlement date.
+ *
+ * Excel Function:
+ * COUPDAYBS(settlement,maturity,frequency[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed frequency the number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * If working in Gnumeric Mode, the following frequency options are
+ * also available
+ * 6 Bimonthly
+ * 12 Monthly
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function COUPDAYBS($settlement, $maturity, $frequency, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $frequency = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::isValidFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
+ $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
+
+ return PHPExcel_Calculation_DateTime::YEARFRAC($prev, $settlement, $basis) * $daysPerYear;
+ }
+
+
+ /**
+ * COUPDAYS
+ *
+ * Returns the number of days in the coupon period that contains the settlement date.
+ *
+ * Excel Function:
+ * COUPDAYS(settlement,maturity,frequency[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed frequency the number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * If working in Gnumeric Mode, the following frequency options are
+ * also available
+ * 6 Bimonthly
+ * 12 Monthly
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function COUPDAYS($settlement, $maturity, $frequency, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $frequency = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::isValidFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ switch ($basis) {
+ case 3:
+ // Actual/365
+ return 365 / $frequency;
+ case 1:
+ // Actual/actual
+ if ($frequency == 1) {
+ $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($maturity), $basis);
+ return ($daysPerYear / $frequency);
+ }
+ $prev = self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
+ $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
+ return ($next - $prev);
+ default:
+ // US (NASD) 30/360, Actual/360 or European 30/360
+ return 360 / $frequency;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * COUPDAYSNC
+ *
+ * Returns the number of days from the settlement date to the next coupon date.
+ *
+ * Excel Function:
+ * COUPDAYSNC(settlement,maturity,frequency[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed frequency the number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * If working in Gnumeric Mode, the following frequency options are
+ * also available
+ * 6 Bimonthly
+ * 12 Monthly
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $frequency = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::isValidFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
+ $next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
+
+ return PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $next, $basis) * $daysPerYear;
+ }
+
+
+ /**
+ * COUPNCD
+ *
+ * Returns the next coupon date after the settlement date.
+ *
+ * Excel Function:
+ * COUPNCD(settlement,maturity,frequency[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed frequency the number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * If working in Gnumeric Mode, the following frequency options are
+ * also available
+ * 6 Bimonthly
+ * 12 Monthly
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function COUPNCD($settlement, $maturity, $frequency, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $frequency = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::isValidFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ return self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
+ }
+
+
+ /**
+ * COUPNUM
+ *
+ * Returns the number of coupons payable between the settlement date and maturity date,
+ * rounded up to the nearest whole coupon.
+ *
+ * Excel Function:
+ * COUPNUM(settlement,maturity,frequency[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed frequency the number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * If working in Gnumeric Mode, the following frequency options are
+ * also available
+ * 6 Bimonthly
+ * 12 Monthly
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return integer
+ */
+ public static function COUPNUM($settlement, $maturity, $frequency, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $frequency = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::isValidFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ $settlement = self::couponFirstPeriodDate($settlement, $maturity, $frequency, true);
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis) * 365;
+
+ switch ($frequency) {
+ case 1: // annual payments
+ return ceil($daysBetweenSettlementAndMaturity / 360);
+ case 2: // half-yearly
+ return ceil($daysBetweenSettlementAndMaturity / 180);
+ case 4: // quarterly
+ return ceil($daysBetweenSettlementAndMaturity / 90);
+ case 6: // bimonthly
+ return ceil($daysBetweenSettlementAndMaturity / 60);
+ case 12: // monthly
+ return ceil($daysBetweenSettlementAndMaturity / 30);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * COUPPCD
+ *
+ * Returns the previous coupon date before the settlement date.
+ *
+ * Excel Function:
+ * COUPPCD(settlement,maturity,frequency[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed frequency the number of coupon payments per year.
+ * Valid frequency values are:
+ * 1 Annual
+ * 2 Semi-Annual
+ * 4 Quarterly
+ * If working in Gnumeric Mode, the following frequency options are
+ * also available
+ * 6 Bimonthly
+ * 12 Monthly
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
+ * depending on the value of the ReturnDateType flag
+ */
+ public static function COUPPCD($settlement, $maturity, $frequency, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $frequency = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::isValidFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ return self::couponFirstPeriodDate($settlement, $maturity, $frequency, false);
+ }
+
+
+ /**
+ * CUMIPMT
+ *
+ * Returns the cumulative interest paid on a loan between the start and end periods.
+ *
+ * Excel Function:
+ * CUMIPMT(rate,nper,pv,start,end[,type])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float $rate The Interest rate
+ * @param integer $nper The total number of payment periods
+ * @param float $pv Present Value
+ * @param integer $start The first period in the calculation.
+ * Payment periods are numbered beginning with 1.
+ * @param integer $end The last period in the calculation.
+ * @param integer $type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ * @return float
+ */
+ public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0)
+ {
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $nper = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
+ $pv = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
+ $start = (int) PHPExcel_Calculation_Functions::flattenSingleValue($start);
+ $end = (int) PHPExcel_Calculation_Functions::flattenSingleValue($end);
+ $type = (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if ($start < 1 || $start > $end) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // Calculate
+ $interest = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $interest += self::IPMT($rate, $per, $nper, $pv, 0, $type);
+ }
+
+ return $interest;
+ }
+
+
+ /**
+ * CUMPRINC
+ *
+ * Returns the cumulative principal paid on a loan between the start and end periods.
+ *
+ * Excel Function:
+ * CUMPRINC(rate,nper,pv,start,end[,type])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float $rate The Interest rate
+ * @param integer $nper The total number of payment periods
+ * @param float $pv Present Value
+ * @param integer $start The first period in the calculation.
+ * Payment periods are numbered beginning with 1.
+ * @param integer $end The last period in the calculation.
+ * @param integer $type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ * @return float
+ */
+ public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0)
+ {
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $nper = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
+ $pv = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
+ $start = (int) PHPExcel_Calculation_Functions::flattenSingleValue($start);
+ $end = (int) PHPExcel_Calculation_Functions::flattenSingleValue($end);
+ $type = (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if ($start < 1 || $start > $end) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // Calculate
+ $principal = 0;
+ for ($per = $start; $per <= $end; ++$per) {
+ $principal += self::PPMT($rate, $per, $nper, $pv, 0, $type);
+ }
+
+ return $principal;
+ }
+
+
+ /**
+ * DB
+ *
+ * Returns the depreciation of an asset for a specified period using the
+ * fixed-declining balance method.
+ * This form of depreciation is used if you want to get a higher depreciation value
+ * at the beginning of the depreciation (as opposed to linear depreciation). The
+ * depreciation value is reduced with every depreciation period by the depreciation
+ * already deducted from the initial cost.
+ *
+ * Excel Function:
+ * DB(cost,salvage,life,period[,month])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float cost Initial cost of the asset.
+ * @param float salvage Value at the end of the depreciation.
+ * (Sometimes called the salvage value of the asset)
+ * @param integer life Number of periods over which the asset is depreciated.
+ * (Sometimes called the useful life of the asset)
+ * @param integer period The period for which you want to calculate the
+ * depreciation. Period must use the same units as life.
+ * @param integer month Number of months in the first year. If month is omitted,
+ * it defaults to 12.
+ * @return float
+ */
+ public static function DB($cost, $salvage, $life, $period, $month = 12)
+ {
+ $cost = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
+ $salvage = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
+ $life = PHPExcel_Calculation_Functions::flattenSingleValue($life);
+ $period = PHPExcel_Calculation_Functions::flattenSingleValue($period);
+ $month = PHPExcel_Calculation_Functions::flattenSingleValue($month);
+
+ // Validate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
+ $cost = (float) $cost;
+ $salvage = (float) $salvage;
+ $life = (int) $life;
+ $period = (int) $period;
+ $month = (int) $month;
+ if ($cost == 0) {
+ return 0.0;
+ } elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ // Set Fixed Depreciation Rate
+ $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
+ $fixedDepreciationRate = round($fixedDepreciationRate, 3);
+
+ // Loop through each period calculating the depreciation
+ $previousDepreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ if ($per == 1) {
+ $depreciation = $cost * $fixedDepreciationRate * $month / 12;
+ } elseif ($per == ($life + 1)) {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
+ } else {
+ $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
+ }
+ $previousDepreciation += $depreciation;
+ }
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
+ $depreciation = round($depreciation, 2);
+ }
+ return $depreciation;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * DDB
+ *
+ * Returns the depreciation of an asset for a specified period using the
+ * double-declining balance method or some other method you specify.
+ *
+ * Excel Function:
+ * DDB(cost,salvage,life,period[,factor])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float cost Initial cost of the asset.
+ * @param float salvage Value at the end of the depreciation.
+ * (Sometimes called the salvage value of the asset)
+ * @param integer life Number of periods over which the asset is depreciated.
+ * (Sometimes called the useful life of the asset)
+ * @param integer period The period for which you want to calculate the
+ * depreciation. Period must use the same units as life.
+ * @param float factor The rate at which the balance declines.
+ * If factor is omitted, it is assumed to be 2 (the
+ * double-declining balance method).
+ * @return float
+ */
+ public static function DDB($cost, $salvage, $life, $period, $factor = 2.0)
+ {
+ $cost = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
+ $salvage = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
+ $life = PHPExcel_Calculation_Functions::flattenSingleValue($life);
+ $period = PHPExcel_Calculation_Functions::flattenSingleValue($period);
+ $factor = PHPExcel_Calculation_Functions::flattenSingleValue($factor);
+
+ // Validate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
+ $cost = (float) $cost;
+ $salvage = (float) $salvage;
+ $life = (int) $life;
+ $period = (int) $period;
+ $factor = (float) $factor;
+ if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ // Set Fixed Depreciation Rate
+ $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
+ $fixedDepreciationRate = round($fixedDepreciationRate, 3);
+
+ // Loop through each period calculating the depreciation
+ $previousDepreciation = 0;
+ for ($per = 1; $per <= $period; ++$per) {
+ $depreciation = min(($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation));
+ $previousDepreciation += $depreciation;
+ }
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
+ $depreciation = round($depreciation, 2);
+ }
+ return $depreciation;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * DISC
+ *
+ * Returns the discount rate for a security.
+ *
+ * Excel Function:
+ * DISC(settlement,maturity,price,redemption[,basis])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue
+ * date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param integer price The security's price per $100 face value.
+ * @param integer redemption The security's redemption value per $100 face value.
+ * @param integer basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function DISC($settlement, $maturity, $price, $redemption, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $price = PHPExcel_Calculation_Functions::flattenSingleValue($price);
+ $redemption = PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
+ $basis = PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($price)) && (is_numeric($redemption)) && (is_numeric($basis))) {
+ $price = (float) $price;
+ $redemption = (float) $redemption;
+ $basis = (int) $basis;
+ if (($price <= 0) || ($redemption <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return ((1 - $price / $redemption) / $daysBetweenSettlementAndMaturity);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * DOLLARDE
+ *
+ * Converts a dollar price expressed as an integer part and a fraction
+ * part into a dollar price expressed as a decimal number.
+ * Fractional dollar numbers are sometimes used for security prices.
+ *
+ * Excel Function:
+ * DOLLARDE(fractional_dollar,fraction)
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float $fractional_dollar Fractional Dollar
+ * @param integer $fraction Fraction
+ * @return float
+ */
+ public static function DOLLARDE($fractional_dollar = null, $fraction = 0)
+ {
+ $fractional_dollar = PHPExcel_Calculation_Functions::flattenSingleValue($fractional_dollar);
+ $fraction = (int)PHPExcel_Calculation_Functions::flattenSingleValue($fraction);
+
+ // Validate parameters
+ if (is_null($fractional_dollar) || $fraction < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if ($fraction == 0) {
+ return PHPExcel_Calculation_Functions::DIV0();
+ }
+
+ $dollars = floor($fractional_dollar);
+ $cents = fmod($fractional_dollar, 1);
+ $cents /= $fraction;
+ $cents *= pow(10, ceil(log10($fraction)));
+ return $dollars + $cents;
+ }
+
+
+ /**
+ * DOLLARFR
+ *
+ * Converts a dollar price expressed as a decimal number into a dollar price
+ * expressed as a fraction.
+ * Fractional dollar numbers are sometimes used for security prices.
+ *
+ * Excel Function:
+ * DOLLARFR(decimal_dollar,fraction)
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float $decimal_dollar Decimal Dollar
+ * @param integer $fraction Fraction
+ * @return float
+ */
+ public static function DOLLARFR($decimal_dollar = null, $fraction = 0)
+ {
+ $decimal_dollar = PHPExcel_Calculation_Functions::flattenSingleValue($decimal_dollar);
+ $fraction = (int)PHPExcel_Calculation_Functions::flattenSingleValue($fraction);
+
+ // Validate parameters
+ if (is_null($decimal_dollar) || $fraction < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if ($fraction == 0) {
+ return PHPExcel_Calculation_Functions::DIV0();
+ }
+
+ $dollars = floor($decimal_dollar);
+ $cents = fmod($decimal_dollar, 1);
+ $cents *= $fraction;
+ $cents *= pow(10, -ceil(log10($fraction)));
+ return $dollars + $cents;
+ }
+
+
+ /**
+ * EFFECT
+ *
+ * Returns the effective interest rate given the nominal rate and the number of
+ * compounding payments per year.
+ *
+ * Excel Function:
+ * EFFECT(nominal_rate,npery)
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float $nominal_rate Nominal interest rate
+ * @param integer $npery Number of compounding payments per year
+ * @return float
+ */
+ public static function EFFECT($nominal_rate = 0, $npery = 0)
+ {
+ $nominal_rate = PHPExcel_Calculation_Functions::flattenSingleValue($nominal_rate);
+ $npery = (int)PHPExcel_Calculation_Functions::flattenSingleValue($npery);
+
+ // Validate parameters
+ if ($nominal_rate <= 0 || $npery < 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ return pow((1 + $nominal_rate / $npery), $npery) - 1;
+ }
+
+
+ /**
+ * FV
+ *
+ * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
+ *
+ * Excel Function:
+ * FV(rate,nper,pmt[,pv[,type]])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float $rate The interest rate per period
+ * @param int $nper Total number of payment periods in an annuity
+ * @param float $pmt The payment made each period: it cannot change over the
+ * life of the annuity. Typically, pmt contains principal
+ * and interest but no other fees or taxes.
+ * @param float $pv Present Value, or the lump-sum amount that a series of
+ * future payments is worth right now.
+ * @param integer $type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ * @return float
+ */
+ public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0)
+ {
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $nper = PHPExcel_Calculation_Functions::flattenSingleValue($nper);
+ $pmt = PHPExcel_Calculation_Functions::flattenSingleValue($pmt);
+ $pv = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
+ $type = PHPExcel_Calculation_Functions::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ return -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
+ }
+ return -$pv - $pmt * $nper;
+ }
+
+
+ /**
+ * FVSCHEDULE
+ *
+ * Returns the future value of an initial principal after applying a series of compound interest rates.
+ * Use FVSCHEDULE to calculate the future value of an investment with a variable or adjustable rate.
+ *
+ * Excel Function:
+ * FVSCHEDULE(principal,schedule)
+ *
+ * @param float $principal The present value.
+ * @param float[] $schedule An array of interest rates to apply.
+ * @return float
+ */
+ public static function FVSCHEDULE($principal, $schedule)
+ {
+ $principal = PHPExcel_Calculation_Functions::flattenSingleValue($principal);
+ $schedule = PHPExcel_Calculation_Functions::flattenArray($schedule);
+
+ foreach ($schedule as $rate) {
+ $principal *= 1 + $rate;
+ }
+
+ return $principal;
+ }
+
+
+ /**
+ * INTRATE
+ *
+ * Returns the interest rate for a fully invested security.
+ *
+ * Excel Function:
+ * INTRATE(settlement,maturity,investment,redemption[,basis])
+ *
+ * @param mixed $settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed $maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param integer $investment The amount invested in the security.
+ * @param integer $redemption The amount to be received at maturity.
+ * @param integer $basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $investment = PHPExcel_Calculation_Functions::flattenSingleValue($investment);
+ $redemption = PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
+ $basis = PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($investment)) && (is_numeric($redemption)) && (is_numeric($basis))) {
+ $investment = (float) $investment;
+ $redemption = (float) $redemption;
+ $basis = (int) $basis;
+ if (($investment <= 0) || ($redemption <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * IPMT
+ *
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ *
+ * Excel Function:
+ * IPMT(rate,per,nper,pv[,fv][,type])
+ *
+ * @param float $rate Interest rate per period
+ * @param int $per Period for which we want to find the interest
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
+ {
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $per = (int) PHPExcel_Calculation_Functions::flattenSingleValue($per);
+ $nper = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
+ $pv = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
+ $fv = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
+ $type = (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if ($per <= 0 || $per > $nper) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // Calculate
+ $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
+ return $interestAndPrincipal[0];
+ }
+
+ /**
+ * IRR
+ *
+ * Returns the internal rate of return for a series of cash flows represented by the numbers in values.
+ * These cash flows do not have to be even, as they would be for an annuity. However, the cash flows must occur
+ * at regular intervals, such as monthly or annually. The internal rate of return is the interest rate received
+ * for an investment consisting of payments (negative values) and income (positive values) that occur at regular
+ * periods.
+ *
+ * Excel Function:
+ * IRR(values[,guess])
+ *
+ * @param float[] $values An array or a reference to cells that contain numbers for which you want
+ * to calculate the internal rate of return.
+ * Values must contain at least one positive value and one negative value to
+ * calculate the internal rate of return.
+ * @param float $guess A number that you guess is close to the result of IRR
+ * @return float
+ */
+ public static function IRR($values, $guess = 0.1)
+ {
+ if (!is_array($values)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $values = PHPExcel_Calculation_Functions::flattenArray($values);
+ $guess = PHPExcel_Calculation_Functions::flattenSingleValue($guess);
+
+ // create an initial range, with a root somewhere between 0 and guess
+ $x1 = 0.0;
+ $x2 = $guess;
+ $f1 = self::NPV($x1, $values);
+ $f2 = self::NPV($x2, $values);
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ if (($f1 * $f2) < 0.0) {
+ break;
+ }
+ if (abs($f1) < abs($f2)) {
+ $f1 = self::NPV($x1 += 1.6 * ($x1 - $x2), $values);
+ } else {
+ $f2 = self::NPV($x2 += 1.6 * ($x2 - $x1), $values);
+ }
+ }
+ if (($f1 * $f2) > 0.0) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ $f = self::NPV($x1, $values);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = self::NPV($x_mid, $values);
+ if ($f_mid <= 0.0) {
+ $rtb = $x_mid;
+ }
+ if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) {
+ return $x_mid;
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * ISPMT
+ *
+ * Returns the interest payment for an investment based on an interest rate and a constant payment schedule.
+ *
+ * Excel Function:
+ * =ISPMT(interest_rate, period, number_payments, PV)
+ *
+ * interest_rate is the interest rate for the investment
+ *
+ * period is the period to calculate the interest rate. It must be betweeen 1 and number_payments.
+ *
+ * number_payments is the number of payments for the annuity
+ *
+ * PV is the loan amount or present value of the payments
+ */
+ public static function ISPMT()
+ {
+ // Return value
+ $returnValue = 0;
+
+ // Get the parameters
+ $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
+ $interestRate = array_shift($aArgs);
+ $period = array_shift($aArgs);
+ $numberPeriods = array_shift($aArgs);
+ $principleRemaining = array_shift($aArgs);
+
+ // Calculate
+ $principlePayment = ($principleRemaining * 1.0) / ($numberPeriods * 1.0);
+ for ($i=0; $i <= $period; ++$i) {
+ $returnValue = $interestRate * $principleRemaining * -1;
+ $principleRemaining -= $principlePayment;
+ // principle needs to be 0 after the last payment, don't let floating point screw it up
+ if ($i == $numberPeriods) {
+ $returnValue = 0;
+ }
+ }
+ return($returnValue);
+ }
+
+
+ /**
+ * MIRR
+ *
+ * Returns the modified internal rate of return for a series of periodic cash flows. MIRR considers both
+ * the cost of the investment and the interest received on reinvestment of cash.
+ *
+ * Excel Function:
+ * MIRR(values,finance_rate, reinvestment_rate)
+ *
+ * @param float[] $values An array or a reference to cells that contain a series of payments and
+ * income occurring at regular intervals.
+ * Payments are negative value, income is positive values.
+ * @param float $finance_rate The interest rate you pay on the money used in the cash flows
+ * @param float $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them
+ * @return float
+ */
+ public static function MIRR($values, $finance_rate, $reinvestment_rate)
+ {
+ if (!is_array($values)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $values = PHPExcel_Calculation_Functions::flattenArray($values);
+ $finance_rate = PHPExcel_Calculation_Functions::flattenSingleValue($finance_rate);
+ $reinvestment_rate = PHPExcel_Calculation_Functions::flattenSingleValue($reinvestment_rate);
+ $n = count($values);
+
+ $rr = 1.0 + $reinvestment_rate;
+ $fr = 1.0 + $finance_rate;
+
+ $npv_pos = $npv_neg = 0.0;
+ foreach ($values as $i => $v) {
+ if ($v >= 0) {
+ $npv_pos += $v / pow($rr, $i);
+ } else {
+ $npv_neg += $v / pow($fr, $i);
+ }
+ }
+
+ if (($npv_neg == 0) || ($npv_pos == 0) || ($reinvestment_rate <= -1)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ $mirr = pow((-$npv_pos * pow($rr, $n))
+ / ($npv_neg * ($rr)), (1.0 / ($n - 1))) - 1.0;
+
+ return (is_finite($mirr) ? $mirr : PHPExcel_Calculation_Functions::VALUE());
+ }
+
+
+ /**
+ * NOMINAL
+ *
+ * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
+ *
+ * @param float $effect_rate Effective interest rate
+ * @param int $npery Number of compounding payments per year
+ * @return float
+ */
+ public static function NOMINAL($effect_rate = 0, $npery = 0)
+ {
+ $effect_rate = PHPExcel_Calculation_Functions::flattenSingleValue($effect_rate);
+ $npery = (int)PHPExcel_Calculation_Functions::flattenSingleValue($npery);
+
+ // Validate parameters
+ if ($effect_rate <= 0 || $npery < 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Calculate
+ return $npery * (pow($effect_rate + 1, 1 / $npery) - 1);
+ }
+
+
+ /**
+ * NPER
+ *
+ * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $pmt Periodic payment (annuity)
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0)
+ {
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $pmt = PHPExcel_Calculation_Functions::flattenSingleValue($pmt);
+ $pv = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
+ $fv = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
+ $type = PHPExcel_Calculation_Functions::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ if ($pmt == 0 && $pv == 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
+ }
+ if ($pmt == 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return (-$pv -$fv) / $pmt;
+ }
+
+ /**
+ * NPV
+ *
+ * Returns the Net Present Value of a cash flow series given a discount rate.
+ *
+ * @return float
+ */
+ public static function NPV()
+ {
+ // Return value
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
+
+ // Calculate
+ $rate = array_shift($aArgs);
+ for ($i = 1; $i <= count($aArgs); ++$i) {
+ // Is it a numeric value?
+ if (is_numeric($aArgs[$i - 1])) {
+ $returnValue += $aArgs[$i - 1] / pow(1 + $rate, $i);
+ }
+ }
+
+ // Return
+ return $returnValue;
+ }
+
+ /**
+ * PMT
+ *
+ * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0)
+ {
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $nper = PHPExcel_Calculation_Functions::flattenSingleValue($nper);
+ $pv = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
+ $fv = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
+ $type = PHPExcel_Calculation_Functions::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ return (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
+ }
+ return (-$pv - $fv) / $nper;
+ }
+
+
+ /**
+ * PPMT
+ *
+ * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
+ *
+ * @param float $rate Interest rate per period
+ * @param int $per Period for which we want to find the interest
+ * @param int $nper Number of periods
+ * @param float $pv Present Value
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0)
+ {
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $per = (int) PHPExcel_Calculation_Functions::flattenSingleValue($per);
+ $nper = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
+ $pv = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
+ $fv = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
+ $type = (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if ($per <= 0 || $per > $nper) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // Calculate
+ $interestAndPrincipal = self::interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
+ return $interestAndPrincipal[1];
+ }
+
+
+ public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $rate = (float) PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $yield = (float) PHPExcel_Calculation_Functions::flattenSingleValue($yield);
+ $redemption = (float) PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
+ $frequency = (int) PHPExcel_Calculation_Functions::flattenSingleValue($frequency);
+ $basis = (is_null($basis)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ if (is_string($settlement = PHPExcel_Calculation_DateTime::getDateValue($settlement))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (($settlement > $maturity) ||
+ (!self::isValidFrequency($frequency)) ||
+ (($basis < 0) || ($basis > 4))) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ $dsc = self::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
+ $e = self::COUPDAYS($settlement, $maturity, $frequency, $basis);
+ $n = self::COUPNUM($settlement, $maturity, $frequency, $basis);
+ $a = self::COUPDAYBS($settlement, $maturity, $frequency, $basis);
+
+ $baseYF = 1.0 + ($yield / $frequency);
+ $rfp = 100 * ($rate / $frequency);
+ $de = $dsc / $e;
+
+ $result = $redemption / pow($baseYF, (--$n + $de));
+ for ($k = 0; $k <= $n; ++$k) {
+ $result += $rfp / (pow($baseYF, ($k + $de)));
+ }
+ $result -= $rfp * ($a / $e);
+
+ return $result;
+ }
+
+
+ /**
+ * PRICEDISC
+ *
+ * Returns the price per $100 face value of a discounted security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int discount The security's discount rate.
+ * @param int redemption The security's redemption value per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $discount = (float) PHPExcel_Calculation_Functions::flattenSingleValue($discount);
+ $redemption = (float) PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
+ $basis = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($discount)) && (is_numeric($redemption)) && (is_numeric($basis))) {
+ if (($discount <= 0) || ($redemption <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * PRICEMAT
+ *
+ * Returns the price per $100 face value of a security that pays interest at maturity.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed issue The security's issue date.
+ * @param int rate The security's interest rate at date of issue.
+ * @param int yield The security's annual yield.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $issue = PHPExcel_Calculation_Functions::flattenSingleValue($issue);
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $yield = PHPExcel_Calculation_Functions::flattenSingleValue($yield);
+ $basis = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // Validate
+ if (is_numeric($rate) && is_numeric($yield)) {
+ if (($rate <= 0) || ($yield <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $maturity, $basis);
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ // return date error
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return ((100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
+ (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
+ (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100));
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * PV
+ *
+ * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
+ *
+ * @param float $rate Interest rate per period
+ * @param int $nper Number of periods
+ * @param float $pmt Periodic payment (annuity)
+ * @param float $fv Future Value
+ * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
+ * @return float
+ */
+ public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0)
+ {
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $nper = PHPExcel_Calculation_Functions::flattenSingleValue($nper);
+ $pmt = PHPExcel_Calculation_Functions::flattenSingleValue($pmt);
+ $fv = PHPExcel_Calculation_Functions::flattenSingleValue($fv);
+ $type = PHPExcel_Calculation_Functions::flattenSingleValue($type);
+
+ // Validate parameters
+ if ($type != 0 && $type != 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // Calculate
+ if (!is_null($rate) && $rate != 0) {
+ return (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
+ }
+ return -$fv - $pmt * $nper;
+ }
+
+
+ /**
+ * RATE
+ *
+ * Returns the interest rate per period of an annuity.
+ * RATE is calculated by iteration and can have zero or more solutions.
+ * If the successive results of RATE do not converge to within 0.0000001 after 20 iterations,
+ * RATE returns the #NUM! error value.
+ *
+ * Excel Function:
+ * RATE(nper,pmt,pv[,fv[,type[,guess]]])
+ *
+ * @access public
+ * @category Financial Functions
+ * @param float nper The total number of payment periods in an annuity.
+ * @param float pmt The payment made each period and cannot change over the life
+ * of the annuity.
+ * Typically, pmt includes principal and interest but no other
+ * fees or taxes.
+ * @param float pv The present value - the total amount that a series of future
+ * payments is worth now.
+ * @param float fv The future value, or a cash balance you want to attain after
+ * the last payment is made. If fv is omitted, it is assumed
+ * to be 0 (the future value of a loan, for example, is 0).
+ * @param integer type A number 0 or 1 and indicates when payments are due:
+ * 0 or omitted At the end of the period.
+ * 1 At the beginning of the period.
+ * @param float guess Your guess for what the rate will be.
+ * If you omit guess, it is assumed to be 10 percent.
+ * @return float
+ **/
+ public static function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1)
+ {
+ $nper = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
+ $pmt = PHPExcel_Calculation_Functions::flattenSingleValue($pmt);
+ $pv = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
+ $fv = (is_null($fv)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($fv);
+ $type = (is_null($type)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
+ $guess = (is_null($guess)) ? 0.1 : PHPExcel_Calculation_Functions::flattenSingleValue($guess);
+
+ $rate = $guess;
+ if (abs($rate) < FINANCIAL_PRECISION) {
+ $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
+ } else {
+ $f = exp($nper * log(1 + $rate));
+ $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
+ }
+ $y0 = $pv + $pmt * $nper + $fv;
+ $y1 = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
+
+ // find root by secant method
+ $i = $x0 = 0.0;
+ $x1 = $rate;
+ while ((abs($y0 - $y1) > FINANCIAL_PRECISION) && ($i < FINANCIAL_MAX_ITERATIONS)) {
+ $rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
+ $x0 = $x1;
+ $x1 = $rate;
+ if (($nper * abs($pmt)) > ($pv - $fv)) {
+ $x1 = abs($x1);
+ }
+ if (abs($rate) < FINANCIAL_PRECISION) {
+ $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
+ } else {
+ $f = exp($nper * log(1 + $rate));
+ $y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
+ }
+
+ $y0 = $y1;
+ $y1 = $y;
+ ++$i;
+ }
+ return $rate;
+ }
+
+
+ /**
+ * RECEIVED
+ *
+ * Returns the price per $100 face value of a discounted security.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int investment The amount invested in the security.
+ * @param int discount The security's discount rate.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $investment = (float) PHPExcel_Calculation_Functions::flattenSingleValue($investment);
+ $discount = (float) PHPExcel_Calculation_Functions::flattenSingleValue($discount);
+ $basis = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // Validate
+ if ((is_numeric($investment)) && (is_numeric($discount)) && (is_numeric($basis))) {
+ if (($investment <= 0) || ($discount <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+
+ return $investment / ( 1 - ($discount * $daysBetweenSettlementAndMaturity));
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * SLN
+ *
+ * Returns the straight-line depreciation of an asset for one period
+ *
+ * @param cost Initial cost of the asset
+ * @param salvage Value at the end of the depreciation
+ * @param life Number of periods over which the asset is depreciated
+ * @return float
+ */
+ public static function SLN($cost, $salvage, $life)
+ {
+ $cost = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
+ $salvage = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
+ $life = PHPExcel_Calculation_Functions::flattenSingleValue($life);
+
+ // Calculate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
+ if ($life < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return ($cost - $salvage) / $life;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * SYD
+ *
+ * Returns the sum-of-years' digits depreciation of an asset for a specified period.
+ *
+ * @param cost Initial cost of the asset
+ * @param salvage Value at the end of the depreciation
+ * @param life Number of periods over which the asset is depreciated
+ * @param period Period
+ * @return float
+ */
+ public static function SYD($cost, $salvage, $life, $period)
+ {
+ $cost = PHPExcel_Calculation_Functions::flattenSingleValue($cost);
+ $salvage = PHPExcel_Calculation_Functions::flattenSingleValue($salvage);
+ $life = PHPExcel_Calculation_Functions::flattenSingleValue($life);
+ $period = PHPExcel_Calculation_Functions::flattenSingleValue($period);
+
+ // Calculate
+ if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
+ if (($life < 1) || ($period > $life)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * TBILLEQ
+ *
+ * Returns the bond-equivalent yield for a Treasury bill.
+ *
+ * @param mixed settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * @param mixed maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param int discount The Treasury bill's discount rate.
+ * @return float
+ */
+ public static function TBILLEQ($settlement, $maturity, $discount)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $discount = PHPExcel_Calculation_Functions::flattenSingleValue($discount);
+
+ // Use TBILLPRICE for validation
+ $testValue = self::TBILLPRICE($settlement, $maturity, $discount);
+ if (is_string($testValue)) {
+ return $testValue;
+ }
+
+ if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ ++$maturity;
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity) * 360;
+ } else {
+ $daysBetweenSettlementAndMaturity = (PHPExcel_Calculation_DateTime::getDateValue($maturity) - PHPExcel_Calculation_DateTime::getDateValue($settlement));
+ }
+
+ return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
+ }
+
+
+ /**
+ * TBILLPRICE
+ *
+ * Returns the yield for a Treasury bill.
+ *
+ * @param mixed settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * @param mixed maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param int discount The Treasury bill's discount rate.
+ * @return float
+ */
+ public static function TBILLPRICE($settlement, $maturity, $discount)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $discount = PHPExcel_Calculation_Functions::flattenSingleValue($discount);
+
+ if (is_string($maturity = PHPExcel_Calculation_DateTime::getDateValue($maturity))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // Validate
+ if (is_numeric($discount)) {
+ if ($discount <= 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ ++$maturity;
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity) * 360;
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ } else {
+ $daysBetweenSettlementAndMaturity = (PHPExcel_Calculation_DateTime::getDateValue($maturity) - PHPExcel_Calculation_DateTime::getDateValue($settlement));
+ }
+
+ if ($daysBetweenSettlementAndMaturity > 360) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
+ if ($price <= 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return $price;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * TBILLYIELD
+ *
+ * Returns the yield for a Treasury bill.
+ *
+ * @param mixed settlement The Treasury bill's settlement date.
+ * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
+ * @param mixed maturity The Treasury bill's maturity date.
+ * The maturity date is the date when the Treasury bill expires.
+ * @param int price The Treasury bill's price per $100 face value.
+ * @return float
+ */
+ public static function TBILLYIELD($settlement, $maturity, $price)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $price = PHPExcel_Calculation_Functions::flattenSingleValue($price);
+
+ // Validate
+ if (is_numeric($price)) {
+ if ($price <= 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
+ ++$maturity;
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity) * 360;
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ } else {
+ $daysBetweenSettlementAndMaturity = (PHPExcel_Calculation_DateTime::getDateValue($maturity) - PHPExcel_Calculation_DateTime::getDateValue($settlement));
+ }
+
+ if ($daysBetweenSettlementAndMaturity > 360) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ public static function XIRR($values, $dates, $guess = 0.1)
+ {
+ if ((!is_array($values)) && (!is_array($dates))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $values = PHPExcel_Calculation_Functions::flattenArray($values);
+ $dates = PHPExcel_Calculation_Functions::flattenArray($dates);
+ $guess = PHPExcel_Calculation_Functions::flattenSingleValue($guess);
+ if (count($values) != count($dates)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+
+ // create an initial range, with a root somewhere between 0 and guess
+ $x1 = 0.0;
+ $x2 = $guess;
+ $f1 = self::XNPV($x1, $values, $dates);
+ $f2 = self::XNPV($x2, $values, $dates);
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ if (($f1 * $f2) < 0.0) {
+ break;
+ } elseif (abs($f1) < abs($f2)) {
+ $f1 = self::XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates);
+ } else {
+ $f2 = self::XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates);
+ }
+ }
+ if (($f1 * $f2) > 0.0) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ $f = self::XNPV($x1, $values, $dates);
+ if ($f < 0.0) {
+ $rtb = $x1;
+ $dx = $x2 - $x1;
+ } else {
+ $rtb = $x2;
+ $dx = $x1 - $x2;
+ }
+
+ for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
+ $dx *= 0.5;
+ $x_mid = $rtb + $dx;
+ $f_mid = self::XNPV($x_mid, $values, $dates);
+ if ($f_mid <= 0.0) {
+ $rtb = $x_mid;
+ }
+ if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) {
+ return $x_mid;
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * XNPV
+ *
+ * Returns the net present value for a schedule of cash flows that is not necessarily periodic.
+ * To calculate the net present value for a series of cash flows that is periodic, use the NPV function.
+ *
+ * Excel Function:
+ * =XNPV(rate,values,dates)
+ *
+ * @param float $rate The discount rate to apply to the cash flows.
+ * @param array of float $values A series of cash flows that corresponds to a schedule of payments in dates.
+ * The first payment is optional and corresponds to a cost or payment that occurs at the beginning of the investment.
+ * If the first value is a cost or payment, it must be a negative value. All succeeding payments are discounted based on a 365-day year.
+ * The series of values must contain at least one positive value and one negative value.
+ * @param array of mixed $dates A schedule of payment dates that corresponds to the cash flow payments.
+ * The first payment date indicates the beginning of the schedule of payments.
+ * All other dates must be later than this date, but they may occur in any order.
+ * @return float
+ */
+ public static function XNPV($rate, $values, $dates)
+ {
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ if (!is_numeric($rate)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if ((!is_array($values)) || (!is_array($dates))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $values = PHPExcel_Calculation_Functions::flattenArray($values);
+ $dates = PHPExcel_Calculation_Functions::flattenArray($dates);
+ $valCount = count($values);
+ if ($valCount != count($dates)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ if ((min($values) > 0) || (max($values) < 0)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ $xnpv = 0.0;
+ for ($i = 0; $i < $valCount; ++$i) {
+ if (!is_numeric($values[$i])) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $xnpv += $values[$i] / pow(1 + $rate, PHPExcel_Calculation_DateTime::DATEDIF($dates[0], $dates[$i], 'd') / 365);
+ }
+ return (is_finite($xnpv)) ? $xnpv : PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * YIELDDISC
+ *
+ * Returns the annual yield of a security that pays interest at maturity.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param int price The security's price per $100 face value.
+ * @param int redemption The security's redemption value per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $price = PHPExcel_Calculation_Functions::flattenSingleValue($price);
+ $redemption = PHPExcel_Calculation_Functions::flattenSingleValue($redemption);
+ $basis = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // Validate
+ if (is_numeric($price) && is_numeric($redemption)) {
+ if (($price <= 0) || ($redemption <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * YIELDMAT
+ *
+ * Returns the annual yield of a security that pays interest at maturity.
+ *
+ * @param mixed settlement The security's settlement date.
+ * The security's settlement date is the date after the issue date when the security is traded to the buyer.
+ * @param mixed maturity The security's maturity date.
+ * The maturity date is the date when the security expires.
+ * @param mixed issue The security's issue date.
+ * @param int rate The security's interest rate at date of issue.
+ * @param int price The security's price per $100 face value.
+ * @param int basis The type of day count to use.
+ * 0 or omitted US (NASD) 30/360
+ * 1 Actual/actual
+ * 2 Actual/360
+ * 3 Actual/365
+ * 4 European 30/360
+ * @return float
+ */
+ public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis = 0)
+ {
+ $settlement = PHPExcel_Calculation_Functions::flattenSingleValue($settlement);
+ $maturity = PHPExcel_Calculation_Functions::flattenSingleValue($maturity);
+ $issue = PHPExcel_Calculation_Functions::flattenSingleValue($issue);
+ $rate = PHPExcel_Calculation_Functions::flattenSingleValue($rate);
+ $price = PHPExcel_Calculation_Functions::flattenSingleValue($price);
+ $basis = (int) PHPExcel_Calculation_Functions::flattenSingleValue($basis);
+
+ // Validate
+ if (is_numeric($rate) && is_numeric($price)) {
+ if (($rate <= 0) || ($price <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $daysPerYear = self::daysPerYear(PHPExcel_Calculation_DateTime::YEAR($settlement), $basis);
+ if (!is_numeric($daysPerYear)) {
+ return $daysPerYear;
+ }
+ $daysBetweenIssueAndSettlement = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $settlement, $basis);
+ if (!is_numeric($daysBetweenIssueAndSettlement)) {
+ // return date error
+ return $daysBetweenIssueAndSettlement;
+ }
+ $daysBetweenIssueAndSettlement *= $daysPerYear;
+ $daysBetweenIssueAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($issue, $maturity, $basis);
+ if (!is_numeric($daysBetweenIssueAndMaturity)) {
+ // return date error
+ return $daysBetweenIssueAndMaturity;
+ }
+ $daysBetweenIssueAndMaturity *= $daysPerYear;
+ $daysBetweenSettlementAndMaturity = PHPExcel_Calculation_DateTime::YEARFRAC($settlement, $maturity, $basis);
+ if (!is_numeric($daysBetweenSettlementAndMaturity)) {
+ // return date error
+ return $daysBetweenSettlementAndMaturity;
+ }
+ $daysBetweenSettlementAndMaturity *= $daysPerYear;
+
+ return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
+ (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
+ ($daysPerYear / $daysBetweenSettlementAndMaturity);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+}
diff --git a/PHPExcel/Calculation/FormulaParser.php b/PHPExcel/Calculation/FormulaParser.php
new file mode 100644
index 0000000..893f19e
--- /dev/null
+++ b/PHPExcel/Calculation/FormulaParser.php
@@ -0,0 +1,622 @@
+<";
+ const OPERATORS_POSTFIX = "%";
+
+ /**
+ * Formula
+ *
+ * @var string
+ */
+ private $formula;
+
+ /**
+ * Tokens
+ *
+ * @var PHPExcel_Calculation_FormulaToken[]
+ */
+ private $tokens = array();
+
+ /**
+ * Create a new PHPExcel_Calculation_FormulaParser
+ *
+ * @param string $pFormula Formula to parse
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function __construct($pFormula = '')
+ {
+ // Check parameters
+ if (is_null($pFormula)) {
+ throw new PHPExcel_Calculation_Exception("Invalid parameter passed: formula");
+ }
+
+ // Initialise values
+ $this->formula = trim($pFormula);
+ // Parse!
+ $this->parseToTokens();
+ }
+
+ /**
+ * Get Formula
+ *
+ * @return string
+ */
+ public function getFormula()
+ {
+ return $this->formula;
+ }
+
+ /**
+ * Get Token
+ *
+ * @param int $pId Token id
+ * @return string
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function getToken($pId = 0)
+ {
+ if (isset($this->tokens[$pId])) {
+ return $this->tokens[$pId];
+ } else {
+ throw new PHPExcel_Calculation_Exception("Token with id $pId does not exist.");
+ }
+ }
+
+ /**
+ * Get Token count
+ *
+ * @return string
+ */
+ public function getTokenCount()
+ {
+ return count($this->tokens);
+ }
+
+ /**
+ * Get Tokens
+ *
+ * @return PHPExcel_Calculation_FormulaToken[]
+ */
+ public function getTokens()
+ {
+ return $this->tokens;
+ }
+
+ /**
+ * Parse to tokens
+ */
+ private function parseToTokens()
+ {
+ // No attempt is made to verify formulas; assumes formulas are derived from Excel, where
+ // they can only exist if valid; stack overflows/underflows sunk as nulls without exceptions.
+
+ // Check if the formula has a valid starting =
+ $formulaLength = strlen($this->formula);
+ if ($formulaLength < 2 || $this->formula{0} != '=') {
+ return;
+ }
+
+ // Helper variables
+ $tokens1 = $tokens2 = $stack = array();
+ $inString = $inPath = $inRange = $inError = false;
+ $token = $previousToken = $nextToken = null;
+
+ $index = 1;
+ $value = '';
+
+ $ERRORS = array("#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", "#NUM!", "#N/A");
+ $COMPARATORS_MULTI = array(">=", "<=", "<>");
+
+ while ($index < $formulaLength) {
+ // state-dependent character evaluation (order is important)
+
+ // double-quoted strings
+ // embeds are doubled
+ // end marks token
+ if ($inString) {
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE) {
+ if ((($index + 2) <= $formulaLength) && ($this->formula{$index + 1} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE)) {
+ $value .= PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE;
+ ++$index;
+ } else {
+ $inString = false;
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_TEXT);
+ $value = "";
+ }
+ } else {
+ $value .= $this->formula{$index};
+ }
+ ++$index;
+ continue;
+ }
+
+ // single-quoted strings (links)
+ // embeds are double
+ // end does not mark a token
+ if ($inPath) {
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE) {
+ if ((($index + 2) <= $formulaLength) && ($this->formula{$index + 1} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE)) {
+ $value .= PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE;
+ ++$index;
+ } else {
+ $inPath = false;
+ }
+ } else {
+ $value .= $this->formula{$index};
+ }
+ ++$index;
+ continue;
+ }
+
+ // bracked strings (R1C1 range index or linked workbook name)
+ // no embeds (changed to "()" by Excel)
+ // end does not mark a token
+ if ($inRange) {
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::BRACKET_CLOSE) {
+ $inRange = false;
+ }
+ $value .= $this->formula{$index};
+ ++$index;
+ continue;
+ }
+
+ // error values
+ // end marks a token, determined from absolute list of values
+ if ($inError) {
+ $value .= $this->formula{$index};
+ ++$index;
+ if (in_array($value, $ERRORS)) {
+ $inError = false;
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_ERROR);
+ $value = "";
+ }
+ continue;
+ }
+
+ // scientific notation check
+ if (strpos(PHPExcel_Calculation_FormulaParser::OPERATORS_SN, $this->formula{$index}) !== false) {
+ if (strlen($value) > 1) {
+ if (preg_match("/^[1-9]{1}(\.[0-9]+)?E{1}$/", $this->formula{$index}) != 0) {
+ $value .= $this->formula{$index};
+ ++$index;
+ continue;
+ }
+ }
+ }
+
+ // independent character evaluation (order not important)
+
+ // establish state-dependent character evaluations
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_DOUBLE) {
+ if (strlen($value > 0)) {
+ // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+ $inString = true;
+ ++$index;
+ continue;
+ }
+
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::QUOTE_SINGLE) {
+ if (strlen($value) > 0) {
+ // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+ $inPath = true;
+ ++$index;
+ continue;
+ }
+
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::BRACKET_OPEN) {
+ $inRange = true;
+ $value .= PHPExcel_Calculation_FormulaParser::BRACKET_OPEN;
+ ++$index;
+ continue;
+ }
+
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::ERROR_START) {
+ if (strlen($value) > 0) {
+ // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+ $inError = true;
+ $value .= PHPExcel_Calculation_FormulaParser::ERROR_START;
+ ++$index;
+ continue;
+ }
+
+ // mark start and end of arrays and array rows
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::BRACE_OPEN) {
+ if (strlen($value) > 0) {
+ // unexpected
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN);
+ $value = "";
+ }
+
+ $tmp = new PHPExcel_Calculation_FormulaToken("ARRAY", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ $tmp = new PHPExcel_Calculation_FormulaToken("ARRAYROW", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::SEMICOLON) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ $tmp = new PHPExcel_Calculation_FormulaToken(",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_ARGUMENT);
+ $tokens1[] = $tmp;
+
+ $tmp = new PHPExcel_Calculation_FormulaToken("ARRAYROW", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::BRACE_CLOSE) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ // trim white-space
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::WHITESPACE) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken("", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_WHITESPACE);
+ ++$index;
+ while (($this->formula{$index} == PHPExcel_Calculation_FormulaParser::WHITESPACE) && ($index < $formulaLength)) {
+ ++$index;
+ }
+ continue;
+ }
+
+ // multi-character comparators
+ if (($index + 2) <= $formulaLength) {
+ if (in_array(substr($this->formula, $index, 2), $COMPARATORS_MULTI)) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken(substr($this->formula, $index, 2), PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ $index += 2;
+ continue;
+ }
+ }
+
+ // standard infix operators
+ if (strpos(PHPExcel_Calculation_FormulaParser::OPERATORS_INFIX, $this->formula{$index}) !== false) {
+ if (strlen($value) > 0) {
+ $tokens1[] =new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($this->formula{$index}, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX);
+ ++$index;
+ continue;
+ }
+
+ // standard postfix operators (only one)
+ if (strpos(PHPExcel_Calculation_FormulaParser::OPERATORS_POSTFIX, $this->formula{$index}) !== false) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($this->formula{$index}, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX);
+ ++$index;
+ continue;
+ }
+
+ // start subexpression or function
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::PAREN_OPEN) {
+ if (strlen($value) > 0) {
+ $tmp = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+ $value = "";
+ } else {
+ $tmp = new PHPExcel_Calculation_FormulaToken("", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START);
+ $tokens1[] = $tmp;
+ $stack[] = clone $tmp;
+ }
+ ++$index;
+ continue;
+ }
+
+ // function, subexpression, or array parameters, or operand unions
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::COMMA) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $stack[] = $tmp;
+
+ if ($tmp->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken(",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_UNION);
+ } else {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken(",", PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_ARGUMENT);
+ }
+ ++$index;
+ continue;
+ }
+
+ // stop subexpression
+ if ($this->formula{$index} == PHPExcel_Calculation_FormulaParser::PAREN_CLOSE) {
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ $value = "";
+ }
+
+ $tmp = array_pop($stack);
+ $tmp->setValue("");
+ $tmp->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP);
+ $tokens1[] = $tmp;
+
+ ++$index;
+ continue;
+ }
+
+ // token accumulation
+ $value .= $this->formula{$index};
+ ++$index;
+ }
+
+ // dump remaining accumulation
+ if (strlen($value) > 0) {
+ $tokens1[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND);
+ }
+
+ // move tokenList to new set, excluding unnecessary white-space tokens and converting necessary ones to intersections
+ $tokenCount = count($tokens1);
+ for ($i = 0; $i < $tokenCount; ++$i) {
+ $token = $tokens1[$i];
+ if (isset($tokens1[$i - 1])) {
+ $previousToken = $tokens1[$i - 1];
+ } else {
+ $previousToken = null;
+ }
+ if (isset($tokens1[$i + 1])) {
+ $nextToken = $tokens1[$i + 1];
+ } else {
+ $nextToken = null;
+ }
+
+ if (is_null($token)) {
+ continue;
+ }
+
+ if ($token->getTokenType() != PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_WHITESPACE) {
+ $tokens2[] = $token;
+ continue;
+ }
+
+ if (is_null($previousToken)) {
+ continue;
+ }
+
+ if (! (
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) ) {
+ continue;
+ }
+
+ if (is_null($nextToken)) {
+ continue;
+ }
+
+ if (! (
+ (($nextToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) && ($nextToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START)) ||
+ (($nextToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) && ($nextToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_START)) ||
+ ($nextToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)
+ ) ) {
+ continue;
+ }
+
+ $tokens2[] = new PHPExcel_Calculation_FormulaToken($value, PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX, PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_INTERSECTION);
+ }
+
+ // move tokens to final list, switching infix "-" operators to prefix when appropriate, switching infix "+" operators
+ // to noop when appropriate, identifying operand and infix-operator subtypes, and pulling "@" from function names
+ $this->tokens = array();
+
+ $tokenCount = count($tokens2);
+ for ($i = 0; $i < $tokenCount; ++$i) {
+ $token = $tokens2[$i];
+ if (isset($tokens2[$i - 1])) {
+ $previousToken = $tokens2[$i - 1];
+ } else {
+ $previousToken = null;
+ }
+ if (isset($tokens2[$i + 1])) {
+ $nextToken = $tokens2[$i + 1];
+ } else {
+ $nextToken = null;
+ }
+
+ if (is_null($token)) {
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == "-") {
+ if ($i == 0) {
+ $token->setTokenType(PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
+ } elseif ((($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) &&
+ ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
+ ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH);
+ } else {
+ $token->setTokenType(PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPREFIX);
+ }
+
+ $this->tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX && $token->getValue() == "+") {
+ if ($i == 0) {
+ continue;
+ } elseif ((($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) &&
+ ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ (($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_SUBEXPRESSION) &&
+ ($previousToken->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_STOP)) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORPOSTFIX) ||
+ ($previousToken->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND)) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH);
+ } else {
+ continue;
+ }
+
+ $this->tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERATORINFIX &&
+ $token->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ if (strpos("<>=", substr($token->getValue(), 0, 1)) !== false) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ } elseif ($token->getValue() == "&") {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_CONCATENATION);
+ } else {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_MATH);
+ }
+
+ $this->tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_OPERAND &&
+ $token->getTokenSubType() == PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING) {
+ if (!is_numeric($token->getValue())) {
+ if (strtoupper($token->getValue()) == "TRUE" || strtoupper($token->getValue() == "FALSE")) {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_LOGICAL);
+ } else {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_RANGE);
+ }
+ } else {
+ $token->setTokenSubType(PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NUMBER);
+ }
+
+ $this->tokens[] = $token;
+ continue;
+ }
+
+ if ($token->getTokenType() == PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_FUNCTION) {
+ if (strlen($token->getValue() > 0)) {
+ if (substr($token->getValue(), 0, 1) == "@") {
+ $token->setValue(substr($token->getValue(), 1));
+ }
+ }
+ }
+
+ $this->tokens[] = $token;
+ }
+ }
+}
diff --git a/PHPExcel/Calculation/FormulaToken.php b/PHPExcel/Calculation/FormulaToken.php
new file mode 100644
index 0000000..41c6e3d
--- /dev/null
+++ b/PHPExcel/Calculation/FormulaToken.php
@@ -0,0 +1,176 @@
+value = $pValue;
+ $this->tokenType = $pTokenType;
+ $this->tokenSubType = $pTokenSubType;
+ }
+
+ /**
+ * Get Value
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set Value
+ *
+ * @param string $value
+ */
+ public function setValue($value)
+ {
+ $this->value = $value;
+ }
+
+ /**
+ * Get Token Type (represented by TOKEN_TYPE_*)
+ *
+ * @return string
+ */
+ public function getTokenType()
+ {
+ return $this->tokenType;
+ }
+
+ /**
+ * Set Token Type
+ *
+ * @param string $value
+ */
+ public function setTokenType($value = PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN)
+ {
+ $this->tokenType = $value;
+ }
+
+ /**
+ * Get Token SubType (represented by TOKEN_SUBTYPE_*)
+ *
+ * @return string
+ */
+ public function getTokenSubType()
+ {
+ return $this->tokenSubType;
+ }
+
+ /**
+ * Set Token SubType
+ *
+ * @param string $value
+ */
+ public function setTokenSubType($value = PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING)
+ {
+ $this->tokenSubType = $value;
+ }
+}
diff --git a/PHPExcel/Calculation/Function.php b/PHPExcel/Calculation/Function.php
new file mode 100644
index 0000000..d58cef2
--- /dev/null
+++ b/PHPExcel/Calculation/Function.php
@@ -0,0 +1,148 @@
+category = $pCategory;
+ $this->excelName = $pExcelName;
+ $this->phpExcelName = $pPHPExcelName;
+ } else {
+ throw new PHPExcel_Calculation_Exception("Invalid parameters passed.");
+ }
+ }
+
+ /**
+ * Get Category (represented by CATEGORY_*)
+ *
+ * @return string
+ */
+ public function getCategory()
+ {
+ return $this->category;
+ }
+
+ /**
+ * Set Category (represented by CATEGORY_*)
+ *
+ * @param string $value
+ * @throws PHPExcel_Calculation_Exception
+ */
+ public function setCategory($value = null)
+ {
+ if (!is_null($value)) {
+ $this->category = $value;
+ } else {
+ throw new PHPExcel_Calculation_Exception("Invalid parameter passed.");
+ }
+ }
+
+ /**
+ * Get Excel name
+ *
+ * @return string
+ */
+ public function getExcelName()
+ {
+ return $this->excelName;
+ }
+
+ /**
+ * Set Excel name
+ *
+ * @param string $value
+ */
+ public function setExcelName($value)
+ {
+ $this->excelName = $value;
+ }
+
+ /**
+ * Get PHPExcel name
+ *
+ * @return string
+ */
+ public function getPHPExcelName()
+ {
+ return $this->phpExcelName;
+ }
+
+ /**
+ * Set PHPExcel name
+ *
+ * @param string $value
+ */
+ public function setPHPExcelName($value)
+ {
+ $this->phpExcelName = $value;
+ }
+}
diff --git a/PHPExcel/Calculation/Functions.php b/PHPExcel/Calculation/Functions.php
new file mode 100644
index 0000000..5a1e5ee
--- /dev/null
+++ b/PHPExcel/Calculation/Functions.php
@@ -0,0 +1,760 @@
+ '#NULL!',
+ 'divisionbyzero' => '#DIV/0!',
+ 'value' => '#VALUE!',
+ 'reference' => '#REF!',
+ 'name' => '#NAME?',
+ 'num' => '#NUM!',
+ 'na' => '#N/A',
+ 'gettingdata' => '#GETTING_DATA'
+ );
+
+
+ /**
+ * Set the Compatibility Mode
+ *
+ * @access public
+ * @category Function Configuration
+ * @param string $compatibilityMode Compatibility Mode
+ * Permitted values are:
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
+ * @return boolean (Success or Failure)
+ */
+ public static function setCompatibilityMode($compatibilityMode)
+ {
+ if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
+ ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
+ ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
+ self::$compatibilityMode = $compatibilityMode;
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Return the current Compatibility Mode
+ *
+ * @access public
+ * @category Function Configuration
+ * @return string Compatibility Mode
+ * Possible Return values are:
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
+ * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
+ */
+ public static function getCompatibilityMode()
+ {
+ return self::$compatibilityMode;
+ }
+
+
+ /**
+ * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
+ *
+ * @access public
+ * @category Function Configuration
+ * @param string $returnDateType Return Date Format
+ * Permitted values are:
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
+ * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
+ * @return boolean Success or failure
+ */
+ public static function setReturnDateType($returnDateType)
+ {
+ if (($returnDateType == self::RETURNDATE_PHP_NUMERIC) ||
+ ($returnDateType == self::RETURNDATE_PHP_OBJECT) ||
+ ($returnDateType == self::RETURNDATE_EXCEL)) {
+ self::$returnDateType = $returnDateType;
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
+ *
+ * @access public
+ * @category Function Configuration
+ * @return string Return Date Format
+ * Possible Return values are:
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
+ * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
+ * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
+ */
+ public static function getReturnDateType()
+ {
+ return self::$returnDateType;
+ }
+
+
+ /**
+ * DUMMY
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #Not Yet Implemented
+ */
+ public static function DUMMY()
+ {
+ return '#Not Yet Implemented';
+ }
+
+
+ /**
+ * DIV0
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #Not Yet Implemented
+ */
+ public static function DIV0()
+ {
+ return self::$errorCodes['divisionbyzero'];
+ }
+
+
+ /**
+ * NA
+ *
+ * Excel Function:
+ * =NA()
+ *
+ * Returns the error value #N/A
+ * #N/A is the error value that means "no value is available."
+ *
+ * @access public
+ * @category Logical Functions
+ * @return string #N/A!
+ */
+ public static function NA()
+ {
+ return self::$errorCodes['na'];
+ }
+
+
+ /**
+ * NaN
+ *
+ * Returns the error value #NUM!
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #NUM!
+ */
+ public static function NaN()
+ {
+ return self::$errorCodes['num'];
+ }
+
+
+ /**
+ * NAME
+ *
+ * Returns the error value #NAME?
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #NAME?
+ */
+ public static function NAME()
+ {
+ return self::$errorCodes['name'];
+ }
+
+
+ /**
+ * REF
+ *
+ * Returns the error value #REF!
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #REF!
+ */
+ public static function REF()
+ {
+ return self::$errorCodes['reference'];
+ }
+
+
+ /**
+ * NULL
+ *
+ * Returns the error value #NULL!
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #NULL!
+ */
+ public static function NULL()
+ {
+ return self::$errorCodes['null'];
+ }
+
+
+ /**
+ * VALUE
+ *
+ * Returns the error value #VALUE!
+ *
+ * @access public
+ * @category Error Returns
+ * @return string #VALUE!
+ */
+ public static function VALUE()
+ {
+ return self::$errorCodes['value'];
+ }
+
+
+ public static function isMatrixValue($idx)
+ {
+ return ((substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0));
+ }
+
+
+ public static function isValue($idx)
+ {
+ return (substr_count($idx, '.') == 0);
+ }
+
+
+ public static function isCellValue($idx)
+ {
+ return (substr_count($idx, '.') > 1);
+ }
+
+
+ public static function ifCondition($condition)
+ {
+ $condition = PHPExcel_Calculation_Functions::flattenSingleValue($condition);
+ if (!isset($condition{0})) {
+ $condition = '=""';
+ }
+ if (!in_array($condition{0}, array('>', '<', '='))) {
+ if (!is_numeric($condition)) {
+ $condition = PHPExcel_Calculation::wrapResult(strtoupper($condition));
+ }
+ return '=' . $condition;
+ } else {
+ preg_match('/([<>=]+)(.*)/', $condition, $matches);
+ list(, $operator, $operand) = $matches;
+
+ if (!is_numeric($operand)) {
+ $operand = str_replace('"', '""', $operand);
+ $operand = PHPExcel_Calculation::wrapResult(strtoupper($operand));
+ }
+
+ return $operator.$operand;
+ }
+ }
+
+ /**
+ * ERROR_TYPE
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function ERROR_TYPE($value = '')
+ {
+ $value = self::flattenSingleValue($value);
+
+ $i = 1;
+ foreach (self::$errorCodes as $errorCode) {
+ if ($value === $errorCode) {
+ return $i;
+ }
+ ++$i;
+ }
+ return self::NA();
+ }
+
+
+ /**
+ * IS_BLANK
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_BLANK($value = null)
+ {
+ if (!is_null($value)) {
+ $value = self::flattenSingleValue($value);
+ }
+
+ return is_null($value);
+ }
+
+
+ /**
+ * IS_ERR
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_ERR($value = '')
+ {
+ $value = self::flattenSingleValue($value);
+
+ return self::IS_ERROR($value) && (!self::IS_NA($value));
+ }
+
+
+ /**
+ * IS_ERROR
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_ERROR($value = '')
+ {
+ $value = self::flattenSingleValue($value);
+
+ if (!is_string($value)) {
+ return false;
+ }
+ return in_array($value, array_values(self::$errorCodes));
+ }
+
+
+ /**
+ * IS_NA
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_NA($value = '')
+ {
+ $value = self::flattenSingleValue($value);
+
+ return ($value === self::NA());
+ }
+
+
+ /**
+ * IS_EVEN
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_EVEN($value = null)
+ {
+ $value = self::flattenSingleValue($value);
+
+ if ($value === null) {
+ return self::NAME();
+ } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return self::VALUE();
+ }
+
+ return ($value % 2 == 0);
+ }
+
+
+ /**
+ * IS_ODD
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_ODD($value = null)
+ {
+ $value = self::flattenSingleValue($value);
+
+ if ($value === null) {
+ return self::NAME();
+ } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
+ return self::VALUE();
+ }
+
+ return (abs($value) % 2 == 1);
+ }
+
+
+ /**
+ * IS_NUMBER
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_NUMBER($value = null)
+ {
+ $value = self::flattenSingleValue($value);
+
+ if (is_string($value)) {
+ return false;
+ }
+ return is_numeric($value);
+ }
+
+
+ /**
+ * IS_LOGICAL
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_LOGICAL($value = null)
+ {
+ $value = self::flattenSingleValue($value);
+
+ return is_bool($value);
+ }
+
+
+ /**
+ * IS_TEXT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_TEXT($value = null)
+ {
+ $value = self::flattenSingleValue($value);
+
+ return (is_string($value) && !self::IS_ERROR($value));
+ }
+
+
+ /**
+ * IS_NONTEXT
+ *
+ * @param mixed $value Value to check
+ * @return boolean
+ */
+ public static function IS_NONTEXT($value = null)
+ {
+ return !self::IS_TEXT($value);
+ }
+
+
+ /**
+ * VERSION
+ *
+ * @return string Version information
+ */
+ public static function VERSION()
+ {
+ return 'PHPExcel ##VERSION##, ##DATE##';
+ }
+
+
+ /**
+ * N
+ *
+ * Returns a value converted to a number
+ *
+ * @param value The value you want converted
+ * @return number N converts values listed in the following table
+ * If value is or refers to N returns
+ * A number That number
+ * A date The serial number of that date
+ * TRUE 1
+ * FALSE 0
+ * An error value The error value
+ * Anything else 0
+ */
+ public static function N($value = null)
+ {
+ while (is_array($value)) {
+ $value = array_shift($value);
+ }
+
+ switch (gettype($value)) {
+ case 'double':
+ case 'float':
+ case 'integer':
+ return $value;
+ case 'boolean':
+ return (integer) $value;
+ case 'string':
+ // Errors
+ if ((strlen($value) > 0) && ($value{0} == '#')) {
+ return $value;
+ }
+ break;
+ }
+ return 0;
+ }
+
+
+ /**
+ * TYPE
+ *
+ * Returns a number that identifies the type of a value
+ *
+ * @param value The value you want tested
+ * @return number N converts values listed in the following table
+ * If value is or refers to N returns
+ * A number 1
+ * Text 2
+ * Logical Value 4
+ * An error value 16
+ * Array or Matrix 64
+ */
+ public static function TYPE($value = null)
+ {
+ $value = self::flattenArrayIndexed($value);
+ if (is_array($value) && (count($value) > 1)) {
+ end($value);
+ $a = key($value);
+ // Range of cells is an error
+ if (self::isCellValue($a)) {
+ return 16;
+ // Test for Matrix
+ } elseif (self::isMatrixValue($a)) {
+ return 64;
+ }
+ } elseif (empty($value)) {
+ // Empty Cell
+ return 1;
+ }
+ $value = self::flattenSingleValue($value);
+
+ if (($value === null) || (is_float($value)) || (is_int($value))) {
+ return 1;
+ } elseif (is_bool($value)) {
+ return 4;
+ } elseif (is_array($value)) {
+ return 64;
+ } elseif (is_string($value)) {
+ // Errors
+ if ((strlen($value) > 0) && ($value{0} == '#')) {
+ return 16;
+ }
+ return 2;
+ }
+ return 0;
+ }
+
+
+ /**
+ * Convert a multi-dimensional array to a simple 1-dimensional array
+ *
+ * @param array $array Array to be flattened
+ * @return array Flattened array
+ */
+ public static function flattenArray($array)
+ {
+ if (!is_array($array)) {
+ return (array) $array;
+ }
+
+ $arrayValues = array();
+ foreach ($array as $value) {
+ if (is_array($value)) {
+ foreach ($value as $val) {
+ if (is_array($val)) {
+ foreach ($val as $v) {
+ $arrayValues[] = $v;
+ }
+ } else {
+ $arrayValues[] = $val;
+ }
+ }
+ } else {
+ $arrayValues[] = $value;
+ }
+ }
+
+ return $arrayValues;
+ }
+
+
+ /**
+ * Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing
+ *
+ * @param array $array Array to be flattened
+ * @return array Flattened array
+ */
+ public static function flattenArrayIndexed($array)
+ {
+ if (!is_array($array)) {
+ return (array) $array;
+ }
+
+ $arrayValues = array();
+ foreach ($array as $k1 => $value) {
+ if (is_array($value)) {
+ foreach ($value as $k2 => $val) {
+ if (is_array($val)) {
+ foreach ($val as $k3 => $v) {
+ $arrayValues[$k1.'.'.$k2.'.'.$k3] = $v;
+ }
+ } else {
+ $arrayValues[$k1.'.'.$k2] = $val;
+ }
+ }
+ } else {
+ $arrayValues[$k1] = $value;
+ }
+ }
+
+ return $arrayValues;
+ }
+
+
+ /**
+ * Convert an array to a single scalar value by extracting the first element
+ *
+ * @param mixed $value Array or scalar value
+ * @return mixed
+ */
+ public static function flattenSingleValue($value = '')
+ {
+ while (is_array($value)) {
+ $value = array_pop($value);
+ }
+
+ return $value;
+ }
+}
+
+
+//
+// There are a few mathematical functions that aren't available on all versions of PHP for all platforms
+// These functions aren't available in Windows implementations of PHP prior to version 5.3.0
+// So we test if they do exist for this version of PHP/operating platform; and if not we create them
+//
+if (!function_exists('acosh')) {
+ function acosh($x)
+ {
+ return 2 * log(sqrt(($x + 1) / 2) + sqrt(($x - 1) / 2));
+ } // function acosh()
+}
+
+if (!function_exists('asinh')) {
+ function asinh($x)
+ {
+ return log($x + sqrt(1 + $x * $x));
+ } // function asinh()
+}
+
+if (!function_exists('atanh')) {
+ function atanh($x)
+ {
+ return (log(1 + $x) - log(1 - $x)) / 2;
+ } // function atanh()
+}
+
+
+//
+// Strangely, PHP doesn't have a mb_str_replace multibyte function
+// As we'll only ever use this function with UTF-8 characters, we can simply "hard-code" the character set
+//
+if ((!function_exists('mb_str_replace')) &&
+ (function_exists('mb_substr')) && (function_exists('mb_strlen')) && (function_exists('mb_strpos'))) {
+ function mb_str_replace($search, $replace, $subject)
+ {
+ if (is_array($subject)) {
+ $ret = array();
+ foreach ($subject as $key => $val) {
+ $ret[$key] = mb_str_replace($search, $replace, $val);
+ }
+ return $ret;
+ }
+
+ foreach ((array) $search as $key => $s) {
+ if ($s == '' && $s !== 0) {
+ continue;
+ }
+ $r = !is_array($replace) ? $replace : (array_key_exists($key, $replace) ? $replace[$key] : '');
+ $pos = mb_strpos($subject, $s, 0, 'UTF-8');
+ while ($pos !== false) {
+ $subject = mb_substr($subject, 0, $pos, 'UTF-8') . $r . mb_substr($subject, $pos + mb_strlen($s, 'UTF-8'), 65535, 'UTF-8');
+ $pos = mb_strpos($subject, $s, $pos + mb_strlen($r, 'UTF-8'), 'UTF-8');
+ }
+ }
+ return $subject;
+ }
+}
diff --git a/PHPExcel/Calculation/Logical.php b/PHPExcel/Calculation/Logical.php
new file mode 100644
index 0000000..dd65f01
--- /dev/null
+++ b/PHPExcel/Calculation/Logical.php
@@ -0,0 +1,285 @@
+ $arg) {
+ // Is it a boolean value?
+ if (is_bool($arg)) {
+ $returnValue = $returnValue && $arg;
+ } elseif ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue = $returnValue && ($arg != 0);
+ } elseif (is_string($arg)) {
+ $arg = strtoupper($arg);
+ if (($arg == 'TRUE') || ($arg == PHPExcel_Calculation::getTRUE())) {
+ $arg = true;
+ } elseif (($arg == 'FALSE') || ($arg == PHPExcel_Calculation::getFALSE())) {
+ $arg = false;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $returnValue = $returnValue && ($arg != 0);
+ }
+ }
+
+ // Return
+ if ($argCount < 0) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ return $returnValue;
+ }
+
+
+ /**
+ * LOGICAL_OR
+ *
+ * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
+ *
+ * Excel Function:
+ * =OR(logical1[,logical2[, ...]])
+ *
+ * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
+ * or references that contain logical values.
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
+ * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $arg,... Data values
+ * @return boolean The logical OR of the arguments.
+ */
+ public static function LOGICAL_OR()
+ {
+ // Return value
+ $returnValue = false;
+
+ // Loop through the arguments
+ $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
+ $argCount = -1;
+ foreach ($aArgs as $argCount => $arg) {
+ // Is it a boolean value?
+ if (is_bool($arg)) {
+ $returnValue = $returnValue || $arg;
+ } elseif ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue = $returnValue || ($arg != 0);
+ } elseif (is_string($arg)) {
+ $arg = strtoupper($arg);
+ if (($arg == 'TRUE') || ($arg == PHPExcel_Calculation::getTRUE())) {
+ $arg = true;
+ } elseif (($arg == 'FALSE') || ($arg == PHPExcel_Calculation::getFALSE())) {
+ $arg = false;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $returnValue = $returnValue || ($arg != 0);
+ }
+ }
+
+ // Return
+ if ($argCount < 0) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ return $returnValue;
+ }
+
+
+ /**
+ * NOT
+ *
+ * Returns the boolean inverse of the argument.
+ *
+ * Excel Function:
+ * =NOT(logical)
+ *
+ * The argument must evaluate to a logical value such as TRUE or FALSE
+ *
+ * Boolean arguments are treated as True or False as appropriate
+ * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
+ * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
+ * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
+ * @return boolean The boolean inverse of the argument.
+ */
+ public static function NOT($logical = false)
+ {
+ $logical = PHPExcel_Calculation_Functions::flattenSingleValue($logical);
+ if (is_string($logical)) {
+ $logical = strtoupper($logical);
+ if (($logical == 'TRUE') || ($logical == PHPExcel_Calculation::getTRUE())) {
+ return false;
+ } elseif (($logical == 'FALSE') || ($logical == PHPExcel_Calculation::getFALSE())) {
+ return true;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+
+ return !$logical;
+ }
+
+ /**
+ * STATEMENT_IF
+ *
+ * Returns one value if a condition you specify evaluates to TRUE and another value if it evaluates to FALSE.
+ *
+ * Excel Function:
+ * =IF(condition[,returnIfTrue[,returnIfFalse]])
+ *
+ * Condition is any value or expression that can be evaluated to TRUE or FALSE.
+ * For example, A10=100 is a logical expression; if the value in cell A10 is equal to 100,
+ * the expression evaluates to TRUE. Otherwise, the expression evaluates to FALSE.
+ * This argument can use any comparison calculation operator.
+ * ReturnIfTrue is the value that is returned if condition evaluates to TRUE.
+ * For example, if this argument is the text string "Within budget" and the condition argument evaluates to TRUE,
+ * then the IF function returns the text "Within budget"
+ * If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero). To display the word TRUE, use
+ * the logical value TRUE for this argument.
+ * ReturnIfTrue can be another formula.
+ * ReturnIfFalse is the value that is returned if condition evaluates to FALSE.
+ * For example, if this argument is the text string "Over budget" and the condition argument evaluates to FALSE,
+ * then the IF function returns the text "Over budget".
+ * If condition is FALSE and ReturnIfFalse is omitted, then the logical value FALSE is returned.
+ * If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned.
+ * ReturnIfFalse can be another formula.
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $condition Condition to evaluate
+ * @param mixed $returnIfTrue Value to return when condition is true
+ * @param mixed $returnIfFalse Optional value to return when condition is false
+ * @return mixed The value of returnIfTrue or returnIfFalse determined by condition
+ */
+ public static function STATEMENT_IF($condition = true, $returnIfTrue = 0, $returnIfFalse = false)
+ {
+ $condition = (is_null($condition)) ? true : (boolean) PHPExcel_Calculation_Functions::flattenSingleValue($condition);
+ $returnIfTrue = (is_null($returnIfTrue)) ? 0 : PHPExcel_Calculation_Functions::flattenSingleValue($returnIfTrue);
+ $returnIfFalse = (is_null($returnIfFalse)) ? false : PHPExcel_Calculation_Functions::flattenSingleValue($returnIfFalse);
+
+ return ($condition) ? $returnIfTrue : $returnIfFalse;
+ }
+
+
+ /**
+ * IFERROR
+ *
+ * Excel Function:
+ * =IFERROR(testValue,errorpart)
+ *
+ * @access public
+ * @category Logical Functions
+ * @param mixed $testValue Value to check, is also the value returned when no error
+ * @param mixed $errorpart Value to return when testValue is an error condition
+ * @return mixed The value of errorpart or testValue determined by error condition
+ */
+ public static function IFERROR($testValue = '', $errorpart = '')
+ {
+ $testValue = (is_null($testValue)) ? '' : PHPExcel_Calculation_Functions::flattenSingleValue($testValue);
+ $errorpart = (is_null($errorpart)) ? '' : PHPExcel_Calculation_Functions::flattenSingleValue($errorpart);
+
+ return self::STATEMENT_IF(PHPExcel_Calculation_Functions::IS_ERROR($testValue), $errorpart, $testValue);
+ }
+}
diff --git a/PHPExcel/Calculation/LookupRef.php b/PHPExcel/Calculation/LookupRef.php
new file mode 100644
index 0000000..1fe7790
--- /dev/null
+++ b/PHPExcel/Calculation/LookupRef.php
@@ -0,0 +1,879 @@
+ '') {
+ if (strpos($sheetText, ' ') !== false) {
+ $sheetText = "'".$sheetText."'";
+ }
+ $sheetText .='!';
+ }
+ if ((!is_bool($referenceStyle)) || $referenceStyle) {
+ $rowRelative = $columnRelative = '$';
+ $column = PHPExcel_Cell::stringFromColumnIndex($column-1);
+ if (($relativity == 2) || ($relativity == 4)) {
+ $columnRelative = '';
+ }
+ if (($relativity == 3) || ($relativity == 4)) {
+ $rowRelative = '';
+ }
+ return $sheetText.$columnRelative.$column.$rowRelative.$row;
+ } else {
+ if (($relativity == 2) || ($relativity == 4)) {
+ $column = '['.$column.']';
+ }
+ if (($relativity == 3) || ($relativity == 4)) {
+ $row = '['.$row.']';
+ }
+ return $sheetText.'R'.$row.'C'.$column;
+ }
+ }
+
+
+ /**
+ * COLUMN
+ *
+ * Returns the column number of the given cell reference
+ * If the cell reference is a range of cells, COLUMN returns the column numbers of each column in the reference as a horizontal array.
+ * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
+ * reference of the cell in which the COLUMN function appears; otherwise this function returns 0.
+ *
+ * Excel Function:
+ * =COLUMN([cellAddress])
+ *
+ * @param cellAddress A reference to a range of cells for which you want the column numbers
+ * @return integer or array of integer
+ */
+ public static function COLUMN($cellAddress = null)
+ {
+ if (is_null($cellAddress) || trim($cellAddress) === '') {
+ return 0;
+ }
+
+ if (is_array($cellAddress)) {
+ foreach ($cellAddress as $columnKey => $value) {
+ $columnKey = preg_replace('/[^a-z]/i', '', $columnKey);
+ return (integer) PHPExcel_Cell::columnIndexFromString($columnKey);
+ }
+ } else {
+ if (strpos($cellAddress, '!') !== false) {
+ list($sheet, $cellAddress) = explode('!', $cellAddress);
+ }
+ if (strpos($cellAddress, ':') !== false) {
+ list($startAddress, $endAddress) = explode(':', $cellAddress);
+ $startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
+ $endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
+ $returnValue = array();
+ do {
+ $returnValue[] = (integer) PHPExcel_Cell::columnIndexFromString($startAddress);
+ } while ($startAddress++ != $endAddress);
+ return $returnValue;
+ } else {
+ $cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress);
+ return (integer) PHPExcel_Cell::columnIndexFromString($cellAddress);
+ }
+ }
+ }
+
+
+ /**
+ * COLUMNS
+ *
+ * Returns the number of columns in an array or reference.
+ *
+ * Excel Function:
+ * =COLUMNS(cellAddress)
+ *
+ * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns
+ * @return integer The number of columns in cellAddress
+ */
+ public static function COLUMNS($cellAddress = null)
+ {
+ if (is_null($cellAddress) || $cellAddress === '') {
+ return 1;
+ } elseif (!is_array($cellAddress)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ reset($cellAddress);
+ $isMatrix = (is_numeric(key($cellAddress)));
+ list($columns, $rows) = PHPExcel_Calculation::_getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $rows;
+ } else {
+ return $columns;
+ }
+ }
+
+
+ /**
+ * ROW
+ *
+ * Returns the row number of the given cell reference
+ * If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference as a vertical array.
+ * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
+ * reference of the cell in which the ROW function appears; otherwise this function returns 0.
+ *
+ * Excel Function:
+ * =ROW([cellAddress])
+ *
+ * @param cellAddress A reference to a range of cells for which you want the row numbers
+ * @return integer or array of integer
+ */
+ public static function ROW($cellAddress = null)
+ {
+ if (is_null($cellAddress) || trim($cellAddress) === '') {
+ return 0;
+ }
+
+ if (is_array($cellAddress)) {
+ foreach ($cellAddress as $columnKey => $rowValue) {
+ foreach ($rowValue as $rowKey => $cellValue) {
+ return (integer) preg_replace('/[^0-9]/i', '', $rowKey);
+ }
+ }
+ } else {
+ if (strpos($cellAddress, '!') !== false) {
+ list($sheet, $cellAddress) = explode('!', $cellAddress);
+ }
+ if (strpos($cellAddress, ':') !== false) {
+ list($startAddress, $endAddress) = explode(':', $cellAddress);
+ $startAddress = preg_replace('/[^0-9]/', '', $startAddress);
+ $endAddress = preg_replace('/[^0-9]/', '', $endAddress);
+ $returnValue = array();
+ do {
+ $returnValue[][] = (integer) $startAddress;
+ } while ($startAddress++ != $endAddress);
+ return $returnValue;
+ } else {
+ list($cellAddress) = explode(':', $cellAddress);
+ return (integer) preg_replace('/[^0-9]/', '', $cellAddress);
+ }
+ }
+ }
+
+
+ /**
+ * ROWS
+ *
+ * Returns the number of rows in an array or reference.
+ *
+ * Excel Function:
+ * =ROWS(cellAddress)
+ *
+ * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
+ * @return integer The number of rows in cellAddress
+ */
+ public static function ROWS($cellAddress = null)
+ {
+ if (is_null($cellAddress) || $cellAddress === '') {
+ return 1;
+ } elseif (!is_array($cellAddress)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ reset($cellAddress);
+ $isMatrix = (is_numeric(key($cellAddress)));
+ list($columns, $rows) = PHPExcel_Calculation::_getMatrixDimensions($cellAddress);
+
+ if ($isMatrix) {
+ return $columns;
+ } else {
+ return $rows;
+ }
+ }
+
+
+ /**
+ * HYPERLINK
+ *
+ * Excel Function:
+ * =HYPERLINK(linkURL,displayName)
+ *
+ * @access public
+ * @category Logical Functions
+ * @param string $linkURL Value to check, is also the value returned when no error
+ * @param string $displayName Value to return when testValue is an error condition
+ * @param PHPExcel_Cell $pCell The cell to set the hyperlink in
+ * @return mixed The value of $displayName (or $linkURL if $displayName was blank)
+ */
+ public static function HYPERLINK($linkURL = '', $displayName = null, PHPExcel_Cell $pCell = null)
+ {
+ $args = func_get_args();
+ $pCell = array_pop($args);
+
+ $linkURL = (is_null($linkURL)) ? '' : PHPExcel_Calculation_Functions::flattenSingleValue($linkURL);
+ $displayName = (is_null($displayName)) ? '' : PHPExcel_Calculation_Functions::flattenSingleValue($displayName);
+
+ if ((!is_object($pCell)) || (trim($linkURL) == '')) {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+
+ if ((is_object($displayName)) || trim($displayName) == '') {
+ $displayName = $linkURL;
+ }
+
+ $pCell->getHyperlink()->setUrl($linkURL);
+ $pCell->getHyperlink()->setTooltip($displayName);
+
+ return $displayName;
+ }
+
+
+ /**
+ * INDIRECT
+ *
+ * Returns the reference specified by a text string.
+ * References are immediately evaluated to display their contents.
+ *
+ * Excel Function:
+ * =INDIRECT(cellAddress)
+ *
+ * NOTE - INDIRECT() does not yet support the optional a1 parameter introduced in Excel 2010
+ *
+ * @param cellAddress $cellAddress The cell address of the current cell (containing this formula)
+ * @param PHPExcel_Cell $pCell The current cell (containing this formula)
+ * @return mixed The cells referenced by cellAddress
+ *
+ * @todo Support for the optional a1 parameter introduced in Excel 2010
+ *
+ */
+ public static function INDIRECT($cellAddress = null, PHPExcel_Cell $pCell = null)
+ {
+ $cellAddress = PHPExcel_Calculation_Functions::flattenSingleValue($cellAddress);
+ if (is_null($cellAddress) || $cellAddress === '') {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+
+ $cellAddress1 = $cellAddress;
+ $cellAddress2 = null;
+ if (strpos($cellAddress, ':') !== false) {
+ list($cellAddress1, $cellAddress2) = explode(':', $cellAddress);
+ }
+
+ if ((!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $cellAddress1, $matches)) ||
+ ((!is_null($cellAddress2)) && (!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $cellAddress2, $matches)))) {
+ if (!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $cellAddress1, $matches)) {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+
+ if (strpos($cellAddress, '!') !== false) {
+ list($sheetName, $cellAddress) = explode('!', $cellAddress);
+ $sheetName = trim($sheetName, "'");
+ $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
+ } else {
+ $pSheet = $pCell->getWorksheet();
+ }
+
+ return PHPExcel_Calculation::getInstance()->extractNamedRange($cellAddress, $pSheet, false);
+ }
+
+ if (strpos($cellAddress, '!') !== false) {
+ list($sheetName, $cellAddress) = explode('!', $cellAddress);
+ $sheetName = trim($sheetName, "'");
+ $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
+ } else {
+ $pSheet = $pCell->getWorksheet();
+ }
+
+ return PHPExcel_Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, false);
+ }
+
+
+ /**
+ * OFFSET
+ *
+ * Returns a reference to a range that is a specified number of rows and columns from a cell or range of cells.
+ * The reference that is returned can be a single cell or a range of cells. You can specify the number of rows and
+ * the number of columns to be returned.
+ *
+ * Excel Function:
+ * =OFFSET(cellAddress, rows, cols, [height], [width])
+ *
+ * @param cellAddress The reference from which you want to base the offset. Reference must refer to a cell or
+ * range of adjacent cells; otherwise, OFFSET returns the #VALUE! error value.
+ * @param rows The number of rows, up or down, that you want the upper-left cell to refer to.
+ * Using 5 as the rows argument specifies that the upper-left cell in the reference is
+ * five rows below reference. Rows can be positive (which means below the starting reference)
+ * or negative (which means above the starting reference).
+ * @param cols The number of columns, to the left or right, that you want the upper-left cell of the result
+ * to refer to. Using 5 as the cols argument specifies that the upper-left cell in the
+ * reference is five columns to the right of reference. Cols can be positive (which means
+ * to the right of the starting reference) or negative (which means to the left of the
+ * starting reference).
+ * @param height The height, in number of rows, that you want the returned reference to be. Height must be a positive number.
+ * @param width The width, in number of columns, that you want the returned reference to be. Width must be a positive number.
+ * @return string A reference to a cell or range of cells
+ */
+ public static function OFFSET($cellAddress = null, $rows = 0, $columns = 0, $height = null, $width = null)
+ {
+ $rows = PHPExcel_Calculation_Functions::flattenSingleValue($rows);
+ $columns = PHPExcel_Calculation_Functions::flattenSingleValue($columns);
+ $height = PHPExcel_Calculation_Functions::flattenSingleValue($height);
+ $width = PHPExcel_Calculation_Functions::flattenSingleValue($width);
+ if ($cellAddress == null) {
+ return 0;
+ }
+
+ $args = func_get_args();
+ $pCell = array_pop($args);
+ if (!is_object($pCell)) {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+
+ $sheetName = null;
+ if (strpos($cellAddress, "!")) {
+ list($sheetName, $cellAddress) = explode("!", $cellAddress);
+ $sheetName = trim($sheetName, "'");
+ }
+ if (strpos($cellAddress, ":")) {
+ list($startCell, $endCell) = explode(":", $cellAddress);
+ } else {
+ $startCell = $endCell = $cellAddress;
+ }
+ list($startCellColumn, $startCellRow) = PHPExcel_Cell::coordinateFromString($startCell);
+ list($endCellColumn, $endCellRow) = PHPExcel_Cell::coordinateFromString($endCell);
+
+ $startCellRow += $rows;
+ $startCellColumn = PHPExcel_Cell::columnIndexFromString($startCellColumn) - 1;
+ $startCellColumn += $columns;
+
+ if (($startCellRow <= 0) || ($startCellColumn < 0)) {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+ $endCellColumn = PHPExcel_Cell::columnIndexFromString($endCellColumn) - 1;
+ if (($width != null) && (!is_object($width))) {
+ $endCellColumn = $startCellColumn + $width - 1;
+ } else {
+ $endCellColumn += $columns;
+ }
+ $startCellColumn = PHPExcel_Cell::stringFromColumnIndex($startCellColumn);
+
+ if (($height != null) && (!is_object($height))) {
+ $endCellRow = $startCellRow + $height - 1;
+ } else {
+ $endCellRow += $rows;
+ }
+
+ if (($endCellRow <= 0) || ($endCellColumn < 0)) {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+ $endCellColumn = PHPExcel_Cell::stringFromColumnIndex($endCellColumn);
+
+ $cellAddress = $startCellColumn.$startCellRow;
+ if (($startCellColumn != $endCellColumn) || ($startCellRow != $endCellRow)) {
+ $cellAddress .= ':'.$endCellColumn.$endCellRow;
+ }
+
+ if ($sheetName !== null) {
+ $pSheet = $pCell->getWorksheet()->getParent()->getSheetByName($sheetName);
+ } else {
+ $pSheet = $pCell->getWorksheet();
+ }
+
+ return PHPExcel_Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, false);
+ }
+
+
+ /**
+ * CHOOSE
+ *
+ * Uses lookup_value to return a value from the list of value arguments.
+ * Use CHOOSE to select one of up to 254 values based on the lookup_value.
+ *
+ * Excel Function:
+ * =CHOOSE(index_num, value1, [value2], ...)
+ *
+ * @param index_num Specifies which value argument is selected.
+ * Index_num must be a number between 1 and 254, or a formula or reference to a cell containing a number
+ * between 1 and 254.
+ * @param value1... Value1 is required, subsequent values are optional.
+ * Between 1 to 254 value arguments from which CHOOSE selects a value or an action to perform based on
+ * index_num. The arguments can be numbers, cell references, defined names, formulas, functions, or
+ * text.
+ * @return mixed The selected value
+ */
+ public static function CHOOSE()
+ {
+ $chooseArgs = func_get_args();
+ $chosenEntry = PHPExcel_Calculation_Functions::flattenArray(array_shift($chooseArgs));
+ $entryCount = count($chooseArgs) - 1;
+
+ if (is_array($chosenEntry)) {
+ $chosenEntry = array_shift($chosenEntry);
+ }
+ if ((is_numeric($chosenEntry)) && (!is_bool($chosenEntry))) {
+ --$chosenEntry;
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $chosenEntry = floor($chosenEntry);
+ if (($chosenEntry < 0) || ($chosenEntry > $entryCount)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (is_array($chooseArgs[$chosenEntry])) {
+ return PHPExcel_Calculation_Functions::flattenArray($chooseArgs[$chosenEntry]);
+ } else {
+ return $chooseArgs[$chosenEntry];
+ }
+ }
+
+
+ /**
+ * MATCH
+ *
+ * The MATCH function searches for a specified item in a range of cells
+ *
+ * Excel Function:
+ * =MATCH(lookup_value, lookup_array, [match_type])
+ *
+ * @param lookup_value The value that you want to match in lookup_array
+ * @param lookup_array The range of cells being searched
+ * @param match_type The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below. If match_type is 1 or -1, the list has to be ordered.
+ * @return integer The relative position of the found item
+ */
+ public static function MATCH($lookup_value, $lookup_array, $match_type = 1)
+ {
+ $lookup_array = PHPExcel_Calculation_Functions::flattenArray($lookup_array);
+ $lookup_value = PHPExcel_Calculation_Functions::flattenSingleValue($lookup_value);
+ $match_type = (is_null($match_type)) ? 1 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($match_type);
+ // MATCH is not case sensitive
+ $lookup_value = strtolower($lookup_value);
+
+ // lookup_value type has to be number, text, or logical values
+ if ((!is_numeric($lookup_value)) && (!is_string($lookup_value)) && (!is_bool($lookup_value))) {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+
+ // match_type is 0, 1 or -1
+ if (($match_type !== 0) && ($match_type !== -1) && ($match_type !== 1)) {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+
+ // lookup_array should not be empty
+ $lookupArraySize = count($lookup_array);
+ if ($lookupArraySize <= 0) {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+
+ // lookup_array should contain only number, text, or logical values, or empty (null) cells
+ foreach ($lookup_array as $i => $lookupArrayValue) {
+ // check the type of the value
+ if ((!is_numeric($lookupArrayValue)) && (!is_string($lookupArrayValue)) &&
+ (!is_bool($lookupArrayValue)) && (!is_null($lookupArrayValue))) {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+ // convert strings to lowercase for case-insensitive testing
+ if (is_string($lookupArrayValue)) {
+ $lookup_array[$i] = strtolower($lookupArrayValue);
+ }
+ if ((is_null($lookupArrayValue)) && (($match_type == 1) || ($match_type == -1))) {
+ $lookup_array = array_slice($lookup_array, 0, $i-1);
+ }
+ }
+
+ // if match_type is 1 or -1, the list has to be ordered
+ if ($match_type == 1) {
+ asort($lookup_array);
+ $keySet = array_keys($lookup_array);
+ } elseif ($match_type == -1) {
+ arsort($lookup_array);
+ $keySet = array_keys($lookup_array);
+ }
+
+ // **
+ // find the match
+ // **
+ foreach ($lookup_array as $i => $lookupArrayValue) {
+ if (($match_type == 0) && ($lookupArrayValue == $lookup_value)) {
+ // exact match
+ return ++$i;
+ } elseif (($match_type == -1) && ($lookupArrayValue <= $lookup_value)) {
+ $i = array_search($i, $keySet);
+ // if match_type is -1 <=> find the smallest value that is greater than or equal to lookup_value
+ if ($i < 1) {
+ // 1st cell was already smaller than the lookup_value
+ break;
+ } else {
+ // the previous cell was the match
+ return $keySet[$i-1]+1;
+ }
+ } elseif (($match_type == 1) && ($lookupArrayValue >= $lookup_value)) {
+ $i = array_search($i, $keySet);
+ // if match_type is 1 <=> find the largest value that is less than or equal to lookup_value
+ if ($i < 1) {
+ // 1st cell was already bigger than the lookup_value
+ break;
+ } else {
+ // the previous cell was the match
+ return $keySet[$i-1]+1;
+ }
+ }
+ }
+
+ // unsuccessful in finding a match, return #N/A error value
+ return PHPExcel_Calculation_Functions::NA();
+ }
+
+
+ /**
+ * INDEX
+ *
+ * Uses an index to choose a value from a reference or array
+ *
+ * Excel Function:
+ * =INDEX(range_array, row_num, [column_num])
+ *
+ * @param range_array A range of cells or an array constant
+ * @param row_num The row in array from which to return a value. If row_num is omitted, column_num is required.
+ * @param column_num The column in array from which to return a value. If column_num is omitted, row_num is required.
+ * @return mixed the value of a specified cell or array of cells
+ */
+ public static function INDEX($arrayValues, $rowNum = 0, $columnNum = 0)
+ {
+ if (($rowNum < 0) || ($columnNum < 0)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ if (!is_array($arrayValues)) {
+ return PHPExcel_Calculation_Functions::REF();
+ }
+
+ $rowKeys = array_keys($arrayValues);
+ $columnKeys = @array_keys($arrayValues[$rowKeys[0]]);
+
+ if ($columnNum > count($columnKeys)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif ($columnNum == 0) {
+ if ($rowNum == 0) {
+ return $arrayValues;
+ }
+ $rowNum = $rowKeys[--$rowNum];
+ $returnArray = array();
+ foreach ($arrayValues as $arrayColumn) {
+ if (is_array($arrayColumn)) {
+ if (isset($arrayColumn[$rowNum])) {
+ $returnArray[] = $arrayColumn[$rowNum];
+ } else {
+ return $arrayValues[$rowNum];
+ }
+ } else {
+ return $arrayValues[$rowNum];
+ }
+ }
+ return $returnArray;
+ }
+ $columnNum = $columnKeys[--$columnNum];
+ if ($rowNum > count($rowKeys)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif ($rowNum == 0) {
+ return $arrayValues[$columnNum];
+ }
+ $rowNum = $rowKeys[--$rowNum];
+
+ return $arrayValues[$rowNum][$columnNum];
+ }
+
+
+ /**
+ * TRANSPOSE
+ *
+ * @param array $matrixData A matrix of values
+ * @return array
+ *
+ * Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix.
+ */
+ public static function TRANSPOSE($matrixData)
+ {
+ $returnMatrix = array();
+ if (!is_array($matrixData)) {
+ $matrixData = array(array($matrixData));
+ }
+
+ $column = 0;
+ foreach ($matrixData as $matrixRow) {
+ $row = 0;
+ foreach ($matrixRow as $matrixCell) {
+ $returnMatrix[$row][$column] = $matrixCell;
+ ++$row;
+ }
+ ++$column;
+ }
+ return $returnMatrix;
+ }
+
+
+ private static function vlookupSort($a, $b)
+ {
+ reset($a);
+ $firstColumn = key($a);
+ if (($aLower = strtolower($a[$firstColumn])) == ($bLower = strtolower($b[$firstColumn]))) {
+ return 0;
+ }
+ return ($aLower < $bLower) ? -1 : 1;
+ }
+
+
+ /**
+ * VLOOKUP
+ * The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value in the same row based on the index_number.
+ * @param lookup_value The value that you want to match in lookup_array
+ * @param lookup_array The range of cells being searched
+ * @param index_number The column number in table_array from which the matching value must be returned. The first column is 1.
+ * @param not_exact_match Determines if you are looking for an exact match based on lookup_value.
+ * @return mixed The value of the found cell
+ */
+ public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
+ {
+ $lookup_value = PHPExcel_Calculation_Functions::flattenSingleValue($lookup_value);
+ $index_number = PHPExcel_Calculation_Functions::flattenSingleValue($index_number);
+ $not_exact_match = PHPExcel_Calculation_Functions::flattenSingleValue($not_exact_match);
+
+ // index_number must be greater than or equal to 1
+ if ($index_number < 1) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // index_number must be less than or equal to the number of columns in lookup_array
+ if ((!is_array($lookup_array)) || (empty($lookup_array))) {
+ return PHPExcel_Calculation_Functions::REF();
+ } else {
+ $f = array_keys($lookup_array);
+ $firstRow = array_pop($f);
+ if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array[$firstRow]))) {
+ return PHPExcel_Calculation_Functions::REF();
+ } else {
+ $columnKeys = array_keys($lookup_array[$firstRow]);
+ $returnColumn = $columnKeys[--$index_number];
+ $firstColumn = array_shift($columnKeys);
+ }
+ }
+
+ if (!$not_exact_match) {
+ uasort($lookup_array, array('self', 'vlookupSort'));
+ }
+
+ $rowNumber = $rowValue = false;
+ foreach ($lookup_array as $rowKey => $rowData) {
+ if ((is_numeric($lookup_value) && is_numeric($rowData[$firstColumn]) && ($rowData[$firstColumn] > $lookup_value)) ||
+ (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && (strtolower($rowData[$firstColumn]) > strtolower($lookup_value)))) {
+ break;
+ }
+ $rowNumber = $rowKey;
+ $rowValue = $rowData[$firstColumn];
+ }
+
+ if ($rowNumber !== false) {
+ if ((!$not_exact_match) && ($rowValue != $lookup_value)) {
+ // if an exact match is required, we have what we need to return an appropriate response
+ return PHPExcel_Calculation_Functions::NA();
+ } else {
+ // otherwise return the appropriate value
+ return $lookup_array[$rowNumber][$returnColumn];
+ }
+ }
+
+ return PHPExcel_Calculation_Functions::NA();
+ }
+
+
+ /**
+ * HLOOKUP
+ * The HLOOKUP function searches for value in the top-most row of lookup_array and returns the value in the same column based on the index_number.
+ * @param lookup_value The value that you want to match in lookup_array
+ * @param lookup_array The range of cells being searched
+ * @param index_number The row number in table_array from which the matching value must be returned. The first row is 1.
+ * @param not_exact_match Determines if you are looking for an exact match based on lookup_value.
+ * @return mixed The value of the found cell
+ */
+ public static function HLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match = true)
+ {
+ $lookup_value = PHPExcel_Calculation_Functions::flattenSingleValue($lookup_value);
+ $index_number = PHPExcel_Calculation_Functions::flattenSingleValue($index_number);
+ $not_exact_match = PHPExcel_Calculation_Functions::flattenSingleValue($not_exact_match);
+
+ // index_number must be greater than or equal to 1
+ if ($index_number < 1) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ // index_number must be less than or equal to the number of columns in lookup_array
+ if ((!is_array($lookup_array)) || (empty($lookup_array))) {
+ return PHPExcel_Calculation_Functions::REF();
+ } else {
+ $f = array_keys($lookup_array);
+ $firstRow = array_pop($f);
+ if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array[$firstRow]))) {
+ return PHPExcel_Calculation_Functions::REF();
+ } else {
+ $columnKeys = array_keys($lookup_array[$firstRow]);
+ $firstkey = $f[0] - 1;
+ $returnColumn = $firstkey + $index_number;
+ $firstColumn = array_shift($f);
+ }
+ }
+
+ if (!$not_exact_match) {
+ $firstRowH = asort($lookup_array[$firstColumn]);
+ }
+
+ $rowNumber = $rowValue = false;
+ foreach ($lookup_array[$firstColumn] as $rowKey => $rowData) {
+ if ((is_numeric($lookup_value) && is_numeric($rowData) && ($rowData > $lookup_value)) ||
+ (!is_numeric($lookup_value) && !is_numeric($rowData) && (strtolower($rowData) > strtolower($lookup_value)))) {
+ break;
+ }
+ $rowNumber = $rowKey;
+ $rowValue = $rowData;
+ }
+
+ if ($rowNumber !== false) {
+ if ((!$not_exact_match) && ($rowValue != $lookup_value)) {
+ // if an exact match is required, we have what we need to return an appropriate response
+ return PHPExcel_Calculation_Functions::NA();
+ } else {
+ // otherwise return the appropriate value
+ return $lookup_array[$returnColumn][$rowNumber];
+ }
+ }
+
+ return PHPExcel_Calculation_Functions::NA();
+ }
+
+
+ /**
+ * LOOKUP
+ * The LOOKUP function searches for value either from a one-row or one-column range or from an array.
+ * @param lookup_value The value that you want to match in lookup_array
+ * @param lookup_vector The range of cells being searched
+ * @param result_vector The column from which the matching value must be returned
+ * @return mixed The value of the found cell
+ */
+ public static function LOOKUP($lookup_value, $lookup_vector, $result_vector = null)
+ {
+ $lookup_value = PHPExcel_Calculation_Functions::flattenSingleValue($lookup_value);
+
+ if (!is_array($lookup_vector)) {
+ return PHPExcel_Calculation_Functions::NA();
+ }
+ $lookupRows = count($lookup_vector);
+ $l = array_keys($lookup_vector);
+ $l = array_shift($l);
+ $lookupColumns = count($lookup_vector[$l]);
+ if ((($lookupRows == 1) && ($lookupColumns > 1)) || (($lookupRows == 2) && ($lookupColumns != 2))) {
+ $lookup_vector = self::TRANSPOSE($lookup_vector);
+ $lookupRows = count($lookup_vector);
+ $l = array_keys($lookup_vector);
+ $lookupColumns = count($lookup_vector[array_shift($l)]);
+ }
+
+ if (is_null($result_vector)) {
+ $result_vector = $lookup_vector;
+ }
+ $resultRows = count($result_vector);
+ $l = array_keys($result_vector);
+ $l = array_shift($l);
+ $resultColumns = count($result_vector[$l]);
+ if ((($resultRows == 1) && ($resultColumns > 1)) || (($resultRows == 2) && ($resultColumns != 2))) {
+ $result_vector = self::TRANSPOSE($result_vector);
+ $resultRows = count($result_vector);
+ $r = array_keys($result_vector);
+ $resultColumns = count($result_vector[array_shift($r)]);
+ }
+
+ if ($lookupRows == 2) {
+ $result_vector = array_pop($lookup_vector);
+ $lookup_vector = array_shift($lookup_vector);
+ }
+ if ($lookupColumns != 2) {
+ foreach ($lookup_vector as &$value) {
+ if (is_array($value)) {
+ $k = array_keys($value);
+ $key1 = $key2 = array_shift($k);
+ $key2++;
+ $dataValue1 = $value[$key1];
+ } else {
+ $key1 = 0;
+ $key2 = 1;
+ $dataValue1 = $value;
+ }
+ $dataValue2 = array_shift($result_vector);
+ if (is_array($dataValue2)) {
+ $dataValue2 = array_shift($dataValue2);
+ }
+ $value = array($key1 => $dataValue1, $key2 => $dataValue2);
+ }
+ unset($value);
+ }
+
+ return self::VLOOKUP($lookup_value, $lookup_vector, 2);
+ }
+}
diff --git a/PHPExcel/Calculation/MathTrig.php b/PHPExcel/Calculation/MathTrig.php
new file mode 100644
index 0000000..894ba9c
--- /dev/null
+++ b/PHPExcel/Calculation/MathTrig.php
@@ -0,0 +1,1459 @@
+ 1; --$i) {
+ if (($value % $i) == 0) {
+ $factorArray = array_merge($factorArray, self::factors($value / $i));
+ $factorArray = array_merge($factorArray, self::factors($i));
+ if ($i <= sqrt($value)) {
+ break;
+ }
+ }
+ }
+ if (!empty($factorArray)) {
+ rsort($factorArray);
+ return $factorArray;
+ } else {
+ return array((integer) $value);
+ }
+ }
+
+
+ private static function romanCut($num, $n)
+ {
+ return ($num - ($num % $n ) ) / $n;
+ }
+
+
+ /**
+ * ATAN2
+ *
+ * This function calculates the arc tangent of the two variables x and y. It is similar to
+ * calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
+ * to determine the quadrant of the result.
+ * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
+ * point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
+ * -pi and pi, excluding -pi.
+ *
+ * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
+ * PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
+ *
+ * Excel Function:
+ * ATAN2(xCoordinate,yCoordinate)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $xCoordinate The x-coordinate of the point.
+ * @param float $yCoordinate The y-coordinate of the point.
+ * @return float The inverse tangent of the specified x- and y-coordinates.
+ */
+ public static function ATAN2($xCoordinate = null, $yCoordinate = null)
+ {
+ $xCoordinate = PHPExcel_Calculation_Functions::flattenSingleValue($xCoordinate);
+ $yCoordinate = PHPExcel_Calculation_Functions::flattenSingleValue($yCoordinate);
+
+ $xCoordinate = ($xCoordinate !== null) ? $xCoordinate : 0.0;
+ $yCoordinate = ($yCoordinate !== null) ? $yCoordinate : 0.0;
+
+ if (((is_numeric($xCoordinate)) || (is_bool($xCoordinate))) &&
+ ((is_numeric($yCoordinate))) || (is_bool($yCoordinate))) {
+ $xCoordinate = (float) $xCoordinate;
+ $yCoordinate = (float) $yCoordinate;
+
+ if (($xCoordinate == 0) && ($yCoordinate == 0)) {
+ return PHPExcel_Calculation_Functions::DIV0();
+ }
+
+ return atan2($yCoordinate, $xCoordinate);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * CEILING
+ *
+ * Returns number rounded up, away from zero, to the nearest multiple of significance.
+ * For example, if you want to avoid using pennies in your prices and your product is
+ * priced at $4.42, use the formula =CEILING(4.42,0.05) to round prices up to the
+ * nearest nickel.
+ *
+ * Excel Function:
+ * CEILING(number[,significance])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $number The number you want to round.
+ * @param float $significance The multiple to which you want to round.
+ * @return float Rounded Number
+ */
+ public static function CEILING($number, $significance = null)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+ $significance = PHPExcel_Calculation_Functions::flattenSingleValue($significance);
+
+ if ((is_null($significance)) &&
+ (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC)) {
+ $significance = $number / abs($number);
+ }
+
+ if ((is_numeric($number)) && (is_numeric($significance))) {
+ if (($number == 0.0 ) || ($significance == 0.0)) {
+ return 0.0;
+ } elseif (self::SIGN($number) == self::SIGN($significance)) {
+ return ceil($number / $significance) * $significance;
+ } else {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * COMBIN
+ *
+ * Returns the number of combinations for a given number of items. Use COMBIN to
+ * determine the total possible number of groups for a given number of items.
+ *
+ * Excel Function:
+ * COMBIN(numObjs,numInSet)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param int $numObjs Number of different objects
+ * @param int $numInSet Number of objects in each combination
+ * @return int Number of combinations
+ */
+ public static function COMBIN($numObjs, $numInSet)
+ {
+ $numObjs = PHPExcel_Calculation_Functions::flattenSingleValue($numObjs);
+ $numInSet = PHPExcel_Calculation_Functions::flattenSingleValue($numInSet);
+
+ if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
+ if ($numObjs < $numInSet) {
+ return PHPExcel_Calculation_Functions::NaN();
+ } elseif ($numInSet < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * EVEN
+ *
+ * Returns number rounded up to the nearest even integer.
+ * You can use this function for processing items that come in twos. For example,
+ * a packing crate accepts rows of one or two items. The crate is full when
+ * the number of items, rounded up to the nearest two, matches the crate's
+ * capacity.
+ *
+ * Excel Function:
+ * EVEN(number)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $number Number to round
+ * @return int Rounded Number
+ */
+ public static function EVEN($number)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+
+ if (is_null($number)) {
+ return 0;
+ } elseif (is_bool($number)) {
+ $number = (int) $number;
+ }
+
+ if (is_numeric($number)) {
+ $significance = 2 * self::SIGN($number);
+ return (int) self::CEILING($number, $significance);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * FACT
+ *
+ * Returns the factorial of a number.
+ * The factorial of a number is equal to 1*2*3*...* number.
+ *
+ * Excel Function:
+ * FACT(factVal)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $factVal Factorial Value
+ * @return int Factorial
+ */
+ public static function FACT($factVal)
+ {
+ $factVal = PHPExcel_Calculation_Functions::flattenSingleValue($factVal);
+
+ if (is_numeric($factVal)) {
+ if ($factVal < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $factLoop = floor($factVal);
+ if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
+ if ($factVal > $factLoop) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ }
+
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop--;
+ }
+ return $factorial ;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * FACTDOUBLE
+ *
+ * Returns the double factorial of a number.
+ *
+ * Excel Function:
+ * FACTDOUBLE(factVal)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $factVal Factorial Value
+ * @return int Double Factorial
+ */
+ public static function FACTDOUBLE($factVal)
+ {
+ $factLoop = PHPExcel_Calculation_Functions::flattenSingleValue($factVal);
+
+ if (is_numeric($factLoop)) {
+ $factLoop = floor($factLoop);
+ if ($factVal < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $factorial = 1;
+ while ($factLoop > 1) {
+ $factorial *= $factLoop--;
+ --$factLoop;
+ }
+ return $factorial ;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * FLOOR
+ *
+ * Rounds number down, toward zero, to the nearest multiple of significance.
+ *
+ * Excel Function:
+ * FLOOR(number[,significance])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $number Number to round
+ * @param float $significance Significance
+ * @return float Rounded Number
+ */
+ public static function FLOOR($number, $significance = null)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+ $significance = PHPExcel_Calculation_Functions::flattenSingleValue($significance);
+
+ if ((is_null($significance)) &&
+ (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC)) {
+ $significance = $number/abs($number);
+ }
+
+ if ((is_numeric($number)) && (is_numeric($significance))) {
+ if ($significance == 0.0) {
+ return PHPExcel_Calculation_Functions::DIV0();
+ } elseif ($number == 0.0) {
+ return 0.0;
+ } elseif (self::SIGN($number) == self::SIGN($significance)) {
+ return floor($number / $significance) * $significance;
+ } else {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ }
+
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * GCD
+ *
+ * Returns the greatest common divisor of a series of numbers.
+ * The greatest common divisor is the largest integer that divides both
+ * number1 and number2 without a remainder.
+ *
+ * Excel Function:
+ * GCD(number1[,number2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return integer Greatest Common Divisor
+ */
+ public static function GCD()
+ {
+ $returnValue = 1;
+ $allValuesFactors = array();
+ // Loop through arguments
+ foreach (PHPExcel_Calculation_Functions::flattenArray(func_get_args()) as $value) {
+ if (!is_numeric($value)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ } elseif ($value == 0) {
+ continue;
+ } elseif ($value < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $myFactors = self::factors($value);
+ $myCountedFactors = array_count_values($myFactors);
+ $allValuesFactors[] = $myCountedFactors;
+ }
+ $allValuesCount = count($allValuesFactors);
+ if ($allValuesCount == 0) {
+ return 0;
+ }
+
+ $mergedArray = $allValuesFactors[0];
+ for ($i=1; $i < $allValuesCount; ++$i) {
+ $mergedArray = array_intersect_key($mergedArray, $allValuesFactors[$i]);
+ }
+ $mergedArrayValues = count($mergedArray);
+ if ($mergedArrayValues == 0) {
+ return $returnValue;
+ } elseif ($mergedArrayValues > 1) {
+ foreach ($mergedArray as $mergedKey => $mergedValue) {
+ foreach ($allValuesFactors as $highestPowerTest) {
+ foreach ($highestPowerTest as $testKey => $testValue) {
+ if (($testKey == $mergedKey) && ($testValue < $mergedValue)) {
+ $mergedArray[$mergedKey] = $testValue;
+ $mergedValue = $testValue;
+ }
+ }
+ }
+ }
+
+ $returnValue = 1;
+ foreach ($mergedArray as $key => $value) {
+ $returnValue *= pow($key, $value);
+ }
+ return $returnValue;
+ } else {
+ $keys = array_keys($mergedArray);
+ $key = $keys[0];
+ $value = $mergedArray[$key];
+ foreach ($allValuesFactors as $testValue) {
+ foreach ($testValue as $mergedKey => $mergedValue) {
+ if (($mergedKey == $key) && ($mergedValue < $value)) {
+ $value = $mergedValue;
+ }
+ }
+ }
+ return pow($key, $value);
+ }
+ }
+
+
+ /**
+ * INT
+ *
+ * Casts a floating point value to an integer
+ *
+ * Excel Function:
+ * INT(number)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $number Number to cast to an integer
+ * @return integer Integer value
+ */
+ public static function INT($number)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+
+ if (is_null($number)) {
+ return 0;
+ } elseif (is_bool($number)) {
+ return (int) $number;
+ }
+ if (is_numeric($number)) {
+ return (int) floor($number);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * LCM
+ *
+ * Returns the lowest common multiplier of a series of numbers
+ * The least common multiple is the smallest positive integer that is a multiple
+ * of all integer arguments number1, number2, and so on. Use LCM to add fractions
+ * with different denominators.
+ *
+ * Excel Function:
+ * LCM(number1[,number2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return int Lowest Common Multiplier
+ */
+ public static function LCM()
+ {
+ $returnValue = 1;
+ $allPoweredFactors = array();
+ // Loop through arguments
+ foreach (PHPExcel_Calculation_Functions::flattenArray(func_get_args()) as $value) {
+ if (!is_numeric($value)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if ($value == 0) {
+ return 0;
+ } elseif ($value < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $myFactors = self::factors(floor($value));
+ $myCountedFactors = array_count_values($myFactors);
+ $myPoweredFactors = array();
+ foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
+ $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor, $myCountedPower);
+ }
+ foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
+ if (array_key_exists($myPoweredValue, $allPoweredFactors)) {
+ if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ } else {
+ $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
+ }
+ }
+ }
+ foreach ($allPoweredFactors as $allPoweredFactor) {
+ $returnValue *= (integer) $allPoweredFactor;
+ }
+ return $returnValue;
+ }
+
+
+ /**
+ * LOG_BASE
+ *
+ * Returns the logarithm of a number to a specified base. The default base is 10.
+ *
+ * Excel Function:
+ * LOG(number[,base])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param float $number The positive real number for which you want the logarithm
+ * @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
+ * @return float
+ */
+ public static function LOG_BASE($number = null, $base = 10)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+ $base = (is_null($base)) ? 10 : (float) PHPExcel_Calculation_Functions::flattenSingleValue($base);
+
+ if ((!is_numeric($base)) || (!is_numeric($number))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ if (($base <= 0) || ($number <= 0)) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return log($number, $base);
+ }
+
+
+ /**
+ * MDETERM
+ *
+ * Returns the matrix determinant of an array.
+ *
+ * Excel Function:
+ * MDETERM(array)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param array $matrixValues A matrix of values
+ * @return float
+ */
+ public static function MDETERM($matrixValues)
+ {
+ $matrixData = array();
+ if (!is_array($matrixValues)) {
+ $matrixValues = array(array($matrixValues));
+ }
+
+ $row = $maxColumn = 0;
+ foreach ($matrixValues as $matrixRow) {
+ if (!is_array($matrixRow)) {
+ $matrixRow = array($matrixRow);
+ }
+ $column = 0;
+ foreach ($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $matrixData[$column][$row] = $matrixCell;
+ ++$column;
+ }
+ if ($column > $maxColumn) {
+ $maxColumn = $column;
+ }
+ ++$row;
+ }
+ if ($row != $maxColumn) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ try {
+ $matrix = new PHPExcel_Shared_JAMA_Matrix($matrixData);
+ return $matrix->det();
+ } catch (PHPExcel_Exception $ex) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+
+
+ /**
+ * MINVERSE
+ *
+ * Returns the inverse matrix for the matrix stored in an array.
+ *
+ * Excel Function:
+ * MINVERSE(array)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param array $matrixValues A matrix of values
+ * @return array
+ */
+ public static function MINVERSE($matrixValues)
+ {
+ $matrixData = array();
+ if (!is_array($matrixValues)) {
+ $matrixValues = array(array($matrixValues));
+ }
+
+ $row = $maxColumn = 0;
+ foreach ($matrixValues as $matrixRow) {
+ if (!is_array($matrixRow)) {
+ $matrixRow = array($matrixRow);
+ }
+ $column = 0;
+ foreach ($matrixRow as $matrixCell) {
+ if ((is_string($matrixCell)) || ($matrixCell === null)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $matrixData[$column][$row] = $matrixCell;
+ ++$column;
+ }
+ if ($column > $maxColumn) {
+ $maxColumn = $column;
+ }
+ ++$row;
+ }
+ if ($row != $maxColumn) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ try {
+ $matrix = new PHPExcel_Shared_JAMA_Matrix($matrixData);
+ return $matrix->inverse()->getArray();
+ } catch (PHPExcel_Exception $ex) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+
+
+ /**
+ * MMULT
+ *
+ * @param array $matrixData1 A matrix of values
+ * @param array $matrixData2 A matrix of values
+ * @return array
+ */
+ public static function MMULT($matrixData1, $matrixData2)
+ {
+ $matrixAData = $matrixBData = array();
+ if (!is_array($matrixData1)) {
+ $matrixData1 = array(array($matrixData1));
+ }
+ if (!is_array($matrixData2)) {
+ $matrixData2 = array(array($matrixData2));
+ }
+
+ try {
+ $rowA = 0;
+ foreach ($matrixData1 as $matrixRow) {
+ if (!is_array($matrixRow)) {
+ $matrixRow = array($matrixRow);
+ }
+ $columnA = 0;
+ foreach ($matrixRow as $matrixCell) {
+ if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $matrixAData[$rowA][$columnA] = $matrixCell;
+ ++$columnA;
+ }
+ ++$rowA;
+ }
+ $matrixA = new PHPExcel_Shared_JAMA_Matrix($matrixAData);
+ $rowB = 0;
+ foreach ($matrixData2 as $matrixRow) {
+ if (!is_array($matrixRow)) {
+ $matrixRow = array($matrixRow);
+ }
+ $columnB = 0;
+ foreach ($matrixRow as $matrixCell) {
+ if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $matrixBData[$rowB][$columnB] = $matrixCell;
+ ++$columnB;
+ }
+ ++$rowB;
+ }
+ $matrixB = new PHPExcel_Shared_JAMA_Matrix($matrixBData);
+
+ if ($columnA != $rowB) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ return $matrixA->times($matrixB)->getArray();
+ } catch (PHPExcel_Exception $ex) {
+ var_dump($ex->getMessage());
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+
+
+ /**
+ * MOD
+ *
+ * @param int $a Dividend
+ * @param int $b Divisor
+ * @return int Remainder
+ */
+ public static function MOD($a = 1, $b = 1)
+ {
+ $a = PHPExcel_Calculation_Functions::flattenSingleValue($a);
+ $b = PHPExcel_Calculation_Functions::flattenSingleValue($b);
+
+ if ($b == 0.0) {
+ return PHPExcel_Calculation_Functions::DIV0();
+ } elseif (($a < 0.0) && ($b > 0.0)) {
+ return $b - fmod(abs($a), $b);
+ } elseif (($a > 0.0) && ($b < 0.0)) {
+ return $b + fmod($a, abs($b));
+ }
+
+ return fmod($a, $b);
+ }
+
+
+ /**
+ * MROUND
+ *
+ * Rounds a number to the nearest multiple of a specified value
+ *
+ * @param float $number Number to round
+ * @param int $multiple Multiple to which you want to round $number
+ * @return float Rounded Number
+ */
+ public static function MROUND($number, $multiple)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+ $multiple = PHPExcel_Calculation_Functions::flattenSingleValue($multiple);
+
+ if ((is_numeric($number)) && (is_numeric($multiple))) {
+ if ($multiple == 0) {
+ return 0;
+ }
+ if ((self::SIGN($number)) == (self::SIGN($multiple))) {
+ $multiplier = 1 / $multiple;
+ return round($number * $multiplier) / $multiplier;
+ }
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * MULTINOMIAL
+ *
+ * Returns the ratio of the factorial of a sum of values to the product of factorials.
+ *
+ * @param array of mixed Data Series
+ * @return float
+ */
+ public static function MULTINOMIAL()
+ {
+ $summer = 0;
+ $divisor = 1;
+ // Loop through arguments
+ foreach (PHPExcel_Calculation_Functions::flattenArray(func_get_args()) as $arg) {
+ // Is it a numeric value?
+ if (is_numeric($arg)) {
+ if ($arg < 1) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ $summer += floor($arg);
+ $divisor *= self::FACT($arg);
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+
+ // Return
+ if ($summer > 0) {
+ $summer = self::FACT($summer);
+ return $summer / $divisor;
+ }
+ return 0;
+ }
+
+
+ /**
+ * ODD
+ *
+ * Returns number rounded up to the nearest odd integer.
+ *
+ * @param float $number Number to round
+ * @return int Rounded Number
+ */
+ public static function ODD($number)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+
+ if (is_null($number)) {
+ return 1;
+ } elseif (is_bool($number)) {
+ return 1;
+ } elseif (is_numeric($number)) {
+ $significance = self::SIGN($number);
+ if ($significance == 0) {
+ return 1;
+ }
+
+ $result = self::CEILING($number, $significance);
+ if ($result == self::EVEN($result)) {
+ $result += $significance;
+ }
+
+ return (int) $result;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * POWER
+ *
+ * Computes x raised to the power y.
+ *
+ * @param float $x
+ * @param float $y
+ * @return float
+ */
+ public static function POWER($x = 0, $y = 2)
+ {
+ $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
+ $y = PHPExcel_Calculation_Functions::flattenSingleValue($y);
+
+ // Validate parameters
+ if ($x == 0.0 && $y == 0.0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ } elseif ($x == 0.0 && $y < 0.0) {
+ return PHPExcel_Calculation_Functions::DIV0();
+ }
+
+ // Return
+ $result = pow($x, $y);
+ return (!is_nan($result) && !is_infinite($result)) ? $result : PHPExcel_Calculation_Functions::NaN();
+ }
+
+
+ /**
+ * PRODUCT
+ *
+ * PRODUCT returns the product of all the values and cells referenced in the argument list.
+ *
+ * Excel Function:
+ * PRODUCT(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function PRODUCT()
+ {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ foreach (PHPExcel_Calculation_Functions::flattenArray(func_get_args()) as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = $arg;
+ } else {
+ $returnValue *= $arg;
+ }
+ }
+ }
+
+ // Return
+ if (is_null($returnValue)) {
+ return 0;
+ }
+ return $returnValue;
+ }
+
+
+ /**
+ * QUOTIENT
+ *
+ * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
+ * and denominator is the divisor.
+ *
+ * Excel Function:
+ * QUOTIENT(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function QUOTIENT()
+ {
+ // Return value
+ $returnValue = null;
+
+ // Loop through arguments
+ foreach (PHPExcel_Calculation_Functions::flattenArray(func_get_args()) as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ if (is_null($returnValue)) {
+ $returnValue = ($arg == 0) ? 0 : $arg;
+ } else {
+ if (($returnValue == 0) || ($arg == 0)) {
+ $returnValue = 0;
+ } else {
+ $returnValue /= $arg;
+ }
+ }
+ }
+ }
+
+ // Return
+ return intval($returnValue);
+ }
+
+
+ /**
+ * RAND
+ *
+ * @param int $min Minimal value
+ * @param int $max Maximal value
+ * @return int Random number
+ */
+ public static function RAND($min = 0, $max = 0)
+ {
+ $min = PHPExcel_Calculation_Functions::flattenSingleValue($min);
+ $max = PHPExcel_Calculation_Functions::flattenSingleValue($max);
+
+ if ($min == 0 && $max == 0) {
+ return (mt_rand(0, 10000000)) / 10000000;
+ } else {
+ return mt_rand($min, $max);
+ }
+ }
+
+
+ public static function ROMAN($aValue, $style = 0)
+ {
+ $aValue = PHPExcel_Calculation_Functions::flattenSingleValue($aValue);
+ $style = (is_null($style)) ? 0 : (integer) PHPExcel_Calculation_Functions::flattenSingleValue($style);
+ if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $aValue = (integer) $aValue;
+ if ($aValue == 0) {
+ return '';
+ }
+
+ $mill = array('', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM');
+ $cent = array('', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM');
+ $tens = array('', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC');
+ $ones = array('', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX');
+
+ $roman = '';
+ while ($aValue > 5999) {
+ $roman .= 'M';
+ $aValue -= 1000;
+ }
+ $m = self::romanCut($aValue, 1000);
+ $aValue %= 1000;
+ $c = self::romanCut($aValue, 100);
+ $aValue %= 100;
+ $t = self::romanCut($aValue, 10);
+ $aValue %= 10;
+
+ return $roman.$mill[$m].$cent[$c].$tens[$t].$ones[$aValue];
+ }
+
+
+ /**
+ * ROUNDUP
+ *
+ * Rounds a number up to a specified number of decimal places
+ *
+ * @param float $number Number to round
+ * @param int $digits Number of digits to which you want to round $number
+ * @return float Rounded Number
+ */
+ public static function ROUNDUP($number, $digits)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+ $digits = PHPExcel_Calculation_Functions::flattenSingleValue($digits);
+
+ if ((is_numeric($number)) && (is_numeric($digits))) {
+ $significance = pow(10, (int) $digits);
+ if ($number < 0.0) {
+ return floor($number * $significance) / $significance;
+ } else {
+ return ceil($number * $significance) / $significance;
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * ROUNDDOWN
+ *
+ * Rounds a number down to a specified number of decimal places
+ *
+ * @param float $number Number to round
+ * @param int $digits Number of digits to which you want to round $number
+ * @return float Rounded Number
+ */
+ public static function ROUNDDOWN($number, $digits)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+ $digits = PHPExcel_Calculation_Functions::flattenSingleValue($digits);
+
+ if ((is_numeric($number)) && (is_numeric($digits))) {
+ $significance = pow(10, (int) $digits);
+ if ($number < 0.0) {
+ return ceil($number * $significance) / $significance;
+ } else {
+ return floor($number * $significance) / $significance;
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * SERIESSUM
+ *
+ * Returns the sum of a power series
+ *
+ * @param float $x Input value to the power series
+ * @param float $n Initial power to which you want to raise $x
+ * @param float $m Step by which to increase $n for each term in the series
+ * @param array of mixed Data Series
+ * @return float
+ */
+ public static function SERIESSUM()
+ {
+ $returnValue = 0;
+
+ // Loop through arguments
+ $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
+
+ $x = array_shift($aArgs);
+ $n = array_shift($aArgs);
+ $m = array_shift($aArgs);
+
+ if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
+ // Calculate
+ $i = 0;
+ foreach ($aArgs as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += $arg * pow($x, $n + ($m * $i++));
+ } else {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ }
+ return $returnValue;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * SIGN
+ *
+ * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
+ * if the number is 0, and -1 if the number is negative.
+ *
+ * @param float $number Number to round
+ * @return int sign value
+ */
+ public static function SIGN($number)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+
+ if (is_bool($number)) {
+ return (int) $number;
+ }
+ if (is_numeric($number)) {
+ if ($number == 0.0) {
+ return 0;
+ }
+ return $number / abs($number);
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * SQRTPI
+ *
+ * Returns the square root of (number * pi).
+ *
+ * @param float $number Number
+ * @return float Square Root of Number * Pi
+ */
+ public static function SQRTPI($number)
+ {
+ $number = PHPExcel_Calculation_Functions::flattenSingleValue($number);
+
+ if (is_numeric($number)) {
+ if ($number < 0) {
+ return PHPExcel_Calculation_Functions::NaN();
+ }
+ return sqrt($number * M_PI) ;
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * SUBTOTAL
+ *
+ * Returns a subtotal in a list or database.
+ *
+ * @param int the number 1 to 11 that specifies which function to
+ * use in calculating subtotals within a list.
+ * @param array of mixed Data Series
+ * @return float
+ */
+ public static function SUBTOTAL()
+ {
+ $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
+
+ // Calculate
+ $subtotal = array_shift($aArgs);
+
+ if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
+ switch ($subtotal) {
+ case 1:
+ return PHPExcel_Calculation_Statistical::AVERAGE($aArgs);
+ case 2:
+ return PHPExcel_Calculation_Statistical::COUNT($aArgs);
+ case 3:
+ return PHPExcel_Calculation_Statistical::COUNTA($aArgs);
+ case 4:
+ return PHPExcel_Calculation_Statistical::MAX($aArgs);
+ case 5:
+ return PHPExcel_Calculation_Statistical::MIN($aArgs);
+ case 6:
+ return self::PRODUCT($aArgs);
+ case 7:
+ return PHPExcel_Calculation_Statistical::STDEV($aArgs);
+ case 8:
+ return PHPExcel_Calculation_Statistical::STDEVP($aArgs);
+ case 9:
+ return self::SUM($aArgs);
+ case 10:
+ return PHPExcel_Calculation_Statistical::VARFunc($aArgs);
+ case 11:
+ return PHPExcel_Calculation_Statistical::VARP($aArgs);
+ }
+ }
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+
+ /**
+ * SUM
+ *
+ * SUM computes the sum of all the values and cells referenced in the argument list.
+ *
+ * Excel Function:
+ * SUM(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function SUM()
+ {
+ $returnValue = 0;
+
+ // Loop through the arguments
+ foreach (PHPExcel_Calculation_Functions::flattenArray(func_get_args()) as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += $arg;
+ }
+ }
+
+ return $returnValue;
+ }
+
+
+ /**
+ * SUMIF
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * SUMIF(value1[,value2[, ...]],condition)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @param string $condition The criteria that defines which cells will be summed.
+ * @return float
+ */
+ public static function SUMIF($aArgs, $condition, $sumArgs = array())
+ {
+ $returnValue = 0;
+
+ $aArgs = PHPExcel_Calculation_Functions::flattenArray($aArgs);
+ $sumArgs = PHPExcel_Calculation_Functions::flattenArray($sumArgs);
+ if (empty($sumArgs)) {
+ $sumArgs = $aArgs;
+ }
+ $condition = PHPExcel_Calculation_Functions::ifCondition($condition);
+ // Loop through arguments
+ foreach ($aArgs as $key => $arg) {
+ if (!is_numeric($arg)) {
+ $arg = str_replace('"', '""', $arg);
+ $arg = PHPExcel_Calculation::wrapResult(strtoupper($arg));
+ }
+
+ $testCondition = '='.$arg.$condition;
+ if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ // Is it a value within our criteria
+ $returnValue += $sumArgs[$key];
+ }
+ }
+
+ return $returnValue;
+ }
+
+
+ /**
+ * SUMIFS
+ *
+ * Counts the number of cells that contain numbers within the list of arguments
+ *
+ * Excel Function:
+ * SUMIFS(value1[,value2[, ...]],condition)
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @param string $condition The criteria that defines which cells will be summed.
+ * @return float
+ */
+ public static function SUMIFS() {
+ $arrayList = func_get_args();
+
+ $sumArgs = PHPExcel_Calculation_Functions::flattenArray(array_shift($arrayList));
+
+ while (count($arrayList) > 0) {
+ $aArgsArray[] = PHPExcel_Calculation_Functions::flattenArray(array_shift($arrayList));
+ $conditions[] = PHPExcel_Calculation_Functions::ifCondition(array_shift($arrayList));
+ }
+
+ // Loop through each set of arguments and conditions
+ foreach ($conditions as $index => $condition) {
+ $aArgs = $aArgsArray[$index];
+ $wildcard = false;
+ if ((strpos($condition, '*') !== false) || (strpos($condition, '?') !== false)) {
+ // * and ? are wildcard characters.
+ // Use ~* and ~? for literal star and question mark
+ // Code logic doesn't yet handle escaping
+ $condition = trim(ltrim($condition, '=<>'), '"');
+ $wildcard = true;
+ }
+ // Loop through arguments
+ foreach ($aArgs as $key => $arg) {
+ if ($wildcard) {
+ if (!fnmatch($condition, $arg, FNM_CASEFOLD)) {
+ // Is it a value within our criteria
+ $sumArgs[$key] = 0.0;
+ }
+ } else {
+ if (!is_numeric($arg)) {
+ $arg = PHPExcel_Calculation::wrapResult(strtoupper($arg));
+ }
+ $testCondition = '='.$arg.$condition;
+ if (!PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
+ // Is it a value within our criteria
+ $sumArgs[$key] = 0.0;
+ }
+ }
+ }
+ }
+
+ // Return
+ return array_sum($sumArgs);
+ }
+
+
+ /**
+ * SUMPRODUCT
+ *
+ * Excel Function:
+ * SUMPRODUCT(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function SUMPRODUCT()
+ {
+ $arrayList = func_get_args();
+
+ $wrkArray = PHPExcel_Calculation_Functions::flattenArray(array_shift($arrayList));
+ $wrkCellCount = count($wrkArray);
+
+ for ($i=0; $i< $wrkCellCount; ++$i) {
+ if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
+ $wrkArray[$i] = 0;
+ }
+ }
+
+ foreach ($arrayList as $matrixData) {
+ $array2 = PHPExcel_Calculation_Functions::flattenArray($matrixData);
+ $count = count($array2);
+ if ($wrkCellCount != $count) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+
+ foreach ($array2 as $i => $val) {
+ if ((!is_numeric($val)) || (is_string($val))) {
+ $val = 0;
+ }
+ $wrkArray[$i] *= $val;
+ }
+ }
+
+ return array_sum($wrkArray);
+ }
+
+
+ /**
+ * SUMSQ
+ *
+ * SUMSQ returns the sum of the squares of the arguments
+ *
+ * Excel Function:
+ * SUMSQ(value1[,value2[, ...]])
+ *
+ * @access public
+ * @category Mathematical and Trigonometric Functions
+ * @param mixed $arg,... Data values
+ * @return float
+ */
+ public static function SUMSQ()
+ {
+ $returnValue = 0;
+
+ // Loop through arguments
+ foreach (PHPExcel_Calculation_Functions::flattenArray(func_get_args()) as $arg) {
+ // Is it a numeric value?
+ if ((is_numeric($arg)) && (!is_string($arg))) {
+ $returnValue += ($arg * $arg);
+ }
+ }
+
+ return $returnValue;
+ }
+
+
+ /**
+ * SUMX2MY2
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ * @return float
+ */
+ public static function SUMX2MY2($matrixData1, $matrixData2)
+ {
+ $array1 = PHPExcel_Calculation_Functions::flattenArray($matrixData1);
+ $array2 = PHPExcel_Calculation_Functions::flattenArray($matrixData2);
+ $count = min(count($array1), count($array2));
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
+ ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
+ $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
+ }
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * SUMX2PY2
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ * @return float
+ */
+ public static function SUMX2PY2($matrixData1, $matrixData2)
+ {
+ $array1 = PHPExcel_Calculation_Functions::flattenArray($matrixData1);
+ $array2 = PHPExcel_Calculation_Functions::flattenArray($matrixData2);
+ $count = min(count($array1), count($array2));
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
+ ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
+ $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
+ }
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * SUMXMY2
+ *
+ * @param mixed[] $matrixData1 Matrix #1
+ * @param mixed[] $matrixData2 Matrix #2
+ * @return float
+ */
+ public static function SUMXMY2($matrixData1, $matrixData2)
+ {
+ $array1 = PHPExcel_Calculation_Functions::flattenArray($matrixData1);
+ $array2 = PHPExcel_Calculation_Functions::flattenArray($matrixData2);
+ $count = min(count($array1), count($array2));
+
+ $result = 0;
+ for ($i = 0; $i < $count; ++$i) {
+ if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
+ ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
+ $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
+ }
+ }
+
+ return $result;
+ }
+
+
+ /**
+ * TRUNC
+ *
+ * Truncates value to the number of fractional digits by number_digits.
+ *
+ * @param float $value
+ * @param int $digits
+ * @return float Truncated value
+ */
+ public static function TRUNC($value = 0, $digits = 0)
+ {
+ $value = PHPExcel_Calculation_Functions::flattenSingleValue($value);
+ $digits = PHPExcel_Calculation_Functions::flattenSingleValue($digits);
+
+ // Validate parameters
+ if ((!is_numeric($value)) || (!is_numeric($digits))) {
+ return PHPExcel_Calculation_Functions::VALUE();
+ }
+ $digits = floor($digits);
+
+ // Truncate
+ $adjust = pow(10, $digits);
+
+ if (($digits > 0) && (rtrim(intval((abs($value) - abs(intval($value))) * $adjust), '0') < $adjust/10)) {
+ return $value;
+ }
+
+ return (intval($value * $adjust)) / $adjust;
+ }
+}
diff --git a/PHPExcel/Calculation/Statistical.php b/PHPExcel/Calculation/Statistical.php
new file mode 100644
index 0000000..1a33610
--- /dev/null
+++ b/PHPExcel/Calculation/Statistical.php
@@ -0,0 +1,3745 @@
+ $value) {
+ if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
+ unset($array1[$key]);
+ unset($array2[$key]);
+ }
+ }
+ foreach ($array2 as $key => $value) {
+ if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
+ unset($array1[$key]);
+ unset($array2[$key]);
+ }
+ }
+ $array1 = array_merge($array1);
+ $array2 = array_merge($array2);
+
+ return true;
+ }
+
+
+ /**
+ * Beta function.
+ *
+ * @author Jaco van Kooten
+ *
+ * @param p require p>0
+ * @param q require q>0
+ * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
+ */
+ private static function beta($p, $q)
+ {
+ if ($p <= 0.0 || $q <= 0.0 || ($p + $q) > LOG_GAMMA_X_MAX_VALUE) {
+ return 0.0;
+ } else {
+ return exp(self::logBeta($p, $q));
+ }
+ }
+
+
+ /**
+ * Incomplete beta function
+ *
+ * @author Jaco van Kooten
+ * @author Paul Meagher
+ *
+ * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
+ * @param x require 0<=x<=1
+ * @param p require p>0
+ * @param q require q>0
+ * @return 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
+ */
+ private static function incompleteBeta($x, $p, $q)
+ {
+ if ($x <= 0.0) {
+ return 0.0;
+ } elseif ($x >= 1.0) {
+ return 1.0;
+ } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
+ return 0.0;
+ }
+ $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
+ if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
+ return $beta_gam * self::betaFraction($x, $p, $q) / $p;
+ } else {
+ return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
+ }
+ }
+
+
+ // Function cache for logBeta function
+ private static $logBetaCacheP = 0.0;
+ private static $logBetaCacheQ = 0.0;
+ private static $logBetaCacheResult = 0.0;
+
+ /**
+ * The natural logarithm of the beta function.
+ *
+ * @param p require p>0
+ * @param q require q>0
+ * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
+ * @author Jaco van Kooten
+ */
+ private static function logBeta($p, $q)
+ {
+ if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
+ self::$logBetaCacheP = $p;
+ self::$logBetaCacheQ = $q;
+ if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
+ self::$logBetaCacheResult = 0.0;
+ } else {
+ self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
+ }
+ }
+ return self::$logBetaCacheResult;
+ }
+
+
+ /**
+ * Evaluates of continued fraction part of incomplete beta function.
+ * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
+ * @author Jaco van Kooten
+ */
+ private static function betaFraction($x, $p, $q)
+ {
+ $c = 1.0;
+ $sum_pq = $p + $q;
+ $p_plus = $p + 1.0;
+ $p_minus = $p - 1.0;
+ $h = 1.0 - $sum_pq * $x / $p_plus;
+ if (abs($h) < XMININ) {
+ $h = XMININ;
+ }
+ $h = 1.0 / $h;
+ $frac = $h;
+ $m = 1;
+ $delta = 0.0;
+ while ($m <= MAX_ITERATIONS && abs($delta-1.0) > PRECISION) {
+ $m2 = 2 * $m;
+ // even index for d
+ $d = $m * ($q - $m) * $x / ( ($p_minus + $m2) * ($p + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < XMININ) {
+ $h = XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < XMININ) {
+ $c = XMININ;
+ }
+ $frac *= $h * $c;
+ // odd index for d
+ $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
+ $h = 1.0 + $d * $h;
+ if (abs($h) < XMININ) {
+ $h = XMININ;
+ }
+ $h = 1.0 / $h;
+ $c = 1.0 + $d / $c;
+ if (abs($c) < XMININ) {
+ $c = XMININ;
+ }
+ $delta = $h * $c;
+ $frac *= $delta;
+ ++$m;
+ }
+ return $frac;
+ }
+
+
+ /**
+ * logGamma function
+ *
+ * @version 1.1
+ * @author Jaco van Kooten
+ *
+ * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
+ *
+ * The natural logarithm of the gamma function.
+ * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
+ * Applied Mathematics Division
+ * Argonne National Laboratory
+ * Argonne, IL 60439
+ *
+ *
+ *
+ * From the original documentation: + *
+ *+ * This routine calculates the LOG(GAMMA) function for a positive real argument X. + * Computation is based on an algorithm outlined in references 1 and 2. + * The program uses rational functions that theoretically approximate LOG(GAMMA) + * to at least 18 significant decimal digits. The approximation for X > 12 is from + * reference 3, while approximations for X < 12.0 are similar to those in reference + * 1, but are unpublished. The accuracy achieved depends on the arithmetic system, + * the compiler, the intrinsic functions, and proper selection of the + * machine-dependent constants. + *
+ *
+ * Error returns:
+ * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
+ * The computation is believed to be free of underflow and overflow.
+ *
'; +// echo htmlentities($gFileData,ENT_QUOTES,'UTF-8'); +// echo '
'; +// print_r($namespacesMeta); +// echo '
'; +// print_r($namespacesContent); +// echo '
= -1; --$k) {
+ if ($k == -1) {
+ break;
+ }
+ if (abs($e[$k]) <= $eps * (abs($this->s[$k]) + abs($this->s[$k+1]))) {
+ $e[$k] = 0.0;
+ break;
+ }
+ }
+ if ($k == $p - 2) {
+ $kase = 4;
+ } else {
+ for ($ks = $p - 1; $ks >= $k; --$ks) {
+ if ($ks == $k) {
+ break;
+ }
+ $t = ($ks != $p ? abs($e[$ks]) : 0.) + ($ks != $k + 1 ? abs($e[$ks-1]) : 0.);
+ if (abs($this->s[$ks]) <= $eps * $t) {
+ $this->s[$ks] = 0.0;
+ break;
+ }
+ }
+ if ($ks == $k) {
+ $kase = 3;
+ } elseif ($ks == $p-1) {
+ $kase = 1;
+ } else {
+ $kase = 2;
+ $k = $ks;
+ }
+ }
+ ++$k;
+
+ // Perform the task indicated by kase.
+ switch ($kase) {
+ // Deflate negligible s(p).
+ case 1:
+ $f = $e[$p-2];
+ $e[$p-2] = 0.0;
+ for ($j = $p - 2; $j >= $k; --$j) {
+ $t = hypo($this->s[$j], $f);
+ $cs = $this->s[$j] / $t;
+ $sn = $f / $t;
+ $this->s[$j] = $t;
+ if ($j != $k) {
+ $f = -$sn * $e[$j-1];
+ $e[$j-1] = $cs * $e[$j-1];
+ }
+ if ($wantv) {
+ for ($i = 0; $i < $this->n; ++$i) {
+ $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$p-1];
+ $this->V[$i][$p-1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$p-1];
+ $this->V[$i][$j] = $t;
+ }
+ }
+ }
+ break;
+ // Split at negligible s(k).
+ case 2:
+ $f = $e[$k-1];
+ $e[$k-1] = 0.0;
+ for ($j = $k; $j < $p; ++$j) {
+ $t = hypo($this->s[$j], $f);
+ $cs = $this->s[$j] / $t;
+ $sn = $f / $t;
+ $this->s[$j] = $t;
+ $f = -$sn * $e[$j];
+ $e[$j] = $cs * $e[$j];
+ if ($wantu) {
+ for ($i = 0; $i < $this->m; ++$i) {
+ $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$k-1];
+ $this->U[$i][$k-1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$k-1];
+ $this->U[$i][$j] = $t;
+ }
+ }
+ }
+ break;
+ // Perform one qr step.
+ case 3:
+ // Calculate the shift.
+ $scale = max(max(max(max(abs($this->s[$p-1]), abs($this->s[$p-2])), abs($e[$p-2])), abs($this->s[$k])), abs($e[$k]));
+ $sp = $this->s[$p-1] / $scale;
+ $spm1 = $this->s[$p-2] / $scale;
+ $epm1 = $e[$p-2] / $scale;
+ $sk = $this->s[$k] / $scale;
+ $ek = $e[$k] / $scale;
+ $b = (($spm1 + $sp) * ($spm1 - $sp) + $epm1 * $epm1) / 2.0;
+ $c = ($sp * $epm1) * ($sp * $epm1);
+ $shift = 0.0;
+ if (($b != 0.0) || ($c != 0.0)) {
+ $shift = sqrt($b * $b + $c);
+ if ($b < 0.0) {
+ $shift = -$shift;
+ }
+ $shift = $c / ($b + $shift);
+ }
+ $f = ($sk + $sp) * ($sk - $sp) + $shift;
+ $g = $sk * $ek;
+ // Chase zeros.
+ for ($j = $k; $j < $p-1; ++$j) {
+ $t = hypo($f, $g);
+ $cs = $f/$t;
+ $sn = $g/$t;
+ if ($j != $k) {
+ $e[$j-1] = $t;
+ }
+ $f = $cs * $this->s[$j] + $sn * $e[$j];
+ $e[$j] = $cs * $e[$j] - $sn * $this->s[$j];
+ $g = $sn * $this->s[$j+1];
+ $this->s[$j+1] = $cs * $this->s[$j+1];
+ if ($wantv) {
+ for ($i = 0; $i < $this->n; ++$i) {
+ $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$j+1];
+ $this->V[$i][$j+1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$j+1];
+ $this->V[$i][$j] = $t;
+ }
+ }
+ $t = hypo($f, $g);
+ $cs = $f/$t;
+ $sn = $g/$t;
+ $this->s[$j] = $t;
+ $f = $cs * $e[$j] + $sn * $this->s[$j+1];
+ $this->s[$j+1] = -$sn * $e[$j] + $cs * $this->s[$j+1];
+ $g = $sn * $e[$j+1];
+ $e[$j+1] = $cs * $e[$j+1];
+ if ($wantu && ($j < $this->m - 1)) {
+ for ($i = 0; $i < $this->m; ++$i) {
+ $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$j+1];
+ $this->U[$i][$j+1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$j+1];
+ $this->U[$i][$j] = $t;
+ }
+ }
+ }
+ $e[$p-2] = $f;
+ $iter = $iter + 1;
+ break;
+ // Convergence.
+ case 4:
+ // Make the singular values positive.
+ if ($this->s[$k] <= 0.0) {
+ $this->s[$k] = ($this->s[$k] < 0.0 ? -$this->s[$k] : 0.0);
+ if ($wantv) {
+ for ($i = 0; $i <= $pp; ++$i) {
+ $this->V[$i][$k] = -$this->V[$i][$k];
+ }
+ }
+ }
+ // Order the singular values.
+ while ($k < $pp) {
+ if ($this->s[$k] >= $this->s[$k+1]) {
+ break;
+ }
+ $t = $this->s[$k];
+ $this->s[$k] = $this->s[$k+1];
+ $this->s[$k+1] = $t;
+ if ($wantv and ($k < $this->n - 1)) {
+ for ($i = 0; $i < $this->n; ++$i) {
+ $t = $this->V[$i][$k+1];
+ $this->V[$i][$k+1] = $this->V[$i][$k];
+ $this->V[$i][$k] = $t;
+ }
+ }
+ if ($wantu and ($k < $this->m-1)) {
+ for ($i = 0; $i < $this->m; ++$i) {
+ $t = $this->U[$i][$k+1];
+ $this->U[$i][$k+1] = $this->U[$i][$k];
+ $this->U[$i][$k] = $t;
+ }
+ }
+ ++$k;
+ }
+ $iter = 0;
+ --$p;
+ break;
+ } // end switch
+ } // end while
+
+ } // end constructor
+
+
+ /**
+ * Return the left singular vectors
+ *
+ * @access public
+ * @return U
+ */
+ public function getU()
+ {
+ return new Matrix($this->U, $this->m, min($this->m + 1, $this->n));
+ }
+
+
+ /**
+ * Return the right singular vectors
+ *
+ * @access public
+ * @return V
+ */
+ public function getV()
+ {
+ return new Matrix($this->V);
+ }
+
+
+ /**
+ * Return the one-dimensional array of singular values
+ *
+ * @access public
+ * @return diagonal of S.
+ */
+ public function getSingularValues()
+ {
+ return $this->s;
+ }
+
+
+ /**
+ * Return the diagonal matrix of singular values
+ *
+ * @access public
+ * @return S
+ */
+ public function getS()
+ {
+ for ($i = 0; $i < $this->n; ++$i) {
+ for ($j = 0; $j < $this->n; ++$j) {
+ $S[$i][$j] = 0.0;
+ }
+ $S[$i][$i] = $this->s[$i];
+ }
+ return new Matrix($S);
+ }
+
+
+ /**
+ * Two norm
+ *
+ * @access public
+ * @return max(S)
+ */
+ public function norm2()
+ {
+ return $this->s[0];
+ }
+
+
+ /**
+ * Two norm condition number
+ *
+ * @access public
+ * @return max(S)/min(S)
+ */
+ public function cond()
+ {
+ return $this->s[0] / $this->s[min($this->m, $this->n) - 1];
+ }
+
+
+ /**
+ * Effective numerical matrix rank
+ *
+ * @access public
+ * @return Number of nonnegligible singular values.
+ */
+ public function rank()
+ {
+ $eps = pow(2.0, -52.0);
+ $tol = max($this->m, $this->n) * $this->s[0] * $eps;
+ $r = 0;
+ for ($i = 0; $i < count($this->s); ++$i) {
+ if ($this->s[$i] > $tol) {
+ ++$r;
+ }
+ }
+ return $r;
+ }
+}
diff --git a/PHPExcel/Shared/JAMA/utils/Error.php b/PHPExcel/Shared/JAMA/utils/Error.php
new file mode 100644
index 0000000..71b8c99
--- /dev/null
+++ b/PHPExcel/Shared/JAMA/utils/Error.php
@@ -0,0 +1,83 @@
+ abs($b)) {
+ $r = $b / $a;
+ $r = abs($a) * sqrt(1 + $r * $r);
+ } elseif ($b != 0) {
+ $r = $a / $b;
+ $r = abs($b) * sqrt(1 + $r * $r);
+ } else {
+ $r = 0.0;
+ }
+ return $r;
+} // function hypo()
+
+
+/**
+ * Mike Bommarito's version.
+ * Compute n-dimensional hyotheneuse.
+ *
+function hypot() {
+ $s = 0;
+ foreach (func_get_args() as $d) {
+ if (is_numeric($d)) {
+ $s += pow($d, 2);
+ } else {
+ throw new PHPExcel_Calculation_Exception(JAMAError(ARGUMENT_TYPE_EXCEPTION));
+ }
+ }
+ return sqrt($s);
+}
+*/
diff --git a/PHPExcel/Shared/OLE.php b/PHPExcel/Shared/OLE.php
new file mode 100644
index 0000000..42a3c52
--- /dev/null
+++ b/PHPExcel/Shared/OLE.php
@@ -0,0 +1,526 @@
+ |
+// | Based on OLE::Storage_Lite by Kawai, Takanori |
+// +----------------------------------------------------------------------+
+//
+// $Id: OLE.php,v 1.13 2007/03/07 14:38:25 schmidt Exp $
+
+
+/**
+* Array for storing OLE instances that are accessed from
+* OLE_ChainedBlockStream::stream_open().
+* @var array
+*/
+$GLOBALS['_OLE_INSTANCES'] = array();
+
+/**
+* OLE package base class.
+*
+* @author Xavier Noguer
';
+ $this->summaryInformation = count($this->props) - 1;
+ }
+
+ // Additional Document Summary information
+ if ($name == chr(5) . 'DocumentSummaryInformation') {
+// echo 'Document Summary Information
';
+ $this->documentSummaryInformation = count($this->props) - 1;
+ }
+
+ $offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
+ }
+ }
+
+ /**
+ * Read 4 bytes of data at specified position
+ *
+ * @param string $data
+ * @param int $pos
+ * @return int
+ */
+ private static function getInt4d($data, $pos)
+ {
+ // FIX: represent numbers correctly on 64-bit system
+ // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
+ // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
+ $_or_24 = ord($data[$pos + 3]);
+ if ($_or_24 >= 128) {
+ // negative number
+ $_ord_24 = -abs((256 - $_or_24) << 24);
+ } else {
+ $_ord_24 = ($_or_24 & 127) << 24;
+ }
+ return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
+ }
+}
diff --git a/PHPExcel/Shared/PCLZip/gnu-lgpl.txt b/PHPExcel/Shared/PCLZip/gnu-lgpl.txt
new file mode 100644
index 0000000..b1e3f5a
--- /dev/null
+++ b/PHPExcel/Shared/PCLZip/gnu-lgpl.txt
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->applyFromArray(
+ * array(
+ * 'font' => array(
+ * 'name' => 'Arial',
+ * 'bold' => true,
+ * 'italic' => false,
+ * 'underline' => PHPExcel_Style_Font::UNDERLINE_DOUBLE,
+ * 'strike' => false,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * ),
+ * 'borders' => array(
+ * 'bottom' => array(
+ * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * ),
+ * 'top' => array(
+ * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * )
+ * ),
+ * 'quotePrefix' => true
+ * )
+ * );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @param boolean $pAdvanced Advanced mode for setting borders.
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style
+ */
+ public function applyFromArray($pStyles = null, $pAdvanced = true)
+ {
+ if (is_array($pStyles)) {
+ if ($this->isSupervisor) {
+ $pRange = $this->getSelectedCells();
+
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ // Is it a cell range or a single cell?
+ if (strpos($pRange, ':') === false) {
+ $rangeA = $pRange;
+ $rangeB = $pRange;
+ } else {
+ list($rangeA, $rangeB) = explode(':', $pRange);
+ }
+
+ // Calculate range outer borders
+ $rangeStart = PHPExcel_Cell::coordinateFromString($rangeA);
+ $rangeEnd = PHPExcel_Cell::coordinateFromString($rangeB);
+
+ // Translate column into index
+ $rangeStart[0] = PHPExcel_Cell::columnIndexFromString($rangeStart[0]) - 1;
+ $rangeEnd[0] = PHPExcel_Cell::columnIndexFromString($rangeEnd[0]) - 1;
+
+ // Make sure we can loop upwards on rows and columns
+ if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
+ $tmp = $rangeStart;
+ $rangeStart = $rangeEnd;
+ $rangeEnd = $tmp;
+ }
+
+ // ADVANCED MODE:
+ if ($pAdvanced && isset($pStyles['borders'])) {
+ // 'allborders' is a shorthand property for 'outline' and 'inside' and
+ // it applies to components that have not been set explicitly
+ if (isset($pStyles['borders']['allborders'])) {
+ foreach (array('outline', 'inside') as $component) {
+ if (!isset($pStyles['borders'][$component])) {
+ $pStyles['borders'][$component] = $pStyles['borders']['allborders'];
+ }
+ }
+ unset($pStyles['borders']['allborders']); // not needed any more
+ }
+ // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
+ // it applies to components that have not been set explicitly
+ if (isset($pStyles['borders']['outline'])) {
+ foreach (array('top', 'right', 'bottom', 'left') as $component) {
+ if (!isset($pStyles['borders'][$component])) {
+ $pStyles['borders'][$component] = $pStyles['borders']['outline'];
+ }
+ }
+ unset($pStyles['borders']['outline']); // not needed any more
+ }
+ // 'inside' is a shorthand property for 'vertical' and 'horizontal'
+ // it applies to components that have not been set explicitly
+ if (isset($pStyles['borders']['inside'])) {
+ foreach (array('vertical', 'horizontal') as $component) {
+ if (!isset($pStyles['borders'][$component])) {
+ $pStyles['borders'][$component] = $pStyles['borders']['inside'];
+ }
+ }
+ unset($pStyles['borders']['inside']); // not needed any more
+ }
+ // width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
+ $xMax = min($rangeEnd[0] - $rangeStart[0] + 1, 3);
+ $yMax = min($rangeEnd[1] - $rangeStart[1] + 1, 3);
+
+ // loop through up to 3 x 3 = 9 regions
+ for ($x = 1; $x <= $xMax; ++$x) {
+ // start column index for region
+ $colStart = ($x == 3) ?
+ PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0])
+ : PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] + $x - 1);
+ // end column index for region
+ $colEnd = ($x == 1) ?
+ PHPExcel_Cell::stringFromColumnIndex($rangeStart[0])
+ : PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0] - $xMax + $x);
+
+ for ($y = 1; $y <= $yMax; ++$y) {
+ // which edges are touching the region
+ $edges = array();
+ if ($x == 1) {
+ // are we at left edge
+ $edges[] = 'left';
+ }
+ if ($x == $xMax) {
+ // are we at right edge
+ $edges[] = 'right';
+ }
+ if ($y == 1) {
+ // are we at top edge?
+ $edges[] = 'top';
+ }
+ if ($y == $yMax) {
+ // are we at bottom edge?
+ $edges[] = 'bottom';
+ }
+
+ // start row index for region
+ $rowStart = ($y == 3) ?
+ $rangeEnd[1] : $rangeStart[1] + $y - 1;
+
+ // end row index for region
+ $rowEnd = ($y == 1) ?
+ $rangeStart[1] : $rangeEnd[1] - $yMax + $y;
+
+ // build range for region
+ $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
+
+ // retrieve relevant style array for region
+ $regionStyles = $pStyles;
+ unset($regionStyles['borders']['inside']);
+
+ // what are the inner edges of the region when looking at the selection
+ $innerEdges = array_diff(array('top', 'right', 'bottom', 'left'), $edges);
+
+ // inner edges that are not touching the region should take the 'inside' border properties if they have been set
+ foreach ($innerEdges as $innerEdge) {
+ switch ($innerEdge) {
+ case 'top':
+ case 'bottom':
+ // should pick up 'horizontal' border property if set
+ if (isset($pStyles['borders']['horizontal'])) {
+ $regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal'];
+ } else {
+ unset($regionStyles['borders'][$innerEdge]);
+ }
+ break;
+ case 'left':
+ case 'right':
+ // should pick up 'vertical' border property if set
+ if (isset($pStyles['borders']['vertical'])) {
+ $regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical'];
+ } else {
+ unset($regionStyles['borders'][$innerEdge]);
+ }
+ break;
+ }
+ }
+
+ // apply region style to region by calling applyFromArray() in simple mode
+ $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
+ }
+ }
+ return $this;
+ }
+
+ // SIMPLE MODE:
+ // Selection type, inspect
+ if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
+ $selectionType = 'COLUMN';
+ } elseif (preg_match('/^A[0-9]+:XFD[0-9]+$/', $pRange)) {
+ $selectionType = 'ROW';
+ } else {
+ $selectionType = 'CELL';
+ }
+
+ // First loop through columns, rows, or cells to find out which styles are affected by this operation
+ switch ($selectionType) {
+ case 'COLUMN':
+ $oldXfIndexes = array();
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
+ }
+ break;
+ case 'ROW':
+ $oldXfIndexes = array();
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) {
+ $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
+ } else {
+ $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
+ }
+ }
+ break;
+ case 'CELL':
+ $oldXfIndexes = array();
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true;
+ }
+ }
+ break;
+ }
+
+ // clone each of the affected styles, apply the style array, and add the new styles to the workbook
+ $workbook = $this->getActiveSheet()->getParent();
+ foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
+ $style = $workbook->getCellXfByIndex($oldXfIndex);
+ $newStyle = clone $style;
+ $newStyle->applyFromArray($pStyles);
+
+ if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
+ // there is already such cell Xf in our collection
+ $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
+ } else {
+ // we don't have such a cell Xf, need to add
+ $workbook->addCellXf($newStyle);
+ $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
+ }
+ }
+
+ // Loop through columns, rows, or cells again and update the XF index
+ switch ($selectionType) {
+ case 'COLUMN':
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
+ $oldXfIndex = $columnDimension->getXfIndex();
+ $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
+ }
+ break;
+
+ case 'ROW':
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $rowDimension = $this->getActiveSheet()->getRowDimension($row);
+ $oldXfIndex = $rowDimension->getXfIndex() === null ?
+ 0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style
+ $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
+ }
+ break;
+
+ case 'CELL':
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row);
+ $oldXfIndex = $cell->getXfIndex();
+ $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
+ }
+ }
+ break;
+ }
+
+ } else {
+ // not a supervisor, just apply the style array directly on style object
+ if (array_key_exists('fill', $pStyles)) {
+ $this->getFill()->applyFromArray($pStyles['fill']);
+ }
+ if (array_key_exists('font', $pStyles)) {
+ $this->getFont()->applyFromArray($pStyles['font']);
+ }
+ if (array_key_exists('borders', $pStyles)) {
+ $this->getBorders()->applyFromArray($pStyles['borders']);
+ }
+ if (array_key_exists('alignment', $pStyles)) {
+ $this->getAlignment()->applyFromArray($pStyles['alignment']);
+ }
+ if (array_key_exists('numberformat', $pStyles)) {
+ $this->getNumberFormat()->applyFromArray($pStyles['numberformat']);
+ }
+ if (array_key_exists('protection', $pStyles)) {
+ $this->getProtection()->applyFromArray($pStyles['protection']);
+ }
+ if (array_key_exists('quotePrefix', $pStyles)) {
+ $this->quotePrefix = $pStyles['quotePrefix'];
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get Fill
+ *
+ * @return PHPExcel_Style_Fill
+ */
+ public function getFill()
+ {
+ return $this->fill;
+ }
+
+ /**
+ * Get Font
+ *
+ * @return PHPExcel_Style_Font
+ */
+ public function getFont()
+ {
+ return $this->font;
+ }
+
+ /**
+ * Set font
+ *
+ * @param PHPExcel_Style_Font $font
+ * @return PHPExcel_Style
+ */
+ public function setFont(PHPExcel_Style_Font $font)
+ {
+ $this->font = $font;
+ return $this;
+ }
+
+ /**
+ * Get Borders
+ *
+ * @return PHPExcel_Style_Borders
+ */
+ public function getBorders()
+ {
+ return $this->borders;
+ }
+
+ /**
+ * Get Alignment
+ *
+ * @return PHPExcel_Style_Alignment
+ */
+ public function getAlignment()
+ {
+ return $this->alignment;
+ }
+
+ /**
+ * Get Number Format
+ *
+ * @return PHPExcel_Style_NumberFormat
+ */
+ public function getNumberFormat()
+ {
+ return $this->numberFormat;
+ }
+
+ /**
+ * Get Conditional Styles. Only used on supervisor.
+ *
+ * @return PHPExcel_Style_Conditional[]
+ */
+ public function getConditionalStyles()
+ {
+ return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
+ }
+
+ /**
+ * Set Conditional Styles. Only used on supervisor.
+ *
+ * @param PHPExcel_Style_Conditional[] $pValue Array of condtional styles
+ * @return PHPExcel_Style
+ */
+ public function setConditionalStyles($pValue = null)
+ {
+ if (is_array($pValue)) {
+ $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue);
+ }
+ return $this;
+ }
+
+ /**
+ * Get Protection
+ *
+ * @return PHPExcel_Style_Protection
+ */
+ public function getProtection()
+ {
+ return $this->protection;
+ }
+
+ /**
+ * Get quote prefix
+ *
+ * @return boolean
+ */
+ public function getQuotePrefix()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getQuotePrefix();
+ }
+ return $this->quotePrefix;
+ }
+
+ /**
+ * Set quote prefix
+ *
+ * @param boolean $pValue
+ */
+ public function setQuotePrefix($pValue)
+ {
+ if ($pValue == '') {
+ $pValue = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = array('quotePrefix' => $pValue);
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->quotePrefix = (boolean) $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ $hashConditionals = '';
+ foreach ($this->conditionalStyles as $conditional) {
+ $hashConditionals .= $conditional->getHashCode();
+ }
+
+ return md5(
+ $this->fill->getHashCode() .
+ $this->font->getHashCode() .
+ $this->borders->getHashCode() .
+ $this->alignment->getHashCode() .
+ $this->numberFormat->getHashCode() .
+ $hashConditionals .
+ $this->protection->getHashCode() .
+ ($this->quotePrefix ? 't' : 'f') .
+ __CLASS__
+ );
+ }
+
+ /**
+ * Get own index in style collection
+ *
+ * @return int
+ */
+ public function getIndex()
+ {
+ return $this->index;
+ }
+
+ /**
+ * Set own index in style collection
+ *
+ * @param int $pValue
+ */
+ public function setIndex($pValue)
+ {
+ $this->index = $pValue;
+ }
+}
diff --git a/PHPExcel/Style/Alignment.php b/PHPExcel/Style/Alignment.php
new file mode 100644
index 0000000..bfc4947
--- /dev/null
+++ b/PHPExcel/Style/Alignment.php
@@ -0,0 +1,464 @@
+horizontal = null;
+ $this->vertical = null;
+ $this->textRotation = null;
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor
+ *
+ * @return PHPExcel_Style_Alignment
+ */
+ public function getSharedComponent()
+ {
+ return $this->parent->getSharedComponent()->getAlignment();
+ }
+
+ /**
+ * Build style array from subcomponents
+ *
+ * @param array $array
+ * @return array
+ */
+ public function getStyleArray($array)
+ {
+ return array('alignment' => $array);
+ }
+
+ /**
+ * Apply styles from array
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->getAlignment()->applyFromArray(
+ * array(
+ * 'horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
+ * 'vertical' => PHPExcel_Style_Alignment::VERTICAL_CENTER,
+ * 'rotation' => 0,
+ * 'wrap' => TRUE
+ * )
+ * );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Alignment
+ */
+ public function applyFromArray($pStyles = null)
+ {
+ if (is_array($pStyles)) {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())
+ ->applyFromArray($this->getStyleArray($pStyles));
+ } else {
+ if (isset($pStyles['horizontal'])) {
+ $this->setHorizontal($pStyles['horizontal']);
+ }
+ if (isset($pStyles['vertical'])) {
+ $this->setVertical($pStyles['vertical']);
+ }
+ if (isset($pStyles['rotation'])) {
+ $this->setTextRotation($pStyles['rotation']);
+ }
+ if (isset($pStyles['wrap'])) {
+ $this->setWrapText($pStyles['wrap']);
+ }
+ if (isset($pStyles['shrinkToFit'])) {
+ $this->setShrinkToFit($pStyles['shrinkToFit']);
+ }
+ if (isset($pStyles['indent'])) {
+ $this->setIndent($pStyles['indent']);
+ }
+ if (isset($pStyles['readorder'])) {
+ $this->setReadorder($pStyles['readorder']);
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get Horizontal
+ *
+ * @return string
+ */
+ public function getHorizontal()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHorizontal();
+ }
+ return $this->horizontal;
+ }
+
+ /**
+ * Set Horizontal
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style_Alignment
+ */
+ public function setHorizontal($pValue = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL)
+ {
+ if ($pValue == '') {
+ $pValue = PHPExcel_Style_Alignment::HORIZONTAL_GENERAL;
+ }
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('horizontal' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->horizontal = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Vertical
+ *
+ * @return string
+ */
+ public function getVertical()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getVertical();
+ }
+ return $this->vertical;
+ }
+
+ /**
+ * Set Vertical
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style_Alignment
+ */
+ public function setVertical($pValue = PHPExcel_Style_Alignment::VERTICAL_BOTTOM)
+ {
+ if ($pValue == '') {
+ $pValue = PHPExcel_Style_Alignment::VERTICAL_BOTTOM;
+ }
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('vertical' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->vertical = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get TextRotation
+ *
+ * @return int
+ */
+ public function getTextRotation()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getTextRotation();
+ }
+ return $this->textRotation;
+ }
+
+ /**
+ * Set TextRotation
+ *
+ * @param int $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Alignment
+ */
+ public function setTextRotation($pValue = 0)
+ {
+ // Excel2007 value 255 => PHPExcel value -165
+ if ($pValue == 255) {
+ $pValue = -165;
+ }
+
+ // Set rotation
+ if (($pValue >= -90 && $pValue <= 90) || $pValue == -165) {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('rotation' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->textRotation = $pValue;
+ }
+ } else {
+ throw new PHPExcel_Exception("Text rotation should be a value between -90 and 90.");
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get Wrap Text
+ *
+ * @return boolean
+ */
+ public function getWrapText()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getWrapText();
+ }
+ return $this->wrapText;
+ }
+
+ /**
+ * Set Wrap Text
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Style_Alignment
+ */
+ public function setWrapText($pValue = false)
+ {
+ if ($pValue == '') {
+ $pValue = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('wrap' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->wrapText = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Shrink to fit
+ *
+ * @return boolean
+ */
+ public function getShrinkToFit()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getShrinkToFit();
+ }
+ return $this->shrinkToFit;
+ }
+
+ /**
+ * Set Shrink to fit
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Style_Alignment
+ */
+ public function setShrinkToFit($pValue = false)
+ {
+ if ($pValue == '') {
+ $pValue = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('shrinkToFit' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->shrinkToFit = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get indent
+ *
+ * @return int
+ */
+ public function getIndent()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getIndent();
+ }
+ return $this->indent;
+ }
+
+ /**
+ * Set indent
+ *
+ * @param int $pValue
+ * @return PHPExcel_Style_Alignment
+ */
+ public function setIndent($pValue = 0)
+ {
+ if ($pValue > 0) {
+ if ($this->getHorizontal() != self::HORIZONTAL_GENERAL &&
+ $this->getHorizontal() != self::HORIZONTAL_LEFT &&
+ $this->getHorizontal() != self::HORIZONTAL_RIGHT) {
+ $pValue = 0; // indent not supported
+ }
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('indent' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->indent = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get read order
+ *
+ * @return integer
+ */
+ public function getReadorder()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getReadorder();
+ }
+ return $this->readorder;
+ }
+
+ /**
+ * Set read order
+ *
+ * @param int $pValue
+ * @return PHPExcel_Style_Alignment
+ */
+ public function setReadorder($pValue = 0)
+ {
+ if ($pValue < 0 || $pValue > 2) {
+ $pValue = 0;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('readorder' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->readorder = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+ return md5(
+ $this->horizontal .
+ $this->vertical .
+ $this->textRotation .
+ ($this->wrapText ? 't' : 'f') .
+ ($this->shrinkToFit ? 't' : 'f') .
+ $this->indent .
+ $this->readorder .
+ __CLASS__
+ );
+ }
+}
diff --git a/PHPExcel/Style/Border.php b/PHPExcel/Style/Border.php
new file mode 100644
index 0000000..55efc50
--- /dev/null
+++ b/PHPExcel/Style/Border.php
@@ -0,0 +1,282 @@
+color = new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_BLACK, $isSupervisor);
+
+ // bind parent if we are a supervisor
+ if ($isSupervisor) {
+ $this->color->bindParent($this, 'color');
+ }
+ }
+
+ /**
+ * Bind parent. Only used for supervisor
+ *
+ * @param PHPExcel_Style_Borders $parent
+ * @param string $parentPropertyName
+ * @return PHPExcel_Style_Border
+ */
+ public function bindParent($parent, $parentPropertyName = null)
+ {
+ $this->parent = $parent;
+ $this->parentPropertyName = $parentPropertyName;
+ return $this;
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor
+ *
+ * @return PHPExcel_Style_Border
+ * @throws PHPExcel_Exception
+ */
+ public function getSharedComponent()
+ {
+ switch ($this->parentPropertyName) {
+ case 'allBorders':
+ case 'horizontal':
+ case 'inside':
+ case 'outline':
+ case 'vertical':
+ throw new PHPExcel_Exception('Cannot get shared component for a pseudo-border.');
+ break;
+ case 'bottom':
+ return $this->parent->getSharedComponent()->getBottom();
+ case 'diagonal':
+ return $this->parent->getSharedComponent()->getDiagonal();
+ case 'left':
+ return $this->parent->getSharedComponent()->getLeft();
+ case 'right':
+ return $this->parent->getSharedComponent()->getRight();
+ case 'top':
+ return $this->parent->getSharedComponent()->getTop();
+ }
+ }
+
+ /**
+ * Build style array from subcomponents
+ *
+ * @param array $array
+ * @return array
+ */
+ public function getStyleArray($array)
+ {
+ switch ($this->parentPropertyName) {
+ case 'allBorders':
+ case 'bottom':
+ case 'diagonal':
+ case 'horizontal':
+ case 'inside':
+ case 'left':
+ case 'outline':
+ case 'right':
+ case 'top':
+ case 'vertical':
+ $key = strtolower('vertical');
+ break;
+ }
+ return $this->parent->getStyleArray(array($key => $array));
+ }
+
+ /**
+ * Apply styles from array
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->getBorders()->getTop()->applyFromArray(
+ * array(
+ * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * )
+ * );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Border
+ */
+ public function applyFromArray($pStyles = null)
+ {
+ if (is_array($pStyles)) {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+ } else {
+ if (isset($pStyles['style'])) {
+ $this->setBorderStyle($pStyles['style']);
+ }
+ if (isset($pStyles['color'])) {
+ $this->getColor()->applyFromArray($pStyles['color']);
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get Border style
+ *
+ * @return string
+ */
+ public function getBorderStyle()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getBorderStyle();
+ }
+ return $this->borderStyle;
+ }
+
+ /**
+ * Set Border style
+ *
+ * @param string|boolean $pValue
+ * When passing a boolean, FALSE equates PHPExcel_Style_Border::BORDER_NONE
+ * and TRUE to PHPExcel_Style_Border::BORDER_MEDIUM
+ * @return PHPExcel_Style_Border
+ */
+ public function setBorderStyle($pValue = PHPExcel_Style_Border::BORDER_NONE)
+ {
+
+ if (empty($pValue)) {
+ $pValue = PHPExcel_Style_Border::BORDER_NONE;
+ } elseif (is_bool($pValue) && $pValue) {
+ $pValue = PHPExcel_Style_Border::BORDER_MEDIUM;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('style' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->borderStyle = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Border Color
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getColor()
+ {
+ return $this->color;
+ }
+
+ /**
+ * Set Border Color
+ *
+ * @param PHPExcel_Style_Color $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Border
+ */
+ public function setColor(PHPExcel_Style_Color $pValue = null)
+ {
+ // make sure parameter is a real color and not a supervisor
+ $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getColor()->getStyleArray(array('argb' => $color->getARGB()));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->color = $color;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+ return md5(
+ $this->borderStyle .
+ $this->color->getHashCode() .
+ __CLASS__
+ );
+ }
+}
diff --git a/PHPExcel/Style/Borders.php b/PHPExcel/Style/Borders.php
new file mode 100644
index 0000000..27c40a4
--- /dev/null
+++ b/PHPExcel/Style/Borders.php
@@ -0,0 +1,429 @@
+left = new PHPExcel_Style_Border($isSupervisor, $isConditional);
+ $this->right = new PHPExcel_Style_Border($isSupervisor, $isConditional);
+ $this->top = new PHPExcel_Style_Border($isSupervisor, $isConditional);
+ $this->bottom = new PHPExcel_Style_Border($isSupervisor, $isConditional);
+ $this->diagonal = new PHPExcel_Style_Border($isSupervisor, $isConditional);
+ $this->diagonalDirection = PHPExcel_Style_Borders::DIAGONAL_NONE;
+
+ // Specially for supervisor
+ if ($isSupervisor) {
+ // Initialize pseudo-borders
+ $this->allBorders = new PHPExcel_Style_Border(true);
+ $this->outline = new PHPExcel_Style_Border(true);
+ $this->inside = new PHPExcel_Style_Border(true);
+ $this->vertical = new PHPExcel_Style_Border(true);
+ $this->horizontal = new PHPExcel_Style_Border(true);
+
+ // bind parent if we are a supervisor
+ $this->left->bindParent($this, 'left');
+ $this->right->bindParent($this, 'right');
+ $this->top->bindParent($this, 'top');
+ $this->bottom->bindParent($this, 'bottom');
+ $this->diagonal->bindParent($this, 'diagonal');
+ $this->allBorders->bindParent($this, 'allBorders');
+ $this->outline->bindParent($this, 'outline');
+ $this->inside->bindParent($this, 'inside');
+ $this->vertical->bindParent($this, 'vertical');
+ $this->horizontal->bindParent($this, 'horizontal');
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor
+ *
+ * @return PHPExcel_Style_Borders
+ */
+ public function getSharedComponent()
+ {
+ return $this->parent->getSharedComponent()->getBorders();
+ }
+
+ /**
+ * Build style array from subcomponents
+ *
+ * @param array $array
+ * @return array
+ */
+ public function getStyleArray($array)
+ {
+ return array('borders' => $array);
+ }
+
+ /**
+ * Apply styles from array
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
+ * array(
+ * 'bottom' => array(
+ * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * ),
+ * 'top' => array(
+ * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * )
+ * )
+ * );
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->getBorders()->applyFromArray(
+ * array(
+ * 'allborders' => array(
+ * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * )
+ * )
+ * );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Borders
+ */
+ public function applyFromArray($pStyles = null)
+ {
+ if (is_array($pStyles)) {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+ } else {
+ if (array_key_exists('left', $pStyles)) {
+ $this->getLeft()->applyFromArray($pStyles['left']);
+ }
+ if (array_key_exists('right', $pStyles)) {
+ $this->getRight()->applyFromArray($pStyles['right']);
+ }
+ if (array_key_exists('top', $pStyles)) {
+ $this->getTop()->applyFromArray($pStyles['top']);
+ }
+ if (array_key_exists('bottom', $pStyles)) {
+ $this->getBottom()->applyFromArray($pStyles['bottom']);
+ }
+ if (array_key_exists('diagonal', $pStyles)) {
+ $this->getDiagonal()->applyFromArray($pStyles['diagonal']);
+ }
+ if (array_key_exists('diagonaldirection', $pStyles)) {
+ $this->setDiagonalDirection($pStyles['diagonaldirection']);
+ }
+ if (array_key_exists('allborders', $pStyles)) {
+ $this->getLeft()->applyFromArray($pStyles['allborders']);
+ $this->getRight()->applyFromArray($pStyles['allborders']);
+ $this->getTop()->applyFromArray($pStyles['allborders']);
+ $this->getBottom()->applyFromArray($pStyles['allborders']);
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get Left
+ *
+ * @return PHPExcel_Style_Border
+ */
+ public function getLeft()
+ {
+ return $this->left;
+ }
+
+ /**
+ * Get Right
+ *
+ * @return PHPExcel_Style_Border
+ */
+ public function getRight()
+ {
+ return $this->right;
+ }
+
+ /**
+ * Get Top
+ *
+ * @return PHPExcel_Style_Border
+ */
+ public function getTop()
+ {
+ return $this->top;
+ }
+
+ /**
+ * Get Bottom
+ *
+ * @return PHPExcel_Style_Border
+ */
+ public function getBottom()
+ {
+ return $this->bottom;
+ }
+
+ /**
+ * Get Diagonal
+ *
+ * @return PHPExcel_Style_Border
+ */
+ public function getDiagonal()
+ {
+ return $this->diagonal;
+ }
+
+ /**
+ * Get AllBorders (pseudo-border). Only applies to supervisor.
+ *
+ * @return PHPExcel_Style_Border
+ * @throws PHPExcel_Exception
+ */
+ public function getAllBorders()
+ {
+ if (!$this->isSupervisor) {
+ throw new PHPExcel_Exception('Can only get pseudo-border for supervisor.');
+ }
+ return $this->allBorders;
+ }
+
+ /**
+ * Get Outline (pseudo-border). Only applies to supervisor.
+ *
+ * @return boolean
+ * @throws PHPExcel_Exception
+ */
+ public function getOutline()
+ {
+ if (!$this->isSupervisor) {
+ throw new PHPExcel_Exception('Can only get pseudo-border for supervisor.');
+ }
+ return $this->outline;
+ }
+
+ /**
+ * Get Inside (pseudo-border). Only applies to supervisor.
+ *
+ * @return boolean
+ * @throws PHPExcel_Exception
+ */
+ public function getInside()
+ {
+ if (!$this->isSupervisor) {
+ throw new PHPExcel_Exception('Can only get pseudo-border for supervisor.');
+ }
+ return $this->inside;
+ }
+
+ /**
+ * Get Vertical (pseudo-border). Only applies to supervisor.
+ *
+ * @return PHPExcel_Style_Border
+ * @throws PHPExcel_Exception
+ */
+ public function getVertical()
+ {
+ if (!$this->isSupervisor) {
+ throw new PHPExcel_Exception('Can only get pseudo-border for supervisor.');
+ }
+ return $this->vertical;
+ }
+
+ /**
+ * Get Horizontal (pseudo-border). Only applies to supervisor.
+ *
+ * @return PHPExcel_Style_Border
+ * @throws PHPExcel_Exception
+ */
+ public function getHorizontal()
+ {
+ if (!$this->isSupervisor) {
+ throw new PHPExcel_Exception('Can only get pseudo-border for supervisor.');
+ }
+ return $this->horizontal;
+ }
+
+ /**
+ * Get DiagonalDirection
+ *
+ * @return int
+ */
+ public function getDiagonalDirection()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getDiagonalDirection();
+ }
+ return $this->diagonalDirection;
+ }
+
+ /**
+ * Set DiagonalDirection
+ *
+ * @param int $pValue
+ * @return PHPExcel_Style_Borders
+ */
+ public function setDiagonalDirection($pValue = PHPExcel_Style_Borders::DIAGONAL_NONE)
+ {
+ if ($pValue == '') {
+ $pValue = PHPExcel_Style_Borders::DIAGONAL_NONE;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('diagonaldirection' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->diagonalDirection = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashcode();
+ }
+ return md5(
+ $this->getLeft()->getHashCode() .
+ $this->getRight()->getHashCode() .
+ $this->getTop()->getHashCode() .
+ $this->getBottom()->getHashCode() .
+ $this->getDiagonal()->getHashCode() .
+ $this->getDiagonalDirection() .
+ __CLASS__
+ );
+ }
+}
diff --git a/PHPExcel/Style/Color.php b/PHPExcel/Style/Color.php
new file mode 100644
index 0000000..9c7790f
--- /dev/null
+++ b/PHPExcel/Style/Color.php
@@ -0,0 +1,443 @@
+argb = $pARGB;
+ }
+ }
+
+ /**
+ * Bind parent. Only used for supervisor
+ *
+ * @param mixed $parent
+ * @param string $parentPropertyName
+ * @return PHPExcel_Style_Color
+ */
+ public function bindParent($parent, $parentPropertyName = null)
+ {
+ $this->parent = $parent;
+ $this->parentPropertyName = $parentPropertyName;
+ return $this;
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getSharedComponent()
+ {
+ switch ($this->parentPropertyName) {
+ case 'endColor':
+ return $this->parent->getSharedComponent()->getEndColor();
+ case 'color':
+ return $this->parent->getSharedComponent()->getColor();
+ case 'startColor':
+ return $this->parent->getSharedComponent()->getStartColor();
+ }
+ }
+
+ /**
+ * Build style array from subcomponents
+ *
+ * @param array $array
+ * @return array
+ */
+ public function getStyleArray($array)
+ {
+ switch ($this->parentPropertyName) {
+ case 'endColor':
+ $key = 'endcolor';
+ break;
+ case 'color':
+ $key = 'color';
+ break;
+ case 'startColor':
+ $key = 'startcolor';
+ break;
+
+ }
+ return $this->parent->getStyleArray(array($key => $array));
+ }
+
+ /**
+ * Apply styles from array
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray( array('rgb' => '808080') );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Color
+ */
+ public function applyFromArray($pStyles = null)
+ {
+ if (is_array($pStyles)) {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+ } else {
+ if (array_key_exists('rgb', $pStyles)) {
+ $this->setRGB($pStyles['rgb']);
+ }
+ if (array_key_exists('argb', $pStyles)) {
+ $this->setARGB($pStyles['argb']);
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get ARGB
+ *
+ * @return string
+ */
+ public function getARGB()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getARGB();
+ }
+ return $this->argb;
+ }
+
+ /**
+ * Set ARGB
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style_Color
+ */
+ public function setARGB($pValue = PHPExcel_Style_Color::COLOR_BLACK)
+ {
+ if ($pValue == '') {
+ $pValue = PHPExcel_Style_Color::COLOR_BLACK;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('argb' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->argb = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get RGB
+ *
+ * @return string
+ */
+ public function getRGB()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getRGB();
+ }
+ return substr($this->argb, 2);
+ }
+
+ /**
+ * Set RGB
+ *
+ * @param string $pValue RGB value
+ * @return PHPExcel_Style_Color
+ */
+ public function setRGB($pValue = '000000')
+ {
+ if ($pValue == '') {
+ $pValue = '000000';
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('argb' => 'FF' . $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->argb = 'FF' . $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get a specified colour component of an RGB value
+ *
+ * @private
+ * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+ * @param int $offset Position within the RGB value to extract
+ * @param boolean $hex Flag indicating whether the component should be returned as a hex or a
+ * decimal value
+ * @return string The extracted colour component
+ */
+ private static function getColourComponent($RGB, $offset, $hex = true)
+ {
+ $colour = substr($RGB, $offset, 2);
+ if (!$hex) {
+ $colour = hexdec($colour);
+ }
+ return $colour;
+ }
+
+ /**
+ * Get the red colour component of an RGB value
+ *
+ * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+ * @param boolean $hex Flag indicating whether the component should be returned as a hex or a
+ * decimal value
+ * @return string The red colour component
+ */
+ public static function getRed($RGB, $hex = true)
+ {
+ return self::getColourComponent($RGB, strlen($RGB) - 6, $hex);
+ }
+
+ /**
+ * Get the green colour component of an RGB value
+ *
+ * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+ * @param boolean $hex Flag indicating whether the component should be returned as a hex or a
+ * decimal value
+ * @return string The green colour component
+ */
+ public static function getGreen($RGB, $hex = true)
+ {
+ return self::getColourComponent($RGB, strlen($RGB) - 4, $hex);
+ }
+
+ /**
+ * Get the blue colour component of an RGB value
+ *
+ * @param string $RGB The colour as an RGB value (e.g. FF00CCCC or CCDDEE
+ * @param boolean $hex Flag indicating whether the component should be returned as a hex or a
+ * decimal value
+ * @return string The blue colour component
+ */
+ public static function getBlue($RGB, $hex = true)
+ {
+ return self::getColourComponent($RGB, strlen($RGB) - 2, $hex);
+ }
+
+ /**
+ * Adjust the brightness of a color
+ *
+ * @param string $hex The colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
+ * @param float $adjustPercentage The percentage by which to adjust the colour as a float from -1 to 1
+ * @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
+ */
+ public static function changeBrightness($hex, $adjustPercentage)
+ {
+ $rgba = (strlen($hex) == 8);
+
+ $red = self::getRed($hex, false);
+ $green = self::getGreen($hex, false);
+ $blue = self::getBlue($hex, false);
+ if ($adjustPercentage > 0) {
+ $red += (255 - $red) * $adjustPercentage;
+ $green += (255 - $green) * $adjustPercentage;
+ $blue += (255 - $blue) * $adjustPercentage;
+ } else {
+ $red += $red * $adjustPercentage;
+ $green += $green * $adjustPercentage;
+ $blue += $blue * $adjustPercentage;
+ }
+
+ if ($red < 0) {
+ $red = 0;
+ } elseif ($red > 255) {
+ $red = 255;
+ }
+ if ($green < 0) {
+ $green = 0;
+ } elseif ($green > 255) {
+ $green = 255;
+ }
+ if ($blue < 0) {
+ $blue = 0;
+ } elseif ($blue > 255) {
+ $blue = 255;
+ }
+
+ $rgb = strtoupper(
+ str_pad(dechex($red), 2, '0', 0) .
+ str_pad(dechex($green), 2, '0', 0) .
+ str_pad(dechex($blue), 2, '0', 0)
+ );
+ return (($rgba) ? 'FF' : '') . $rgb;
+ }
+
+ /**
+ * Get indexed color
+ *
+ * @param int $pIndex Index entry point into the colour array
+ * @param boolean $background Flag to indicate whether default background or foreground colour
+ * should be returned if the indexed colour doesn't exist
+ * @return PHPExcel_Style_Color
+ */
+ public static function indexedColor($pIndex, $background = false)
+ {
+ // Clean parameter
+ $pIndex = intval($pIndex);
+
+ // Indexed colors
+ if (is_null(self::$indexedColors)) {
+ self::$indexedColors = array(
+ 1 => 'FF000000', // System Colour #1 - Black
+ 2 => 'FFFFFFFF', // System Colour #2 - White
+ 3 => 'FFFF0000', // System Colour #3 - Red
+ 4 => 'FF00FF00', // System Colour #4 - Green
+ 5 => 'FF0000FF', // System Colour #5 - Blue
+ 6 => 'FFFFFF00', // System Colour #6 - Yellow
+ 7 => 'FFFF00FF', // System Colour #7- Magenta
+ 8 => 'FF00FFFF', // System Colour #8- Cyan
+ 9 => 'FF800000', // Standard Colour #9
+ 10 => 'FF008000', // Standard Colour #10
+ 11 => 'FF000080', // Standard Colour #11
+ 12 => 'FF808000', // Standard Colour #12
+ 13 => 'FF800080', // Standard Colour #13
+ 14 => 'FF008080', // Standard Colour #14
+ 15 => 'FFC0C0C0', // Standard Colour #15
+ 16 => 'FF808080', // Standard Colour #16
+ 17 => 'FF9999FF', // Chart Fill Colour #17
+ 18 => 'FF993366', // Chart Fill Colour #18
+ 19 => 'FFFFFFCC', // Chart Fill Colour #19
+ 20 => 'FFCCFFFF', // Chart Fill Colour #20
+ 21 => 'FF660066', // Chart Fill Colour #21
+ 22 => 'FFFF8080', // Chart Fill Colour #22
+ 23 => 'FF0066CC', // Chart Fill Colour #23
+ 24 => 'FFCCCCFF', // Chart Fill Colour #24
+ 25 => 'FF000080', // Chart Line Colour #25
+ 26 => 'FFFF00FF', // Chart Line Colour #26
+ 27 => 'FFFFFF00', // Chart Line Colour #27
+ 28 => 'FF00FFFF', // Chart Line Colour #28
+ 29 => 'FF800080', // Chart Line Colour #29
+ 30 => 'FF800000', // Chart Line Colour #30
+ 31 => 'FF008080', // Chart Line Colour #31
+ 32 => 'FF0000FF', // Chart Line Colour #32
+ 33 => 'FF00CCFF', // Standard Colour #33
+ 34 => 'FFCCFFFF', // Standard Colour #34
+ 35 => 'FFCCFFCC', // Standard Colour #35
+ 36 => 'FFFFFF99', // Standard Colour #36
+ 37 => 'FF99CCFF', // Standard Colour #37
+ 38 => 'FFFF99CC', // Standard Colour #38
+ 39 => 'FFCC99FF', // Standard Colour #39
+ 40 => 'FFFFCC99', // Standard Colour #40
+ 41 => 'FF3366FF', // Standard Colour #41
+ 42 => 'FF33CCCC', // Standard Colour #42
+ 43 => 'FF99CC00', // Standard Colour #43
+ 44 => 'FFFFCC00', // Standard Colour #44
+ 45 => 'FFFF9900', // Standard Colour #45
+ 46 => 'FFFF6600', // Standard Colour #46
+ 47 => 'FF666699', // Standard Colour #47
+ 48 => 'FF969696', // Standard Colour #48
+ 49 => 'FF003366', // Standard Colour #49
+ 50 => 'FF339966', // Standard Colour #50
+ 51 => 'FF003300', // Standard Colour #51
+ 52 => 'FF333300', // Standard Colour #52
+ 53 => 'FF993300', // Standard Colour #53
+ 54 => 'FF993366', // Standard Colour #54
+ 55 => 'FF333399', // Standard Colour #55
+ 56 => 'FF333333' // Standard Colour #56
+ );
+ }
+
+ if (array_key_exists($pIndex, self::$indexedColors)) {
+ return new PHPExcel_Style_Color(self::$indexedColors[$pIndex]);
+ }
+
+ if ($background) {
+ return new PHPExcel_Style_Color(self::COLOR_WHITE);
+ }
+ return new PHPExcel_Style_Color(self::COLOR_BLACK);
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+ return md5(
+ $this->argb .
+ __CLASS__
+ );
+ }
+}
diff --git a/PHPExcel/Style/Conditional.php b/PHPExcel/Style/Conditional.php
new file mode 100644
index 0000000..331362b
--- /dev/null
+++ b/PHPExcel/Style/Conditional.php
@@ -0,0 +1,293 @@
+conditionType = PHPExcel_Style_Conditional::CONDITION_NONE;
+ $this->operatorType = PHPExcel_Style_Conditional::OPERATOR_NONE;
+ $this->text = null;
+ $this->condition = array();
+ $this->style = new PHPExcel_Style(false, true);
+ }
+
+ /**
+ * Get Condition type
+ *
+ * @return string
+ */
+ public function getConditionType()
+ {
+ return $this->conditionType;
+ }
+
+ /**
+ * Set Condition type
+ *
+ * @param string $pValue PHPExcel_Style_Conditional condition type
+ * @return PHPExcel_Style_Conditional
+ */
+ public function setConditionType($pValue = PHPExcel_Style_Conditional::CONDITION_NONE)
+ {
+ $this->conditionType = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Operator type
+ *
+ * @return string
+ */
+ public function getOperatorType()
+ {
+ return $this->operatorType;
+ }
+
+ /**
+ * Set Operator type
+ *
+ * @param string $pValue PHPExcel_Style_Conditional operator type
+ * @return PHPExcel_Style_Conditional
+ */
+ public function setOperatorType($pValue = PHPExcel_Style_Conditional::OPERATOR_NONE)
+ {
+ $this->operatorType = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get text
+ *
+ * @return string
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * Set text
+ *
+ * @param string $value
+ * @return PHPExcel_Style_Conditional
+ */
+ public function setText($value = null)
+ {
+ $this->text = $value;
+ return $this;
+ }
+
+ /**
+ * Get Condition
+ *
+ * @deprecated Deprecated, use getConditions instead
+ * @return string
+ */
+ public function getCondition()
+ {
+ if (isset($this->condition[0])) {
+ return $this->condition[0];
+ }
+
+ return '';
+ }
+
+ /**
+ * Set Condition
+ *
+ * @deprecated Deprecated, use setConditions instead
+ * @param string $pValue Condition
+ * @return PHPExcel_Style_Conditional
+ */
+ public function setCondition($pValue = '')
+ {
+ if (!is_array($pValue)) {
+ $pValue = array($pValue);
+ }
+
+ return $this->setConditions($pValue);
+ }
+
+ /**
+ * Get Conditions
+ *
+ * @return string[]
+ */
+ public function getConditions()
+ {
+ return $this->condition;
+ }
+
+ /**
+ * Set Conditions
+ *
+ * @param string[] $pValue Condition
+ * @return PHPExcel_Style_Conditional
+ */
+ public function setConditions($pValue)
+ {
+ if (!is_array($pValue)) {
+ $pValue = array($pValue);
+ }
+ $this->condition = $pValue;
+ return $this;
+ }
+
+ /**
+ * Add Condition
+ *
+ * @param string $pValue Condition
+ * @return PHPExcel_Style_Conditional
+ */
+ public function addCondition($pValue = '')
+ {
+ $this->condition[] = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Style
+ *
+ * @return PHPExcel_Style
+ */
+ public function getStyle()
+ {
+ return $this->style;
+ }
+
+ /**
+ * Set Style
+ *
+ * @param PHPExcel_Style $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Conditional
+ */
+ public function setStyle(PHPExcel_Style $pValue = null)
+ {
+ $this->style = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ return md5(
+ $this->conditionType .
+ $this->operatorType .
+ implode(';', $this->condition) .
+ $this->style->getHashCode() .
+ __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Style/Fill.php b/PHPExcel/Style/Fill.php
new file mode 100644
index 0000000..26343ff
--- /dev/null
+++ b/PHPExcel/Style/Fill.php
@@ -0,0 +1,322 @@
+fillType = null;
+ }
+ $this->startColor = new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_WHITE, $isSupervisor, $isConditional);
+ $this->endColor = new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_BLACK, $isSupervisor, $isConditional);
+
+ // bind parent if we are a supervisor
+ if ($isSupervisor) {
+ $this->startColor->bindParent($this, 'startColor');
+ $this->endColor->bindParent($this, 'endColor');
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor
+ *
+ * @return PHPExcel_Style_Fill
+ */
+ public function getSharedComponent()
+ {
+ return $this->parent->getSharedComponent()->getFill();
+ }
+
+ /**
+ * Build style array from subcomponents
+ *
+ * @param array $array
+ * @return array
+ */
+ public function getStyleArray($array)
+ {
+ return array('fill' => $array);
+ }
+
+ /**
+ * Apply styles from array
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->getFill()->applyFromArray(
+ * array(
+ * 'type' => PHPExcel_Style_Fill::FILL_GRADIENT_LINEAR,
+ * 'rotation' => 0,
+ * 'startcolor' => array(
+ * 'rgb' => '000000'
+ * ),
+ * 'endcolor' => array(
+ * 'argb' => 'FFFFFFFF'
+ * )
+ * )
+ * );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Fill
+ */
+ public function applyFromArray($pStyles = null)
+ {
+ if (is_array($pStyles)) {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+ } else {
+ if (array_key_exists('type', $pStyles)) {
+ $this->setFillType($pStyles['type']);
+ }
+ if (array_key_exists('rotation', $pStyles)) {
+ $this->setRotation($pStyles['rotation']);
+ }
+ if (array_key_exists('startcolor', $pStyles)) {
+ $this->getStartColor()->applyFromArray($pStyles['startcolor']);
+ }
+ if (array_key_exists('endcolor', $pStyles)) {
+ $this->getEndColor()->applyFromArray($pStyles['endcolor']);
+ }
+ if (array_key_exists('color', $pStyles)) {
+ $this->getStartColor()->applyFromArray($pStyles['color']);
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get Fill Type
+ *
+ * @return string
+ */
+ public function getFillType()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getFillType();
+ }
+ return $this->fillType;
+ }
+
+ /**
+ * Set Fill Type
+ *
+ * @param string $pValue PHPExcel_Style_Fill fill type
+ * @return PHPExcel_Style_Fill
+ */
+ public function setFillType($pValue = PHPExcel_Style_Fill::FILL_NONE)
+ {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('type' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->fillType = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Rotation
+ *
+ * @return double
+ */
+ public function getRotation()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getRotation();
+ }
+ return $this->rotation;
+ }
+
+ /**
+ * Set Rotation
+ *
+ * @param double $pValue
+ * @return PHPExcel_Style_Fill
+ */
+ public function setRotation($pValue = 0)
+ {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('rotation' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->rotation = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Start Color
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getStartColor()
+ {
+ return $this->startColor;
+ }
+
+ /**
+ * Set Start Color
+ *
+ * @param PHPExcel_Style_Color $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Fill
+ */
+ public function setStartColor(PHPExcel_Style_Color $pValue = null)
+ {
+ // make sure parameter is a real color and not a supervisor
+ $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStartColor()->getStyleArray(array('argb' => $color->getARGB()));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->startColor = $color;
+ }
+ return $this;
+ }
+
+ /**
+ * Get End Color
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getEndColor()
+ {
+ return $this->endColor;
+ }
+
+ /**
+ * Set End Color
+ *
+ * @param PHPExcel_Style_Color $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Fill
+ */
+ public function setEndColor(PHPExcel_Style_Color $pValue = null)
+ {
+ // make sure parameter is a real color and not a supervisor
+ $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getEndColor()->getStyleArray(array('argb' => $color->getARGB()));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->endColor = $color;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+ return md5(
+ $this->getFillType() .
+ $this->getRotation() .
+ $this->getStartColor()->getHashCode() .
+ $this->getEndColor()->getHashCode() .
+ __CLASS__
+ );
+ }
+}
diff --git a/PHPExcel/Style/Font.php b/PHPExcel/Style/Font.php
new file mode 100644
index 0000000..9566e1d
--- /dev/null
+++ b/PHPExcel/Style/Font.php
@@ -0,0 +1,543 @@
+name = null;
+ $this->size = null;
+ $this->bold = null;
+ $this->italic = null;
+ $this->superScript = null;
+ $this->subScript = null;
+ $this->underline = null;
+ $this->strikethrough = null;
+ $this->color = new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_BLACK, $isSupervisor, $isConditional);
+ } else {
+ $this->color = new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_BLACK, $isSupervisor);
+ }
+ // bind parent if we are a supervisor
+ if ($isSupervisor) {
+ $this->color->bindParent($this, 'color');
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor
+ *
+ * @return PHPExcel_Style_Font
+ */
+ public function getSharedComponent()
+ {
+ return $this->parent->getSharedComponent()->getFont();
+ }
+
+ /**
+ * Build style array from subcomponents
+ *
+ * @param array $array
+ * @return array
+ */
+ public function getStyleArray($array)
+ {
+ return array('font' => $array);
+ }
+
+ /**
+ * Apply styles from array
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->getFont()->applyFromArray(
+ * array(
+ * 'name' => 'Arial',
+ * 'bold' => TRUE,
+ * 'italic' => FALSE,
+ * 'underline' => PHPExcel_Style_Font::UNDERLINE_DOUBLE,
+ * 'strike' => FALSE,
+ * 'color' => array(
+ * 'rgb' => '808080'
+ * )
+ * )
+ * );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Font
+ */
+ public function applyFromArray($pStyles = null)
+ {
+ if (is_array($pStyles)) {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+ } else {
+ if (array_key_exists('name', $pStyles)) {
+ $this->setName($pStyles['name']);
+ }
+ if (array_key_exists('bold', $pStyles)) {
+ $this->setBold($pStyles['bold']);
+ }
+ if (array_key_exists('italic', $pStyles)) {
+ $this->setItalic($pStyles['italic']);
+ }
+ if (array_key_exists('superScript', $pStyles)) {
+ $this->setSuperScript($pStyles['superScript']);
+ }
+ if (array_key_exists('subScript', $pStyles)) {
+ $this->setSubScript($pStyles['subScript']);
+ }
+ if (array_key_exists('underline', $pStyles)) {
+ $this->setUnderline($pStyles['underline']);
+ }
+ if (array_key_exists('strike', $pStyles)) {
+ $this->setStrikethrough($pStyles['strike']);
+ }
+ if (array_key_exists('color', $pStyles)) {
+ $this->getColor()->applyFromArray($pStyles['color']);
+ }
+ if (array_key_exists('size', $pStyles)) {
+ $this->setSize($pStyles['size']);
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get Name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getName();
+ }
+ return $this->name;
+ }
+
+ /**
+ * Set Name
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style_Font
+ */
+ public function setName($pValue = 'Calibri')
+ {
+ if ($pValue == '') {
+ $pValue = 'Calibri';
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('name' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->name = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Size
+ *
+ * @return double
+ */
+ public function getSize()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getSize();
+ }
+ return $this->size;
+ }
+
+ /**
+ * Set Size
+ *
+ * @param double $pValue
+ * @return PHPExcel_Style_Font
+ */
+ public function setSize($pValue = 10)
+ {
+ if ($pValue == '') {
+ $pValue = 10;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('size' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->size = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Bold
+ *
+ * @return boolean
+ */
+ public function getBold()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getBold();
+ }
+ return $this->bold;
+ }
+
+ /**
+ * Set Bold
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Style_Font
+ */
+ public function setBold($pValue = false)
+ {
+ if ($pValue == '') {
+ $pValue = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('bold' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->bold = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Italic
+ *
+ * @return boolean
+ */
+ public function getItalic()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getItalic();
+ }
+ return $this->italic;
+ }
+
+ /**
+ * Set Italic
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Style_Font
+ */
+ public function setItalic($pValue = false)
+ {
+ if ($pValue == '') {
+ $pValue = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('italic' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->italic = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get SuperScript
+ *
+ * @return boolean
+ */
+ public function getSuperScript()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getSuperScript();
+ }
+ return $this->superScript;
+ }
+
+ /**
+ * Set SuperScript
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Style_Font
+ */
+ public function setSuperScript($pValue = false)
+ {
+ if ($pValue == '') {
+ $pValue = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('superScript' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->superScript = $pValue;
+ $this->subScript = !$pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get SubScript
+ *
+ * @return boolean
+ */
+ public function getSubScript()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getSubScript();
+ }
+ return $this->subScript;
+ }
+
+ /**
+ * Set SubScript
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Style_Font
+ */
+ public function setSubScript($pValue = false)
+ {
+ if ($pValue == '') {
+ $pValue = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('subScript' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->subScript = $pValue;
+ $this->superScript = !$pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Underline
+ *
+ * @return string
+ */
+ public function getUnderline()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getUnderline();
+ }
+ return $this->underline;
+ }
+
+ /**
+ * Set Underline
+ *
+ * @param string|boolean $pValue PHPExcel_Style_Font underline type
+ * If a boolean is passed, then TRUE equates to UNDERLINE_SINGLE,
+ * false equates to UNDERLINE_NONE
+ * @return PHPExcel_Style_Font
+ */
+ public function setUnderline($pValue = self::UNDERLINE_NONE)
+ {
+ if (is_bool($pValue)) {
+ $pValue = ($pValue) ? self::UNDERLINE_SINGLE : self::UNDERLINE_NONE;
+ } elseif ($pValue == '') {
+ $pValue = self::UNDERLINE_NONE;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('underline' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->underline = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Strikethrough
+ *
+ * @return boolean
+ */
+ public function getStrikethrough()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getStrikethrough();
+ }
+ return $this->strikethrough;
+ }
+
+ /**
+ * Set Strikethrough
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Style_Font
+ */
+ public function setStrikethrough($pValue = false)
+ {
+ if ($pValue == '') {
+ $pValue = false;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('strike' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->strikethrough = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Color
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getColor()
+ {
+ return $this->color;
+ }
+
+ /**
+ * Set Color
+ *
+ * @param PHPExcel_Style_Color $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Font
+ */
+ public function setColor(PHPExcel_Style_Color $pValue = null)
+ {
+ // make sure parameter is a real color and not a supervisor
+ $color = $pValue->getIsSupervisor() ? $pValue->getSharedComponent() : $pValue;
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getColor()->getStyleArray(array('argb' => $color->getARGB()));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->color = $color;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+ return md5(
+ $this->name .
+ $this->size .
+ ($this->bold ? 't' : 'f') .
+ ($this->italic ? 't' : 'f') .
+ ($this->superScript ? 't' : 'f') .
+ ($this->subScript ? 't' : 'f') .
+ $this->underline .
+ ($this->strikethrough ? 't' : 'f') .
+ $this->color->getHashCode() .
+ __CLASS__
+ );
+ }
+}
diff --git a/PHPExcel/Style/NumberFormat.php b/PHPExcel/Style/NumberFormat.php
new file mode 100644
index 0000000..1b72cda
--- /dev/null
+++ b/PHPExcel/Style/NumberFormat.php
@@ -0,0 +1,751 @@
+formatCode = null;
+ $this->builtInFormatCode = false;
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor
+ *
+ * @return PHPExcel_Style_NumberFormat
+ */
+ public function getSharedComponent()
+ {
+ return $this->parent->getSharedComponent()->getNumberFormat();
+ }
+
+ /**
+ * Build style array from subcomponents
+ *
+ * @param array $array
+ * @return array
+ */
+ public function getStyleArray($array)
+ {
+ return array('numberformat' => $array);
+ }
+
+ /**
+ * Apply styles from array
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->getNumberFormat()->applyFromArray(
+ * array(
+ * 'code' => PHPExcel_Style_NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE
+ * )
+ * );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_NumberFormat
+ */
+ public function applyFromArray($pStyles = null)
+ {
+ if (is_array($pStyles)) {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+ } else {
+ if (array_key_exists('code', $pStyles)) {
+ $this->setFormatCode($pStyles['code']);
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get Format Code
+ *
+ * @return string
+ */
+ public function getFormatCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getFormatCode();
+ }
+ if ($this->builtInFormatCode !== false) {
+ return self::builtInFormatCode($this->builtInFormatCode);
+ }
+ return $this->formatCode;
+ }
+
+ /**
+ * Set Format Code
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style_NumberFormat
+ */
+ public function setFormatCode($pValue = PHPExcel_Style_NumberFormat::FORMAT_GENERAL)
+ {
+ if ($pValue == '') {
+ $pValue = PHPExcel_Style_NumberFormat::FORMAT_GENERAL;
+ }
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('code' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->formatCode = $pValue;
+ $this->builtInFormatCode = self::builtInFormatCodeIndex($pValue);
+ }
+ return $this;
+ }
+
+ /**
+ * Get Built-In Format Code
+ *
+ * @return int
+ */
+ public function getBuiltInFormatCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getBuiltInFormatCode();
+ }
+ return $this->builtInFormatCode;
+ }
+
+ /**
+ * Set Built-In Format Code
+ *
+ * @param int $pValue
+ * @return PHPExcel_Style_NumberFormat
+ */
+ public function setBuiltInFormatCode($pValue = 0)
+ {
+
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('code' => self::builtInFormatCode($pValue)));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->builtInFormatCode = $pValue;
+ $this->formatCode = self::builtInFormatCode($pValue);
+ }
+ return $this;
+ }
+
+ /**
+ * Fill built-in format codes
+ */
+ private static function fillBuiltInFormatCodes()
+ {
+ // [MS-OI29500: Microsoft Office Implementation Information for ISO/IEC-29500 Standard Compliance]
+ // 18.8.30. numFmt (Number Format)
+ //
+ // The ECMA standard defines built-in format IDs
+ // 14: "mm-dd-yy"
+ // 22: "m/d/yy h:mm"
+ // 37: "#,##0 ;(#,##0)"
+ // 38: "#,##0 ;[Red](#,##0)"
+ // 39: "#,##0.00;(#,##0.00)"
+ // 40: "#,##0.00;[Red](#,##0.00)"
+ // 47: "mmss.0"
+ // KOR fmt 55: "yyyy-mm-dd"
+ // Excel defines built-in format IDs
+ // 14: "m/d/yyyy"
+ // 22: "m/d/yyyy h:mm"
+ // 37: "#,##0_);(#,##0)"
+ // 38: "#,##0_);[Red](#,##0)"
+ // 39: "#,##0.00_);(#,##0.00)"
+ // 40: "#,##0.00_);[Red](#,##0.00)"
+ // 47: "mm:ss.0"
+ // KOR fmt 55: "yyyy/mm/dd"
+
+ // Built-in format codes
+ if (is_null(self::$builtInFormats)) {
+ self::$builtInFormats = array();
+
+ // General
+ self::$builtInFormats[0] = PHPExcel_Style_NumberFormat::FORMAT_GENERAL;
+ self::$builtInFormats[1] = '0';
+ self::$builtInFormats[2] = '0.00';
+ self::$builtInFormats[3] = '#,##0';
+ self::$builtInFormats[4] = '#,##0.00';
+
+ self::$builtInFormats[9] = '0%';
+ self::$builtInFormats[10] = '0.00%';
+ self::$builtInFormats[11] = '0.00E+00';
+ self::$builtInFormats[12] = '# ?/?';
+ self::$builtInFormats[13] = '# ??/??';
+ self::$builtInFormats[14] = 'm/d/yyyy'; // Despite ECMA 'mm-dd-yy';
+ self::$builtInFormats[15] = 'd-mmm-yy';
+ self::$builtInFormats[16] = 'd-mmm';
+ self::$builtInFormats[17] = 'mmm-yy';
+ self::$builtInFormats[18] = 'h:mm AM/PM';
+ self::$builtInFormats[19] = 'h:mm:ss AM/PM';
+ self::$builtInFormats[20] = 'h:mm';
+ self::$builtInFormats[21] = 'h:mm:ss';
+ self::$builtInFormats[22] = 'm/d/yyyy h:mm'; // Despite ECMA 'm/d/yy h:mm';
+
+ self::$builtInFormats[37] = '#,##0_);(#,##0)'; // Despite ECMA '#,##0 ;(#,##0)';
+ self::$builtInFormats[38] = '#,##0_);[Red](#,##0)'; // Despite ECMA '#,##0 ;[Red](#,##0)';
+ self::$builtInFormats[39] = '#,##0.00_);(#,##0.00)'; // Despite ECMA '#,##0.00;(#,##0.00)';
+ self::$builtInFormats[40] = '#,##0.00_);[Red](#,##0.00)'; // Despite ECMA '#,##0.00;[Red](#,##0.00)';
+
+ self::$builtInFormats[44] = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
+ self::$builtInFormats[45] = 'mm:ss';
+ self::$builtInFormats[46] = '[h]:mm:ss';
+ self::$builtInFormats[47] = 'mm:ss.0'; // Despite ECMA 'mmss.0';
+ self::$builtInFormats[48] = '##0.0E+0';
+ self::$builtInFormats[49] = '@';
+
+ // CHT
+ self::$builtInFormats[27] = '[$-404]e/m/d';
+ self::$builtInFormats[30] = 'm/d/yy';
+ self::$builtInFormats[36] = '[$-404]e/m/d';
+ self::$builtInFormats[50] = '[$-404]e/m/d';
+ self::$builtInFormats[57] = '[$-404]e/m/d';
+
+ // THA
+ self::$builtInFormats[59] = 't0';
+ self::$builtInFormats[60] = 't0.00';
+ self::$builtInFormats[61] = 't#,##0';
+ self::$builtInFormats[62] = 't#,##0.00';
+ self::$builtInFormats[67] = 't0%';
+ self::$builtInFormats[68] = 't0.00%';
+ self::$builtInFormats[69] = 't# ?/?';
+ self::$builtInFormats[70] = 't# ??/??';
+
+ // Flip array (for faster lookups)
+ self::$flippedBuiltInFormats = array_flip(self::$builtInFormats);
+ }
+ }
+
+ /**
+ * Get built-in format code
+ *
+ * @param int $pIndex
+ * @return string
+ */
+ public static function builtInFormatCode($pIndex)
+ {
+ // Clean parameter
+ $pIndex = intval($pIndex);
+
+ // Ensure built-in format codes are available
+ self::fillBuiltInFormatCodes();
+ // Lookup format code
+ if (isset(self::$builtInFormats[$pIndex])) {
+ return self::$builtInFormats[$pIndex];
+ }
+
+ return '';
+ }
+
+ /**
+ * Get built-in format code index
+ *
+ * @param string $formatCode
+ * @return int|boolean
+ */
+ public static function builtInFormatCodeIndex($formatCode)
+ {
+ // Ensure built-in format codes are available
+ self::fillBuiltInFormatCodes();
+
+ // Lookup format code
+ if (isset(self::$flippedBuiltInFormats[$formatCode])) {
+ return self::$flippedBuiltInFormats[$formatCode];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+ return md5(
+ $this->formatCode .
+ $this->builtInFormatCode .
+ __CLASS__
+ );
+ }
+
+ /**
+ * Search/replace values to convert Excel date/time format masks to PHP format masks
+ *
+ * @var array
+ */
+ private static $dateFormatReplacements = array(
+ // first remove escapes related to non-format characters
+ '\\' => '',
+ // 12-hour suffix
+ 'am/pm' => 'A',
+ // 4-digit year
+ 'e' => 'Y',
+ 'yyyy' => 'Y',
+ // 2-digit year
+ 'yy' => 'y',
+ // first letter of month - no php equivalent
+ 'mmmmm' => 'M',
+ // full month name
+ 'mmmm' => 'F',
+ // short month name
+ 'mmm' => 'M',
+ // mm is minutes if time, but can also be month w/leading zero
+ // so we try to identify times be the inclusion of a : separator in the mask
+ // It isn't perfect, but the best way I know how
+ ':mm' => ':i',
+ 'mm:' => 'i:',
+ // month leading zero
+ 'mm' => 'm',
+ // month no leading zero
+ 'm' => 'n',
+ // full day of week name
+ 'dddd' => 'l',
+ // short day of week name
+ 'ddd' => 'D',
+ // days leading zero
+ 'dd' => 'd',
+ // days no leading zero
+ 'd' => 'j',
+ // seconds
+ 'ss' => 's',
+ // fractional seconds - no php equivalent
+ '.s' => ''
+ );
+ /**
+ * Search/replace values to convert Excel date/time format masks hours to PHP format masks (24 hr clock)
+ *
+ * @var array
+ */
+ private static $dateFormatReplacements24 = array(
+ 'hh' => 'H',
+ 'h' => 'G'
+ );
+ /**
+ * Search/replace values to convert Excel date/time format masks hours to PHP format masks (12 hr clock)
+ *
+ * @var array
+ */
+ private static $dateFormatReplacements12 = array(
+ 'hh' => 'h',
+ 'h' => 'g'
+ );
+
+ private static function setLowercaseCallback($matches) {
+ return mb_strtolower($matches[0]);
+ }
+
+ private static function escapeQuotesCallback($matches) {
+ return '\\' . implode('\\', str_split($matches[1]));
+ }
+
+ private static function formatAsDate(&$value, &$format)
+ {
+ // strip off first part containing e.g. [$-F800] or [$USD-409]
+ // general syntax: [$
';
+ if ($value != (int)$value) {
+ self::formatAsFraction($value, $format);
+ }
+
+ } else {
+ // Handle the number itself
+
+ // scale number
+ $value = $value / $scale;
+
+ // Strip #
+ $format = preg_replace('/\\#/', '0', $format);
+
+ $n = "/\[[^\]]+\]/";
+ $m = preg_replace($n, '', $format);
+ $number_regex = "/(0+)(\.?)(0*)/";
+ if (preg_match($number_regex, $m, $matches)) {
+ $left = $matches[1];
+ $dec = $matches[2];
+ $right = $matches[3];
+
+ // minimun width of formatted number (including dot)
+ $minWidth = strlen($left) + strlen($dec) + strlen($right);
+ if ($useThousands) {
+ $value = number_format(
+ $value,
+ strlen($right),
+ PHPExcel_Shared_String::getDecimalSeparator(),
+ PHPExcel_Shared_String::getThousandsSeparator()
+ );
+ $value = preg_replace($number_regex, $value, $format);
+ } else {
+ if (preg_match('/[0#]E[+-]0/i', $format)) {
+ // Scientific format
+ $value = sprintf('%5.2E', $value);
+ } elseif (preg_match('/0([^\d\.]+)0/', $format)) {
+ $value = self::complexNumberFormatMask($value, $format);
+ } else {
+ $sprintf_pattern = "%0$minWidth." . strlen($right) . "f";
+ $value = sprintf($sprintf_pattern, $value);
+ $value = preg_replace($number_regex, $value, $format);
+ }
+ }
+ }
+ }
+ if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
+ // Currency or Accounting
+ $currencyFormat = $m[0];
+ $currencyCode = $m[1];
+ list($currencyCode) = explode('-', $currencyCode);
+ if ($currencyCode == '') {
+ $currencyCode = PHPExcel_Shared_String::getCurrencyCode();
+ }
+ $value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
+ }
+ }
+ }
+
+ // Escape any escaped slashes to a single slash
+ $format = preg_replace("/\\\\/u", '\\', $format);
+
+ // Additional formatting provided by callback function
+ if ($callBack !== null) {
+ list($writerInstance, $function) = $callBack;
+ $value = $writerInstance->$function($value, $formatColor);
+ }
+
+ return $value;
+ }
+}
diff --git a/PHPExcel/Style/Protection.php b/PHPExcel/Style/Protection.php
new file mode 100644
index 0000000..d5568f6
--- /dev/null
+++ b/PHPExcel/Style/Protection.php
@@ -0,0 +1,204 @@
+locked = self::PROTECTION_INHERIT;
+ $this->hidden = self::PROTECTION_INHERIT;
+ }
+ }
+
+ /**
+ * Get the shared style component for the currently active cell in currently active sheet.
+ * Only used for style supervisor
+ *
+ * @return PHPExcel_Style_Protection
+ */
+ public function getSharedComponent()
+ {
+ return $this->parent->getSharedComponent()->getProtection();
+ }
+
+ /**
+ * Build style array from subcomponents
+ *
+ * @param array $array
+ * @return array
+ */
+ public function getStyleArray($array)
+ {
+ return array('protection' => $array);
+ }
+
+ /**
+ * Apply styles from array
+ *
+ *
+ * $objPHPExcel->getActiveSheet()->getStyle('B2')->getLocked()->applyFromArray(
+ * array(
+ * 'locked' => TRUE,
+ * 'hidden' => FALSE
+ * )
+ * );
+ *
+ *
+ * @param array $pStyles Array containing style information
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Style_Protection
+ */
+ public function applyFromArray($pStyles = null)
+ {
+ if (is_array($pStyles)) {
+ if ($this->isSupervisor) {
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
+ } else {
+ if (isset($pStyles['locked'])) {
+ $this->setLocked($pStyles['locked']);
+ }
+ if (isset($pStyles['hidden'])) {
+ $this->setHidden($pStyles['hidden']);
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception("Invalid style array passed.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get locked
+ *
+ * @return string
+ */
+ public function getLocked()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getLocked();
+ }
+ return $this->locked;
+ }
+
+ /**
+ * Set locked
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style_Protection
+ */
+ public function setLocked($pValue = self::PROTECTION_INHERIT)
+ {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('locked' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->locked = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hidden
+ *
+ * @return string
+ */
+ public function getHidden()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHidden();
+ }
+ return $this->hidden;
+ }
+
+ /**
+ * Set hidden
+ *
+ * @param string $pValue
+ * @return PHPExcel_Style_Protection
+ */
+ public function setHidden($pValue = self::PROTECTION_INHERIT)
+ {
+ if ($this->isSupervisor) {
+ $styleArray = $this->getStyleArray(array('hidden' => $pValue));
+ $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
+ } else {
+ $this->hidden = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->isSupervisor) {
+ return $this->getSharedComponent()->getHashCode();
+ }
+ return md5(
+ $this->locked .
+ $this->hidden .
+ __CLASS__
+ );
+ }
+}
diff --git a/PHPExcel/Style/Supervisor.php b/PHPExcel/Style/Supervisor.php
new file mode 100644
index 0000000..a90e1c6
--- /dev/null
+++ b/PHPExcel/Style/Supervisor.php
@@ -0,0 +1,125 @@
+isSupervisor = $isSupervisor;
+ }
+
+ /**
+ * Bind parent. Only used for supervisor
+ *
+ * @param PHPExcel $parent
+ * @return PHPExcel_Style_Supervisor
+ */
+ public function bindParent($parent, $parentPropertyName = null)
+ {
+ $this->parent = $parent;
+ return $this;
+ }
+
+ /**
+ * Is this a supervisor or a cell style component?
+ *
+ * @return boolean
+ */
+ public function getIsSupervisor()
+ {
+ return $this->isSupervisor;
+ }
+
+ /**
+ * Get the currently active sheet. Only used for supervisor
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getActiveSheet()
+ {
+ return $this->parent->getActiveSheet();
+ }
+
+ /**
+ * Get the currently active cell coordinate in currently active sheet.
+ * Only used for supervisor
+ *
+ * @return string E.g. 'A1'
+ */
+ public function getSelectedCells()
+ {
+ return $this->getActiveSheet()->getSelectedCells();
+ }
+
+ /**
+ * Get the currently active cell coordinate in currently active sheet.
+ * Only used for supervisor
+ *
+ * @return string E.g. 'A1'
+ */
+ public function getActiveCell()
+ {
+ return $this->getActiveSheet()->getActiveCell();
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if ((is_object($value)) && ($key != 'parent')) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet.php b/PHPExcel/Worksheet.php
new file mode 100644
index 0000000..483aa00
--- /dev/null
+++ b/PHPExcel/Worksheet.php
@@ -0,0 +1,2968 @@
+parent = $pParent;
+ $this->setTitle($pTitle, false);
+ // setTitle can change $pTitle
+ $this->setCodeName($this->getTitle());
+ $this->setSheetState(PHPExcel_Worksheet::SHEETSTATE_VISIBLE);
+
+ $this->cellCollection = PHPExcel_CachedObjectStorageFactory::getInstance($this);
+ // Set page setup
+ $this->pageSetup = new PHPExcel_Worksheet_PageSetup();
+ // Set page margins
+ $this->pageMargins = new PHPExcel_Worksheet_PageMargins();
+ // Set page header/footer
+ $this->headerFooter = new PHPExcel_Worksheet_HeaderFooter();
+ // Set sheet view
+ $this->sheetView = new PHPExcel_Worksheet_SheetView();
+ // Drawing collection
+ $this->drawingCollection = new ArrayObject();
+ // Chart collection
+ $this->chartCollection = new ArrayObject();
+ // Protection
+ $this->protection = new PHPExcel_Worksheet_Protection();
+ // Default row dimension
+ $this->defaultRowDimension = new PHPExcel_Worksheet_RowDimension(null);
+ // Default column dimension
+ $this->defaultColumnDimension = new PHPExcel_Worksheet_ColumnDimension(null);
+ $this->autoFilter = new PHPExcel_Worksheet_AutoFilter(null, $this);
+ }
+
+
+ /**
+ * Disconnect all cells from this PHPExcel_Worksheet object,
+ * typically so that the worksheet object can be unset
+ *
+ */
+ public function disconnectCells()
+ {
+ if ($this->cellCollection !== null) {
+ $this->cellCollection->unsetWorksheetCells();
+ $this->cellCollection = null;
+ }
+ // detach ourself from the workbook, so that it can then delete this worksheet successfully
+ $this->parent = null;
+ }
+
+ /**
+ * Code to execute when this worksheet is unset()
+ *
+ */
+ public function __destruct()
+ {
+ PHPExcel_Calculation::getInstance($this->parent)->clearCalculationCacheForWorksheet($this->title);
+
+ $this->disconnectCells();
+ }
+
+ /**
+ * Return the cache controller for the cell collection
+ *
+ * @return PHPExcel_CachedObjectStorage_xxx
+ */
+ public function getCellCacheController()
+ {
+ return $this->cellCollection;
+ }
+
+
+ /**
+ * Get array of invalid characters for sheet title
+ *
+ * @return array
+ */
+ public static function getInvalidCharacters()
+ {
+ return self::$invalidCharacters;
+ }
+
+ /**
+ * Check sheet code name for valid Excel syntax
+ *
+ * @param string $pValue The string to check
+ * @return string The valid string
+ * @throws Exception
+ */
+ private static function checkSheetCodeName($pValue)
+ {
+ $CharCount = PHPExcel_Shared_String::CountCharacters($pValue);
+ if ($CharCount == 0) {
+ throw new PHPExcel_Exception('Sheet code name cannot be empty.');
+ }
+ // Some of the printable ASCII characters are invalid: * : / \ ? [ ] and first and last characters cannot be a "'"
+ if ((str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) ||
+ (PHPExcel_Shared_String::Substring($pValue, -1, 1)=='\'') ||
+ (PHPExcel_Shared_String::Substring($pValue, 0, 1)=='\'')) {
+ throw new PHPExcel_Exception('Invalid character found in sheet code name');
+ }
+
+ // Maximum 31 characters allowed for sheet title
+ if ($CharCount > 31) {
+ throw new PHPExcel_Exception('Maximum 31 characters allowed in sheet code name.');
+ }
+
+ return $pValue;
+ }
+
+ /**
+ * Check sheet title for valid Excel syntax
+ *
+ * @param string $pValue The string to check
+ * @return string The valid string
+ * @throws PHPExcel_Exception
+ */
+ private static function checkSheetTitle($pValue)
+ {
+ // Some of the printable ASCII characters are invalid: * : / \ ? [ ]
+ if (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) {
+ throw new PHPExcel_Exception('Invalid character found in sheet title');
+ }
+
+ // Maximum 31 characters allowed for sheet title
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 31) {
+ throw new PHPExcel_Exception('Maximum 31 characters allowed in sheet title.');
+ }
+
+ return $pValue;
+ }
+
+ /**
+ * Get collection of cells
+ *
+ * @param boolean $pSorted Also sort the cell collection?
+ * @return PHPExcel_Cell[]
+ */
+ public function getCellCollection($pSorted = true)
+ {
+ if ($pSorted) {
+ // Re-order cell collection
+ return $this->sortCellCollection();
+ }
+ if ($this->cellCollection !== null) {
+ return $this->cellCollection->getCellList();
+ }
+ return array();
+ }
+
+ /**
+ * Sort collection of cells
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function sortCellCollection()
+ {
+ if ($this->cellCollection !== null) {
+ return $this->cellCollection->getSortedCellList();
+ }
+ return array();
+ }
+
+ /**
+ * Get collection of row dimensions
+ *
+ * @return PHPExcel_Worksheet_RowDimension[]
+ */
+ public function getRowDimensions()
+ {
+ return $this->rowDimensions;
+ }
+
+ /**
+ * Get default row dimension
+ *
+ * @return PHPExcel_Worksheet_RowDimension
+ */
+ public function getDefaultRowDimension()
+ {
+ return $this->defaultRowDimension;
+ }
+
+ /**
+ * Get collection of column dimensions
+ *
+ * @return PHPExcel_Worksheet_ColumnDimension[]
+ */
+ public function getColumnDimensions()
+ {
+ return $this->columnDimensions;
+ }
+
+ /**
+ * Get default column dimension
+ *
+ * @return PHPExcel_Worksheet_ColumnDimension
+ */
+ public function getDefaultColumnDimension()
+ {
+ return $this->defaultColumnDimension;
+ }
+
+ /**
+ * Get collection of drawings
+ *
+ * @return PHPExcel_Worksheet_BaseDrawing[]
+ */
+ public function getDrawingCollection()
+ {
+ return $this->drawingCollection;
+ }
+
+ /**
+ * Get collection of charts
+ *
+ * @return PHPExcel_Chart[]
+ */
+ public function getChartCollection()
+ {
+ return $this->chartCollection;
+ }
+
+ /**
+ * Add chart
+ *
+ * @param PHPExcel_Chart $pChart
+ * @param int|null $iChartIndex Index where chart should go (0,1,..., or null for last)
+ * @return PHPExcel_Chart
+ */
+ public function addChart(PHPExcel_Chart $pChart = null, $iChartIndex = null)
+ {
+ $pChart->setWorksheet($this);
+ if (is_null($iChartIndex)) {
+ $this->chartCollection[] = $pChart;
+ } else {
+ // Insert the chart at the requested index
+ array_splice($this->chartCollection, $iChartIndex, 0, array($pChart));
+ }
+
+ return $pChart;
+ }
+
+ /**
+ * Return the count of charts on this worksheet
+ *
+ * @return int The number of charts
+ */
+ public function getChartCount()
+ {
+ return count($this->chartCollection);
+ }
+
+ /**
+ * Get a chart by its index position
+ *
+ * @param string $index Chart index position
+ * @return false|PHPExcel_Chart
+ * @throws PHPExcel_Exception
+ */
+ public function getChartByIndex($index = null)
+ {
+ $chartCount = count($this->chartCollection);
+ if ($chartCount == 0) {
+ return false;
+ }
+ if (is_null($index)) {
+ $index = --$chartCount;
+ }
+ if (!isset($this->chartCollection[$index])) {
+ return false;
+ }
+
+ return $this->chartCollection[$index];
+ }
+
+ /**
+ * Return an array of the names of charts on this worksheet
+ *
+ * @return string[] The names of charts
+ * @throws PHPExcel_Exception
+ */
+ public function getChartNames()
+ {
+ $chartNames = array();
+ foreach ($this->chartCollection as $chart) {
+ $chartNames[] = $chart->getName();
+ }
+ return $chartNames;
+ }
+
+ /**
+ * Get a chart by name
+ *
+ * @param string $chartName Chart name
+ * @return false|PHPExcel_Chart
+ * @throws PHPExcel_Exception
+ */
+ public function getChartByName($chartName = '')
+ {
+ $chartCount = count($this->chartCollection);
+ if ($chartCount == 0) {
+ return false;
+ }
+ foreach ($this->chartCollection as $index => $chart) {
+ if ($chart->getName() == $chartName) {
+ return $this->chartCollection[$index];
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Refresh column dimensions
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function refreshColumnDimensions()
+ {
+ $currentColumnDimensions = $this->getColumnDimensions();
+ $newColumnDimensions = array();
+
+ foreach ($currentColumnDimensions as $objColumnDimension) {
+ $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
+ }
+
+ $this->columnDimensions = $newColumnDimensions;
+
+ return $this;
+ }
+
+ /**
+ * Refresh row dimensions
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function refreshRowDimensions()
+ {
+ $currentRowDimensions = $this->getRowDimensions();
+ $newRowDimensions = array();
+
+ foreach ($currentRowDimensions as $objRowDimension) {
+ $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
+ }
+
+ $this->rowDimensions = $newRowDimensions;
+
+ return $this;
+ }
+
+ /**
+ * Calculate worksheet dimension
+ *
+ * @return string String containing the dimension of this worksheet
+ */
+ public function calculateWorksheetDimension()
+ {
+ // Return
+ return 'A1' . ':' . $this->getHighestColumn() . $this->getHighestRow();
+ }
+
+ /**
+ * Calculate worksheet data dimension
+ *
+ * @return string String containing the dimension of this worksheet that actually contain data
+ */
+ public function calculateWorksheetDataDimension()
+ {
+ // Return
+ return 'A1' . ':' . $this->getHighestDataColumn() . $this->getHighestDataRow();
+ }
+
+ /**
+ * Calculate widths for auto-size columns
+ *
+ * @param boolean $calculateMergeCells Calculate merge cell width
+ * @return PHPExcel_Worksheet;
+ */
+ public function calculateColumnWidths($calculateMergeCells = false)
+ {
+ // initialize $autoSizes array
+ $autoSizes = array();
+ foreach ($this->getColumnDimensions() as $colDimension) {
+ if ($colDimension->getAutoSize()) {
+ $autoSizes[$colDimension->getColumnIndex()] = -1;
+ }
+ }
+
+ // There is only something to do if there are some auto-size columns
+ if (!empty($autoSizes)) {
+ // build list of cells references that participate in a merge
+ $isMergeCell = array();
+ foreach ($this->getMergeCells() as $cells) {
+ foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cells) as $cellReference) {
+ $isMergeCell[$cellReference] = true;
+ }
+ }
+
+ // loop through all cells in the worksheet
+ foreach ($this->getCellCollection(false) as $cellID) {
+ $cell = $this->getCell($cellID, false);
+ if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
+ // Determine width if cell does not participate in a merge
+ if (!isset($isMergeCell[$this->cellCollection->getCurrentAddress()])) {
+ // Calculated value
+ // To formatted string
+ $cellValue = PHPExcel_Style_NumberFormat::toFormattedString(
+ $cell->getCalculatedValue(),
+ $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
+ );
+
+ $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
+ (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
+ (float)PHPExcel_Shared_Font::calculateColumnWidth(
+ $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
+ $cellValue,
+ $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
+ $this->getDefaultStyle()->getFont()
+ )
+ );
+ }
+ }
+ }
+
+ // adjust column widths
+ foreach ($autoSizes as $columnIndex => $width) {
+ if ($width == -1) {
+ $width = $this->getDefaultColumnDimension()->getWidth();
+ }
+ $this->getColumnDimension($columnIndex)->setWidth($width);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get parent
+ *
+ * @return PHPExcel
+ */
+ public function getParent()
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Re-bind parent
+ *
+ * @param PHPExcel $parent
+ * @return PHPExcel_Worksheet
+ */
+ public function rebindParent(PHPExcel $parent)
+ {
+ if ($this->parent !== null) {
+ $namedRanges = $this->parent->getNamedRanges();
+ foreach ($namedRanges as $namedRange) {
+ $parent->addNamedRange($namedRange);
+ }
+
+ $this->parent->removeSheetByIndex(
+ $this->parent->getIndex($this)
+ );
+ }
+ $this->parent = $parent;
+
+ return $this;
+ }
+
+ /**
+ * Get title
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * Set title
+ *
+ * @param string $pValue String containing the dimension of this worksheet
+ * @param string $updateFormulaCellReferences boolean Flag indicating whether cell references in formulae should
+ * be updated to reflect the new sheet name.
+ * This should be left as the default true, unless you are
+ * certain that no formula cells on any worksheet contain
+ * references to this worksheet
+ * @return PHPExcel_Worksheet
+ */
+ public function setTitle($pValue = 'Worksheet', $updateFormulaCellReferences = true)
+ {
+ // Is this a 'rename' or not?
+ if ($this->getTitle() == $pValue) {
+ return $this;
+ }
+
+ // Syntax check
+ self::checkSheetTitle($pValue);
+
+ // Old title
+ $oldTitle = $this->getTitle();
+
+ if ($this->parent) {
+ // Is there already such sheet name?
+ if ($this->parent->sheetNameExists($pValue)) {
+ // Use name, but append with lowest possible integer
+
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 29) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue, 0, 29);
+ }
+ $i = 1;
+ while ($this->parent->sheetNameExists($pValue . ' ' . $i)) {
+ ++$i;
+ if ($i == 10) {
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 28) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue, 0, 28);
+ }
+ } elseif ($i == 100) {
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 27) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue, 0, 27);
+ }
+ }
+ }
+
+ $altTitle = $pValue . ' ' . $i;
+ return $this->setTitle($altTitle, $updateFormulaCellReferences);
+ }
+ }
+
+ // Set title
+ $this->title = $pValue;
+ $this->dirty = true;
+
+ if ($this->parent && $this->parent->getCalculationEngine()) {
+ // New title
+ $newTitle = $this->getTitle();
+ $this->parent->getCalculationEngine()
+ ->renameCalculationCacheForWorksheet($oldTitle, $newTitle);
+ if ($updateFormulaCellReferences) {
+ PHPExcel_ReferenceHelper::getInstance()->updateNamedFormulas($this->parent, $oldTitle, $newTitle);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get sheet state
+ *
+ * @return string Sheet state (visible, hidden, veryHidden)
+ */
+ public function getSheetState()
+ {
+ return $this->sheetState;
+ }
+
+ /**
+ * Set sheet state
+ *
+ * @param string $value Sheet state (visible, hidden, veryHidden)
+ * @return PHPExcel_Worksheet
+ */
+ public function setSheetState($value = PHPExcel_Worksheet::SHEETSTATE_VISIBLE)
+ {
+ $this->sheetState = $value;
+ return $this;
+ }
+
+ /**
+ * Get page setup
+ *
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function getPageSetup()
+ {
+ return $this->pageSetup;
+ }
+
+ /**
+ * Set page setup
+ *
+ * @param PHPExcel_Worksheet_PageSetup $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setPageSetup(PHPExcel_Worksheet_PageSetup $pValue)
+ {
+ $this->pageSetup = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get page margins
+ *
+ * @return PHPExcel_Worksheet_PageMargins
+ */
+ public function getPageMargins()
+ {
+ return $this->pageMargins;
+ }
+
+ /**
+ * Set page margins
+ *
+ * @param PHPExcel_Worksheet_PageMargins $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setPageMargins(PHPExcel_Worksheet_PageMargins $pValue)
+ {
+ $this->pageMargins = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get page header/footer
+ *
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function getHeaderFooter()
+ {
+ return $this->headerFooter;
+ }
+
+ /**
+ * Set page header/footer
+ *
+ * @param PHPExcel_Worksheet_HeaderFooter $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setHeaderFooter(PHPExcel_Worksheet_HeaderFooter $pValue)
+ {
+ $this->headerFooter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get sheet view
+ *
+ * @return PHPExcel_Worksheet_SheetView
+ */
+ public function getSheetView()
+ {
+ return $this->sheetView;
+ }
+
+ /**
+ * Set sheet view
+ *
+ * @param PHPExcel_Worksheet_SheetView $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setSheetView(PHPExcel_Worksheet_SheetView $pValue)
+ {
+ $this->sheetView = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Protection
+ *
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function getProtection()
+ {
+ return $this->protection;
+ }
+
+ /**
+ * Set Protection
+ *
+ * @param PHPExcel_Worksheet_Protection $pValue
+ * @return PHPExcel_Worksheet
+ */
+ public function setProtection(PHPExcel_Worksheet_Protection $pValue)
+ {
+ $this->protection = $pValue;
+ $this->dirty = true;
+
+ return $this;
+ }
+
+ /**
+ * Get highest worksheet column
+ *
+ * @param string $row Return the data highest column for the specified row,
+ * or the highest column of any row if no row number is passed
+ * @return string Highest column name
+ */
+ public function getHighestColumn($row = null)
+ {
+ if ($row == null) {
+ return $this->cachedHighestColumn;
+ }
+ return $this->getHighestDataColumn($row);
+ }
+
+ /**
+ * Get highest worksheet column that contains data
+ *
+ * @param string $row Return the highest data column for the specified row,
+ * or the highest data column of any row if no row number is passed
+ * @return string Highest column name that contains data
+ */
+ public function getHighestDataColumn($row = null)
+ {
+ return $this->cellCollection->getHighestColumn($row);
+ }
+
+ /**
+ * Get highest worksheet row
+ *
+ * @param string $column Return the highest data row for the specified column,
+ * or the highest row of any column if no column letter is passed
+ * @return int Highest row number
+ */
+ public function getHighestRow($column = null)
+ {
+ if ($column == null) {
+ return $this->cachedHighestRow;
+ }
+ return $this->getHighestDataRow($column);
+ }
+
+ /**
+ * Get highest worksheet row that contains data
+ *
+ * @param string $column Return the highest data row for the specified column,
+ * or the highest data row of any column if no column letter is passed
+ * @return string Highest row number that contains data
+ */
+ public function getHighestDataRow($column = null)
+ {
+ return $this->cellCollection->getHighestRow($column);
+ }
+
+ /**
+ * Get highest worksheet column and highest row that have cell records
+ *
+ * @return array Highest column name and highest row number
+ */
+ public function getHighestRowAndColumn()
+ {
+ return $this->cellCollection->getHighestRowAndColumn();
+ }
+
+ /**
+ * Set a cell value
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @param mixed $pValue Value of the cell
+ * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
+ * @return PHPExcel_Worksheet|PHPExcel_Cell Depending on the last parameter being specified
+ */
+ public function setCellValue($pCoordinate = 'A1', $pValue = null, $returnCell = false)
+ {
+ $cell = $this->getCell(strtoupper($pCoordinate))->setValue($pValue);
+ return ($returnCell) ? $cell : $this;
+ }
+
+ /**
+ * Set a cell value by using numeric cell coordinates
+ *
+ * @param string $pColumn Numeric column coordinate of the cell (A = 0)
+ * @param string $pRow Numeric row coordinate of the cell
+ * @param mixed $pValue Value of the cell
+ * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
+ * @return PHPExcel_Worksheet|PHPExcel_Cell Depending on the last parameter being specified
+ */
+ public function setCellValueByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $returnCell = false)
+ {
+ $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValue($pValue);
+ return ($returnCell) ? $cell : $this;
+ }
+
+ /**
+ * Set a cell value
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @param mixed $pValue Value of the cell
+ * @param string $pDataType Explicit data type
+ * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
+ * @return PHPExcel_Worksheet|PHPExcel_Cell Depending on the last parameter being specified
+ */
+ public function setCellValueExplicit($pCoordinate = 'A1', $pValue = null, $pDataType = PHPExcel_Cell_DataType::TYPE_STRING, $returnCell = false)
+ {
+ // Set value
+ $cell = $this->getCell(strtoupper($pCoordinate))->setValueExplicit($pValue, $pDataType);
+ return ($returnCell) ? $cell : $this;
+ }
+
+ /**
+ * Set a cell value by using numeric cell coordinates
+ *
+ * @param string $pColumn Numeric column coordinate of the cell
+ * @param string $pRow Numeric row coordinate of the cell
+ * @param mixed $pValue Value of the cell
+ * @param string $pDataType Explicit data type
+ * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
+ * @return PHPExcel_Worksheet|PHPExcel_Cell Depending on the last parameter being specified
+ */
+ public function setCellValueExplicitByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $pDataType = PHPExcel_Cell_DataType::TYPE_STRING, $returnCell = false)
+ {
+ $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValueExplicit($pValue, $pDataType);
+ return ($returnCell) ? $cell : $this;
+ }
+
+ /**
+ * Get cell at a specific coordinate
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @param boolean $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
+ * already exist, or a null should be returned instead
+ * @throws PHPExcel_Exception
+ * @return null|PHPExcel_Cell Cell that was found/created or null
+ */
+ public function getCell($pCoordinate = 'A1', $createIfNotExists = true)
+ {
+ // Check cell collection
+ if ($this->cellCollection->isDataSet(strtoupper($pCoordinate))) {
+ return $this->cellCollection->getCacheData($pCoordinate);
+ }
+
+ // Worksheet reference?
+ if (strpos($pCoordinate, '!') !== false) {
+ $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pCoordinate, true);
+ return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
+ }
+
+ // Named range?
+ if ((!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $pCoordinate, $matches)) &&
+ (preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $pCoordinate, $matches))) {
+ $namedRange = PHPExcel_NamedRange::resolveRange($pCoordinate, $this);
+ if ($namedRange !== null) {
+ $pCoordinate = $namedRange->getRange();
+ return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
+ }
+ }
+
+ // Uppercase coordinate
+ $pCoordinate = strtoupper($pCoordinate);
+
+ if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate can not be a range of cells.');
+ } elseif (strpos($pCoordinate, '$') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate must not be absolute.');
+ }
+
+ // Create new cell object, if required
+ return $createIfNotExists ? $this->createNewCell($pCoordinate) : null;
+ }
+
+ /**
+ * Get cell at a specific coordinate by using numeric cell coordinates
+ *
+ * @param string $pColumn Numeric column coordinate of the cell (starting from 0)
+ * @param string $pRow Numeric row coordinate of the cell
+ * @param boolean $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
+ * already exist, or a null should be returned instead
+ * @return null|PHPExcel_Cell Cell that was found/created or null
+ */
+ public function getCellByColumnAndRow($pColumn = 0, $pRow = 1, $createIfNotExists = true)
+ {
+ $columnLetter = PHPExcel_Cell::stringFromColumnIndex($pColumn);
+ $coordinate = $columnLetter . $pRow;
+
+ if ($this->cellCollection->isDataSet($coordinate)) {
+ return $this->cellCollection->getCacheData($coordinate);
+ }
+
+ // Create new cell object, if required
+ return $createIfNotExists ? $this->createNewCell($coordinate) : null;
+ }
+
+ /**
+ * Create a new cell at the specified coordinate
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @return PHPExcel_Cell Cell that was created
+ */
+ private function createNewCell($pCoordinate)
+ {
+ $cell = $this->cellCollection->addCacheData(
+ $pCoordinate,
+ new PHPExcel_Cell(null, PHPExcel_Cell_DataType::TYPE_NULL, $this)
+ );
+ $this->cellCollectionIsSorted = false;
+
+ // Coordinates
+ $aCoordinates = PHPExcel_Cell::coordinateFromString($pCoordinate);
+ if (PHPExcel_Cell::columnIndexFromString($this->cachedHighestColumn) < PHPExcel_Cell::columnIndexFromString($aCoordinates[0])) {
+ $this->cachedHighestColumn = $aCoordinates[0];
+ }
+ $this->cachedHighestRow = max($this->cachedHighestRow, $aCoordinates[1]);
+
+ // Cell needs appropriate xfIndex from dimensions records
+ // but don't create dimension records if they don't already exist
+ $rowDimension = $this->getRowDimension($aCoordinates[1], false);
+ $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
+
+ if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
+ // then there is a row dimension with explicit style, assign it to the cell
+ $cell->setXfIndex($rowDimension->getXfIndex());
+ } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
+ // then there is a column dimension, assign it to the cell
+ $cell->setXfIndex($columnDimension->getXfIndex());
+ }
+
+ return $cell;
+ }
+
+ /**
+ * Does the cell at a specific coordinate exist?
+ *
+ * @param string $pCoordinate Coordinate of the cell
+ * @throws PHPExcel_Exception
+ * @return boolean
+ */
+ public function cellExists($pCoordinate = 'A1')
+ {
+ // Worksheet reference?
+ if (strpos($pCoordinate, '!') !== false) {
+ $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pCoordinate, true);
+ return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
+ }
+
+ // Named range?
+ if ((!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $pCoordinate, $matches)) &&
+ (preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $pCoordinate, $matches))) {
+ $namedRange = PHPExcel_NamedRange::resolveRange($pCoordinate, $this);
+ if ($namedRange !== null) {
+ $pCoordinate = $namedRange->getRange();
+ if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
+ if (!$namedRange->getLocalOnly()) {
+ return $namedRange->getWorksheet()->cellExists($pCoordinate);
+ } else {
+ throw new PHPExcel_Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+
+ // Uppercase coordinate
+ $pCoordinate = strtoupper($pCoordinate);
+
+ if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate can not be a range of cells.');
+ } elseif (strpos($pCoordinate, '$') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate must not be absolute.');
+ } else {
+ // Coordinates
+ $aCoordinates = PHPExcel_Cell::coordinateFromString($pCoordinate);
+
+ // Cell exists?
+ return $this->cellCollection->isDataSet($pCoordinate);
+ }
+ }
+
+ /**
+ * Cell at a specific coordinate by using numeric cell coordinates exists?
+ *
+ * @param string $pColumn Numeric column coordinate of the cell
+ * @param string $pRow Numeric row coordinate of the cell
+ * @return boolean
+ */
+ public function cellExistsByColumnAndRow($pColumn = 0, $pRow = 1)
+ {
+ return $this->cellExists(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Get row dimension at a specific row
+ *
+ * @param int $pRow Numeric index of the row
+ * @return PHPExcel_Worksheet_RowDimension
+ */
+ public function getRowDimension($pRow = 1, $create = true)
+ {
+ // Found
+ $found = null;
+
+ // Get row dimension
+ if (!isset($this->rowDimensions[$pRow])) {
+ if (!$create) {
+ return null;
+ }
+ $this->rowDimensions[$pRow] = new PHPExcel_Worksheet_RowDimension($pRow);
+
+ $this->cachedHighestRow = max($this->cachedHighestRow, $pRow);
+ }
+ return $this->rowDimensions[$pRow];
+ }
+
+ /**
+ * Get column dimension at a specific column
+ *
+ * @param string $pColumn String index of the column
+ * @return PHPExcel_Worksheet_ColumnDimension
+ */
+ public function getColumnDimension($pColumn = 'A', $create = true)
+ {
+ // Uppercase coordinate
+ $pColumn = strtoupper($pColumn);
+
+ // Fetch dimensions
+ if (!isset($this->columnDimensions[$pColumn])) {
+ if (!$create) {
+ return null;
+ }
+ $this->columnDimensions[$pColumn] = new PHPExcel_Worksheet_ColumnDimension($pColumn);
+
+ if (PHPExcel_Cell::columnIndexFromString($this->cachedHighestColumn) < PHPExcel_Cell::columnIndexFromString($pColumn)) {
+ $this->cachedHighestColumn = $pColumn;
+ }
+ }
+ return $this->columnDimensions[$pColumn];
+ }
+
+ /**
+ * Get column dimension at a specific column by using numeric cell coordinates
+ *
+ * @param string $pColumn Numeric column coordinate of the cell
+ * @return PHPExcel_Worksheet_ColumnDimension
+ */
+ public function getColumnDimensionByColumn($pColumn = 0)
+ {
+ return $this->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($pColumn));
+ }
+
+ /**
+ * Get styles
+ *
+ * @return PHPExcel_Style[]
+ */
+ public function getStyles()
+ {
+ return $this->styles;
+ }
+
+ /**
+ * Get default style of workbook.
+ *
+ * @deprecated
+ * @return PHPExcel_Style
+ * @throws PHPExcel_Exception
+ */
+ public function getDefaultStyle()
+ {
+ return $this->parent->getDefaultStyle();
+ }
+
+ /**
+ * Set default style - should only be used by PHPExcel_IReader implementations!
+ *
+ * @deprecated
+ * @param PHPExcel_Style $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setDefaultStyle(PHPExcel_Style $pValue)
+ {
+ $this->parent->getDefaultStyle()->applyFromArray(array(
+ 'font' => array(
+ 'name' => $pValue->getFont()->getName(),
+ 'size' => $pValue->getFont()->getSize(),
+ ),
+ ));
+ return $this;
+ }
+
+ /**
+ * Get style for cell
+ *
+ * @param string $pCellCoordinate Cell coordinate (or range) to get style for
+ * @return PHPExcel_Style
+ * @throws PHPExcel_Exception
+ */
+ public function getStyle($pCellCoordinate = 'A1')
+ {
+ // set this sheet as active
+ $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
+
+ // set cell coordinate as active
+ $this->setSelectedCells(strtoupper($pCellCoordinate));
+
+ return $this->parent->getCellXfSupervisor();
+ }
+
+ /**
+ * Get conditional styles for a cell
+ *
+ * @param string $pCoordinate
+ * @return PHPExcel_Style_Conditional[]
+ */
+ public function getConditionalStyles($pCoordinate = 'A1')
+ {
+ $pCoordinate = strtoupper($pCoordinate);
+ if (!isset($this->conditionalStylesCollection[$pCoordinate])) {
+ $this->conditionalStylesCollection[$pCoordinate] = array();
+ }
+ return $this->conditionalStylesCollection[$pCoordinate];
+ }
+
+ /**
+ * Do conditional styles exist for this cell?
+ *
+ * @param string $pCoordinate
+ * @return boolean
+ */
+ public function conditionalStylesExists($pCoordinate = 'A1')
+ {
+ if (isset($this->conditionalStylesCollection[strtoupper($pCoordinate)])) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes conditional styles for a cell
+ *
+ * @param string $pCoordinate
+ * @return PHPExcel_Worksheet
+ */
+ public function removeConditionalStyles($pCoordinate = 'A1')
+ {
+ unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
+ return $this;
+ }
+
+ /**
+ * Get collection of conditional styles
+ *
+ * @return array
+ */
+ public function getConditionalStylesCollection()
+ {
+ return $this->conditionalStylesCollection;
+ }
+
+ /**
+ * Set conditional styles
+ *
+ * @param $pCoordinate string E.g. 'A1'
+ * @param $pValue PHPExcel_Style_Conditional[]
+ * @return PHPExcel_Worksheet
+ */
+ public function setConditionalStyles($pCoordinate = 'A1', $pValue)
+ {
+ $this->conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get style for cell by using numeric cell coordinates
+ *
+ * @param int $pColumn Numeric column coordinate of the cell
+ * @param int $pRow Numeric row coordinate of the cell
+ * @param int pColumn2 Numeric column coordinate of the range cell
+ * @param int pRow2 Numeric row coordinate of the range cell
+ * @return PHPExcel_Style
+ */
+ public function getStyleByColumnAndRow($pColumn = 0, $pRow = 1, $pColumn2 = null, $pRow2 = null)
+ {
+ if (!is_null($pColumn2) && !is_null($pRow2)) {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow . ':' . PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->getStyle($cellRange);
+ }
+
+ return $this->getStyle(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Set shared cell style to a range of cells
+ *
+ * Please note that this will overwrite existing cell styles for cells in range!
+ *
+ * @deprecated
+ * @param PHPExcel_Style $pSharedCellStyle Cell style to share
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setSharedStyle(PHPExcel_Style $pSharedCellStyle = null, $pRange = '')
+ {
+ $this->duplicateStyle($pSharedCellStyle, $pRange);
+ return $this;
+ }
+
+ /**
+ * Duplicate cell style to a range of cells
+ *
+ * Please note that this will overwrite existing cell styles for cells in range!
+ *
+ * @param PHPExcel_Style $pCellStyle Cell style to duplicate
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function duplicateStyle(PHPExcel_Style $pCellStyle = null, $pRange = '')
+ {
+ // make sure we have a real style and not supervisor
+ $style = $pCellStyle->getIsSupervisor() ? $pCellStyle->getSharedComponent() : $pCellStyle;
+
+ // Add the style to the workbook if necessary
+ $workbook = $this->parent;
+ if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
+ // there is already such cell Xf in our collection
+ $xfIndex = $existingStyle->getIndex();
+ } else {
+ // we don't have such a cell Xf, need to add
+ $workbook->addCellXf($pCellStyle);
+ $xfIndex = $pCellStyle->getIndex();
+ }
+
+ // Calculate range outer borders
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange . ':' . $pRange);
+
+ // Make sure we can loop upwards on rows and columns
+ if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
+ $tmp = $rangeStart;
+ $rangeStart = $rangeEnd;
+ $rangeEnd = $tmp;
+ }
+
+ // Loop through cells and apply styles
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $this->getCell(PHPExcel_Cell::stringFromColumnIndex($col - 1) . $row)->setXfIndex($xfIndex);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Duplicate conditional style to a range of cells
+ *
+ * Please note that this will overwrite existing cell styles for cells in range!
+ *
+ * @param array of PHPExcel_Style_Conditional $pCellStyle Cell style to duplicate
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function duplicateConditionalStyle(array $pCellStyle = null, $pRange = '')
+ {
+ foreach ($pCellStyle as $cellStyle) {
+ if (!($cellStyle instanceof PHPExcel_Style_Conditional)) {
+ throw new PHPExcel_Exception('Style is not a conditional style');
+ }
+ }
+
+ // Calculate range outer borders
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange . ':' . $pRange);
+
+ // Make sure we can loop upwards on rows and columns
+ if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
+ $tmp = $rangeStart;
+ $rangeStart = $rangeEnd;
+ $rangeEnd = $tmp;
+ }
+
+ // Loop through cells and apply styles
+ for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
+ for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
+ $this->setConditionalStyles(PHPExcel_Cell::stringFromColumnIndex($col - 1) . $row, $pCellStyle);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Duplicate cell style array to a range of cells
+ *
+ * Please note that this will overwrite existing cell styles for cells in range,
+ * if they are in the styles array. For example, if you decide to set a range of
+ * cells to font bold, only include font bold in the styles array.
+ *
+ * @deprecated
+ * @param array $pStyles Array containing style information
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @param boolean $pAdvanced Advanced mode for setting borders.
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function duplicateStyleArray($pStyles = null, $pRange = '', $pAdvanced = true)
+ {
+ $this->getStyle($pRange)->applyFromArray($pStyles, $pAdvanced);
+ return $this;
+ }
+
+ /**
+ * Set break on a cell
+ *
+ * @param string $pCell Cell coordinate (e.g. A1)
+ * @param int $pBreak Break type (type of PHPExcel_Worksheet::BREAK_*)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setBreak($pCell = 'A1', $pBreak = PHPExcel_Worksheet::BREAK_NONE)
+ {
+ // Uppercase coordinate
+ $pCell = strtoupper($pCell);
+
+ if ($pCell != '') {
+ if ($pBreak == PHPExcel_Worksheet::BREAK_NONE) {
+ if (isset($this->breaks[$pCell])) {
+ unset($this->breaks[$pCell]);
+ }
+ } else {
+ $this->breaks[$pCell] = $pBreak;
+ }
+ } else {
+ throw new PHPExcel_Exception('No cell coordinate specified.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set break on a cell by using numeric cell coordinates
+ *
+ * @param integer $pColumn Numeric column coordinate of the cell
+ * @param integer $pRow Numeric row coordinate of the cell
+ * @param integer $pBreak Break type (type of PHPExcel_Worksheet::BREAK_*)
+ * @return PHPExcel_Worksheet
+ */
+ public function setBreakByColumnAndRow($pColumn = 0, $pRow = 1, $pBreak = PHPExcel_Worksheet::BREAK_NONE)
+ {
+ return $this->setBreak(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow, $pBreak);
+ }
+
+ /**
+ * Get breaks
+ *
+ * @return array[]
+ */
+ public function getBreaks()
+ {
+ return $this->breaks;
+ }
+
+ /**
+ * Set merge on a cell range
+ *
+ * @param string $pRange Cell range (e.g. A1:E1)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function mergeCells($pRange = 'A1:A1')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ if (strpos($pRange, ':') !== false) {
+ $this->mergeCells[$pRange] = $pRange;
+
+ // make sure cells are created
+
+ // get the cells in the range
+ $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
+
+ // create upper left cell if it does not already exist
+ $upperLeft = $aReferences[0];
+ if (!$this->cellExists($upperLeft)) {
+ $this->getCell($upperLeft)->setValueExplicit(null, PHPExcel_Cell_DataType::TYPE_NULL);
+ }
+
+ // Blank out the rest of the cells in the range (if they exist)
+ $count = count($aReferences);
+ for ($i = 1; $i < $count; $i++) {
+ if ($this->cellExists($aReferences[$i])) {
+ $this->getCell($aReferences[$i])->setValueExplicit(null, PHPExcel_Cell_DataType::TYPE_NULL);
+ }
+ }
+ } else {
+ throw new PHPExcel_Exception('Merge must be set on a range of cells.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set merge on a cell range by using numeric cell coordinates
+ *
+ * @param int $pColumn1 Numeric column coordinate of the first cell
+ * @param int $pRow1 Numeric row coordinate of the first cell
+ * @param int $pColumn2 Numeric column coordinate of the last cell
+ * @param int $pRow2 Numeric row coordinate of the last cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function mergeCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
+ {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->mergeCells($cellRange);
+ }
+
+ /**
+ * Remove merge on a cell range
+ *
+ * @param string $pRange Cell range (e.g. A1:E1)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function unmergeCells($pRange = 'A1:A1')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ if (strpos($pRange, ':') !== false) {
+ if (isset($this->mergeCells[$pRange])) {
+ unset($this->mergeCells[$pRange]);
+ } else {
+ throw new PHPExcel_Exception('Cell range ' . $pRange . ' not known as merged.');
+ }
+ } else {
+ throw new PHPExcel_Exception('Merge can only be removed from a range of cells.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Remove merge on a cell range by using numeric cell coordinates
+ *
+ * @param int $pColumn1 Numeric column coordinate of the first cell
+ * @param int $pRow1 Numeric row coordinate of the first cell
+ * @param int $pColumn2 Numeric column coordinate of the last cell
+ * @param int $pRow2 Numeric row coordinate of the last cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function unmergeCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
+ {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->unmergeCells($cellRange);
+ }
+
+ /**
+ * Get merge cells array.
+ *
+ * @return array[]
+ */
+ public function getMergeCells()
+ {
+ return $this->mergeCells;
+ }
+
+ /**
+ * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
+ * a single cell range.
+ *
+ * @param array
+ */
+ public function setMergeCells($pValue = array())
+ {
+ $this->mergeCells = $pValue;
+ return $this;
+ }
+
+ /**
+ * Set protection on a cell range
+ *
+ * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
+ * @param string $pPassword Password to unlock the protection
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function protectCells($pRange = 'A1', $pPassword = '', $pAlreadyHashed = false)
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ if (!$pAlreadyHashed) {
+ $pPassword = PHPExcel_Shared_PasswordHasher::hashPassword($pPassword);
+ }
+ $this->protectedCells[$pRange] = $pPassword;
+
+ return $this;
+ }
+
+ /**
+ * Set protection on a cell range by using numeric cell coordinates
+ *
+ * @param int $pColumn1 Numeric column coordinate of the first cell
+ * @param int $pRow1 Numeric row coordinate of the first cell
+ * @param int $pColumn2 Numeric column coordinate of the last cell
+ * @param int $pRow2 Numeric row coordinate of the last cell
+ * @param string $pPassword Password to unlock the protection
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function protectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $pPassword = '', $pAlreadyHashed = false)
+ {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->protectCells($cellRange, $pPassword, $pAlreadyHashed);
+ }
+
+ /**
+ * Remove protection on a cell range
+ *
+ * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function unprotectCells($pRange = 'A1')
+ {
+ // Uppercase coordinate
+ $pRange = strtoupper($pRange);
+
+ if (isset($this->protectedCells[$pRange])) {
+ unset($this->protectedCells[$pRange]);
+ } else {
+ throw new PHPExcel_Exception('Cell range ' . $pRange . ' not known as protected.');
+ }
+ return $this;
+ }
+
+ /**
+ * Remove protection on a cell range by using numeric cell coordinates
+ *
+ * @param int $pColumn1 Numeric column coordinate of the first cell
+ * @param int $pRow1 Numeric row coordinate of the first cell
+ * @param int $pColumn2 Numeric column coordinate of the last cell
+ * @param int $pRow2 Numeric row coordinate of the last cell
+ * @param string $pPassword Password to unlock the protection
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function unprotectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $pPassword = '', $pAlreadyHashed = false)
+ {
+ $cellRange = PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2;
+ return $this->unprotectCells($cellRange, $pPassword, $pAlreadyHashed);
+ }
+
+ /**
+ * Get protected cells
+ *
+ * @return array[]
+ */
+ public function getProtectedCells()
+ {
+ return $this->protectedCells;
+ }
+
+ /**
+ * Get Autofilter
+ *
+ * @return PHPExcel_Worksheet_AutoFilter
+ */
+ public function getAutoFilter()
+ {
+ return $this->autoFilter;
+ }
+
+ /**
+ * Set AutoFilter
+ *
+ * @param PHPExcel_Worksheet_AutoFilter|string $pValue
+ * A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setAutoFilter($pValue)
+ {
+ $pRange = strtoupper($pValue);
+ if (is_string($pValue)) {
+ $this->autoFilter->setRange($pValue);
+ } elseif (is_object($pValue) && ($pValue instanceof PHPExcel_Worksheet_AutoFilter)) {
+ $this->autoFilter = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Set Autofilter Range by using numeric cell coordinates
+ *
+ * @param integer $pColumn1 Numeric column coordinate of the first cell
+ * @param integer $pRow1 Numeric row coordinate of the first cell
+ * @param integer $pColumn2 Numeric column coordinate of the second cell
+ * @param integer $pRow2 Numeric row coordinate of the second cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setAutoFilterByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
+ {
+ return $this->setAutoFilter(
+ PHPExcel_Cell::stringFromColumnIndex($pColumn1) . $pRow1
+ . ':' .
+ PHPExcel_Cell::stringFromColumnIndex($pColumn2) . $pRow2
+ );
+ }
+
+ /**
+ * Remove autofilter
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function removeAutoFilter()
+ {
+ $this->autoFilter->setRange(null);
+ return $this;
+ }
+
+ /**
+ * Get Freeze Pane
+ *
+ * @return string
+ */
+ public function getFreezePane()
+ {
+ return $this->freezePane;
+ }
+
+ /**
+ * Freeze Pane
+ *
+ * @param string $pCell Cell (i.e. A2)
+ * Examples:
+ * A2 will freeze the rows above cell A2 (i.e row 1)
+ * B1 will freeze the columns to the left of cell B1 (i.e column A)
+ * B2 will freeze the rows above and to the left of cell A2
+ * (i.e row 1 and column A)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function freezePane($pCell = '')
+ {
+ // Uppercase coordinate
+ $pCell = strtoupper($pCell);
+ if (strpos($pCell, ':') === false && strpos($pCell, ',') === false) {
+ $this->freezePane = $pCell;
+ } else {
+ throw new PHPExcel_Exception('Freeze pane can not be set on a range of cells.');
+ }
+ return $this;
+ }
+
+ /**
+ * Freeze Pane by using numeric cell coordinates
+ *
+ * @param int $pColumn Numeric column coordinate of the cell
+ * @param int $pRow Numeric row coordinate of the cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function freezePaneByColumnAndRow($pColumn = 0, $pRow = 1)
+ {
+ return $this->freezePane(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Unfreeze Pane
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function unfreezePane()
+ {
+ return $this->freezePane('');
+ }
+
+ /**
+ * Insert a new row, updating all possible related data
+ *
+ * @param int $pBefore Insert before this one
+ * @param int $pNumRows Number of rows to insert
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function insertNewRowBefore($pBefore = 1, $pNumRows = 1)
+ {
+ if ($pBefore >= 1) {
+ $objReferenceHelper = PHPExcel_ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore('A' . $pBefore, 0, $pNumRows, $this);
+ } else {
+ throw new PHPExcel_Exception("Rows can only be inserted before at least row 1.");
+ }
+ return $this;
+ }
+
+ /**
+ * Insert a new column, updating all possible related data
+ *
+ * @param int $pBefore Insert before this one
+ * @param int $pNumCols Number of columns to insert
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function insertNewColumnBefore($pBefore = 'A', $pNumCols = 1)
+ {
+ if (!is_numeric($pBefore)) {
+ $objReferenceHelper = PHPExcel_ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
+ } else {
+ throw new PHPExcel_Exception("Column references should not be numeric.");
+ }
+ return $this;
+ }
+
+ /**
+ * Insert a new column, updating all possible related data
+ *
+ * @param int $pBefore Insert before this one (numeric column coordinate of the cell)
+ * @param int $pNumCols Number of columns to insert
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function insertNewColumnBeforeByIndex($pBefore = 0, $pNumCols = 1)
+ {
+ if ($pBefore >= 0) {
+ return $this->insertNewColumnBefore(PHPExcel_Cell::stringFromColumnIndex($pBefore), $pNumCols);
+ } else {
+ throw new PHPExcel_Exception("Columns can only be inserted before at least column A (0).");
+ }
+ }
+
+ /**
+ * Delete a row, updating all possible related data
+ *
+ * @param int $pRow Remove starting with this one
+ * @param int $pNumRows Number of rows to remove
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function removeRow($pRow = 1, $pNumRows = 1)
+ {
+ if ($pRow >= 1) {
+ $highestRow = $this->getHighestDataRow();
+ $objReferenceHelper = PHPExcel_ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
+ for ($r = 0; $r < $pNumRows; ++$r) {
+ $this->getCellCacheController()->removeRow($highestRow);
+ --$highestRow;
+ }
+ } else {
+ throw new PHPExcel_Exception("Rows to be deleted should at least start from row 1.");
+ }
+ return $this;
+ }
+
+ /**
+ * Remove a column, updating all possible related data
+ *
+ * @param string $pColumn Remove starting with this one
+ * @param int $pNumCols Number of columns to remove
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function removeColumn($pColumn = 'A', $pNumCols = 1)
+ {
+ if (!is_numeric($pColumn)) {
+ $highestColumn = $this->getHighestDataColumn();
+ $pColumn = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($pColumn) - 1 + $pNumCols);
+ $objReferenceHelper = PHPExcel_ReferenceHelper::getInstance();
+ $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
+ for ($c = 0; $c < $pNumCols; ++$c) {
+ $this->getCellCacheController()->removeColumn($highestColumn);
+ $highestColumn = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::columnIndexFromString($highestColumn) - 2);
+ }
+ } else {
+ throw new PHPExcel_Exception("Column references should not be numeric.");
+ }
+ return $this;
+ }
+
+ /**
+ * Remove a column, updating all possible related data
+ *
+ * @param int $pColumn Remove starting with this one (numeric column coordinate of the cell)
+ * @param int $pNumCols Number of columns to remove
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function removeColumnByIndex($pColumn = 0, $pNumCols = 1)
+ {
+ if ($pColumn >= 0) {
+ return $this->removeColumn(PHPExcel_Cell::stringFromColumnIndex($pColumn), $pNumCols);
+ } else {
+ throw new PHPExcel_Exception("Columns to be deleted should at least start from column 0");
+ }
+ }
+
+ /**
+ * Show gridlines?
+ *
+ * @return boolean
+ */
+ public function getShowGridlines()
+ {
+ return $this->showGridlines;
+ }
+
+ /**
+ * Set show gridlines
+ *
+ * @param boolean $pValue Show gridlines (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setShowGridlines($pValue = false)
+ {
+ $this->showGridlines = $pValue;
+ return $this;
+ }
+
+ /**
+ * Print gridlines?
+ *
+ * @return boolean
+ */
+ public function getPrintGridlines()
+ {
+ return $this->printGridlines;
+ }
+
+ /**
+ * Set print gridlines
+ *
+ * @param boolean $pValue Print gridlines (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setPrintGridlines($pValue = false)
+ {
+ $this->printGridlines = $pValue;
+ return $this;
+ }
+
+ /**
+ * Show row and column headers?
+ *
+ * @return boolean
+ */
+ public function getShowRowColHeaders()
+ {
+ return $this->showRowColHeaders;
+ }
+
+ /**
+ * Set show row and column headers
+ *
+ * @param boolean $pValue Show row and column headers (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setShowRowColHeaders($pValue = false)
+ {
+ $this->showRowColHeaders = $pValue;
+ return $this;
+ }
+
+ /**
+ * Show summary below? (Row/Column outlining)
+ *
+ * @return boolean
+ */
+ public function getShowSummaryBelow()
+ {
+ return $this->showSummaryBelow;
+ }
+
+ /**
+ * Set show summary below
+ *
+ * @param boolean $pValue Show summary below (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setShowSummaryBelow($pValue = true)
+ {
+ $this->showSummaryBelow = $pValue;
+ return $this;
+ }
+
+ /**
+ * Show summary right? (Row/Column outlining)
+ *
+ * @return boolean
+ */
+ public function getShowSummaryRight()
+ {
+ return $this->showSummaryRight;
+ }
+
+ /**
+ * Set show summary right
+ *
+ * @param boolean $pValue Show summary right (true/false)
+ * @return PHPExcel_Worksheet
+ */
+ public function setShowSummaryRight($pValue = true)
+ {
+ $this->showSummaryRight = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get comments
+ *
+ * @return PHPExcel_Comment[]
+ */
+ public function getComments()
+ {
+ return $this->comments;
+ }
+
+ /**
+ * Set comments array for the entire sheet.
+ *
+ * @param array of PHPExcel_Comment
+ * @return PHPExcel_Worksheet
+ */
+ public function setComments($pValue = array())
+ {
+ $this->comments = $pValue;
+
+ return $this;
+ }
+
+ /**
+ * Get comment for cell
+ *
+ * @param string $pCellCoordinate Cell coordinate to get comment for
+ * @return PHPExcel_Comment
+ * @throws PHPExcel_Exception
+ */
+ public function getComment($pCellCoordinate = 'A1')
+ {
+ // Uppercase coordinate
+ $pCellCoordinate = strtoupper($pCellCoordinate);
+
+ if (strpos($pCellCoordinate, ':') !== false || strpos($pCellCoordinate, ',') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate string can not be a range of cells.');
+ } elseif (strpos($pCellCoordinate, '$') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate string must not be absolute.');
+ } elseif ($pCellCoordinate == '') {
+ throw new PHPExcel_Exception('Cell coordinate can not be zero-length string.');
+ } else {
+ // Check if we already have a comment for this cell.
+ // If not, create a new comment.
+ if (isset($this->comments[$pCellCoordinate])) {
+ return $this->comments[$pCellCoordinate];
+ } else {
+ $newComment = new PHPExcel_Comment();
+ $this->comments[$pCellCoordinate] = $newComment;
+ return $newComment;
+ }
+ }
+ }
+
+ /**
+ * Get comment for cell by using numeric cell coordinates
+ *
+ * @param int $pColumn Numeric column coordinate of the cell
+ * @param int $pRow Numeric row coordinate of the cell
+ * @return PHPExcel_Comment
+ */
+ public function getCommentByColumnAndRow($pColumn = 0, $pRow = 1)
+ {
+ return $this->getComment(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Get selected cell
+ *
+ * @deprecated
+ * @return string
+ */
+ public function getSelectedCell()
+ {
+ return $this->getSelectedCells();
+ }
+
+ /**
+ * Get active cell
+ *
+ * @return string Example: 'A1'
+ */
+ public function getActiveCell()
+ {
+ return $this->activeCell;
+ }
+
+ /**
+ * Get selected cells
+ *
+ * @return string
+ */
+ public function getSelectedCells()
+ {
+ return $this->selectedCells;
+ }
+
+ /**
+ * Selected cell
+ *
+ * @param string $pCoordinate Cell (i.e. A1)
+ * @return PHPExcel_Worksheet
+ */
+ public function setSelectedCell($pCoordinate = 'A1')
+ {
+ return $this->setSelectedCells($pCoordinate);
+ }
+
+ /**
+ * Select a range of cells.
+ *
+ * @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setSelectedCells($pCoordinate = 'A1')
+ {
+ // Uppercase coordinate
+ $pCoordinate = strtoupper($pCoordinate);
+
+ // Convert 'A' to 'A:A'
+ $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
+
+ // Convert '1' to '1:1'
+ $pCoordinate = preg_replace('/^([0-9]+)$/', '${1}:${1}', $pCoordinate);
+
+ // Convert 'A:C' to 'A1:C1048576'
+ $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
+
+ // Convert '1:3' to 'A1:XFD3'
+ $pCoordinate = preg_replace('/^([0-9]+):([0-9]+)$/', 'A${1}:XFD${2}', $pCoordinate);
+
+ if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
+ list($first, ) = PHPExcel_Cell::splitRange($pCoordinate);
+ $this->activeCell = $first[0];
+ } else {
+ $this->activeCell = $pCoordinate;
+ }
+ $this->selectedCells = $pCoordinate;
+ return $this;
+ }
+
+ /**
+ * Selected cell by using numeric cell coordinates
+ *
+ * @param int $pColumn Numeric column coordinate of the cell
+ * @param int $pRow Numeric row coordinate of the cell
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function setSelectedCellByColumnAndRow($pColumn = 0, $pRow = 1)
+ {
+ return $this->setSelectedCells(PHPExcel_Cell::stringFromColumnIndex($pColumn) . $pRow);
+ }
+
+ /**
+ * Get right-to-left
+ *
+ * @return boolean
+ */
+ public function getRightToLeft()
+ {
+ return $this->rightToLeft;
+ }
+
+ /**
+ * Set right-to-left
+ *
+ * @param boolean $value Right-to-left true/false
+ * @return PHPExcel_Worksheet
+ */
+ public function setRightToLeft($value = false)
+ {
+ $this->rightToLeft = $value;
+ return $this;
+ }
+
+ /**
+ * Fill worksheet from values in array
+ *
+ * @param array $source Source array
+ * @param mixed $nullValue Value in source array that stands for blank cell
+ * @param string $startCell Insert array starting from this cell address as the top left coordinate
+ * @param boolean $strictNullComparison Apply strict comparison when testing for null values in the array
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet
+ */
+ public function fromArray($source = null, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
+ {
+ if (is_array($source)) {
+ // Convert a 1-D array to 2-D (for ease of looping)
+ if (!is_array(end($source))) {
+ $source = array($source);
+ }
+
+ // start coordinate
+ list ($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($startCell);
+
+ // Loop through $source
+ foreach ($source as $rowData) {
+ $currentColumn = $startColumn;
+ foreach ($rowData as $cellValue) {
+ if ($strictNullComparison) {
+ if ($cellValue !== $nullValue) {
+ // Set cell value
+ $this->getCell($currentColumn . $startRow)->setValue($cellValue);
+ }
+ } else {
+ if ($cellValue != $nullValue) {
+ // Set cell value
+ $this->getCell($currentColumn . $startRow)->setValue($cellValue);
+ }
+ }
+ ++$currentColumn;
+ }
+ ++$startRow;
+ }
+ } else {
+ throw new PHPExcel_Exception("Parameter \$source should be an array.");
+ }
+ return $this;
+ }
+
+ /**
+ * Create array from a range of cells
+ *
+ * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
+ * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+ * @param boolean $calculateFormulas Should formulas be calculated?
+ * @param boolean $formatData Should formatting be applied to cell values?
+ * @param boolean $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+ * True - Return rows and columns indexed by their actual row and column IDs
+ * @return array
+ */
+ public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
+ {
+ // Returnvalue
+ $returnValue = array();
+ // Identify the range that we need to extract from the worksheet
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($pRange);
+ $minCol = PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] -1);
+ $minRow = $rangeStart[1];
+ $maxCol = PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0] -1);
+ $maxRow = $rangeEnd[1];
+
+ $maxCol++;
+ // Loop through rows
+ $r = -1;
+ for ($row = $minRow; $row <= $maxRow; ++$row) {
+ $rRef = ($returnCellRef) ? $row : ++$r;
+ $c = -1;
+ // Loop through columns in the current row
+ for ($col = $minCol; $col != $maxCol; ++$col) {
+ $cRef = ($returnCellRef) ? $col : ++$c;
+ // Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
+ // so we test and retrieve directly against cellCollection
+ if ($this->cellCollection->isDataSet($col.$row)) {
+ // Cell exists
+ $cell = $this->cellCollection->getCacheData($col.$row);
+ if ($cell->getValue() !== null) {
+ if ($cell->getValue() instanceof PHPExcel_RichText) {
+ $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
+ } else {
+ if ($calculateFormulas) {
+ $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
+ } else {
+ $returnValue[$rRef][$cRef] = $cell->getValue();
+ }
+ }
+
+ if ($formatData) {
+ $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
+ $returnValue[$rRef][$cRef] = PHPExcel_Style_NumberFormat::toFormattedString(
+ $returnValue[$rRef][$cRef],
+ ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : PHPExcel_Style_NumberFormat::FORMAT_GENERAL
+ );
+ }
+ } else {
+ // Cell holds a NULL
+ $returnValue[$rRef][$cRef] = $nullValue;
+ }
+ } else {
+ // Cell doesn't exist
+ $returnValue[$rRef][$cRef] = $nullValue;
+ }
+ }
+ }
+
+ // Return
+ return $returnValue;
+ }
+
+
+ /**
+ * Create array from a range of cells
+ *
+ * @param string $pNamedRange Name of the Named Range
+ * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+ * @param boolean $calculateFormulas Should formulas be calculated?
+ * @param boolean $formatData Should formatting be applied to cell values?
+ * @param boolean $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+ * True - Return rows and columns indexed by their actual row and column IDs
+ * @return array
+ * @throws PHPExcel_Exception
+ */
+ public function namedRangeToArray($pNamedRange = '', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
+ {
+ $namedRange = PHPExcel_NamedRange::resolveRange($pNamedRange, $this);
+ if ($namedRange !== null) {
+ $pWorkSheet = $namedRange->getWorksheet();
+ $pCellRange = $namedRange->getRange();
+
+ return $pWorkSheet->rangeToArray($pCellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
+ }
+
+ throw new PHPExcel_Exception('Named Range '.$pNamedRange.' does not exist.');
+ }
+
+
+ /**
+ * Create array from worksheet
+ *
+ * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
+ * @param boolean $calculateFormulas Should formulas be calculated?
+ * @param boolean $formatData Should formatting be applied to cell values?
+ * @param boolean $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
+ * True - Return rows and columns indexed by their actual row and column IDs
+ * @return array
+ */
+ public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
+ {
+ // Garbage collect...
+ $this->garbageCollect();
+
+ // Identify the range that we need to extract from the worksheet
+ $maxCol = $this->getHighestColumn();
+ $maxRow = $this->getHighestRow();
+ // Return
+ return $this->rangeToArray('A1:'.$maxCol.$maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
+ }
+
+ /**
+ * Get row iterator
+ *
+ * @param integer $startRow The row number at which to start iterating
+ * @param integer $endRow The row number at which to stop iterating
+ *
+ * @return PHPExcel_Worksheet_RowIterator
+ */
+ public function getRowIterator($startRow = 1, $endRow = null)
+ {
+ return new PHPExcel_Worksheet_RowIterator($this, $startRow, $endRow);
+ }
+
+ /**
+ * Get column iterator
+ *
+ * @param string $startColumn The column address at which to start iterating
+ * @param string $endColumn The column address at which to stop iterating
+ *
+ * @return PHPExcel_Worksheet_ColumnIterator
+ */
+ public function getColumnIterator($startColumn = 'A', $endColumn = null)
+ {
+ return new PHPExcel_Worksheet_ColumnIterator($this, $startColumn, $endColumn);
+ }
+
+ /**
+ * Run PHPExcel garabage collector.
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function garbageCollect()
+ {
+ // Flush cache
+ $this->cellCollection->getCacheData('A1');
+ // Build a reference table from images
+// $imageCoordinates = array();
+// $iterator = $this->getDrawingCollection()->getIterator();
+// while ($iterator->valid()) {
+// $imageCoordinates[$iterator->current()->getCoordinates()] = true;
+//
+// $iterator->next();
+// }
+//
+ // Lookup highest column and highest row if cells are cleaned
+ $colRow = $this->cellCollection->getHighestRowAndColumn();
+ $highestRow = $colRow['row'];
+ $highestColumn = PHPExcel_Cell::columnIndexFromString($colRow['column']);
+
+ // Loop through column dimensions
+ foreach ($this->columnDimensions as $dimension) {
+ $highestColumn = max($highestColumn, PHPExcel_Cell::columnIndexFromString($dimension->getColumnIndex()));
+ }
+
+ // Loop through row dimensions
+ foreach ($this->rowDimensions as $dimension) {
+ $highestRow = max($highestRow, $dimension->getRowIndex());
+ }
+
+ // Cache values
+ if ($highestColumn < 0) {
+ $this->cachedHighestColumn = 'A';
+ } else {
+ $this->cachedHighestColumn = PHPExcel_Cell::stringFromColumnIndex(--$highestColumn);
+ }
+ $this->cachedHighestRow = $highestRow;
+
+ // Return
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ if ($this->dirty) {
+ $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
+ $this->dirty = false;
+ }
+ return $this->hash;
+ }
+
+ /**
+ * Extract worksheet title from range.
+ *
+ * Example: extractSheetTitle("testSheet!A1") ==> 'A1'
+ * Example: extractSheetTitle("'testSheet 1'!A1", true) ==> array('testSheet 1', 'A1');
+ *
+ * @param string $pRange Range to extract title from
+ * @param bool $returnRange Return range? (see example)
+ * @return mixed
+ */
+ public static function extractSheetTitle($pRange, $returnRange = false)
+ {
+ // Sheet title included?
+ if (($sep = strpos($pRange, '!')) === false) {
+ return '';
+ }
+
+ if ($returnRange) {
+ return array(trim(substr($pRange, 0, $sep), "'"), substr($pRange, $sep + 1));
+ }
+
+ return substr($pRange, $sep + 1);
+ }
+
+ /**
+ * Get hyperlink
+ *
+ * @param string $pCellCoordinate Cell coordinate to get hyperlink for
+ */
+ public function getHyperlink($pCellCoordinate = 'A1')
+ {
+ // return hyperlink if we already have one
+ if (isset($this->hyperlinkCollection[$pCellCoordinate])) {
+ return $this->hyperlinkCollection[$pCellCoordinate];
+ }
+
+ // else create hyperlink
+ $this->hyperlinkCollection[$pCellCoordinate] = new PHPExcel_Cell_Hyperlink();
+ return $this->hyperlinkCollection[$pCellCoordinate];
+ }
+
+ /**
+ * Set hyperlnk
+ *
+ * @param string $pCellCoordinate Cell coordinate to insert hyperlink
+ * @param PHPExcel_Cell_Hyperlink $pHyperlink
+ * @return PHPExcel_Worksheet
+ */
+ public function setHyperlink($pCellCoordinate = 'A1', PHPExcel_Cell_Hyperlink $pHyperlink = null)
+ {
+ if ($pHyperlink === null) {
+ unset($this->hyperlinkCollection[$pCellCoordinate]);
+ } else {
+ $this->hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
+ }
+ return $this;
+ }
+
+ /**
+ * Hyperlink at a specific coordinate exists?
+ *
+ * @param string $pCoordinate
+ * @return boolean
+ */
+ public function hyperlinkExists($pCoordinate = 'A1')
+ {
+ return isset($this->hyperlinkCollection[$pCoordinate]);
+ }
+
+ /**
+ * Get collection of hyperlinks
+ *
+ * @return PHPExcel_Cell_Hyperlink[]
+ */
+ public function getHyperlinkCollection()
+ {
+ return $this->hyperlinkCollection;
+ }
+
+ /**
+ * Get data validation
+ *
+ * @param string $pCellCoordinate Cell coordinate to get data validation for
+ */
+ public function getDataValidation($pCellCoordinate = 'A1')
+ {
+ // return data validation if we already have one
+ if (isset($this->dataValidationCollection[$pCellCoordinate])) {
+ return $this->dataValidationCollection[$pCellCoordinate];
+ }
+
+ // else create data validation
+ $this->dataValidationCollection[$pCellCoordinate] = new PHPExcel_Cell_DataValidation();
+ return $this->dataValidationCollection[$pCellCoordinate];
+ }
+
+ /**
+ * Set data validation
+ *
+ * @param string $pCellCoordinate Cell coordinate to insert data validation
+ * @param PHPExcel_Cell_DataValidation $pDataValidation
+ * @return PHPExcel_Worksheet
+ */
+ public function setDataValidation($pCellCoordinate = 'A1', PHPExcel_Cell_DataValidation $pDataValidation = null)
+ {
+ if ($pDataValidation === null) {
+ unset($this->dataValidationCollection[$pCellCoordinate]);
+ } else {
+ $this->dataValidationCollection[$pCellCoordinate] = $pDataValidation;
+ }
+ return $this;
+ }
+
+ /**
+ * Data validation at a specific coordinate exists?
+ *
+ * @param string $pCoordinate
+ * @return boolean
+ */
+ public function dataValidationExists($pCoordinate = 'A1')
+ {
+ return isset($this->dataValidationCollection[$pCoordinate]);
+ }
+
+ /**
+ * Get collection of data validations
+ *
+ * @return PHPExcel_Cell_DataValidation[]
+ */
+ public function getDataValidationCollection()
+ {
+ return $this->dataValidationCollection;
+ }
+
+ /**
+ * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet
+ *
+ * @param string $range
+ * @return string Adjusted range value
+ */
+ public function shrinkRangeToFit($range)
+ {
+ $maxCol = $this->getHighestColumn();
+ $maxRow = $this->getHighestRow();
+ $maxCol = PHPExcel_Cell::columnIndexFromString($maxCol);
+
+ $rangeBlocks = explode(' ', $range);
+ foreach ($rangeBlocks as &$rangeSet) {
+ $rangeBoundaries = PHPExcel_Cell::getRangeBoundaries($rangeSet);
+
+ if (PHPExcel_Cell::columnIndexFromString($rangeBoundaries[0][0]) > $maxCol) {
+ $rangeBoundaries[0][0] = PHPExcel_Cell::stringFromColumnIndex($maxCol);
+ }
+ if ($rangeBoundaries[0][1] > $maxRow) {
+ $rangeBoundaries[0][1] = $maxRow;
+ }
+ if (PHPExcel_Cell::columnIndexFromString($rangeBoundaries[1][0]) > $maxCol) {
+ $rangeBoundaries[1][0] = PHPExcel_Cell::stringFromColumnIndex($maxCol);
+ }
+ if ($rangeBoundaries[1][1] > $maxRow) {
+ $rangeBoundaries[1][1] = $maxRow;
+ }
+ $rangeSet = $rangeBoundaries[0][0].$rangeBoundaries[0][1].':'.$rangeBoundaries[1][0].$rangeBoundaries[1][1];
+ }
+ unset($rangeSet);
+ $stRange = implode(' ', $rangeBlocks);
+
+ return $stRange;
+ }
+
+ /**
+ * Get tab color
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getTabColor()
+ {
+ if ($this->tabColor === null) {
+ $this->tabColor = new PHPExcel_Style_Color();
+ }
+ return $this->tabColor;
+ }
+
+ /**
+ * Reset tab color
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function resetTabColor()
+ {
+ $this->tabColor = null;
+ unset($this->tabColor);
+
+ return $this;
+ }
+
+ /**
+ * Tab color set?
+ *
+ * @return boolean
+ */
+ public function isTabColorSet()
+ {
+ return ($this->tabColor !== null);
+ }
+
+ /**
+ * Copy worksheet (!= clone!)
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function copy()
+ {
+ $copied = clone $this;
+
+ return $copied;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ foreach ($this as $key => $val) {
+ if ($key == 'parent') {
+ continue;
+ }
+
+ if (is_object($val) || (is_array($val))) {
+ if ($key == 'cellCollection') {
+ $newCollection = clone $this->cellCollection;
+ $newCollection->copyCellCollection($this);
+ $this->cellCollection = $newCollection;
+ } elseif ($key == 'drawingCollection') {
+ $newCollection = clone $this->drawingCollection;
+ $this->drawingCollection = $newCollection;
+ } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof PHPExcel_Worksheet_AutoFilter)) {
+ $newAutoFilter = clone $this->autoFilter;
+ $this->autoFilter = $newAutoFilter;
+ $this->autoFilter->setParent($this);
+ } else {
+ $this->{$key} = unserialize(serialize($val));
+ }
+ }
+ }
+ }
+/**
+ * Define the code name of the sheet
+ *
+ * @param null|string Same rule as Title minus space not allowed (but, like Excel, change silently space to underscore)
+ * @return objWorksheet
+ * @throws PHPExcel_Exception
+ */
+ public function setCodeName($pValue = null)
+ {
+ // Is this a 'rename' or not?
+ if ($this->getCodeName() == $pValue) {
+ return $this;
+ }
+ $pValue = str_replace(' ', '_', $pValue);//Excel does this automatically without flinching, we are doing the same
+ // Syntax check
+ // throw an exception if not valid
+ self::checkSheetCodeName($pValue);
+
+ // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
+
+ if ($this->getParent()) {
+ // Is there already such sheet name?
+ if ($this->getParent()->sheetCodeNameExists($pValue)) {
+ // Use name, but append with lowest possible integer
+
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 29) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue, 0, 29);
+ }
+ $i = 1;
+ while ($this->getParent()->sheetCodeNameExists($pValue . '_' . $i)) {
+ ++$i;
+ if ($i == 10) {
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 28) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue, 0, 28);
+ }
+ } elseif ($i == 100) {
+ if (PHPExcel_Shared_String::CountCharacters($pValue) > 27) {
+ $pValue = PHPExcel_Shared_String::Substring($pValue, 0, 27);
+ }
+ }
+ }
+
+ $pValue = $pValue . '_' . $i;// ok, we have a valid name
+ //codeName is'nt used in formula : no need to call for an update
+ //return $this->setTitle($altTitle, $updateFormulaCellReferences);
+ }
+ }
+
+ $this->codeName=$pValue;
+ return $this;
+ }
+ /**
+ * Return the code name of the sheet
+ *
+ * @return null|string
+ */
+ public function getCodeName()
+ {
+ return $this->codeName;
+ }
+ /**
+ * Sheet has a code name ?
+ * @return boolean
+ */
+ public function hasCodeName()
+ {
+ return !(is_null($this->codeName));
+ }
+}
diff --git a/PHPExcel/Worksheet/AutoFilter.php b/PHPExcel/Worksheet/AutoFilter.php
new file mode 100644
index 0000000..6ec8a44
--- /dev/null
+++ b/PHPExcel/Worksheet/AutoFilter.php
@@ -0,0 +1,846 @@
+range = $pRange;
+ $this->workSheet = $pSheet;
+ }
+
+ /**
+ * Get AutoFilter Parent Worksheet
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function getParent()
+ {
+ return $this->workSheet;
+ }
+
+ /**
+ * Set AutoFilter Parent Worksheet
+ *
+ * @param PHPExcel_Worksheet $pSheet
+ * @return PHPExcel_Worksheet_AutoFilter
+ */
+ public function setParent(PHPExcel_Worksheet $pSheet = null)
+ {
+ $this->workSheet = $pSheet;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Range
+ *
+ * @return string
+ */
+ public function getRange()
+ {
+ return $this->range;
+ }
+
+ /**
+ * Set AutoFilter Range
+ *
+ * @param string $pRange Cell range (i.e. A1:E10)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter
+ */
+ public function setRange($pRange = '')
+ {
+ // Uppercase coordinate
+ $cellAddress = explode('!', strtoupper($pRange));
+ if (count($cellAddress) > 1) {
+ list($worksheet, $pRange) = $cellAddress;
+ }
+
+ if (strpos($pRange, ':') !== false) {
+ $this->range = $pRange;
+ } elseif (empty($pRange)) {
+ $this->range = '';
+ } else {
+ throw new PHPExcel_Exception('Autofilter must be set on a range of cells.');
+ }
+
+ if (empty($pRange)) {
+ // Discard all column rules
+ $this->columns = array();
+ } else {
+ // Discard any column rules that are no longer valid within this range
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($this->range);
+ foreach ($this->columns as $key => $value) {
+ $colIndex = PHPExcel_Cell::columnIndexFromString($key);
+ if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) {
+ unset($this->columns[$key]);
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get all AutoFilter Columns
+ *
+ * @throws PHPExcel_Exception
+ * @return array of PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function getColumns()
+ {
+ return $this->columns;
+ }
+
+ /**
+ * Validate that the specified column is in the AutoFilter range
+ *
+ * @param string $column Column name (e.g. A)
+ * @throws PHPExcel_Exception
+ * @return integer The column offset within the autofilter range
+ */
+ public function testColumnInRange($column)
+ {
+ if (empty($this->range)) {
+ throw new PHPExcel_Exception("No autofilter range is defined.");
+ }
+
+ $columnIndex = PHPExcel_Cell::columnIndexFromString($column);
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($this->range);
+ if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) {
+ throw new PHPExcel_Exception("Column is outside of current autofilter range.");
+ }
+
+ return $columnIndex - $rangeStart[0];
+ }
+
+ /**
+ * Get a specified AutoFilter Column Offset within the defined AutoFilter range
+ *
+ * @param string $pColumn Column name (e.g. A)
+ * @throws PHPExcel_Exception
+ * @return integer The offset of the specified column within the autofilter range
+ */
+ public function getColumnOffset($pColumn)
+ {
+ return $this->testColumnInRange($pColumn);
+ }
+
+ /**
+ * Get a specified AutoFilter Column
+ *
+ * @param string $pColumn Column name (e.g. A)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function getColumn($pColumn)
+ {
+ $this->testColumnInRange($pColumn);
+
+ if (!isset($this->columns[$pColumn])) {
+ $this->columns[$pColumn] = new PHPExcel_Worksheet_AutoFilter_Column($pColumn, $this);
+ }
+
+ return $this->columns[$pColumn];
+ }
+
+ /**
+ * Get a specified AutoFilter Column by it's offset
+ *
+ * @param integer $pColumnOffset Column offset within range (starting from 0)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function getColumnByOffset($pColumnOffset = 0)
+ {
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($this->range);
+ $pColumn = PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] + $pColumnOffset - 1);
+
+ return $this->getColumn($pColumn);
+ }
+
+ /**
+ * Set AutoFilter
+ *
+ * @param PHPExcel_Worksheet_AutoFilter_Column|string $pColumn
+ * A simple string containing a Column ID like 'A' is permitted
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter
+ */
+ public function setColumn($pColumn)
+ {
+ if ((is_string($pColumn)) && (!empty($pColumn))) {
+ $column = $pColumn;
+ } elseif (is_object($pColumn) && ($pColumn instanceof PHPExcel_Worksheet_AutoFilter_Column)) {
+ $column = $pColumn->getColumnIndex();
+ } else {
+ throw new PHPExcel_Exception("Column is not within the autofilter range.");
+ }
+ $this->testColumnInRange($column);
+
+ if (is_string($pColumn)) {
+ $this->columns[$pColumn] = new PHPExcel_Worksheet_AutoFilter_Column($pColumn, $this);
+ } elseif (is_object($pColumn) && ($pColumn instanceof PHPExcel_Worksheet_AutoFilter_Column)) {
+ $pColumn->setParent($this);
+ $this->columns[$column] = $pColumn;
+ }
+ ksort($this->columns);
+
+ return $this;
+ }
+
+ /**
+ * Clear a specified AutoFilter Column
+ *
+ * @param string $pColumn Column name (e.g. A)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter
+ */
+ public function clearColumn($pColumn)
+ {
+ $this->testColumnInRange($pColumn);
+
+ if (isset($this->columns[$pColumn])) {
+ unset($this->columns[$pColumn]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Shift an AutoFilter Column Rule to a different column
+ *
+ * Note: This method bypasses validation of the destination column to ensure it is within this AutoFilter range.
+ * Nor does it verify whether any column rule already exists at $toColumn, but will simply overrideany existing value.
+ * Use with caution.
+ *
+ * @param string $fromColumn Column name (e.g. A)
+ * @param string $toColumn Column name (e.g. B)
+ * @return PHPExcel_Worksheet_AutoFilter
+ */
+ public function shiftColumn($fromColumn = null, $toColumn = null)
+ {
+ $fromColumn = strtoupper($fromColumn);
+ $toColumn = strtoupper($toColumn);
+
+ if (($fromColumn !== null) && (isset($this->columns[$fromColumn])) && ($toColumn !== null)) {
+ $this->columns[$fromColumn]->setParent();
+ $this->columns[$fromColumn]->setColumnIndex($toColumn);
+ $this->columns[$toColumn] = $this->columns[$fromColumn];
+ $this->columns[$toColumn]->setParent($this);
+ unset($this->columns[$fromColumn]);
+
+ ksort($this->columns);
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Test if cell value is in the defined set of values
+ *
+ * @param mixed $cellValue
+ * @param mixed[] $dataSet
+ * @return boolean
+ */
+ private static function filterTestInSimpleDataSet($cellValue, $dataSet)
+ {
+ $dataSetValues = $dataSet['filterValues'];
+ $blanks = $dataSet['blanks'];
+ if (($cellValue == '') || ($cellValue === null)) {
+ return $blanks;
+ }
+ return in_array($cellValue, $dataSetValues);
+ }
+
+ /**
+ * Test if cell value is in the defined set of Excel date values
+ *
+ * @param mixed $cellValue
+ * @param mixed[] $dataSet
+ * @return boolean
+ */
+ private static function filterTestInDateGroupSet($cellValue, $dataSet)
+ {
+ $dateSet = $dataSet['filterValues'];
+ $blanks = $dataSet['blanks'];
+ if (($cellValue == '') || ($cellValue === null)) {
+ return $blanks;
+ }
+
+ if (is_numeric($cellValue)) {
+ $dateValue = PHPExcel_Shared_Date::ExcelToPHP($cellValue);
+ if ($cellValue < 1) {
+ // Just the time part
+ $dtVal = date('His', $dateValue);
+ $dateSet = $dateSet['time'];
+ } elseif ($cellValue == floor($cellValue)) {
+ // Just the date part
+ $dtVal = date('Ymd', $dateValue);
+ $dateSet = $dateSet['date'];
+ } else {
+ // date and time parts
+ $dtVal = date('YmdHis', $dateValue);
+ $dateSet = $dateSet['dateTime'];
+ }
+ foreach ($dateSet as $dateValue) {
+ // Use of substr to extract value at the appropriate group level
+ if (substr($dtVal, 0, strlen($dateValue)) == $dateValue) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test if cell value is within a set of values defined by a ruleset
+ *
+ * @param mixed $cellValue
+ * @param mixed[] $ruleSet
+ * @return boolean
+ */
+ private static function filterTestInCustomDataSet($cellValue, $ruleSet)
+ {
+ $dataSet = $ruleSet['filterRules'];
+ $join = $ruleSet['join'];
+ $customRuleForBlanks = isset($ruleSet['customRuleForBlanks']) ? $ruleSet['customRuleForBlanks'] : false;
+
+ if (!$customRuleForBlanks) {
+ // Blank cells are always ignored, so return a FALSE
+ if (($cellValue == '') || ($cellValue === null)) {
+ return false;
+ }
+ }
+ $returnVal = ($join == PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_AND);
+ foreach ($dataSet as $rule) {
+ if (is_numeric($rule['value'])) {
+ // Numeric values are tested using the appropriate operator
+ switch ($rule['operator']) {
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
+ $retVal = ($cellValue == $rule['value']);
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
+ $retVal = ($cellValue != $rule['value']);
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN:
+ $retVal = ($cellValue > $rule['value']);
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL:
+ $retVal = ($cellValue >= $rule['value']);
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN:
+ $retVal = ($cellValue < $rule['value']);
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL:
+ $retVal = ($cellValue <= $rule['value']);
+ break;
+ }
+ } elseif ($rule['value'] == '') {
+ switch ($rule['operator']) {
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
+ $retVal = (($cellValue == '') || ($cellValue === null));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
+ $retVal = (($cellValue != '') && ($cellValue !== null));
+ break;
+ default:
+ $retVal = true;
+ break;
+ }
+ } else {
+ // String values are always tested for equality, factoring in for wildcards (hence a regexp test)
+ $retVal = preg_match('/^'.$rule['value'].'$/i', $cellValue);
+ }
+ // If there are multiple conditions, then we need to test both using the appropriate join operator
+ switch ($join) {
+ case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_OR:
+ $returnVal = $returnVal || $retVal;
+ // Break as soon as we have a TRUE match for OR joins,
+ // to avoid unnecessary additional code execution
+ if ($returnVal) {
+ return $returnVal;
+ }
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_AND:
+ $returnVal = $returnVal && $retVal;
+ break;
+ }
+ }
+
+ return $returnVal;
+ }
+
+ /**
+ * Test if cell date value is matches a set of values defined by a set of months
+ *
+ * @param mixed $cellValue
+ * @param mixed[] $monthSet
+ * @return boolean
+ */
+ private static function filterTestInPeriodDateSet($cellValue, $monthSet)
+ {
+ // Blank cells are always ignored, so return a FALSE
+ if (($cellValue == '') || ($cellValue === null)) {
+ return false;
+ }
+
+ if (is_numeric($cellValue)) {
+ $dateValue = date('m', PHPExcel_Shared_Date::ExcelToPHP($cellValue));
+ if (in_array($dateValue, $monthSet)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Search/Replace arrays to convert Excel wildcard syntax to a regexp syntax for preg_matching
+ *
+ * @var array
+ */
+ private static $fromReplace = array('\*', '\?', '~~', '~.*', '~.?');
+ private static $toReplace = array('.*', '.', '~', '\*', '\?');
+
+
+ /**
+ * Convert a dynamic rule daterange to a custom filter range expression for ease of calculation
+ *
+ * @param string $dynamicRuleType
+ * @param PHPExcel_Worksheet_AutoFilter_Column &$filterColumn
+ * @return mixed[]
+ */
+ private function dynamicFilterDateRange($dynamicRuleType, &$filterColumn)
+ {
+ $rDateType = PHPExcel_Calculation_Functions::getReturnDateType();
+ PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC);
+ $val = $maxVal = null;
+
+ $ruleValues = array();
+ $baseDate = PHPExcel_Calculation_DateTime::DATENOW();
+ // Calculate start/end dates for the required date range based on current date
+ switch ($dynamicRuleType) {
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
+ $baseDate = strtotime('-7 days', $baseDate);
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
+ $baseDate = strtotime('-7 days', $baseDate);
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
+ $baseDate = strtotime('-1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
+ $baseDate = strtotime('+1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
+ $baseDate = strtotime('-3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
+ $baseDate = strtotime('+3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
+ $baseDate = strtotime('-1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
+ $baseDate = strtotime('+1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+ break;
+ }
+
+ switch ($dynamicRuleType) {
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_TODAY:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
+ $maxVal = (int) PHPExcel_Shared_Date::PHPtoExcel(strtotime('+1 day', $baseDate));
+ $val = (int) PHPExcel_Shared_Date::PHPToExcel($baseDate);
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE:
+ $maxVal = (int) PHPExcel_Shared_Date::PHPtoExcel(strtotime('+1 day', $baseDate));
+ $val = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
+ $maxVal = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0, 0, 0, 31, 12, date('Y', $baseDate)));
+ ++$maxVal;
+ $val = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
+ $thisMonth = date('m', $baseDate);
+ $thisQuarter = floor(--$thisMonth / 3);
+ $maxVal = (int) PHPExcel_Shared_Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), (1+$thisQuarter)*3, date('Y', $baseDate)));
+ ++$maxVal;
+ $val = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1+$thisQuarter*3, date('Y', $baseDate)));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
+ $maxVal = (int) PHPExcel_Shared_Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), date('m', $baseDate), date('Y', $baseDate)));
+ ++$maxVal;
+ $val = (int) PHPExcel_Shared_Date::PHPToExcel(gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
+ $dayOfWeek = date('w', $baseDate);
+ $val = (int) PHPExcel_Shared_Date::PHPToExcel($baseDate) - $dayOfWeek;
+ $maxVal = $val + 7;
+ break;
+ }
+
+ switch ($dynamicRuleType) {
+ // Adjust Today dates for Yesterday and Tomorrow
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
+ --$maxVal;
+ --$val;
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
+ ++$maxVal;
+ ++$val;
+ break;
+ }
+
+ // Set the filter column rule attributes ready for writing
+ $filterColumn->setAttributes(array('val' => $val, 'maxVal' => $maxVal));
+
+ // Set the rules for identifying rows for hide/show
+ $ruleValues[] = array('operator' => PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL, 'value' => $val);
+ $ruleValues[] = array('operator' => PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN, 'value' => $maxVal);
+ PHPExcel_Calculation_Functions::setReturnDateType($rDateType);
+
+ return array('method' => 'filterTestInCustomDataSet', 'arguments' => array('filterRules' => $ruleValues, 'join' => PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_AND));
+ }
+
+ private function calculateTopTenValue($columnID, $startRow, $endRow, $ruleType, $ruleValue)
+ {
+ $range = $columnID.$startRow.':'.$columnID.$endRow;
+ $dataValues = PHPExcel_Calculation_Functions::flattenArray($this->workSheet->rangeToArray($range, null, true, false));
+
+ $dataValues = array_filter($dataValues);
+ if ($ruleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) {
+ rsort($dataValues);
+ } else {
+ sort($dataValues);
+ }
+
+ return array_pop(array_slice($dataValues, 0, $ruleValue));
+ }
+
+ /**
+ * Apply the AutoFilter rules to the AutoFilter Range
+ *
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter
+ */
+ public function showHideRows()
+ {
+ list($rangeStart, $rangeEnd) = PHPExcel_Cell::rangeBoundaries($this->range);
+
+ // The heading row should always be visible
+// echo 'AutoFilter Heading Row ', $rangeStart[1],' is always SHOWN',PHP_EOL;
+ $this->workSheet->getRowDimension($rangeStart[1])->setVisible(true);
+
+ $columnFilterTests = array();
+ foreach ($this->columns as $columnID => $filterColumn) {
+ $rules = $filterColumn->getRules();
+ switch ($filterColumn->getFilterType()) {
+ case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_FILTER:
+ $ruleValues = array();
+ // Build a list of the filter value selections
+ foreach ($rules as $rule) {
+ $ruleType = $rule->getRuleType();
+ $ruleValues[] = $rule->getValue();
+ }
+ // Test if we want to include blanks in our filter criteria
+ $blanks = false;
+ $ruleDataSet = array_filter($ruleValues);
+ if (count($ruleValues) != count($ruleDataSet)) {
+ $blanks = true;
+ }
+ if ($ruleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_FILTER) {
+ // Filter on absolute values
+ $columnFilterTests[$columnID] = array(
+ 'method' => 'filterTestInSimpleDataSet',
+ 'arguments' => array('filterValues' => $ruleDataSet, 'blanks' => $blanks)
+ );
+ } else {
+ // Filter on date group values
+ $arguments = array(
+ 'date' => array(),
+ 'time' => array(),
+ 'dateTime' => array(),
+ );
+ foreach ($ruleDataSet as $ruleValue) {
+ $date = $time = '';
+ if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) &&
+ ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '')) {
+ $date .= sprintf('%04d', $ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]);
+ }
+ if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) &&
+ ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '')) {
+ $date .= sprintf('%02d', $ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]);
+ }
+ if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) &&
+ ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '')) {
+ $date .= sprintf('%02d', $ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]);
+ }
+ if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) &&
+ ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '')) {
+ $time .= sprintf('%02d', $ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]);
+ }
+ if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) &&
+ ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '')) {
+ $time .= sprintf('%02d', $ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]);
+ }
+ if ((isset($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) &&
+ ($ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '')) {
+ $time .= sprintf('%02d', $ruleValue[PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]);
+ }
+ $dateTime = $date . $time;
+ $arguments['date'][] = $date;
+ $arguments['time'][] = $time;
+ $arguments['dateTime'][] = $dateTime;
+ }
+ // Remove empty elements
+ $arguments['date'] = array_filter($arguments['date']);
+ $arguments['time'] = array_filter($arguments['time']);
+ $arguments['dateTime'] = array_filter($arguments['dateTime']);
+ $columnFilterTests[$columnID] = array(
+ 'method' => 'filterTestInDateGroupSet',
+ 'arguments' => array('filterValues' => $arguments, 'blanks' => $blanks)
+ );
+ }
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER:
+ $customRuleForBlanks = false;
+ $ruleValues = array();
+ // Build a list of the filter value selections
+ foreach ($rules as $rule) {
+ $ruleType = $rule->getRuleType();
+ $ruleValue = $rule->getValue();
+ if (!is_numeric($ruleValue)) {
+ // Convert to a regexp allowing for regexp reserved characters, wildcards and escaped wildcards
+ $ruleValue = preg_quote($ruleValue);
+ $ruleValue = str_replace(self::$fromReplace, self::$toReplace, $ruleValue);
+ if (trim($ruleValue) == '') {
+ $customRuleForBlanks = true;
+ $ruleValue = trim($ruleValue);
+ }
+ }
+ $ruleValues[] = array('operator' => $rule->getOperator(), 'value' => $ruleValue);
+ }
+ $join = $filterColumn->getJoin();
+ $columnFilterTests[$columnID] = array(
+ 'method' => 'filterTestInCustomDataSet',
+ 'arguments' => array('filterRules' => $ruleValues, 'join' => $join, 'customRuleForBlanks' => $customRuleForBlanks)
+ );
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER:
+ $ruleValues = array();
+ foreach ($rules as $rule) {
+ // We should only ever have one Dynamic Filter Rule anyway
+ $dynamicRuleType = $rule->getGrouping();
+ if (($dynamicRuleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) ||
+ ($dynamicRuleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)) {
+ // Number (Average) based
+ // Calculate the average
+ $averageFormula = '=AVERAGE('.$columnID.($rangeStart[1]+1).':'.$columnID.$rangeEnd[1].')';
+ $average = PHPExcel_Calculation::getInstance()->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1'));
+ // Set above/below rule based on greaterThan or LessTan
+ $operator = ($dynamicRuleType === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE)
+ ? PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN
+ : PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN;
+ $ruleValues[] = array('operator' => $operator,
+ 'value' => $average
+ );
+ $columnFilterTests[$columnID] = array(
+ 'method' => 'filterTestInCustomDataSet',
+ 'arguments' => array('filterRules' => $ruleValues, 'join' => PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_OR)
+ );
+ } else {
+ // Date based
+ if ($dynamicRuleType{0} == 'M' || $dynamicRuleType{0} == 'Q') {
+ // Month or Quarter
+ sscanf($dynamicRuleType, '%[A-Z]%d', $periodType, $period);
+ if ($periodType == 'M') {
+ $ruleValues = array($period);
+ } else {
+ --$period;
+ $periodEnd = (1+$period)*3;
+ $periodStart = 1+$period*3;
+ $ruleValues = range($periodStart, $periodEnd);
+ }
+ $columnFilterTests[$columnID] = array(
+ 'method' => 'filterTestInPeriodDateSet',
+ 'arguments' => $ruleValues
+ );
+ $filterColumn->setAttributes(array());
+ } else {
+ // Date Range
+ $columnFilterTests[$columnID] = $this->dynamicFilterDateRange($dynamicRuleType, $filterColumn);
+ break;
+ }
+ }
+ }
+ break;
+ case PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER:
+ $ruleValues = array();
+ $dataRowCount = $rangeEnd[1] - $rangeStart[1];
+ foreach ($rules as $rule) {
+ // We should only ever have one Dynamic Filter Rule anyway
+ $toptenRuleType = $rule->getGrouping();
+ $ruleValue = $rule->getValue();
+ $ruleOperator = $rule->getOperator();
+ }
+ if ($ruleOperator === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) {
+ $ruleValue = floor($ruleValue * ($dataRowCount / 100));
+ }
+ if ($ruleValue < 1) {
+ $ruleValue = 1;
+ }
+ if ($ruleValue > 500) {
+ $ruleValue = 500;
+ }
+
+ $maxVal = $this->calculateTopTenValue($columnID, $rangeStart[1]+1, $rangeEnd[1], $toptenRuleType, $ruleValue);
+
+ $operator = ($toptenRuleType == PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP)
+ ? PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL
+ : PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL;
+ $ruleValues[] = array('operator' => $operator, 'value' => $maxVal);
+ $columnFilterTests[$columnID] = array(
+ 'method' => 'filterTestInCustomDataSet',
+ 'arguments' => array('filterRules' => $ruleValues, 'join' => PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_OR)
+ );
+ $filterColumn->setAttributes(array('maxVal' => $maxVal));
+ break;
+ }
+ }
+
+// echo 'Column Filter Test CRITERIA',PHP_EOL;
+// var_dump($columnFilterTests);
+//
+ // Execute the column tests for each row in the autoFilter range to determine show/hide,
+ for ($row = $rangeStart[1]+1; $row <= $rangeEnd[1]; ++$row) {
+// echo 'Testing Row = ', $row,PHP_EOL;
+ $result = true;
+ foreach ($columnFilterTests as $columnID => $columnFilterTest) {
+// echo 'Testing cell ', $columnID.$row,PHP_EOL;
+ $cellValue = $this->workSheet->getCell($columnID.$row)->getCalculatedValue();
+// echo 'Value is ', $cellValue,PHP_EOL;
+ // Execute the filter test
+ $result = $result &&
+ call_user_func_array(
+ array('PHPExcel_Worksheet_AutoFilter', $columnFilterTest['method']),
+ array($cellValue, $columnFilterTest['arguments'])
+ );
+// echo (($result) ? 'VALID' : 'INVALID'),PHP_EOL;
+ // If filter test has resulted in FALSE, exit the loop straightaway rather than running any more tests
+ if (!$result) {
+ break;
+ }
+ }
+ // Set show/hide for the row based on the result of the autoFilter result
+// echo (($result) ? 'SHOW' : 'HIDE'),PHP_EOL;
+ $this->workSheet->getRowDimension($row)->setVisible($result);
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ if ($key == 'workSheet') {
+ // Detach from worksheet
+ $this->{$key} = null;
+ } else {
+ $this->{$key} = clone $value;
+ }
+ } elseif ((is_array($value)) && ($key == 'columns')) {
+ // The columns array of PHPExcel_Worksheet_AutoFilter objects
+ $this->{$key} = array();
+ foreach ($value as $k => $v) {
+ $this->{$key}[$k] = clone $v;
+ // attach the new cloned Column to this new cloned Autofilter object
+ $this->{$key}[$k]->setParent($this);
+ }
+ } else {
+ $this->{$key} = $value;
+ }
+ }
+ }
+
+ /**
+ * toString method replicates previous behavior by returning the range if object is
+ * referenced as a property of its parent.
+ */
+ public function __toString()
+ {
+ return (string) $this->range;
+ }
+}
diff --git a/PHPExcel/Worksheet/AutoFilter/Column.php b/PHPExcel/Worksheet/AutoFilter/Column.php
new file mode 100644
index 0000000..d64fd81
--- /dev/null
+++ b/PHPExcel/Worksheet/AutoFilter/Column.php
@@ -0,0 +1,405 @@
+columnIndex = $pColumn;
+ $this->parent = $pParent;
+ }
+
+ /**
+ * Get AutoFilter Column Index
+ *
+ * @return string
+ */
+ public function getColumnIndex()
+ {
+ return $this->columnIndex;
+ }
+
+ /**
+ * Set AutoFilter Column Index
+ *
+ * @param string $pColumn Column (e.g. A)
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function setColumnIndex($pColumn)
+ {
+ // Uppercase coordinate
+ $pColumn = strtoupper($pColumn);
+ if ($this->parent !== null) {
+ $this->parent->testColumnInRange($pColumn);
+ }
+
+ $this->columnIndex = $pColumn;
+
+ return $this;
+ }
+
+ /**
+ * Get this Column's AutoFilter Parent
+ *
+ * @return PHPExcel_Worksheet_AutoFilter
+ */
+ public function getParent()
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Set this Column's AutoFilter Parent
+ *
+ * @param PHPExcel_Worksheet_AutoFilter
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function setParent(PHPExcel_Worksheet_AutoFilter $pParent = null)
+ {
+ $this->parent = $pParent;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Type
+ *
+ * @return string
+ */
+ public function getFilterType()
+ {
+ return $this->filterType;
+ }
+
+ /**
+ * Set AutoFilter Type
+ *
+ * @param string $pFilterType
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function setFilterType($pFilterType = self::AUTOFILTER_FILTERTYPE_FILTER)
+ {
+ if (!in_array($pFilterType, self::$filterTypes)) {
+ throw new PHPExcel_Exception('Invalid filter type for column AutoFilter.');
+ }
+
+ $this->filterType = $pFilterType;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Multiple Rules And/Or Join
+ *
+ * @return string
+ */
+ public function getJoin()
+ {
+ return $this->join;
+ }
+
+ /**
+ * Set AutoFilter Multiple Rules And/Or
+ *
+ * @param string $pJoin And/Or
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function setJoin($pJoin = self::AUTOFILTER_COLUMN_JOIN_OR)
+ {
+ // Lowercase And/Or
+ $pJoin = strtolower($pJoin);
+ if (!in_array($pJoin, self::$ruleJoins)) {
+ throw new PHPExcel_Exception('Invalid rule connection for column AutoFilter.');
+ }
+
+ $this->join = $pJoin;
+
+ return $this;
+ }
+
+ /**
+ * Set AutoFilter Attributes
+ *
+ * @param string[] $pAttributes
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function setAttributes($pAttributes = array())
+ {
+ $this->attributes = $pAttributes;
+
+ return $this;
+ }
+
+ /**
+ * Set An AutoFilter Attribute
+ *
+ * @param string $pName Attribute Name
+ * @param string $pValue Attribute Value
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function setAttribute($pName, $pValue)
+ {
+ $this->attributes[$pName] = $pValue;
+
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter Column Attributes
+ *
+ * @return string
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * Get specific AutoFilter Column Attribute
+ *
+ * @param string $pName Attribute Name
+ * @return string
+ */
+ public function getAttribute($pName)
+ {
+ if (isset($this->attributes[$pName])) {
+ return $this->attributes[$pName];
+ }
+ return null;
+ }
+
+ /**
+ * Get all AutoFilter Column Rules
+ *
+ * @throws PHPExcel_Exception
+ * @return array of PHPExcel_Worksheet_AutoFilter_Column_Rule
+ */
+ public function getRules()
+ {
+ return $this->ruleset;
+ }
+
+ /**
+ * Get a specified AutoFilter Column Rule
+ *
+ * @param integer $pIndex Rule index in the ruleset array
+ * @return PHPExcel_Worksheet_AutoFilter_Column_Rule
+ */
+ public function getRule($pIndex)
+ {
+ if (!isset($this->ruleset[$pIndex])) {
+ $this->ruleset[$pIndex] = new PHPExcel_Worksheet_AutoFilter_Column_Rule($this);
+ }
+ return $this->ruleset[$pIndex];
+ }
+
+ /**
+ * Create a new AutoFilter Column Rule in the ruleset
+ *
+ * @return PHPExcel_Worksheet_AutoFilter_Column_Rule
+ */
+ public function createRule()
+ {
+ $this->ruleset[] = new PHPExcel_Worksheet_AutoFilter_Column_Rule($this);
+
+ return end($this->ruleset);
+ }
+
+ /**
+ * Add a new AutoFilter Column Rule to the ruleset
+ *
+ * @param PHPExcel_Worksheet_AutoFilter_Column_Rule $pRule
+ * @param boolean $returnRule Flag indicating whether the rule object or the column object should be returned
+ * @return PHPExcel_Worksheet_AutoFilter_Column|PHPExcel_Worksheet_AutoFilter_Column_Rule
+ */
+ public function addRule(PHPExcel_Worksheet_AutoFilter_Column_Rule $pRule, $returnRule = true)
+ {
+ $pRule->setParent($this);
+ $this->ruleset[] = $pRule;
+
+ return ($returnRule) ? $pRule : $this;
+ }
+
+ /**
+ * Delete a specified AutoFilter Column Rule
+ * If the number of rules is reduced to 1, then we reset And/Or logic to Or
+ *
+ * @param integer $pIndex Rule index in the ruleset array
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function deleteRule($pIndex)
+ {
+ if (isset($this->ruleset[$pIndex])) {
+ unset($this->ruleset[$pIndex]);
+ // If we've just deleted down to a single rule, then reset And/Or joining to Or
+ if (count($this->ruleset) <= 1) {
+ $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Delete all AutoFilter Column Rules
+ *
+ * @return PHPExcel_Worksheet_AutoFilter_Column
+ */
+ public function clearRules()
+ {
+ $this->ruleset = array();
+ $this->setJoin(self::AUTOFILTER_COLUMN_JOIN_OR);
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ if ($key == 'parent') {
+ // Detach from autofilter parent
+ $this->$key = null;
+ } else {
+ $this->$key = clone $value;
+ }
+ } elseif ((is_array($value)) && ($key == 'ruleset')) {
+ // The columns array of PHPExcel_Worksheet_AutoFilter objects
+ $this->$key = array();
+ foreach ($value as $k => $v) {
+ $this->$key[$k] = clone $v;
+ // attach the new cloned Rule to this new cloned Autofilter Cloned object
+ $this->$key[$k]->setParent($this);
+ }
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/AutoFilter/Column/Rule.php b/PHPExcel/Worksheet/AutoFilter/Column/Rule.php
new file mode 100644
index 0000000..39ad18b
--- /dev/null
+++ b/PHPExcel/Worksheet/AutoFilter/Column/Rule.php
@@ -0,0 +1,468 @@
+
+ *
+ * $objDrawing->setResizeProportional(true);
+ * $objDrawing->setWidthAndHeight(160,120);
+ *
+ *
+ * @author Vincent@luo MSN:kele_100@hotmail.com
+ * @param int $width
+ * @param int $height
+ * @return PHPExcel_Worksheet_BaseDrawing
+ */
+ public function setWidthAndHeight($width = 0, $height = 0)
+ {
+ $xratio = $width / ($this->width != 0 ? $this->width : 1);
+ $yratio = $height / ($this->height != 0 ? $this->height : 1);
+ if ($this->resizeProportional && !($width == 0 || $height == 0)) {
+ if (($xratio * $this->height) < $height) {
+ $this->height = ceil($xratio * $this->height);
+ $this->width = $width;
+ } else {
+ $this->width = ceil($yratio * $this->width);
+ $this->height = $height;
+ }
+ } else {
+ $this->width = $width;
+ $this->height = $height;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get ResizeProportional
+ *
+ * @return boolean
+ */
+ public function getResizeProportional()
+ {
+ return $this->resizeProportional;
+ }
+
+ /**
+ * Set ResizeProportional
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_BaseDrawing
+ */
+ public function setResizeProportional($pValue = true)
+ {
+ $this->resizeProportional = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Rotation
+ *
+ * @return int
+ */
+ public function getRotation()
+ {
+ return $this->rotation;
+ }
+
+ /**
+ * Set Rotation
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_BaseDrawing
+ */
+ public function setRotation($pValue = 0)
+ {
+ $this->rotation = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Shadow
+ *
+ * @return PHPExcel_Worksheet_Drawing_Shadow
+ */
+ public function getShadow()
+ {
+ return $this->shadow;
+ }
+
+ /**
+ * Set Shadow
+ *
+ * @param PHPExcel_Worksheet_Drawing_Shadow $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_BaseDrawing
+ */
+ public function setShadow(PHPExcel_Worksheet_Drawing_Shadow $pValue = null)
+ {
+ $this->shadow = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ return md5(
+ $this->name .
+ $this->description .
+ $this->worksheet->getHashCode() .
+ $this->coordinates .
+ $this->offsetX .
+ $this->offsetY .
+ $this->width .
+ $this->height .
+ $this->rotation .
+ $this->shadow->getHashCode() .
+ __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/CellIterator.php b/PHPExcel/Worksheet/CellIterator.php
new file mode 100644
index 0000000..151c17f
--- /dev/null
+++ b/PHPExcel/Worksheet/CellIterator.php
@@ -0,0 +1,88 @@
+subject);
+ }
+
+ /**
+ * Get loop only existing cells
+ *
+ * @return boolean
+ */
+ public function getIterateOnlyExistingCells()
+ {
+ return $this->onlyExistingCells;
+ }
+
+ /**
+ * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary
+ *
+ * @throws PHPExcel_Exception
+ */
+ abstract protected function adjustForExistingOnlyRange();
+
+ /**
+ * Set the iterator to loop only existing cells
+ *
+ * @param boolean $value
+ * @throws PHPExcel_Exception
+ */
+ public function setIterateOnlyExistingCells($value = true)
+ {
+ $this->onlyExistingCells = (boolean) $value;
+
+ $this->adjustForExistingOnlyRange();
+ }
+}
diff --git a/PHPExcel/Worksheet/Column.php b/PHPExcel/Worksheet/Column.php
new file mode 100644
index 0000000..6d3f36d
--- /dev/null
+++ b/PHPExcel/Worksheet/Column.php
@@ -0,0 +1,86 @@
+parent = $parent;
+ $this->columnIndex = $columnIndex;
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ unset($this->parent);
+ }
+
+ /**
+ * Get column index
+ *
+ * @return string
+ */
+ public function getColumnIndex()
+ {
+ return $this->columnIndex;
+ }
+
+ /**
+ * Get cell iterator
+ *
+ * @param integer $startRow The row number at which to start iterating
+ * @param integer $endRow Optionally, the row number at which to stop iterating
+ * @return PHPExcel_Worksheet_CellIterator
+ */
+ public function getCellIterator($startRow = 1, $endRow = null)
+ {
+ return new PHPExcel_Worksheet_ColumnCellIterator($this->parent, $this->columnIndex, $startRow, $endRow);
+ }
+}
diff --git a/PHPExcel/Worksheet/ColumnCellIterator.php b/PHPExcel/Worksheet/ColumnCellIterator.php
new file mode 100644
index 0000000..7b8c219
--- /dev/null
+++ b/PHPExcel/Worksheet/ColumnCellIterator.php
@@ -0,0 +1,216 @@
+subject = $subject;
+ $this->columnIndex = PHPExcel_Cell::columnIndexFromString($columnIndex) - 1;
+ $this->resetEnd($endRow);
+ $this->resetStart($startRow);
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ unset($this->subject);
+ }
+
+ /**
+ * (Re)Set the start row and the current row pointer
+ *
+ * @param integer $startRow The row number at which to start iterating
+ * @return PHPExcel_Worksheet_ColumnCellIterator
+ * @throws PHPExcel_Exception
+ */
+ public function resetStart($startRow = 1)
+ {
+ $this->startRow = $startRow;
+ $this->adjustForExistingOnlyRange();
+ $this->seek($startRow);
+
+ return $this;
+ }
+
+ /**
+ * (Re)Set the end row
+ *
+ * @param integer $endRow The row number at which to stop iterating
+ * @return PHPExcel_Worksheet_ColumnCellIterator
+ * @throws PHPExcel_Exception
+ */
+ public function resetEnd($endRow = null)
+ {
+ $this->endRow = ($endRow) ? $endRow : $this->subject->getHighestRow();
+ $this->adjustForExistingOnlyRange();
+
+ return $this;
+ }
+
+ /**
+ * Set the row pointer to the selected row
+ *
+ * @param integer $row The row number to set the current pointer at
+ * @return PHPExcel_Worksheet_ColumnCellIterator
+ * @throws PHPExcel_Exception
+ */
+ public function seek($row = 1)
+ {
+ if (($row < $this->startRow) || ($row > $this->endRow)) {
+ throw new PHPExcel_Exception("Row $row is out of range ({$this->startRow} - {$this->endRow})");
+ } elseif ($this->onlyExistingCells && !($this->subject->cellExistsByColumnAndRow($this->columnIndex, $row))) {
+ throw new PHPExcel_Exception('In "IterateOnlyExistingCells" mode and Cell does not exist');
+ }
+ $this->position = $row;
+
+ return $this;
+ }
+
+ /**
+ * Rewind the iterator to the starting row
+ */
+ public function rewind()
+ {
+ $this->position = $this->startRow;
+ }
+
+ /**
+ * Return the current cell in this worksheet column
+ *
+ * @return PHPExcel_Worksheet_Row
+ */
+ public function current()
+ {
+ return $this->subject->getCellByColumnAndRow($this->columnIndex, $this->position);
+ }
+
+ /**
+ * Return the current iterator key
+ *
+ * @return int
+ */
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * Set the iterator to its next value
+ */
+ public function next()
+ {
+ do {
+ ++$this->position;
+ } while (($this->onlyExistingCells) &&
+ (!$this->subject->cellExistsByColumnAndRow($this->columnIndex, $this->position)) &&
+ ($this->position <= $this->endRow));
+ }
+
+ /**
+ * Set the iterator to its previous value
+ */
+ public function prev()
+ {
+ if ($this->position <= $this->startRow) {
+ throw new PHPExcel_Exception("Row is already at the beginning of range ({$this->startRow} - {$this->endRow})");
+ }
+
+ do {
+ --$this->position;
+ } while (($this->onlyExistingCells) &&
+ (!$this->subject->cellExistsByColumnAndRow($this->columnIndex, $this->position)) &&
+ ($this->position >= $this->startRow));
+ }
+
+ /**
+ * Indicate if more rows exist in the worksheet range of rows that we're iterating
+ *
+ * @return boolean
+ */
+ public function valid()
+ {
+ return $this->position <= $this->endRow;
+ }
+
+ /**
+ * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary
+ *
+ * @throws PHPExcel_Exception
+ */
+ protected function adjustForExistingOnlyRange()
+ {
+ if ($this->onlyExistingCells) {
+ while ((!$this->subject->cellExistsByColumnAndRow($this->columnIndex, $this->startRow)) &&
+ ($this->startRow <= $this->endRow)) {
+ ++$this->startRow;
+ }
+ if ($this->startRow > $this->endRow) {
+ throw new PHPExcel_Exception('No cells exist within the specified range');
+ }
+ while ((!$this->subject->cellExistsByColumnAndRow($this->columnIndex, $this->endRow)) &&
+ ($this->endRow >= $this->startRow)) {
+ --$this->endRow;
+ }
+ if ($this->endRow < $this->startRow) {
+ throw new PHPExcel_Exception('No cells exist within the specified range');
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/ColumnDimension.php b/PHPExcel/Worksheet/ColumnDimension.php
new file mode 100644
index 0000000..405b825
--- /dev/null
+++ b/PHPExcel/Worksheet/ColumnDimension.php
@@ -0,0 +1,132 @@
+columnIndex = $pIndex;
+
+ // set dimension as unformatted by default
+ parent::__construct(0);
+ }
+
+ /**
+ * Get ColumnIndex
+ *
+ * @return string
+ */
+ public function getColumnIndex()
+ {
+ return $this->columnIndex;
+ }
+
+ /**
+ * Set ColumnIndex
+ *
+ * @param string $pValue
+ * @return PHPExcel_Worksheet_ColumnDimension
+ */
+ public function setColumnIndex($pValue)
+ {
+ $this->columnIndex = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Width
+ *
+ * @return double
+ */
+ public function getWidth()
+ {
+ return $this->width;
+ }
+
+ /**
+ * Set Width
+ *
+ * @param double $pValue
+ * @return PHPExcel_Worksheet_ColumnDimension
+ */
+ public function setWidth($pValue = -1)
+ {
+ $this->width = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Auto Size
+ *
+ * @return bool
+ */
+ public function getAutoSize()
+ {
+ return $this->autoSize;
+ }
+
+ /**
+ * Set Auto Size
+ *
+ * @param bool $pValue
+ * @return PHPExcel_Worksheet_ColumnDimension
+ */
+ public function setAutoSize($pValue = false)
+ {
+ $this->autoSize = $pValue;
+ return $this;
+ }
+}
diff --git a/PHPExcel/Worksheet/ColumnIterator.php b/PHPExcel/Worksheet/ColumnIterator.php
new file mode 100644
index 0000000..0db3e53
--- /dev/null
+++ b/PHPExcel/Worksheet/ColumnIterator.php
@@ -0,0 +1,201 @@
+subject = $subject;
+ $this->resetEnd($endColumn);
+ $this->resetStart($startColumn);
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ unset($this->subject);
+ }
+
+ /**
+ * (Re)Set the start column and the current column pointer
+ *
+ * @param integer $startColumn The column address at which to start iterating
+ * @return PHPExcel_Worksheet_ColumnIterator
+ * @throws PHPExcel_Exception
+ */
+ public function resetStart($startColumn = 'A')
+ {
+ $startColumnIndex = PHPExcel_Cell::columnIndexFromString($startColumn) - 1;
+ if ($startColumnIndex > PHPExcel_Cell::columnIndexFromString($this->subject->getHighestColumn()) - 1) {
+ throw new PHPExcel_Exception("Start column ({$startColumn}) is beyond highest column ({$this->subject->getHighestColumn()})");
+ }
+
+ $this->startColumn = $startColumnIndex;
+ if ($this->endColumn < $this->startColumn) {
+ $this->endColumn = $this->startColumn;
+ }
+ $this->seek($startColumn);
+
+ return $this;
+ }
+
+ /**
+ * (Re)Set the end column
+ *
+ * @param string $endColumn The column address at which to stop iterating
+ * @return PHPExcel_Worksheet_ColumnIterator
+ */
+ public function resetEnd($endColumn = null)
+ {
+ $endColumn = ($endColumn) ? $endColumn : $this->subject->getHighestColumn();
+ $this->endColumn = PHPExcel_Cell::columnIndexFromString($endColumn) - 1;
+
+ return $this;
+ }
+
+ /**
+ * Set the column pointer to the selected column
+ *
+ * @param string $column The column address to set the current pointer at
+ * @return PHPExcel_Worksheet_ColumnIterator
+ * @throws PHPExcel_Exception
+ */
+ public function seek($column = 'A')
+ {
+ $column = PHPExcel_Cell::columnIndexFromString($column) - 1;
+ if (($column < $this->startColumn) || ($column > $this->endColumn)) {
+ throw new PHPExcel_Exception("Column $column is out of range ({$this->startColumn} - {$this->endColumn})");
+ }
+ $this->position = $column;
+
+ return $this;
+ }
+
+ /**
+ * Rewind the iterator to the starting column
+ */
+ public function rewind()
+ {
+ $this->position = $this->startColumn;
+ }
+
+ /**
+ * Return the current column in this worksheet
+ *
+ * @return PHPExcel_Worksheet_Column
+ */
+ public function current()
+ {
+ return new PHPExcel_Worksheet_Column($this->subject, PHPExcel_Cell::stringFromColumnIndex($this->position));
+ }
+
+ /**
+ * Return the current iterator key
+ *
+ * @return string
+ */
+ public function key()
+ {
+ return PHPExcel_Cell::stringFromColumnIndex($this->position);
+ }
+
+ /**
+ * Set the iterator to its next value
+ */
+ public function next()
+ {
+ ++$this->position;
+ }
+
+ /**
+ * Set the iterator to its previous value
+ *
+ * @throws PHPExcel_Exception
+ */
+ public function prev()
+ {
+ if ($this->position <= $this->startColumn) {
+ throw new PHPExcel_Exception(
+ "Column is already at the beginning of range (" .
+ PHPExcel_Cell::stringFromColumnIndex($this->endColumn) . " - " .
+ PHPExcel_Cell::stringFromColumnIndex($this->endColumn) . ")"
+ );
+ }
+
+ --$this->position;
+ }
+
+ /**
+ * Indicate if more columns exist in the worksheet range of columns that we're iterating
+ *
+ * @return boolean
+ */
+ public function valid()
+ {
+ return $this->position <= $this->endColumn;
+ }
+}
diff --git a/PHPExcel/Worksheet/Dimension.php b/PHPExcel/Worksheet/Dimension.php
new file mode 100644
index 0000000..84f692c
--- /dev/null
+++ b/PHPExcel/Worksheet/Dimension.php
@@ -0,0 +1,178 @@
+xfIndex = $initialValue;
+ }
+
+ /**
+ * Get Visible
+ *
+ * @return bool
+ */
+ public function getVisible()
+ {
+ return $this->visible;
+ }
+
+ /**
+ * Set Visible
+ *
+ * @param bool $pValue
+ * @return PHPExcel_Worksheet_Dimension
+ */
+ public function setVisible($pValue = true)
+ {
+ $this->visible = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Outline Level
+ *
+ * @return int
+ */
+ public function getOutlineLevel()
+ {
+ return $this->outlineLevel;
+ }
+
+ /**
+ * Set Outline Level
+ *
+ * Value must be between 0 and 7
+ *
+ * @param int $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_Dimension
+ */
+ public function setOutlineLevel($pValue)
+ {
+ if ($pValue < 0 || $pValue > 7) {
+ throw new PHPExcel_Exception("Outline level must range between 0 and 7.");
+ }
+
+ $this->outlineLevel = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Collapsed
+ *
+ * @return bool
+ */
+ public function getCollapsed()
+ {
+ return $this->collapsed;
+ }
+
+ /**
+ * Set Collapsed
+ *
+ * @param bool $pValue
+ * @return PHPExcel_Worksheet_Dimension
+ */
+ public function setCollapsed($pValue = true)
+ {
+ $this->collapsed = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get index to cellXf
+ *
+ * @return int
+ */
+ public function getXfIndex()
+ {
+ return $this->xfIndex;
+ }
+
+ /**
+ * Set index to cellXf
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_Dimension
+ */
+ public function setXfIndex($pValue = 0)
+ {
+ $this->xfIndex = $pValue;
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/Drawing.php b/PHPExcel/Worksheet/Drawing.php
new file mode 100644
index 0000000..e39d3eb
--- /dev/null
+++ b/PHPExcel/Worksheet/Drawing.php
@@ -0,0 +1,147 @@
+path = '';
+
+ // Initialize parent
+ parent::__construct();
+ }
+
+ /**
+ * Get Filename
+ *
+ * @return string
+ */
+ public function getFilename()
+ {
+ return basename($this->path);
+ }
+
+ /**
+ * Get indexed filename (using image index)
+ *
+ * @return string
+ */
+ public function getIndexedFilename()
+ {
+ $fileName = $this->getFilename();
+ $fileName = str_replace(' ', '_', $fileName);
+ return str_replace('.' . $this->getExtension(), '', $fileName) . $this->getImageIndex() . '.' . $this->getExtension();
+ }
+
+ /**
+ * Get Extension
+ *
+ * @return string
+ */
+ public function getExtension()
+ {
+ $exploded = explode(".", basename($this->path));
+ return $exploded[count($exploded) - 1];
+ }
+
+ /**
+ * Get Path
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Set Path
+ *
+ * @param string $pValue File path
+ * @param boolean $pVerifyFile Verify file
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_Drawing
+ */
+ public function setPath($pValue = '', $pVerifyFile = true)
+ {
+ if ($pVerifyFile) {
+ if (file_exists($pValue)) {
+ $this->path = $pValue;
+
+ if ($this->width == 0 && $this->height == 0) {
+ // Get width/height
+ list($this->width, $this->height) = getimagesize($pValue);
+ }
+ } else {
+ throw new PHPExcel_Exception("File $pValue not found!");
+ }
+ } else {
+ $this->path = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ return md5(
+ $this->path .
+ parent::getHashCode() .
+ __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/Drawing/Shadow.php b/PHPExcel/Worksheet/Drawing/Shadow.php
new file mode 100644
index 0000000..84db91d
--- /dev/null
+++ b/PHPExcel/Worksheet/Drawing/Shadow.php
@@ -0,0 +1,296 @@
+visible = false;
+ $this->blurRadius = 6;
+ $this->distance = 2;
+ $this->direction = 0;
+ $this->alignment = PHPExcel_Worksheet_Drawing_Shadow::SHADOW_BOTTOM_RIGHT;
+ $this->color = new PHPExcel_Style_Color(PHPExcel_Style_Color::COLOR_BLACK);
+ $this->alpha = 50;
+ }
+
+ /**
+ * Get Visible
+ *
+ * @return boolean
+ */
+ public function getVisible()
+ {
+ return $this->visible;
+ }
+
+ /**
+ * Set Visible
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Drawing_Shadow
+ */
+ public function setVisible($pValue = false)
+ {
+ $this->visible = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Blur radius
+ *
+ * @return int
+ */
+ public function getBlurRadius()
+ {
+ return $this->blurRadius;
+ }
+
+ /**
+ * Set Blur radius
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_Drawing_Shadow
+ */
+ public function setBlurRadius($pValue = 6)
+ {
+ $this->blurRadius = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Shadow distance
+ *
+ * @return int
+ */
+ public function getDistance()
+ {
+ return $this->distance;
+ }
+
+ /**
+ * Set Shadow distance
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_Drawing_Shadow
+ */
+ public function setDistance($pValue = 2)
+ {
+ $this->distance = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Shadow direction (in degrees)
+ *
+ * @return int
+ */
+ public function getDirection()
+ {
+ return $this->direction;
+ }
+
+ /**
+ * Set Shadow direction (in degrees)
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_Drawing_Shadow
+ */
+ public function setDirection($pValue = 0)
+ {
+ $this->direction = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Shadow alignment
+ *
+ * @return int
+ */
+ public function getAlignment()
+ {
+ return $this->alignment;
+ }
+
+ /**
+ * Set Shadow alignment
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_Drawing_Shadow
+ */
+ public function setAlignment($pValue = 0)
+ {
+ $this->alignment = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Color
+ *
+ * @return PHPExcel_Style_Color
+ */
+ public function getColor()
+ {
+ return $this->color;
+ }
+
+ /**
+ * Set Color
+ *
+ * @param PHPExcel_Style_Color $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_Drawing_Shadow
+ */
+ public function setColor(PHPExcel_Style_Color $pValue = null)
+ {
+ $this->color = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Alpha
+ *
+ * @return int
+ */
+ public function getAlpha()
+ {
+ return $this->alpha;
+ }
+
+ /**
+ * Set Alpha
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_Drawing_Shadow
+ */
+ public function setAlpha($pValue = 0)
+ {
+ $this->alpha = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ return md5(
+ ($this->visible ? 't' : 'f') .
+ $this->blurRadius .
+ $this->distance .
+ $this->direction .
+ $this->alignment .
+ $this->color->getHashCode() .
+ $this->alpha .
+ __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/HeaderFooter.php b/PHPExcel/Worksheet/HeaderFooter.php
new file mode 100644
index 0000000..3a88923
--- /dev/null
+++ b/PHPExcel/Worksheet/HeaderFooter.php
@@ -0,0 +1,494 @@
+
+ * Header/Footer Formatting Syntax taken from Office Open XML Part 4 - Markup Language Reference, page 1970:
+ *
+ * There are a number of formatting codes that can be written inline with the actual header / footer text, which
+ * affect the formatting in the header or footer.
+ *
+ * Example: This example shows the text "Center Bold Header" on the first line (center section), and the date on
+ * the second line (center section).
+ * &CCenter &"-,Bold"Bold&"-,Regular"Header_x000A_&D
+ *
+ * General Rules:
+ * There is no required order in which these codes must appear.
+ *
+ * The first occurrence of the following codes turns the formatting ON, the second occurrence turns it OFF again:
+ * - strikethrough
+ * - superscript
+ * - subscript
+ * Superscript and subscript cannot both be ON at same time. Whichever comes first wins and the other is ignored,
+ * while the first is ON.
+ * &L - code for "left section" (there are three header / footer locations, "left", "center", and "right"). When
+ * two or more occurrences of this section marker exist, the contents from all markers are concatenated, in the
+ * order of appearance, and placed into the left section.
+ * &P - code for "current page #"
+ * &N - code for "total pages"
+ * &font size - code for "text font size", where font size is a font size in points.
+ * &K - code for "text font color"
+ * RGB Color is specified as RRGGBB
+ * Theme Color is specifed as TTSNN where TT is the theme color Id, S is either "+" or "-" of the tint/shade
+ * value, NN is the tint/shade value.
+ * &S - code for "text strikethrough" on / off
+ * &X - code for "text super script" on / off
+ * &Y - code for "text subscript" on / off
+ * &C - code for "center section". When two or more occurrences of this section marker exist, the contents
+ * from all markers are concatenated, in the order of appearance, and placed into the center section.
+ *
+ * &D - code for "date"
+ * &T - code for "time"
+ * &G - code for "picture as background"
+ * &U - code for "text single underline"
+ * &E - code for "double underline"
+ * &R - code for "right section". When two or more occurrences of this section marker exist, the contents
+ * from all markers are concatenated, in the order of appearance, and placed into the right section.
+ * &Z - code for "this workbook's file path"
+ * &F - code for "this workbook's file name"
+ * &A - code for "sheet tab name"
+ * &+ - code for add to page #.
+ * &- - code for subtract from page #.
+ * &"font name,font type" - code for "text font name" and "text font type", where font name and font type
+ * are strings specifying the name and type of the font, separated by a comma. When a hyphen appears in font
+ * name, it means "none specified". Both of font name and font type can be localized values.
+ * &"-,Bold" - code for "bold font style"
+ * &B - also means "bold font style".
+ * &"-,Regular" - code for "regular font style"
+ * &"-,Italic" - code for "italic font style"
+ * &I - also means "italic font style"
+ * &"-,Bold Italic" code for "bold italic font style"
+ * &O - code for "outline style"
+ * &H - code for "shadow style"
+ *
+ *
+ */
+class PHPExcel_Worksheet_HeaderFooter
+{
+ /* Header/footer image location */
+ const IMAGE_HEADER_LEFT = 'LH';
+ const IMAGE_HEADER_CENTER = 'CH';
+ const IMAGE_HEADER_RIGHT = 'RH';
+ const IMAGE_FOOTER_LEFT = 'LF';
+ const IMAGE_FOOTER_CENTER = 'CF';
+ const IMAGE_FOOTER_RIGHT = 'RF';
+
+ /**
+ * OddHeader
+ *
+ * @var string
+ */
+ private $oddHeader = '';
+
+ /**
+ * OddFooter
+ *
+ * @var string
+ */
+ private $oddFooter = '';
+
+ /**
+ * EvenHeader
+ *
+ * @var string
+ */
+ private $evenHeader = '';
+
+ /**
+ * EvenFooter
+ *
+ * @var string
+ */
+ private $evenFooter = '';
+
+ /**
+ * FirstHeader
+ *
+ * @var string
+ */
+ private $firstHeader = '';
+
+ /**
+ * FirstFooter
+ *
+ * @var string
+ */
+ private $firstFooter = '';
+
+ /**
+ * Different header for Odd/Even, defaults to false
+ *
+ * @var boolean
+ */
+ private $differentOddEven = false;
+
+ /**
+ * Different header for first page, defaults to false
+ *
+ * @var boolean
+ */
+ private $differentFirst = false;
+
+ /**
+ * Scale with document, defaults to true
+ *
+ * @var boolean
+ */
+ private $scaleWithDocument = true;
+
+ /**
+ * Align with margins, defaults to true
+ *
+ * @var boolean
+ */
+ private $alignWithMargins = true;
+
+ /**
+ * Header/footer images
+ *
+ * @var PHPExcel_Worksheet_HeaderFooterDrawing[]
+ */
+ private $headerFooterImages = array();
+
+ /**
+ * Create a new PHPExcel_Worksheet_HeaderFooter
+ */
+ public function __construct()
+ {
+ }
+
+ /**
+ * Get OddHeader
+ *
+ * @return string
+ */
+ public function getOddHeader()
+ {
+ return $this->oddHeader;
+ }
+
+ /**
+ * Set OddHeader
+ *
+ * @param string $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setOddHeader($pValue)
+ {
+ $this->oddHeader = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get OddFooter
+ *
+ * @return string
+ */
+ public function getOddFooter()
+ {
+ return $this->oddFooter;
+ }
+
+ /**
+ * Set OddFooter
+ *
+ * @param string $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setOddFooter($pValue)
+ {
+ $this->oddFooter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get EvenHeader
+ *
+ * @return string
+ */
+ public function getEvenHeader()
+ {
+ return $this->evenHeader;
+ }
+
+ /**
+ * Set EvenHeader
+ *
+ * @param string $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setEvenHeader($pValue)
+ {
+ $this->evenHeader = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get EvenFooter
+ *
+ * @return string
+ */
+ public function getEvenFooter()
+ {
+ return $this->evenFooter;
+ }
+
+ /**
+ * Set EvenFooter
+ *
+ * @param string $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setEvenFooter($pValue)
+ {
+ $this->evenFooter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get FirstHeader
+ *
+ * @return string
+ */
+ public function getFirstHeader()
+ {
+ return $this->firstHeader;
+ }
+
+ /**
+ * Set FirstHeader
+ *
+ * @param string $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setFirstHeader($pValue)
+ {
+ $this->firstHeader = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get FirstFooter
+ *
+ * @return string
+ */
+ public function getFirstFooter()
+ {
+ return $this->firstFooter;
+ }
+
+ /**
+ * Set FirstFooter
+ *
+ * @param string $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setFirstFooter($pValue)
+ {
+ $this->firstFooter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get DifferentOddEven
+ *
+ * @return boolean
+ */
+ public function getDifferentOddEven()
+ {
+ return $this->differentOddEven;
+ }
+
+ /**
+ * Set DifferentOddEven
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setDifferentOddEven($pValue = false)
+ {
+ $this->differentOddEven = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get DifferentFirst
+ *
+ * @return boolean
+ */
+ public function getDifferentFirst()
+ {
+ return $this->differentFirst;
+ }
+
+ /**
+ * Set DifferentFirst
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setDifferentFirst($pValue = false)
+ {
+ $this->differentFirst = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get ScaleWithDocument
+ *
+ * @return boolean
+ */
+ public function getScaleWithDocument()
+ {
+ return $this->scaleWithDocument;
+ }
+
+ /**
+ * Set ScaleWithDocument
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setScaleWithDocument($pValue = true)
+ {
+ $this->scaleWithDocument = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get AlignWithMargins
+ *
+ * @return boolean
+ */
+ public function getAlignWithMargins()
+ {
+ return $this->alignWithMargins;
+ }
+
+ /**
+ * Set AlignWithMargins
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setAlignWithMargins($pValue = true)
+ {
+ $this->alignWithMargins = $pValue;
+ return $this;
+ }
+
+ /**
+ * Add header/footer image
+ *
+ * @param PHPExcel_Worksheet_HeaderFooterDrawing $image
+ * @param string $location
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function addImage(PHPExcel_Worksheet_HeaderFooterDrawing $image = null, $location = self::IMAGE_HEADER_LEFT)
+ {
+ $this->headerFooterImages[$location] = $image;
+ return $this;
+ }
+
+ /**
+ * Remove header/footer image
+ *
+ * @param string $location
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function removeImage($location = self::IMAGE_HEADER_LEFT)
+ {
+ if (isset($this->headerFooterImages[$location])) {
+ unset($this->headerFooterImages[$location]);
+ }
+ return $this;
+ }
+
+ /**
+ * Set header/footer images
+ *
+ * @param PHPExcel_Worksheet_HeaderFooterDrawing[] $images
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setImages($images)
+ {
+ if (!is_array($images)) {
+ throw new PHPExcel_Exception('Invalid parameter!');
+ }
+
+ $this->headerFooterImages = $images;
+ return $this;
+ }
+
+ /**
+ * Get header/footer images
+ *
+ * @return PHPExcel_Worksheet_HeaderFooterDrawing[]
+ */
+ public function getImages()
+ {
+ // Sort array
+ $images = array();
+ if (isset($this->headerFooterImages[self::IMAGE_HEADER_LEFT])) {
+ $images[self::IMAGE_HEADER_LEFT] = $this->headerFooterImages[self::IMAGE_HEADER_LEFT];
+ }
+ if (isset($this->headerFooterImages[self::IMAGE_HEADER_CENTER])) {
+ $images[self::IMAGE_HEADER_CENTER] = $this->headerFooterImages[self::IMAGE_HEADER_CENTER];
+ }
+ if (isset($this->headerFooterImages[self::IMAGE_HEADER_RIGHT])) {
+ $images[self::IMAGE_HEADER_RIGHT] = $this->headerFooterImages[self::IMAGE_HEADER_RIGHT];
+ }
+ if (isset($this->headerFooterImages[self::IMAGE_FOOTER_LEFT])) {
+ $images[self::IMAGE_FOOTER_LEFT] = $this->headerFooterImages[self::IMAGE_FOOTER_LEFT];
+ }
+ if (isset($this->headerFooterImages[self::IMAGE_FOOTER_CENTER])) {
+ $images[self::IMAGE_FOOTER_CENTER] = $this->headerFooterImages[self::IMAGE_FOOTER_CENTER];
+ }
+ if (isset($this->headerFooterImages[self::IMAGE_FOOTER_RIGHT])) {
+ $images[self::IMAGE_FOOTER_RIGHT] = $this->headerFooterImages[self::IMAGE_FOOTER_RIGHT];
+ }
+ $this->headerFooterImages = $images;
+
+ return $this->headerFooterImages;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/HeaderFooterDrawing.php b/PHPExcel/Worksheet/HeaderFooterDrawing.php
new file mode 100644
index 0000000..a491f4b
--- /dev/null
+++ b/PHPExcel/Worksheet/HeaderFooterDrawing.php
@@ -0,0 +1,361 @@
+path = '';
+ $this->name = '';
+ $this->offsetX = 0;
+ $this->offsetY = 0;
+ $this->width = 0;
+ $this->height = 0;
+ $this->resizeProportional = true;
+ }
+
+ /**
+ * Get Name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set Name
+ *
+ * @param string $pValue
+ * @return PHPExcel_Worksheet_HeaderFooterDrawing
+ */
+ public function setName($pValue = '')
+ {
+ $this->name = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get OffsetX
+ *
+ * @return int
+ */
+ public function getOffsetX()
+ {
+ return $this->offsetX;
+ }
+
+ /**
+ * Set OffsetX
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_HeaderFooterDrawing
+ */
+ public function setOffsetX($pValue = 0)
+ {
+ $this->offsetX = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get OffsetY
+ *
+ * @return int
+ */
+ public function getOffsetY()
+ {
+ return $this->offsetY;
+ }
+
+ /**
+ * Set OffsetY
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_HeaderFooterDrawing
+ */
+ public function setOffsetY($pValue = 0)
+ {
+ $this->offsetY = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Width
+ *
+ * @return int
+ */
+ public function getWidth()
+ {
+ return $this->width;
+ }
+
+ /**
+ * Set Width
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_HeaderFooterDrawing
+ */
+ public function setWidth($pValue = 0)
+ {
+ // Resize proportional?
+ if ($this->resizeProportional && $pValue != 0) {
+ $ratio = $this->width / $this->height;
+ $this->height = round($ratio * $pValue);
+ }
+
+ // Set width
+ $this->width = $pValue;
+
+ return $this;
+ }
+
+ /**
+ * Get Height
+ *
+ * @return int
+ */
+ public function getHeight()
+ {
+ return $this->height;
+ }
+
+ /**
+ * Set Height
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_HeaderFooterDrawing
+ */
+ public function setHeight($pValue = 0)
+ {
+ // Resize proportional?
+ if ($this->resizeProportional && $pValue != 0) {
+ $ratio = $this->width / $this->height;
+ $this->width = round($ratio * $pValue);
+ }
+
+ // Set height
+ $this->height = $pValue;
+
+ return $this;
+ }
+
+ /**
+ * Set width and height with proportional resize
+ * Example:
+ *
+ * $objDrawing->setResizeProportional(true);
+ * $objDrawing->setWidthAndHeight(160,120);
+ *
+ *
+ * @author Vincent@luo MSN:kele_100@hotmail.com
+ * @param int $width
+ * @param int $height
+ * @return PHPExcel_Worksheet_HeaderFooterDrawing
+ */
+ public function setWidthAndHeight($width = 0, $height = 0)
+ {
+ $xratio = $width / $this->width;
+ $yratio = $height / $this->height;
+ if ($this->resizeProportional && !($width == 0 || $height == 0)) {
+ if (($xratio * $this->height) < $height) {
+ $this->height = ceil($xratio * $this->height);
+ $this->width = $width;
+ } else {
+ $this->width = ceil($yratio * $this->width);
+ $this->height = $height;
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Get ResizeProportional
+ *
+ * @return boolean
+ */
+ public function getResizeProportional()
+ {
+ return $this->resizeProportional;
+ }
+
+ /**
+ * Set ResizeProportional
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_HeaderFooterDrawing
+ */
+ public function setResizeProportional($pValue = true)
+ {
+ $this->resizeProportional = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Filename
+ *
+ * @return string
+ */
+ public function getFilename()
+ {
+ return basename($this->path);
+ }
+
+ /**
+ * Get Extension
+ *
+ * @return string
+ */
+ public function getExtension()
+ {
+ $parts = explode(".", basename($this->path));
+ return end($parts);
+ }
+
+ /**
+ * Get Path
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Set Path
+ *
+ * @param string $pValue File path
+ * @param boolean $pVerifyFile Verify file
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_HeaderFooterDrawing
+ */
+ public function setPath($pValue = '', $pVerifyFile = true)
+ {
+ if ($pVerifyFile) {
+ if (file_exists($pValue)) {
+ $this->path = $pValue;
+
+ if ($this->width == 0 && $this->height == 0) {
+ // Get width/height
+ list($this->width, $this->height) = getimagesize($pValue);
+ }
+ } else {
+ throw new PHPExcel_Exception("File $pValue not found!");
+ }
+ } else {
+ $this->path = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ return md5(
+ $this->path .
+ $this->name .
+ $this->offsetX .
+ $this->offsetY .
+ $this->width .
+ $this->height .
+ __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/MemoryDrawing.php b/PHPExcel/Worksheet/MemoryDrawing.php
new file mode 100644
index 0000000..438ed2c
--- /dev/null
+++ b/PHPExcel/Worksheet/MemoryDrawing.php
@@ -0,0 +1,201 @@
+imageResource = null;
+ $this->renderingFunction = self::RENDERING_DEFAULT;
+ $this->mimeType = self::MIMETYPE_DEFAULT;
+ $this->uniqueName = md5(rand(0, 9999). time() . rand(0, 9999));
+
+ // Initialize parent
+ parent::__construct();
+ }
+
+ /**
+ * Get image resource
+ *
+ * @return resource
+ */
+ public function getImageResource()
+ {
+ return $this->imageResource;
+ }
+
+ /**
+ * Set image resource
+ *
+ * @param $value resource
+ * @return PHPExcel_Worksheet_MemoryDrawing
+ */
+ public function setImageResource($value = null)
+ {
+ $this->imageResource = $value;
+
+ if (!is_null($this->imageResource)) {
+ // Get width/height
+ $this->width = imagesx($this->imageResource);
+ $this->height = imagesy($this->imageResource);
+ }
+ return $this;
+ }
+
+ /**
+ * Get rendering function
+ *
+ * @return string
+ */
+ public function getRenderingFunction()
+ {
+ return $this->renderingFunction;
+ }
+
+ /**
+ * Set rendering function
+ *
+ * @param string $value
+ * @return PHPExcel_Worksheet_MemoryDrawing
+ */
+ public function setRenderingFunction($value = PHPExcel_Worksheet_MemoryDrawing::RENDERING_DEFAULT)
+ {
+ $this->renderingFunction = $value;
+ return $this;
+ }
+
+ /**
+ * Get mime type
+ *
+ * @return string
+ */
+ public function getMimeType()
+ {
+ return $this->mimeType;
+ }
+
+ /**
+ * Set mime type
+ *
+ * @param string $value
+ * @return PHPExcel_Worksheet_MemoryDrawing
+ */
+ public function setMimeType($value = PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_DEFAULT)
+ {
+ $this->mimeType = $value;
+ return $this;
+ }
+
+ /**
+ * Get indexed filename (using image index)
+ *
+ * @return string
+ */
+ public function getIndexedFilename()
+ {
+ $extension = strtolower($this->getMimeType());
+ $extension = explode('/', $extension);
+ $extension = $extension[1];
+
+ return $this->uniqueName . $this->getImageIndex() . '.' . $extension;
+ }
+
+ /**
+ * Get hash code
+ *
+ * @return string Hash code
+ */
+ public function getHashCode()
+ {
+ return md5(
+ $this->renderingFunction .
+ $this->mimeType .
+ $this->uniqueName .
+ parent::getHashCode() .
+ __CLASS__
+ );
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/PageMargins.php b/PHPExcel/Worksheet/PageMargins.php
new file mode 100644
index 0000000..70f5ee0
--- /dev/null
+++ b/PHPExcel/Worksheet/PageMargins.php
@@ -0,0 +1,233 @@
+left;
+ }
+
+ /**
+ * Set Left
+ *
+ * @param double $pValue
+ * @return PHPExcel_Worksheet_PageMargins
+ */
+ public function setLeft($pValue)
+ {
+ $this->left = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Right
+ *
+ * @return double
+ */
+ public function getRight()
+ {
+ return $this->right;
+ }
+
+ /**
+ * Set Right
+ *
+ * @param double $pValue
+ * @return PHPExcel_Worksheet_PageMargins
+ */
+ public function setRight($pValue)
+ {
+ $this->right = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Top
+ *
+ * @return double
+ */
+ public function getTop()
+ {
+ return $this->top;
+ }
+
+ /**
+ * Set Top
+ *
+ * @param double $pValue
+ * @return PHPExcel_Worksheet_PageMargins
+ */
+ public function setTop($pValue)
+ {
+ $this->top = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Bottom
+ *
+ * @return double
+ */
+ public function getBottom()
+ {
+ return $this->bottom;
+ }
+
+ /**
+ * Set Bottom
+ *
+ * @param double $pValue
+ * @return PHPExcel_Worksheet_PageMargins
+ */
+ public function setBottom($pValue)
+ {
+ $this->bottom = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Header
+ *
+ * @return double
+ */
+ public function getHeader()
+ {
+ return $this->header;
+ }
+
+ /**
+ * Set Header
+ *
+ * @param double $pValue
+ * @return PHPExcel_Worksheet_PageMargins
+ */
+ public function setHeader($pValue)
+ {
+ $this->header = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Footer
+ *
+ * @return double
+ */
+ public function getFooter()
+ {
+ return $this->footer;
+ }
+
+ /**
+ * Set Footer
+ *
+ * @param double $pValue
+ * @return PHPExcel_Worksheet_PageMargins
+ */
+ public function setFooter($pValue)
+ {
+ $this->footer = $pValue;
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/PageSetup.php b/PHPExcel/Worksheet/PageSetup.php
new file mode 100644
index 0000000..c67053e
--- /dev/null
+++ b/PHPExcel/Worksheet/PageSetup.php
@@ -0,0 +1,839 @@
+
+ * Paper size taken from Office Open XML Part 4 - Markup Language Reference, page 1988:
+ *
+ * 1 = Letter paper (8.5 in. by 11 in.)
+ * 2 = Letter small paper (8.5 in. by 11 in.)
+ * 3 = Tabloid paper (11 in. by 17 in.)
+ * 4 = Ledger paper (17 in. by 11 in.)
+ * 5 = Legal paper (8.5 in. by 14 in.)
+ * 6 = Statement paper (5.5 in. by 8.5 in.)
+ * 7 = Executive paper (7.25 in. by 10.5 in.)
+ * 8 = A3 paper (297 mm by 420 mm)
+ * 9 = A4 paper (210 mm by 297 mm)
+ * 10 = A4 small paper (210 mm by 297 mm)
+ * 11 = A5 paper (148 mm by 210 mm)
+ * 12 = B4 paper (250 mm by 353 mm)
+ * 13 = B5 paper (176 mm by 250 mm)
+ * 14 = Folio paper (8.5 in. by 13 in.)
+ * 15 = Quarto paper (215 mm by 275 mm)
+ * 16 = Standard paper (10 in. by 14 in.)
+ * 17 = Standard paper (11 in. by 17 in.)
+ * 18 = Note paper (8.5 in. by 11 in.)
+ * 19 = #9 envelope (3.875 in. by 8.875 in.)
+ * 20 = #10 envelope (4.125 in. by 9.5 in.)
+ * 21 = #11 envelope (4.5 in. by 10.375 in.)
+ * 22 = #12 envelope (4.75 in. by 11 in.)
+ * 23 = #14 envelope (5 in. by 11.5 in.)
+ * 24 = C paper (17 in. by 22 in.)
+ * 25 = D paper (22 in. by 34 in.)
+ * 26 = E paper (34 in. by 44 in.)
+ * 27 = DL envelope (110 mm by 220 mm)
+ * 28 = C5 envelope (162 mm by 229 mm)
+ * 29 = C3 envelope (324 mm by 458 mm)
+ * 30 = C4 envelope (229 mm by 324 mm)
+ * 31 = C6 envelope (114 mm by 162 mm)
+ * 32 = C65 envelope (114 mm by 229 mm)
+ * 33 = B4 envelope (250 mm by 353 mm)
+ * 34 = B5 envelope (176 mm by 250 mm)
+ * 35 = B6 envelope (176 mm by 125 mm)
+ * 36 = Italy envelope (110 mm by 230 mm)
+ * 37 = Monarch envelope (3.875 in. by 7.5 in.).
+ * 38 = 6 3/4 envelope (3.625 in. by 6.5 in.)
+ * 39 = US standard fanfold (14.875 in. by 11 in.)
+ * 40 = German standard fanfold (8.5 in. by 12 in.)
+ * 41 = German legal fanfold (8.5 in. by 13 in.)
+ * 42 = ISO B4 (250 mm by 353 mm)
+ * 43 = Japanese double postcard (200 mm by 148 mm)
+ * 44 = Standard paper (9 in. by 11 in.)
+ * 45 = Standard paper (10 in. by 11 in.)
+ * 46 = Standard paper (15 in. by 11 in.)
+ * 47 = Invite envelope (220 mm by 220 mm)
+ * 50 = Letter extra paper (9.275 in. by 12 in.)
+ * 51 = Legal extra paper (9.275 in. by 15 in.)
+ * 52 = Tabloid extra paper (11.69 in. by 18 in.)
+ * 53 = A4 extra paper (236 mm by 322 mm)
+ * 54 = Letter transverse paper (8.275 in. by 11 in.)
+ * 55 = A4 transverse paper (210 mm by 297 mm)
+ * 56 = Letter extra transverse paper (9.275 in. by 12 in.)
+ * 57 = SuperA/SuperA/A4 paper (227 mm by 356 mm)
+ * 58 = SuperB/SuperB/A3 paper (305 mm by 487 mm)
+ * 59 = Letter plus paper (8.5 in. by 12.69 in.)
+ * 60 = A4 plus paper (210 mm by 330 mm)
+ * 61 = A5 transverse paper (148 mm by 210 mm)
+ * 62 = JIS B5 transverse paper (182 mm by 257 mm)
+ * 63 = A3 extra paper (322 mm by 445 mm)
+ * 64 = A5 extra paper (174 mm by 235 mm)
+ * 65 = ISO B5 extra paper (201 mm by 276 mm)
+ * 66 = A2 paper (420 mm by 594 mm)
+ * 67 = A3 transverse paper (297 mm by 420 mm)
+ * 68 = A3 extra transverse paper (322 mm by 445 mm)
+ *
+ *
+ * @category PHPExcel
+ * @package PHPExcel_Worksheet
+ * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
+ */
+class PHPExcel_Worksheet_PageSetup
+{
+ /* Paper size */
+ const PAPERSIZE_LETTER = 1;
+ const PAPERSIZE_LETTER_SMALL = 2;
+ const PAPERSIZE_TABLOID = 3;
+ const PAPERSIZE_LEDGER = 4;
+ const PAPERSIZE_LEGAL = 5;
+ const PAPERSIZE_STATEMENT = 6;
+ const PAPERSIZE_EXECUTIVE = 7;
+ const PAPERSIZE_A3 = 8;
+ const PAPERSIZE_A4 = 9;
+ const PAPERSIZE_A4_SMALL = 10;
+ const PAPERSIZE_A5 = 11;
+ const PAPERSIZE_B4 = 12;
+ const PAPERSIZE_B5 = 13;
+ const PAPERSIZE_FOLIO = 14;
+ const PAPERSIZE_QUARTO = 15;
+ const PAPERSIZE_STANDARD_1 = 16;
+ const PAPERSIZE_STANDARD_2 = 17;
+ const PAPERSIZE_NOTE = 18;
+ const PAPERSIZE_NO9_ENVELOPE = 19;
+ const PAPERSIZE_NO10_ENVELOPE = 20;
+ const PAPERSIZE_NO11_ENVELOPE = 21;
+ const PAPERSIZE_NO12_ENVELOPE = 22;
+ const PAPERSIZE_NO14_ENVELOPE = 23;
+ const PAPERSIZE_C = 24;
+ const PAPERSIZE_D = 25;
+ const PAPERSIZE_E = 26;
+ const PAPERSIZE_DL_ENVELOPE = 27;
+ const PAPERSIZE_C5_ENVELOPE = 28;
+ const PAPERSIZE_C3_ENVELOPE = 29;
+ const PAPERSIZE_C4_ENVELOPE = 30;
+ const PAPERSIZE_C6_ENVELOPE = 31;
+ const PAPERSIZE_C65_ENVELOPE = 32;
+ const PAPERSIZE_B4_ENVELOPE = 33;
+ const PAPERSIZE_B5_ENVELOPE = 34;
+ const PAPERSIZE_B6_ENVELOPE = 35;
+ const PAPERSIZE_ITALY_ENVELOPE = 36;
+ const PAPERSIZE_MONARCH_ENVELOPE = 37;
+ const PAPERSIZE_6_3_4_ENVELOPE = 38;
+ const PAPERSIZE_US_STANDARD_FANFOLD = 39;
+ const PAPERSIZE_GERMAN_STANDARD_FANFOLD = 40;
+ const PAPERSIZE_GERMAN_LEGAL_FANFOLD = 41;
+ const PAPERSIZE_ISO_B4 = 42;
+ const PAPERSIZE_JAPANESE_DOUBLE_POSTCARD = 43;
+ const PAPERSIZE_STANDARD_PAPER_1 = 44;
+ const PAPERSIZE_STANDARD_PAPER_2 = 45;
+ const PAPERSIZE_STANDARD_PAPER_3 = 46;
+ const PAPERSIZE_INVITE_ENVELOPE = 47;
+ const PAPERSIZE_LETTER_EXTRA_PAPER = 48;
+ const PAPERSIZE_LEGAL_EXTRA_PAPER = 49;
+ const PAPERSIZE_TABLOID_EXTRA_PAPER = 50;
+ const PAPERSIZE_A4_EXTRA_PAPER = 51;
+ const PAPERSIZE_LETTER_TRANSVERSE_PAPER = 52;
+ const PAPERSIZE_A4_TRANSVERSE_PAPER = 53;
+ const PAPERSIZE_LETTER_EXTRA_TRANSVERSE_PAPER = 54;
+ const PAPERSIZE_SUPERA_SUPERA_A4_PAPER = 55;
+ const PAPERSIZE_SUPERB_SUPERB_A3_PAPER = 56;
+ const PAPERSIZE_LETTER_PLUS_PAPER = 57;
+ const PAPERSIZE_A4_PLUS_PAPER = 58;
+ const PAPERSIZE_A5_TRANSVERSE_PAPER = 59;
+ const PAPERSIZE_JIS_B5_TRANSVERSE_PAPER = 60;
+ const PAPERSIZE_A3_EXTRA_PAPER = 61;
+ const PAPERSIZE_A5_EXTRA_PAPER = 62;
+ const PAPERSIZE_ISO_B5_EXTRA_PAPER = 63;
+ const PAPERSIZE_A2_PAPER = 64;
+ const PAPERSIZE_A3_TRANSVERSE_PAPER = 65;
+ const PAPERSIZE_A3_EXTRA_TRANSVERSE_PAPER = 66;
+
+ /* Page orientation */
+ const ORIENTATION_DEFAULT = 'default';
+ const ORIENTATION_LANDSCAPE = 'landscape';
+ const ORIENTATION_PORTRAIT = 'portrait';
+
+ /* Print Range Set Method */
+ const SETPRINTRANGE_OVERWRITE = 'O';
+ const SETPRINTRANGE_INSERT = 'I';
+
+
+ /**
+ * Paper size
+ *
+ * @var int
+ */
+ private $paperSize = PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER;
+
+ /**
+ * Orientation
+ *
+ * @var string
+ */
+ private $orientation = PHPExcel_Worksheet_PageSetup::ORIENTATION_DEFAULT;
+
+ /**
+ * Scale (Print Scale)
+ *
+ * Print scaling. Valid values range from 10 to 400
+ * This setting is overridden when fitToWidth and/or fitToHeight are in use
+ *
+ * @var int?
+ */
+ private $scale = 100;
+
+ /**
+ * Fit To Page
+ * Whether scale or fitToWith / fitToHeight applies
+ *
+ * @var boolean
+ */
+ private $fitToPage = false;
+
+ /**
+ * Fit To Height
+ * Number of vertical pages to fit on
+ *
+ * @var int?
+ */
+ private $fitToHeight = 1;
+
+ /**
+ * Fit To Width
+ * Number of horizontal pages to fit on
+ *
+ * @var int?
+ */
+ private $fitToWidth = 1;
+
+ /**
+ * Columns to repeat at left
+ *
+ * @var array Containing start column and end column, empty array if option unset
+ */
+ private $columnsToRepeatAtLeft = array('', '');
+
+ /**
+ * Rows to repeat at top
+ *
+ * @var array Containing start row number and end row number, empty array if option unset
+ */
+ private $rowsToRepeatAtTop = array(0, 0);
+
+ /**
+ * Center page horizontally
+ *
+ * @var boolean
+ */
+ private $horizontalCentered = false;
+
+ /**
+ * Center page vertically
+ *
+ * @var boolean
+ */
+ private $verticalCentered = false;
+
+ /**
+ * Print area
+ *
+ * @var string
+ */
+ private $printArea = null;
+
+ /**
+ * First page number
+ *
+ * @var int
+ */
+ private $firstPageNumber = null;
+
+ /**
+ * Create a new PHPExcel_Worksheet_PageSetup
+ */
+ public function __construct()
+ {
+ }
+
+ /**
+ * Get Paper Size
+ *
+ * @return int
+ */
+ public function getPaperSize()
+ {
+ return $this->paperSize;
+ }
+
+ /**
+ * Set Paper Size
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setPaperSize($pValue = PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER)
+ {
+ $this->paperSize = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Orientation
+ *
+ * @return string
+ */
+ public function getOrientation()
+ {
+ return $this->orientation;
+ }
+
+ /**
+ * Set Orientation
+ *
+ * @param string $pValue
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setOrientation($pValue = PHPExcel_Worksheet_PageSetup::ORIENTATION_DEFAULT)
+ {
+ $this->orientation = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Scale
+ *
+ * @return int?
+ */
+ public function getScale()
+ {
+ return $this->scale;
+ }
+
+ /**
+ * Set Scale
+ *
+ * Print scaling. Valid values range from 10 to 400
+ * This setting is overridden when fitToWidth and/or fitToHeight are in use
+ *
+ * @param int? $pValue
+ * @param boolean $pUpdate Update fitToPage so scaling applies rather than fitToHeight / fitToWidth
+ * @return PHPExcel_Worksheet_PageSetup
+ * @throws PHPExcel_Exception
+ */
+ public function setScale($pValue = 100, $pUpdate = true)
+ {
+ // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
+ // but it is apparently still able to handle any scale >= 0, where 0 results in 100
+ if (($pValue >= 0) || is_null($pValue)) {
+ $this->scale = $pValue;
+ if ($pUpdate) {
+ $this->fitToPage = false;
+ }
+ } else {
+ throw new PHPExcel_Exception("Scale must not be negative");
+ }
+ return $this;
+ }
+
+ /**
+ * Get Fit To Page
+ *
+ * @return boolean
+ */
+ public function getFitToPage()
+ {
+ return $this->fitToPage;
+ }
+
+ /**
+ * Set Fit To Page
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setFitToPage($pValue = true)
+ {
+ $this->fitToPage = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Fit To Height
+ *
+ * @return int?
+ */
+ public function getFitToHeight()
+ {
+ return $this->fitToHeight;
+ }
+
+ /**
+ * Set Fit To Height
+ *
+ * @param int? $pValue
+ * @param boolean $pUpdate Update fitToPage so it applies rather than scaling
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setFitToHeight($pValue = 1, $pUpdate = true)
+ {
+ $this->fitToHeight = $pValue;
+ if ($pUpdate) {
+ $this->fitToPage = true;
+ }
+ return $this;
+ }
+
+ /**
+ * Get Fit To Width
+ *
+ * @return int?
+ */
+ public function getFitToWidth()
+ {
+ return $this->fitToWidth;
+ }
+
+ /**
+ * Set Fit To Width
+ *
+ * @param int? $pValue
+ * @param boolean $pUpdate Update fitToPage so it applies rather than scaling
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setFitToWidth($pValue = 1, $pUpdate = true)
+ {
+ $this->fitToWidth = $pValue;
+ if ($pUpdate) {
+ $this->fitToPage = true;
+ }
+ return $this;
+ }
+
+ /**
+ * Is Columns to repeat at left set?
+ *
+ * @return boolean
+ */
+ public function isColumnsToRepeatAtLeftSet()
+ {
+ if (is_array($this->columnsToRepeatAtLeft)) {
+ if ($this->columnsToRepeatAtLeft[0] != '' && $this->columnsToRepeatAtLeft[1] != '') {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get Columns to repeat at left
+ *
+ * @return array Containing start column and end column, empty array if option unset
+ */
+ public function getColumnsToRepeatAtLeft()
+ {
+ return $this->columnsToRepeatAtLeft;
+ }
+
+ /**
+ * Set Columns to repeat at left
+ *
+ * @param array $pValue Containing start column and end column, empty array if option unset
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setColumnsToRepeatAtLeft($pValue = null)
+ {
+ if (is_array($pValue)) {
+ $this->columnsToRepeatAtLeft = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Set Columns to repeat at left by start and end
+ *
+ * @param string $pStart
+ * @param string $pEnd
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setColumnsToRepeatAtLeftByStartAndEnd($pStart = 'A', $pEnd = 'A')
+ {
+ $this->columnsToRepeatAtLeft = array($pStart, $pEnd);
+ return $this;
+ }
+
+ /**
+ * Is Rows to repeat at top set?
+ *
+ * @return boolean
+ */
+ public function isRowsToRepeatAtTopSet()
+ {
+ if (is_array($this->rowsToRepeatAtTop)) {
+ if ($this->rowsToRepeatAtTop[0] != 0 && $this->rowsToRepeatAtTop[1] != 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get Rows to repeat at top
+ *
+ * @return array Containing start column and end column, empty array if option unset
+ */
+ public function getRowsToRepeatAtTop()
+ {
+ return $this->rowsToRepeatAtTop;
+ }
+
+ /**
+ * Set Rows to repeat at top
+ *
+ * @param array $pValue Containing start column and end column, empty array if option unset
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setRowsToRepeatAtTop($pValue = null)
+ {
+ if (is_array($pValue)) {
+ $this->rowsToRepeatAtTop = $pValue;
+ }
+ return $this;
+ }
+
+ /**
+ * Set Rows to repeat at top by start and end
+ *
+ * @param int $pStart
+ * @param int $pEnd
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setRowsToRepeatAtTopByStartAndEnd($pStart = 1, $pEnd = 1)
+ {
+ $this->rowsToRepeatAtTop = array($pStart, $pEnd);
+ return $this;
+ }
+
+ /**
+ * Get center page horizontally
+ *
+ * @return bool
+ */
+ public function getHorizontalCentered()
+ {
+ return $this->horizontalCentered;
+ }
+
+ /**
+ * Set center page horizontally
+ *
+ * @param bool $value
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setHorizontalCentered($value = false)
+ {
+ $this->horizontalCentered = $value;
+ return $this;
+ }
+
+ /**
+ * Get center page vertically
+ *
+ * @return bool
+ */
+ public function getVerticalCentered()
+ {
+ return $this->verticalCentered;
+ }
+
+ /**
+ * Set center page vertically
+ *
+ * @param bool $value
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function setVerticalCentered($value = false)
+ {
+ $this->verticalCentered = $value;
+ return $this;
+ }
+
+ /**
+ * Get print area
+ *
+ * @param int $index Identifier for a specific print area range if several ranges have been set
+ * Default behaviour, or a index value of 0, will return all ranges as a comma-separated string
+ * Otherwise, the specific range identified by the value of $index will be returned
+ * Print areas are numbered from 1
+ * @throws PHPExcel_Exception
+ * @return string
+ */
+ public function getPrintArea($index = 0)
+ {
+ if ($index == 0) {
+ return $this->printArea;
+ }
+ $printAreas = explode(',', $this->printArea);
+ if (isset($printAreas[$index-1])) {
+ return $printAreas[$index-1];
+ }
+ throw new PHPExcel_Exception("Requested Print Area does not exist");
+ }
+
+ /**
+ * Is print area set?
+ *
+ * @param int $index Identifier for a specific print area range if several ranges have been set
+ * Default behaviour, or an index value of 0, will identify whether any print range is set
+ * Otherwise, existence of the range identified by the value of $index will be returned
+ * Print areas are numbered from 1
+ * @return boolean
+ */
+ public function isPrintAreaSet($index = 0)
+ {
+ if ($index == 0) {
+ return !is_null($this->printArea);
+ }
+ $printAreas = explode(',', $this->printArea);
+ return isset($printAreas[$index-1]);
+ }
+
+ /**
+ * Clear a print area
+ *
+ * @param int $index Identifier for a specific print area range if several ranges have been set
+ * Default behaviour, or an index value of 0, will clear all print ranges that are set
+ * Otherwise, the range identified by the value of $index will be removed from the series
+ * Print areas are numbered from 1
+ * @return PHPExcel_Worksheet_PageSetup
+ */
+ public function clearPrintArea($index = 0)
+ {
+ if ($index == 0) {
+ $this->printArea = null;
+ } else {
+ $printAreas = explode(',', $this->printArea);
+ if (isset($printAreas[$index-1])) {
+ unset($printAreas[$index-1]);
+ $this->printArea = implode(',', $printAreas);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set print area. e.g. 'A1:D10' or 'A1:D10,G5:M20'
+ *
+ * @param string $value
+ * @param int $index Identifier for a specific print area range allowing several ranges to be set
+ * When the method is "O"verwrite, then a positive integer index will overwrite that indexed
+ * entry in the print areas list; a negative index value will identify which entry to
+ * overwrite working bacward through the print area to the list, with the last entry as -1.
+ * Specifying an index value of 0, will overwrite all existing print ranges.
+ * When the method is "I"nsert, then a positive index will insert after that indexed entry in
+ * the print areas list, while a negative index will insert before the indexed entry.
+ * Specifying an index value of 0, will always append the new print range at the end of the
+ * list.
+ * Print areas are numbered from 1
+ * @param string $method Determines the method used when setting multiple print areas
+ * Default behaviour, or the "O" method, overwrites existing print area
+ * The "I" method, inserts the new print area before any specified index, or at the end of the list
+ * @return PHPExcel_Worksheet_PageSetup
+ * @throws PHPExcel_Exception
+ */
+ public function setPrintArea($value, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
+ {
+ if (strpos($value, '!') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate must not specify a worksheet.');
+ } elseif (strpos($value, ':') === false) {
+ throw new PHPExcel_Exception('Cell coordinate must be a range of cells.');
+ } elseif (strpos($value, '$') !== false) {
+ throw new PHPExcel_Exception('Cell coordinate must not be absolute.');
+ }
+ $value = strtoupper($value);
+
+ if ($method == self::SETPRINTRANGE_OVERWRITE) {
+ if ($index == 0) {
+ $this->printArea = $value;
+ } else {
+ $printAreas = explode(',', $this->printArea);
+ if ($index < 0) {
+ $index = count($printAreas) - abs($index) + 1;
+ }
+ if (($index <= 0) || ($index > count($printAreas))) {
+ throw new PHPExcel_Exception('Invalid index for setting print range.');
+ }
+ $printAreas[$index-1] = $value;
+ $this->printArea = implode(',', $printAreas);
+ }
+ } elseif ($method == self::SETPRINTRANGE_INSERT) {
+ if ($index == 0) {
+ $this->printArea .= ($this->printArea == '') ? $value : ','.$value;
+ } else {
+ $printAreas = explode(',', $this->printArea);
+ if ($index < 0) {
+ $index = abs($index) - 1;
+ }
+ if ($index > count($printAreas)) {
+ throw new PHPExcel_Exception('Invalid index for setting print range.');
+ }
+ $printAreas = array_merge(array_slice($printAreas, 0, $index), array($value), array_slice($printAreas, $index));
+ $this->printArea = implode(',', $printAreas);
+ }
+ } else {
+ throw new PHPExcel_Exception('Invalid method for setting print range.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a new print area (e.g. 'A1:D10' or 'A1:D10,G5:M20') to the list of print areas
+ *
+ * @param string $value
+ * @param int $index Identifier for a specific print area range allowing several ranges to be set
+ * A positive index will insert after that indexed entry in the print areas list, while a
+ * negative index will insert before the indexed entry.
+ * Specifying an index value of 0, will always append the new print range at the end of the
+ * list.
+ * Print areas are numbered from 1
+ * @return PHPExcel_Worksheet_PageSetup
+ * @throws PHPExcel_Exception
+ */
+ public function addPrintArea($value, $index = -1)
+ {
+ return $this->setPrintArea($value, $index, self::SETPRINTRANGE_INSERT);
+ }
+
+ /**
+ * Set print area
+ *
+ * @param int $column1 Column 1
+ * @param int $row1 Row 1
+ * @param int $column2 Column 2
+ * @param int $row2 Row 2
+ * @param int $index Identifier for a specific print area range allowing several ranges to be set
+ * When the method is "O"verwrite, then a positive integer index will overwrite that indexed
+ * entry in the print areas list; a negative index value will identify which entry to
+ * overwrite working bacward through the print area to the list, with the last entry as -1.
+ * Specifying an index value of 0, will overwrite all existing print ranges.
+ * When the method is "I"nsert, then a positive index will insert after that indexed entry in
+ * the print areas list, while a negative index will insert before the indexed entry.
+ * Specifying an index value of 0, will always append the new print range at the end of the
+ * list.
+ * Print areas are numbered from 1
+ * @param string $method Determines the method used when setting multiple print areas
+ * Default behaviour, or the "O" method, overwrites existing print area
+ * The "I" method, inserts the new print area before any specified index, or at the end of the list
+ * @return PHPExcel_Worksheet_PageSetup
+ * @throws PHPExcel_Exception
+ */
+ public function setPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = 0, $method = self::SETPRINTRANGE_OVERWRITE)
+ {
+ return $this->setPrintArea(
+ PHPExcel_Cell::stringFromColumnIndex($column1) . $row1 . ':' . PHPExcel_Cell::stringFromColumnIndex($column2) . $row2,
+ $index,
+ $method
+ );
+ }
+
+ /**
+ * Add a new print area to the list of print areas
+ *
+ * @param int $column1 Start Column for the print area
+ * @param int $row1 Start Row for the print area
+ * @param int $column2 End Column for the print area
+ * @param int $row2 End Row for the print area
+ * @param int $index Identifier for a specific print area range allowing several ranges to be set
+ * A positive index will insert after that indexed entry in the print areas list, while a
+ * negative index will insert before the indexed entry.
+ * Specifying an index value of 0, will always append the new print range at the end of the
+ * list.
+ * Print areas are numbered from 1
+ * @return PHPExcel_Worksheet_PageSetup
+ * @throws PHPExcel_Exception
+ */
+ public function addPrintAreaByColumnAndRow($column1, $row1, $column2, $row2, $index = -1)
+ {
+ return $this->setPrintArea(
+ PHPExcel_Cell::stringFromColumnIndex($column1) . $row1 . ':' . PHPExcel_Cell::stringFromColumnIndex($column2) . $row2,
+ $index,
+ self::SETPRINTRANGE_INSERT
+ );
+ }
+
+ /**
+ * Get first page number
+ *
+ * @return int
+ */
+ public function getFirstPageNumber()
+ {
+ return $this->firstPageNumber;
+ }
+
+ /**
+ * Set first page number
+ *
+ * @param int $value
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function setFirstPageNumber($value = null)
+ {
+ $this->firstPageNumber = $value;
+ return $this;
+ }
+
+ /**
+ * Reset first page number
+ *
+ * @return PHPExcel_Worksheet_HeaderFooter
+ */
+ public function resetFirstPageNumber()
+ {
+ return $this->setFirstPageNumber(null);
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/Protection.php b/PHPExcel/Worksheet/Protection.php
new file mode 100644
index 0000000..00633ee
--- /dev/null
+++ b/PHPExcel/Worksheet/Protection.php
@@ -0,0 +1,581 @@
+sheet ||
+ $this->objects ||
+ $this->scenarios ||
+ $this->formatCells ||
+ $this->formatColumns ||
+ $this->formatRows ||
+ $this->insertColumns ||
+ $this->insertRows ||
+ $this->insertHyperlinks ||
+ $this->deleteColumns ||
+ $this->deleteRows ||
+ $this->selectLockedCells ||
+ $this->sort ||
+ $this->autoFilter ||
+ $this->pivotTables ||
+ $this->selectUnlockedCells;
+ }
+
+ /**
+ * Get Sheet
+ *
+ * @return boolean
+ */
+ public function getSheet()
+ {
+ return $this->sheet;
+ }
+
+ /**
+ * Set Sheet
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setSheet($pValue = false)
+ {
+ $this->sheet = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Objects
+ *
+ * @return boolean
+ */
+ public function getObjects()
+ {
+ return $this->objects;
+ }
+
+ /**
+ * Set Objects
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setObjects($pValue = false)
+ {
+ $this->objects = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Scenarios
+ *
+ * @return boolean
+ */
+ public function getScenarios()
+ {
+ return $this->scenarios;
+ }
+
+ /**
+ * Set Scenarios
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setScenarios($pValue = false)
+ {
+ $this->scenarios = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get FormatCells
+ *
+ * @return boolean
+ */
+ public function getFormatCells()
+ {
+ return $this->formatCells;
+ }
+
+ /**
+ * Set FormatCells
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setFormatCells($pValue = false)
+ {
+ $this->formatCells = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get FormatColumns
+ *
+ * @return boolean
+ */
+ public function getFormatColumns()
+ {
+ return $this->formatColumns;
+ }
+
+ /**
+ * Set FormatColumns
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setFormatColumns($pValue = false)
+ {
+ $this->formatColumns = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get FormatRows
+ *
+ * @return boolean
+ */
+ public function getFormatRows()
+ {
+ return $this->formatRows;
+ }
+
+ /**
+ * Set FormatRows
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setFormatRows($pValue = false)
+ {
+ $this->formatRows = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get InsertColumns
+ *
+ * @return boolean
+ */
+ public function getInsertColumns()
+ {
+ return $this->insertColumns;
+ }
+
+ /**
+ * Set InsertColumns
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setInsertColumns($pValue = false)
+ {
+ $this->insertColumns = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get InsertRows
+ *
+ * @return boolean
+ */
+ public function getInsertRows()
+ {
+ return $this->insertRows;
+ }
+
+ /**
+ * Set InsertRows
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setInsertRows($pValue = false)
+ {
+ $this->insertRows = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get InsertHyperlinks
+ *
+ * @return boolean
+ */
+ public function getInsertHyperlinks()
+ {
+ return $this->insertHyperlinks;
+ }
+
+ /**
+ * Set InsertHyperlinks
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setInsertHyperlinks($pValue = false)
+ {
+ $this->insertHyperlinks = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get DeleteColumns
+ *
+ * @return boolean
+ */
+ public function getDeleteColumns()
+ {
+ return $this->deleteColumns;
+ }
+
+ /**
+ * Set DeleteColumns
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setDeleteColumns($pValue = false)
+ {
+ $this->deleteColumns = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get DeleteRows
+ *
+ * @return boolean
+ */
+ public function getDeleteRows()
+ {
+ return $this->deleteRows;
+ }
+
+ /**
+ * Set DeleteRows
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setDeleteRows($pValue = false)
+ {
+ $this->deleteRows = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get SelectLockedCells
+ *
+ * @return boolean
+ */
+ public function getSelectLockedCells()
+ {
+ return $this->selectLockedCells;
+ }
+
+ /**
+ * Set SelectLockedCells
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setSelectLockedCells($pValue = false)
+ {
+ $this->selectLockedCells = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Sort
+ *
+ * @return boolean
+ */
+ public function getSort()
+ {
+ return $this->sort;
+ }
+
+ /**
+ * Set Sort
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setSort($pValue = false)
+ {
+ $this->sort = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get AutoFilter
+ *
+ * @return boolean
+ */
+ public function getAutoFilter()
+ {
+ return $this->autoFilter;
+ }
+
+ /**
+ * Set AutoFilter
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setAutoFilter($pValue = false)
+ {
+ $this->autoFilter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get PivotTables
+ *
+ * @return boolean
+ */
+ public function getPivotTables()
+ {
+ return $this->pivotTables;
+ }
+
+ /**
+ * Set PivotTables
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setPivotTables($pValue = false)
+ {
+ $this->pivotTables = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get SelectUnlockedCells
+ *
+ * @return boolean
+ */
+ public function getSelectUnlockedCells()
+ {
+ return $this->selectUnlockedCells;
+ }
+
+ /**
+ * Set SelectUnlockedCells
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setSelectUnlockedCells($pValue = false)
+ {
+ $this->selectUnlockedCells = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Password (hashed)
+ *
+ * @return string
+ */
+ public function getPassword()
+ {
+ return $this->password;
+ }
+
+ /**
+ * Set Password
+ *
+ * @param string $pValue
+ * @param boolean $pAlreadyHashed If the password has already been hashed, set this to true
+ * @return PHPExcel_Worksheet_Protection
+ */
+ public function setPassword($pValue = '', $pAlreadyHashed = false)
+ {
+ if (!$pAlreadyHashed) {
+ $pValue = PHPExcel_Shared_PasswordHasher::hashPassword($pValue);
+ }
+ $this->password = $pValue;
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/Row.php b/PHPExcel/Worksheet/Row.php
new file mode 100644
index 0000000..4f6034f
--- /dev/null
+++ b/PHPExcel/Worksheet/Row.php
@@ -0,0 +1,86 @@
+parent = $parent;
+ $this->rowIndex = $rowIndex;
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ unset($this->parent);
+ }
+
+ /**
+ * Get row index
+ *
+ * @return int
+ */
+ public function getRowIndex()
+ {
+ return $this->rowIndex;
+ }
+
+ /**
+ * Get cell iterator
+ *
+ * @param string $startColumn The column address at which to start iterating
+ * @param string $endColumn Optionally, the column address at which to stop iterating
+ * @return PHPExcel_Worksheet_CellIterator
+ */
+ public function getCellIterator($startColumn = 'A', $endColumn = null)
+ {
+ return new PHPExcel_Worksheet_RowCellIterator($this->parent, $this->rowIndex, $startColumn, $endColumn);
+ }
+}
diff --git a/PHPExcel/Worksheet/RowCellIterator.php b/PHPExcel/Worksheet/RowCellIterator.php
new file mode 100644
index 0000000..90eb9c9
--- /dev/null
+++ b/PHPExcel/Worksheet/RowCellIterator.php
@@ -0,0 +1,225 @@
+subject = $subject;
+ $this->rowIndex = $rowIndex;
+ $this->resetEnd($endColumn);
+ $this->resetStart($startColumn);
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ unset($this->subject);
+ }
+
+ /**
+ * (Re)Set the start column and the current column pointer
+ *
+ * @param integer $startColumn The column address at which to start iterating
+ * @return PHPExcel_Worksheet_RowCellIterator
+ * @throws PHPExcel_Exception
+ */
+ public function resetStart($startColumn = 'A')
+ {
+ $startColumnIndex = PHPExcel_Cell::columnIndexFromString($startColumn) - 1;
+ $this->startColumn = $startColumnIndex;
+ $this->adjustForExistingOnlyRange();
+ $this->seek(PHPExcel_Cell::stringFromColumnIndex($this->startColumn));
+
+ return $this;
+ }
+
+ /**
+ * (Re)Set the end column
+ *
+ * @param string $endColumn The column address at which to stop iterating
+ * @return PHPExcel_Worksheet_RowCellIterator
+ * @throws PHPExcel_Exception
+ */
+ public function resetEnd($endColumn = null)
+ {
+ $endColumn = ($endColumn) ? $endColumn : $this->subject->getHighestColumn();
+ $this->endColumn = PHPExcel_Cell::columnIndexFromString($endColumn) - 1;
+ $this->adjustForExistingOnlyRange();
+
+ return $this;
+ }
+
+ /**
+ * Set the column pointer to the selected column
+ *
+ * @param string $column The column address to set the current pointer at
+ * @return PHPExcel_Worksheet_RowCellIterator
+ * @throws PHPExcel_Exception
+ */
+ public function seek($column = 'A')
+ {
+ $column = PHPExcel_Cell::columnIndexFromString($column) - 1;
+ if (($column < $this->startColumn) || ($column > $this->endColumn)) {
+ throw new PHPExcel_Exception("Column $column is out of range ({$this->startColumn} - {$this->endColumn})");
+ } elseif ($this->onlyExistingCells && !($this->subject->cellExistsByColumnAndRow($column, $this->rowIndex))) {
+ throw new PHPExcel_Exception('In "IterateOnlyExistingCells" mode and Cell does not exist');
+ }
+ $this->position = $column;
+
+ return $this;
+ }
+
+ /**
+ * Rewind the iterator to the starting column
+ */
+ public function rewind()
+ {
+ $this->position = $this->startColumn;
+ }
+
+ /**
+ * Return the current cell in this worksheet row
+ *
+ * @return PHPExcel_Cell
+ */
+ public function current()
+ {
+ return $this->subject->getCellByColumnAndRow($this->position, $this->rowIndex);
+ }
+
+ /**
+ * Return the current iterator key
+ *
+ * @return string
+ */
+ public function key()
+ {
+ return PHPExcel_Cell::stringFromColumnIndex($this->position);
+ }
+
+ /**
+ * Set the iterator to its next value
+ */
+ public function next()
+ {
+ do {
+ ++$this->position;
+ } while (($this->onlyExistingCells) &&
+ (!$this->subject->cellExistsByColumnAndRow($this->position, $this->rowIndex)) &&
+ ($this->position <= $this->endColumn));
+ }
+
+ /**
+ * Set the iterator to its previous value
+ *
+ * @throws PHPExcel_Exception
+ */
+ public function prev()
+ {
+ if ($this->position <= $this->startColumn) {
+ throw new PHPExcel_Exception(
+ "Column is already at the beginning of range (" .
+ PHPExcel_Cell::stringFromColumnIndex($this->endColumn) . " - " .
+ PHPExcel_Cell::stringFromColumnIndex($this->endColumn) . ")"
+ );
+ }
+
+ do {
+ --$this->position;
+ } while (($this->onlyExistingCells) &&
+ (!$this->subject->cellExistsByColumnAndRow($this->position, $this->rowIndex)) &&
+ ($this->position >= $this->startColumn));
+ }
+
+ /**
+ * Indicate if more columns exist in the worksheet range of columns that we're iterating
+ *
+ * @return boolean
+ */
+ public function valid()
+ {
+ return $this->position <= $this->endColumn;
+ }
+
+ /**
+ * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary
+ *
+ * @throws PHPExcel_Exception
+ */
+ protected function adjustForExistingOnlyRange()
+ {
+ if ($this->onlyExistingCells) {
+ while ((!$this->subject->cellExistsByColumnAndRow($this->startColumn, $this->rowIndex)) &&
+ ($this->startColumn <= $this->endColumn)) {
+ ++$this->startColumn;
+ }
+ if ($this->startColumn > $this->endColumn) {
+ throw new PHPExcel_Exception('No cells exist within the specified range');
+ }
+ while ((!$this->subject->cellExistsByColumnAndRow($this->endColumn, $this->rowIndex)) &&
+ ($this->endColumn >= $this->startColumn)) {
+ --$this->endColumn;
+ }
+ if ($this->endColumn < $this->startColumn) {
+ throw new PHPExcel_Exception('No cells exist within the specified range');
+ }
+ }
+ }
+}
diff --git a/PHPExcel/Worksheet/RowDimension.php b/PHPExcel/Worksheet/RowDimension.php
new file mode 100644
index 0000000..c147486
--- /dev/null
+++ b/PHPExcel/Worksheet/RowDimension.php
@@ -0,0 +1,132 @@
+rowIndex = $pIndex;
+
+ // set dimension as unformatted by default
+ parent::__construct(null);
+ }
+
+ /**
+ * Get Row Index
+ *
+ * @return int
+ */
+ public function getRowIndex()
+ {
+ return $this->rowIndex;
+ }
+
+ /**
+ * Set Row Index
+ *
+ * @param int $pValue
+ * @return PHPExcel_Worksheet_RowDimension
+ */
+ public function setRowIndex($pValue)
+ {
+ $this->rowIndex = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Row Height
+ *
+ * @return double
+ */
+ public function getRowHeight()
+ {
+ return $this->height;
+ }
+
+ /**
+ * Set Row Height
+ *
+ * @param double $pValue
+ * @return PHPExcel_Worksheet_RowDimension
+ */
+ public function setRowHeight($pValue = -1)
+ {
+ $this->height = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get ZeroHeight
+ *
+ * @return bool
+ */
+ public function getZeroHeight()
+ {
+ return $this->zeroHeight;
+ }
+
+ /**
+ * Set ZeroHeight
+ *
+ * @param bool $pValue
+ * @return PHPExcel_Worksheet_RowDimension
+ */
+ public function setZeroHeight($pValue = false)
+ {
+ $this->zeroHeight = $pValue;
+ return $this;
+ }
+}
diff --git a/PHPExcel/Worksheet/RowIterator.php b/PHPExcel/Worksheet/RowIterator.php
new file mode 100644
index 0000000..9ddb3e0
--- /dev/null
+++ b/PHPExcel/Worksheet/RowIterator.php
@@ -0,0 +1,192 @@
+subject = $subject;
+ $this->resetEnd($endRow);
+ $this->resetStart($startRow);
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ unset($this->subject);
+ }
+
+ /**
+ * (Re)Set the start row and the current row pointer
+ *
+ * @param integer $startRow The row number at which to start iterating
+ * @return PHPExcel_Worksheet_RowIterator
+ * @throws PHPExcel_Exception
+ */
+ public function resetStart($startRow = 1)
+ {
+ if ($startRow > $this->subject->getHighestRow()) {
+ throw new PHPExcel_Exception("Start row ({$startRow}) is beyond highest row ({$this->subject->getHighestRow()})");
+ }
+
+ $this->startRow = $startRow;
+ if ($this->endRow < $this->startRow) {
+ $this->endRow = $this->startRow;
+ }
+ $this->seek($startRow);
+
+ return $this;
+ }
+
+ /**
+ * (Re)Set the end row
+ *
+ * @param integer $endRow The row number at which to stop iterating
+ * @return PHPExcel_Worksheet_RowIterator
+ */
+ public function resetEnd($endRow = null)
+ {
+ $this->endRow = ($endRow) ? $endRow : $this->subject->getHighestRow();
+
+ return $this;
+ }
+
+ /**
+ * Set the row pointer to the selected row
+ *
+ * @param integer $row The row number to set the current pointer at
+ * @return PHPExcel_Worksheet_RowIterator
+ * @throws PHPExcel_Exception
+ */
+ public function seek($row = 1)
+ {
+ if (($row < $this->startRow) || ($row > $this->endRow)) {
+ throw new PHPExcel_Exception("Row $row is out of range ({$this->startRow} - {$this->endRow})");
+ }
+ $this->position = $row;
+
+ return $this;
+ }
+
+ /**
+ * Rewind the iterator to the starting row
+ */
+ public function rewind()
+ {
+ $this->position = $this->startRow;
+ }
+
+ /**
+ * Return the current row in this worksheet
+ *
+ * @return PHPExcel_Worksheet_Row
+ */
+ public function current()
+ {
+ return new PHPExcel_Worksheet_Row($this->subject, $this->position);
+ }
+
+ /**
+ * Return the current iterator key
+ *
+ * @return int
+ */
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * Set the iterator to its next value
+ */
+ public function next()
+ {
+ ++$this->position;
+ }
+
+ /**
+ * Set the iterator to its previous value
+ */
+ public function prev()
+ {
+ if ($this->position <= $this->startRow) {
+ throw new PHPExcel_Exception("Row is already at the beginning of range ({$this->startRow} - {$this->endRow})");
+ }
+
+ --$this->position;
+ }
+
+ /**
+ * Indicate if more rows exist in the worksheet range of rows that we're iterating
+ *
+ * @return boolean
+ */
+ public function valid()
+ {
+ return $this->position <= $this->endRow;
+ }
+}
diff --git a/PHPExcel/Worksheet/SheetView.php b/PHPExcel/Worksheet/SheetView.php
new file mode 100644
index 0000000..5aaef3b
--- /dev/null
+++ b/PHPExcel/Worksheet/SheetView.php
@@ -0,0 +1,187 @@
+zoomScale;
+ }
+
+ /**
+ * Set ZoomScale
+ *
+ * Valid values range from 10 to 400.
+ *
+ * @param int $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_SheetView
+ */
+ public function setZoomScale($pValue = 100)
+ {
+ // Microsoft Office Excel 2007 only allows setting a scale between 10 and 400 via the user interface,
+ // but it is apparently still able to handle any scale >= 1
+ if (($pValue >= 1) || is_null($pValue)) {
+ $this->zoomScale = $pValue;
+ } else {
+ throw new PHPExcel_Exception("Scale must be greater than or equal to 1.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get ZoomScaleNormal
+ *
+ * @return int
+ */
+ public function getZoomScaleNormal()
+ {
+ return $this->zoomScaleNormal;
+ }
+
+ /**
+ * Set ZoomScale
+ *
+ * Valid values range from 10 to 400.
+ *
+ * @param int $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_SheetView
+ */
+ public function setZoomScaleNormal($pValue = 100)
+ {
+ if (($pValue >= 1) || is_null($pValue)) {
+ $this->zoomScaleNormal = $pValue;
+ } else {
+ throw new PHPExcel_Exception("Scale must be greater than or equal to 1.");
+ }
+ return $this;
+ }
+
+ /**
+ * Get View
+ *
+ * @return string
+ */
+ public function getView()
+ {
+ return $this->sheetviewType;
+ }
+
+ /**
+ * Set View
+ *
+ * Valid values are
+ * 'normal' self::SHEETVIEW_NORMAL
+ * 'pageLayout' self::SHEETVIEW_PAGE_LAYOUT
+ * 'pageBreakPreview' self::SHEETVIEW_PAGE_BREAK_PREVIEW
+ *
+ * @param string $pValue
+ * @throws PHPExcel_Exception
+ * @return PHPExcel_Worksheet_SheetView
+ */
+ public function setView($pValue = null)
+ {
+ // MS Excel 2007 allows setting the view to 'normal', 'pageLayout' or 'pageBreakPreview' via the user interface
+ if ($pValue === null) {
+ $pValue = self::SHEETVIEW_NORMAL;
+ }
+ if (in_array($pValue, self::$sheetViewTypes)) {
+ $this->sheetviewType = $pValue;
+ } else {
+ throw new PHPExcel_Exception("Invalid sheetview layout type.");
+ }
+
+ return $this;
+ }
+
+ /**
+ * Implement PHP __clone to create a deep clone, not just a shallow copy.
+ */
+ public function __clone()
+ {
+ $vars = get_object_vars($this);
+ foreach ($vars as $key => $value) {
+ if (is_object($value)) {
+ $this->$key = clone $value;
+ } else {
+ $this->$key = $value;
+ }
+ }
+ }
+}
diff --git a/PHPExcel/WorksheetIterator.php b/PHPExcel/WorksheetIterator.php
new file mode 100644
index 0000000..cb1b281
--- /dev/null
+++ b/PHPExcel/WorksheetIterator.php
@@ -0,0 +1,108 @@
+subject = $subject;
+ }
+
+ /**
+ * Destructor
+ */
+ public function __destruct()
+ {
+ unset($this->subject);
+ }
+
+ /**
+ * Rewind iterator
+ */
+ public function rewind()
+ {
+ $this->position = 0;
+ }
+
+ /**
+ * Current PHPExcel_Worksheet
+ *
+ * @return PHPExcel_Worksheet
+ */
+ public function current()
+ {
+ return $this->subject->getSheet($this->position);
+ }
+
+ /**
+ * Current key
+ *
+ * @return int
+ */
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * Next value
+ */
+ public function next()
+ {
+ ++$this->position;
+ }
+
+ /**
+ * More PHPExcel_Worksheet instances available?
+ *
+ * @return boolean
+ */
+ public function valid()
+ {
+ return $this->position < $this->subject->getSheetCount();
+ }
+}
diff --git a/PHPExcel/Writer/Abstract.php b/PHPExcel/Writer/Abstract.php
new file mode 100644
index 0000000..2a797a9
--- /dev/null
+++ b/PHPExcel/Writer/Abstract.php
@@ -0,0 +1,157 @@
+includeCharts;
+ }
+
+ /**
+ * Set write charts in workbook
+ * Set to true, to advise the Writer to include any charts that exist in the PHPExcel object.
+ * Set to false (the default) to ignore charts.
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Writer_IWriter
+ */
+ public function setIncludeCharts($pValue = false)
+ {
+ $this->includeCharts = (boolean) $pValue;
+ return $this;
+ }
+
+ /**
+ * Get Pre-Calculate Formulas flag
+ * If this is true (the default), then the writer will recalculate all formulae in a workbook when saving,
+ * so that the pre-calculated values are immediately available to MS Excel or other office spreadsheet
+ * viewer when opening the file
+ * If false, then formulae are not calculated on save. This is faster for saving in PHPExcel, but slower
+ * when opening the resulting file in MS Excel, because Excel has to recalculate the formulae itself
+ *
+ * @return boolean
+ */
+ public function getPreCalculateFormulas()
+ {
+ return $this->preCalculateFormulas;
+ }
+
+ /**
+ * Set Pre-Calculate Formulas
+ * Set to true (the default) to advise the Writer to calculate all formulae on save
+ * Set to false to prevent precalculation of formulae on save.
+ *
+ * @param boolean $pValue Pre-Calculate Formulas?
+ * @return PHPExcel_Writer_IWriter
+ */
+ public function setPreCalculateFormulas($pValue = true)
+ {
+ $this->preCalculateFormulas = (boolean) $pValue;
+ return $this;
+ }
+
+ /**
+ * Get use disk caching where possible?
+ *
+ * @return boolean
+ */
+ public function getUseDiskCaching()
+ {
+ return $this->_useDiskCaching;
+ }
+
+ /**
+ * Set use disk caching where possible?
+ *
+ * @param boolean $pValue
+ * @param string $pDirectory Disk caching directory
+ * @throws PHPExcel_Writer_Exception when directory does not exist
+ * @return PHPExcel_Writer_Excel2007
+ */
+ public function setUseDiskCaching($pValue = false, $pDirectory = null)
+ {
+ $this->_useDiskCaching = $pValue;
+
+ if ($pDirectory !== null) {
+ if (is_dir($pDirectory)) {
+ $this->_diskCachingDirectory = $pDirectory;
+ } else {
+ throw new PHPExcel_Writer_Exception("Directory does not exist: $pDirectory");
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Get disk caching directory
+ *
+ * @return string
+ */
+ public function getDiskCachingDirectory()
+ {
+ return $this->_diskCachingDirectory;
+ }
+}
diff --git a/PHPExcel/Writer/CSV.php b/PHPExcel/Writer/CSV.php
new file mode 100644
index 0000000..e59c301
--- /dev/null
+++ b/PHPExcel/Writer/CSV.php
@@ -0,0 +1,352 @@
+phpExcel = $phpExcel;
+ }
+
+ /**
+ * Save PHPExcel to file
+ *
+ * @param string $pFilename
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function save($pFilename = null)
+ {
+ // Fetch sheet
+ $sheet = $this->phpExcel->getSheet($this->sheetIndex);
+
+ $saveDebugLog = PHPExcel_Calculation::getInstance($this->phpExcel)->getDebugLog()->getWriteDebugLog();
+ PHPExcel_Calculation::getInstance($this->phpExcel)->getDebugLog()->setWriteDebugLog(false);
+ $saveArrayReturnType = PHPExcel_Calculation::getArrayReturnType();
+ PHPExcel_Calculation::setArrayReturnType(PHPExcel_Calculation::RETURN_ARRAY_AS_VALUE);
+
+ // Open file
+ $fileHandle = fopen($pFilename, 'wb+');
+ if ($fileHandle === false) {
+ throw new PHPExcel_Writer_Exception("Could not open file $pFilename for writing.");
+ }
+
+ if ($this->excelCompatibility) {
+ $this->setUseBOM(true); // Enforce UTF-8 BOM Header
+ $this->setIncludeSeparatorLine(true); // Set separator line
+ $this->setEnclosure('"'); // Set enclosure to "
+ $this->setDelimiter(";"); // Set delimiter to a semi-colon
+ $this->setLineEnding("\r\n");
+ }
+ if ($this->useBOM) {
+ // Write the UTF-8 BOM code if required
+ fwrite($fileHandle, "\xEF\xBB\xBF");
+ }
+ if ($this->includeSeparatorLine) {
+ // Write the separator line if required
+ fwrite($fileHandle, 'sep=' . $this->getDelimiter() . $this->lineEnding);
+ }
+
+ // Identify the range that we need to extract from the worksheet
+ $maxCol = $sheet->getHighestDataColumn();
+ $maxRow = $sheet->getHighestDataRow();
+
+ // Write rows to file
+ for ($row = 1; $row <= $maxRow; ++$row) {
+ // Convert the row to an array...
+ $cellsArray = $sheet->rangeToArray('A'.$row.':'.$maxCol.$row, '', $this->preCalculateFormulas);
+ // ... and write to the file
+ $this->writeLine($fileHandle, $cellsArray[0]);
+ }
+
+ // Close file
+ fclose($fileHandle);
+
+ PHPExcel_Calculation::setArrayReturnType($saveArrayReturnType);
+ PHPExcel_Calculation::getInstance($this->phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog);
+ }
+
+ /**
+ * Get delimiter
+ *
+ * @return string
+ */
+ public function getDelimiter()
+ {
+ return $this->delimiter;
+ }
+
+ /**
+ * Set delimiter
+ *
+ * @param string $pValue Delimiter, defaults to ,
+ * @return PHPExcel_Writer_CSV
+ */
+ public function setDelimiter($pValue = ',')
+ {
+ $this->delimiter = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get enclosure
+ *
+ * @return string
+ */
+ public function getEnclosure()
+ {
+ return $this->enclosure;
+ }
+
+ /**
+ * Set enclosure
+ *
+ * @param string $pValue Enclosure, defaults to "
+ * @return PHPExcel_Writer_CSV
+ */
+ public function setEnclosure($pValue = '"')
+ {
+ if ($pValue == '') {
+ $pValue = null;
+ }
+ $this->enclosure = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get line ending
+ *
+ * @return string
+ */
+ public function getLineEnding()
+ {
+ return $this->lineEnding;
+ }
+
+ /**
+ * Set line ending
+ *
+ * @param string $pValue Line ending, defaults to OS line ending (PHP_EOL)
+ * @return PHPExcel_Writer_CSV
+ */
+ public function setLineEnding($pValue = PHP_EOL)
+ {
+ $this->lineEnding = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get whether BOM should be used
+ *
+ * @return boolean
+ */
+ public function getUseBOM()
+ {
+ return $this->useBOM;
+ }
+
+ /**
+ * Set whether BOM should be used
+ *
+ * @param boolean $pValue Use UTF-8 byte-order mark? Defaults to false
+ * @return PHPExcel_Writer_CSV
+ */
+ public function setUseBOM($pValue = false)
+ {
+ $this->useBOM = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get whether a separator line should be included
+ *
+ * @return boolean
+ */
+ public function getIncludeSeparatorLine()
+ {
+ return $this->includeSeparatorLine;
+ }
+
+ /**
+ * Set whether a separator line should be included as the first line of the file
+ *
+ * @param boolean $pValue Use separator line? Defaults to false
+ * @return PHPExcel_Writer_CSV
+ */
+ public function setIncludeSeparatorLine($pValue = false)
+ {
+ $this->includeSeparatorLine = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get whether the file should be saved with full Excel Compatibility
+ *
+ * @return boolean
+ */
+ public function getExcelCompatibility()
+ {
+ return $this->excelCompatibility;
+ }
+
+ /**
+ * Set whether the file should be saved with full Excel Compatibility
+ *
+ * @param boolean $pValue Set the file to be written as a fully Excel compatible csv file
+ * Note that this overrides other settings such as useBOM, enclosure and delimiter
+ * @return PHPExcel_Writer_CSV
+ */
+ public function setExcelCompatibility($pValue = false)
+ {
+ $this->excelCompatibility = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get sheet index
+ *
+ * @return int
+ */
+ public function getSheetIndex()
+ {
+ return $this->sheetIndex;
+ }
+
+ /**
+ * Set sheet index
+ *
+ * @param int $pValue Sheet index
+ * @return PHPExcel_Writer_CSV
+ */
+ public function setSheetIndex($pValue = 0)
+ {
+ $this->sheetIndex = $pValue;
+ return $this;
+ }
+
+ /**
+ * Write line to CSV file
+ *
+ * @param mixed $pFileHandle PHP filehandle
+ * @param array $pValues Array containing values in a row
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeLine($pFileHandle = null, $pValues = null)
+ {
+ if (is_array($pValues)) {
+ // No leading delimiter
+ $writeDelimiter = false;
+
+ // Build the line
+ $line = '';
+
+ foreach ($pValues as $element) {
+ // Escape enclosures
+ $element = str_replace($this->enclosure, $this->enclosure . $this->enclosure, $element);
+
+ // Add delimiter
+ if ($writeDelimiter) {
+ $line .= $this->delimiter;
+ } else {
+ $writeDelimiter = true;
+ }
+
+ // Add enclosed string
+ $line .= $this->enclosure . $element . $this->enclosure;
+ }
+
+ // Add line ending
+ $line .= $this->lineEnding;
+
+ // Write to file
+ fwrite($pFileHandle, $line);
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid data row passed to CSV writer.");
+ }
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007.php b/PHPExcel/Writer/Excel2007.php
new file mode 100644
index 0000000..11d354b
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007.php
@@ -0,0 +1,533 @@
+setPHPExcel($pPHPExcel);
+
+ $writerPartsArray = array( 'stringtable' => 'PHPExcel_Writer_Excel2007_StringTable',
+ 'contenttypes' => 'PHPExcel_Writer_Excel2007_ContentTypes',
+ 'docprops' => 'PHPExcel_Writer_Excel2007_DocProps',
+ 'rels' => 'PHPExcel_Writer_Excel2007_Rels',
+ 'theme' => 'PHPExcel_Writer_Excel2007_Theme',
+ 'style' => 'PHPExcel_Writer_Excel2007_Style',
+ 'workbook' => 'PHPExcel_Writer_Excel2007_Workbook',
+ 'worksheet' => 'PHPExcel_Writer_Excel2007_Worksheet',
+ 'drawing' => 'PHPExcel_Writer_Excel2007_Drawing',
+ 'comments' => 'PHPExcel_Writer_Excel2007_Comments',
+ 'chart' => 'PHPExcel_Writer_Excel2007_Chart',
+ 'relsvba' => 'PHPExcel_Writer_Excel2007_RelsVBA',
+ 'relsribbonobjects' => 'PHPExcel_Writer_Excel2007_RelsRibbon'
+ );
+
+ // Initialise writer parts
+ // and Assign their parent IWriters
+ foreach ($writerPartsArray as $writer => $class) {
+ $this->writerParts[$writer] = new $class($this);
+ }
+
+ $hashTablesArray = array( 'stylesConditionalHashTable', 'fillHashTable', 'fontHashTable',
+ 'bordersHashTable', 'numFmtHashTable', 'drawingHashTable',
+ 'styleHashTable'
+ );
+
+ // Set HashTable variables
+ foreach ($hashTablesArray as $tableName) {
+ $this->$tableName = new PHPExcel_HashTable();
+ }
+ }
+
+ /**
+ * Get writer part
+ *
+ * @param string $pPartName Writer part name
+ * @return PHPExcel_Writer_Excel2007_WriterPart
+ */
+ public function getWriterPart($pPartName = '')
+ {
+ if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) {
+ return $this->writerParts[strtolower($pPartName)];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Save PHPExcel to file
+ *
+ * @param string $pFilename
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function save($pFilename = null)
+ {
+ if ($this->spreadSheet !== null) {
+ // garbage collect
+ $this->spreadSheet->garbageCollect();
+
+ // If $pFilename is php://output or php://stdout, make it a temporary file...
+ $originalFilename = $pFilename;
+ if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') {
+ $pFilename = @tempnam(PHPExcel_Shared_File::sys_get_temp_dir(), 'phpxltmp');
+ if ($pFilename == '') {
+ $pFilename = $originalFilename;
+ }
+ }
+
+ $saveDebugLog = PHPExcel_Calculation::getInstance($this->spreadSheet)->getDebugLog()->getWriteDebugLog();
+ PHPExcel_Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog(false);
+ $saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType();
+ PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL);
+
+ // Create string lookup table
+ $this->stringTable = array();
+ for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
+ $this->stringTable = $this->getWriterPart('StringTable')->createStringTable($this->spreadSheet->getSheet($i), $this->stringTable);
+ }
+
+ // Create styles dictionaries
+ $this->styleHashTable->addFromSource($this->getWriterPart('Style')->allStyles($this->spreadSheet));
+ $this->stylesConditionalHashTable->addFromSource($this->getWriterPart('Style')->allConditionalStyles($this->spreadSheet));
+ $this->fillHashTable->addFromSource($this->getWriterPart('Style')->allFills($this->spreadSheet));
+ $this->fontHashTable->addFromSource($this->getWriterPart('Style')->allFonts($this->spreadSheet));
+ $this->bordersHashTable->addFromSource($this->getWriterPart('Style')->allBorders($this->spreadSheet));
+ $this->numFmtHashTable->addFromSource($this->getWriterPart('Style')->allNumberFormats($this->spreadSheet));
+
+ // Create drawing dictionary
+ $this->drawingHashTable->addFromSource($this->getWriterPart('Drawing')->allDrawings($this->spreadSheet));
+
+ // Create new ZIP file and open it for writing
+ $zipClass = PHPExcel_Settings::getZipClass();
+ $objZip = new $zipClass();
+
+ // Retrieve OVERWRITE and CREATE constants from the instantiated zip class
+ // This method of accessing constant values from a dynamic class should work with all appropriate versions of PHP
+ $ro = new ReflectionObject($objZip);
+ $zipOverWrite = $ro->getConstant('OVERWRITE');
+ $zipCreate = $ro->getConstant('CREATE');
+
+ if (file_exists($pFilename)) {
+ unlink($pFilename);
+ }
+ // Try opening the ZIP file
+ if ($objZip->open($pFilename, $zipOverWrite) !== true) {
+ if ($objZip->open($pFilename, $zipCreate) !== true) {
+ throw new PHPExcel_Writer_Exception("Could not open " . $pFilename . " for writing.");
+ }
+ }
+
+ // Add [Content_Types].xml to ZIP file
+ $objZip->addFromString('[Content_Types].xml', $this->getWriterPart('ContentTypes')->writeContentTypes($this->spreadSheet, $this->includeCharts));
+
+ //if hasMacros, add the vbaProject.bin file, Certificate file(if exists)
+ if ($this->spreadSheet->hasMacros()) {
+ $macrosCode=$this->spreadSheet->getMacrosCode();
+ if (!is_null($macrosCode)) {// we have the code ?
+ $objZip->addFromString('xl/vbaProject.bin', $macrosCode);//allways in 'xl', allways named vbaProject.bin
+ if ($this->spreadSheet->hasMacrosCertificate()) {//signed macros ?
+ // Yes : add the certificate file and the related rels file
+ $objZip->addFromString('xl/vbaProjectSignature.bin', $this->spreadSheet->getMacrosCertificate());
+ $objZip->addFromString('xl/_rels/vbaProject.bin.rels', $this->getWriterPart('RelsVBA')->writeVBARelationships($this->spreadSheet));
+ }
+ }
+ }
+ //a custom UI in this workbook ? add it ("base" xml and additional objects (pictures) and rels)
+ if ($this->spreadSheet->hasRibbon()) {
+ $tmpRibbonTarget=$this->spreadSheet->getRibbonXMLData('target');
+ $objZip->addFromString($tmpRibbonTarget, $this->spreadSheet->getRibbonXMLData('data'));
+ if ($this->spreadSheet->hasRibbonBinObjects()) {
+ $tmpRootPath=dirname($tmpRibbonTarget).'/';
+ $ribbonBinObjects=$this->spreadSheet->getRibbonBinObjects('data');//the files to write
+ foreach ($ribbonBinObjects as $aPath => $aContent) {
+ $objZip->addFromString($tmpRootPath.$aPath, $aContent);
+ }
+ //the rels for files
+ $objZip->addFromString($tmpRootPath.'_rels/'.basename($tmpRibbonTarget).'.rels', $this->getWriterPart('RelsRibbonObjects')->writeRibbonRelationships($this->spreadSheet));
+ }
+ }
+
+ // Add relationships to ZIP file
+ $objZip->addFromString('_rels/.rels', $this->getWriterPart('Rels')->writeRelationships($this->spreadSheet));
+ $objZip->addFromString('xl/_rels/workbook.xml.rels', $this->getWriterPart('Rels')->writeWorkbookRelationships($this->spreadSheet));
+
+ // Add document properties to ZIP file
+ $objZip->addFromString('docProps/app.xml', $this->getWriterPart('DocProps')->writeDocPropsApp($this->spreadSheet));
+ $objZip->addFromString('docProps/core.xml', $this->getWriterPart('DocProps')->writeDocPropsCore($this->spreadSheet));
+ $customPropertiesPart = $this->getWriterPart('DocProps')->writeDocPropsCustom($this->spreadSheet);
+ if ($customPropertiesPart !== null) {
+ $objZip->addFromString('docProps/custom.xml', $customPropertiesPart);
+ }
+
+ // Add theme to ZIP file
+ $objZip->addFromString('xl/theme/theme1.xml', $this->getWriterPart('Theme')->writeTheme($this->spreadSheet));
+
+ // Add string table to ZIP file
+ $objZip->addFromString('xl/sharedStrings.xml', $this->getWriterPart('StringTable')->writeStringTable($this->stringTable));
+
+ // Add styles to ZIP file
+ $objZip->addFromString('xl/styles.xml', $this->getWriterPart('Style')->writeStyles($this->spreadSheet));
+
+ // Add workbook to ZIP file
+ $objZip->addFromString('xl/workbook.xml', $this->getWriterPart('Workbook')->writeWorkbook($this->spreadSheet, $this->preCalculateFormulas));
+
+ $chartCount = 0;
+ // Add worksheets
+ for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
+ $objZip->addFromString('xl/worksheets/sheet' . ($i + 1) . '.xml', $this->getWriterPart('Worksheet')->writeWorksheet($this->spreadSheet->getSheet($i), $this->stringTable, $this->includeCharts));
+ if ($this->includeCharts) {
+ $charts = $this->spreadSheet->getSheet($i)->getChartCollection();
+ if (count($charts) > 0) {
+ foreach ($charts as $chart) {
+ $objZip->addFromString('xl/charts/chart' . ($chartCount + 1) . '.xml', $this->getWriterPart('Chart')->writeChart($chart, $this->preCalculateFormulas));
+ $chartCount++;
+ }
+ }
+ }
+ }
+
+ $chartRef1 = $chartRef2 = 0;
+ // Add worksheet relationships (drawings, ...)
+ for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
+ // Add relationships
+ $objZip->addFromString('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts));
+
+ $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection();
+ $drawingCount = count($drawings);
+ if ($this->includeCharts) {
+ $chartCount = $this->spreadSheet->getSheet($i)->getChartCount();
+ }
+
+ // Add drawing and image relationship parts
+ if (($drawingCount > 0) || ($chartCount > 0)) {
+ // Drawing relationships
+ $objZip->addFromString('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts));
+
+ // Drawings
+ $objZip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $chartRef2, $this->includeCharts));
+ }
+
+ // Add comment relationship parts
+ if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
+ // VML Comments
+ $objZip->addFromString('xl/drawings/vmlDrawing' . ($i + 1) . '.vml', $this->getWriterPart('Comments')->writeVMLComments($this->spreadSheet->getSheet($i)));
+
+ // Comments
+ $objZip->addFromString('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i)));
+ }
+
+ // Add header/footer relationship parts
+ if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
+ // VML Drawings
+ $objZip->addFromString('xl/drawings/vmlDrawingHF' . ($i + 1) . '.vml', $this->getWriterPart('Drawing')->writeVMLHeaderFooterImages($this->spreadSheet->getSheet($i)));
+
+ // VML Drawing relationships
+ $objZip->addFromString('xl/drawings/_rels/vmlDrawingHF' . ($i + 1) . '.vml.rels', $this->getWriterPart('Rels')->writeHeaderFooterDrawingRelationships($this->spreadSheet->getSheet($i)));
+
+ // Media
+ foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
+ $objZip->addFromString('xl/media/' . $image->getIndexedFilename(), file_get_contents($image->getPath()));
+ }
+ }
+ }
+
+ // Add media
+ for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
+ if ($this->getDrawingHashTable()->getByIndex($i) instanceof PHPExcel_Worksheet_Drawing) {
+ $imageContents = null;
+ $imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
+ if (strpos($imagePath, 'zip://') !== false) {
+ $imagePath = substr($imagePath, 6);
+ $imagePathSplitted = explode('#', $imagePath);
+
+ $imageZip = new ZipArchive();
+ $imageZip->open($imagePathSplitted[0]);
+ $imageContents = $imageZip->getFromName($imagePathSplitted[1]);
+ $imageZip->close();
+ unset($imageZip);
+ } else {
+ $imageContents = file_get_contents($imagePath);
+ }
+
+ $objZip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
+ } elseif ($this->getDrawingHashTable()->getByIndex($i) instanceof PHPExcel_Worksheet_MemoryDrawing) {
+ ob_start();
+ call_user_func(
+ $this->getDrawingHashTable()->getByIndex($i)->getRenderingFunction(),
+ $this->getDrawingHashTable()->getByIndex($i)->getImageResource()
+ );
+ $imageContents = ob_get_contents();
+ ob_end_clean();
+
+ $objZip->addFromString('xl/media/' . str_replace(' ', '_', $this->getDrawingHashTable()->getByIndex($i)->getIndexedFilename()), $imageContents);
+ }
+ }
+
+ PHPExcel_Calculation_Functions::setReturnDateType($saveDateReturnType);
+ PHPExcel_Calculation::getInstance($this->spreadSheet)->getDebugLog()->setWriteDebugLog($saveDebugLog);
+
+ // Close file
+ if ($objZip->close() === false) {
+ throw new PHPExcel_Writer_Exception("Could not close zip file $pFilename.");
+ }
+
+ // If a temporary file was used, copy it to the correct file stream
+ if ($originalFilename != $pFilename) {
+ if (copy($pFilename, $originalFilename) === false) {
+ throw new PHPExcel_Writer_Exception("Could not copy temporary zip file $pFilename to $originalFilename.");
+ }
+ @unlink($pFilename);
+ }
+ } else {
+ throw new PHPExcel_Writer_Exception("PHPExcel object unassigned.");
+ }
+ }
+
+ /**
+ * Get PHPExcel object
+ *
+ * @return PHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function getPHPExcel()
+ {
+ if ($this->spreadSheet !== null) {
+ return $this->spreadSheet;
+ } else {
+ throw new PHPExcel_Writer_Exception("No PHPExcel object assigned.");
+ }
+ }
+
+ /**
+ * Set PHPExcel object
+ *
+ * @param PHPExcel $pPHPExcel PHPExcel object
+ * @throws PHPExcel_Writer_Exception
+ * @return PHPExcel_Writer_Excel2007
+ */
+ public function setPHPExcel(PHPExcel $pPHPExcel = null)
+ {
+ $this->spreadSheet = $pPHPExcel;
+ return $this;
+ }
+
+ /**
+ * Get string table
+ *
+ * @return string[]
+ */
+ public function getStringTable()
+ {
+ return $this->stringTable;
+ }
+
+ /**
+ * Get PHPExcel_Style HashTable
+ *
+ * @return PHPExcel_HashTable
+ */
+ public function getStyleHashTable()
+ {
+ return $this->styleHashTable;
+ }
+
+ /**
+ * Get PHPExcel_Style_Conditional HashTable
+ *
+ * @return PHPExcel_HashTable
+ */
+ public function getStylesConditionalHashTable()
+ {
+ return $this->stylesConditionalHashTable;
+ }
+
+ /**
+ * Get PHPExcel_Style_Fill HashTable
+ *
+ * @return PHPExcel_HashTable
+ */
+ public function getFillHashTable()
+ {
+ return $this->fillHashTable;
+ }
+
+ /**
+ * Get PHPExcel_Style_Font HashTable
+ *
+ * @return PHPExcel_HashTable
+ */
+ public function getFontHashTable()
+ {
+ return $this->fontHashTable;
+ }
+
+ /**
+ * Get PHPExcel_Style_Borders HashTable
+ *
+ * @return PHPExcel_HashTable
+ */
+ public function getBordersHashTable()
+ {
+ return $this->bordersHashTable;
+ }
+
+ /**
+ * Get PHPExcel_Style_NumberFormat HashTable
+ *
+ * @return PHPExcel_HashTable
+ */
+ public function getNumFmtHashTable()
+ {
+ return $this->numFmtHashTable;
+ }
+
+ /**
+ * Get PHPExcel_Worksheet_BaseDrawing HashTable
+ *
+ * @return PHPExcel_HashTable
+ */
+ public function getDrawingHashTable()
+ {
+ return $this->drawingHashTable;
+ }
+
+ /**
+ * Get Office2003 compatibility
+ *
+ * @return boolean
+ */
+ public function getOffice2003Compatibility()
+ {
+ return $this->office2003compatibility;
+ }
+
+ /**
+ * Set Office2003 compatibility
+ *
+ * @param boolean $pValue Office2003 compatibility?
+ * @return PHPExcel_Writer_Excel2007
+ */
+ public function setOffice2003Compatibility($pValue = false)
+ {
+ $this->office2003compatibility = $pValue;
+ return $this;
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/Chart.php b/PHPExcel/Writer/Excel2007/Chart.php
new file mode 100644
index 0000000..92fa215
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/Chart.php
@@ -0,0 +1,1520 @@
+calculateCellValues = $calculateCellValues;
+
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+ // Ensure that data series values are up-to-date before we save
+ if ($this->calculateCellValues) {
+ $pChart->refresh();
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // c:chartSpace
+ $objWriter->startElement('c:chartSpace');
+ $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
+ $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
+ $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+ $objWriter->startElement('c:date1904');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+ $objWriter->startElement('c:lang');
+ $objWriter->writeAttribute('val', "en-GB");
+ $objWriter->endElement();
+ $objWriter->startElement('c:roundedCorners');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ $this->writeAlternateContent($objWriter);
+
+ $objWriter->startElement('c:chart');
+
+ $this->writeTitle($pChart->getTitle(), $objWriter);
+
+ $objWriter->startElement('c:autoTitleDeleted');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ $this->writePlotArea($pChart->getPlotArea(), $pChart->getXAxisLabel(), $pChart->getYAxisLabel(), $objWriter, $pChart->getWorksheet(), $pChart->getChartAxisX(), $pChart->getChartAxisY(), $pChart->getMajorGridlines(), $pChart->getMinorGridlines());
+
+ $this->writeLegend($pChart->getLegend(), $objWriter);
+
+ $objWriter->startElement('c:plotVisOnly');
+ $objWriter->writeAttribute('val', 1);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:dispBlanksAs');
+ $objWriter->writeAttribute('val', "gap");
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:showDLblsOverMax');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $this->writePrintSettings($objWriter);
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write Chart Title
+ *
+ * @param PHPExcel_Chart_Title $title
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeTitle(PHPExcel_Chart_Title $title = null, $objWriter)
+ {
+ if (is_null($title)) {
+ return;
+ }
+
+ $objWriter->startElement('c:title');
+ $objWriter->startElement('c:tx');
+ $objWriter->startElement('c:rich');
+
+ $objWriter->startElement('a:bodyPr');
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:lstStyle');
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:p');
+
+ $caption = $title->getCaption();
+ if ((is_array($caption)) && (count($caption) > 0)) {
+ $caption = $caption[0];
+ }
+ $this->getParentWriter()->getWriterPart('stringtable')->writeRichTextForCharts($objWriter, $caption, 'a');
+
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $this->writeLayout($title->getLayout(), $objWriter);
+
+ $objWriter->startElement('c:overlay');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Chart Legend
+ *
+ * @param PHPExcel_Chart_Legend $legend
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeLegend(PHPExcel_Chart_Legend $legend = null, $objWriter)
+ {
+ if (is_null($legend)) {
+ return;
+ }
+
+ $objWriter->startElement('c:legend');
+
+ $objWriter->startElement('c:legendPos');
+ $objWriter->writeAttribute('val', $legend->getPosition());
+ $objWriter->endElement();
+
+ $this->writeLayout($legend->getLayout(), $objWriter);
+
+ $objWriter->startElement('c:overlay');
+ $objWriter->writeAttribute('val', ($legend->getOverlay()) ? '1' : '0');
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:txPr');
+ $objWriter->startElement('a:bodyPr');
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:lstStyle');
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:p');
+ $objWriter->startElement('a:pPr');
+ $objWriter->writeAttribute('rtl', 0);
+
+ $objWriter->startElement('a:defRPr');
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:endParaRPr');
+ $objWriter->writeAttribute('lang', "en-US");
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Chart Plot Area
+ *
+ * @param PHPExcel_Chart_PlotArea $plotArea
+ * @param PHPExcel_Chart_Title $xAxisLabel
+ * @param PHPExcel_Chart_Title $yAxisLabel
+ * @param PHPExcel_Chart_Axis $xAxis
+ * @param PHPExcel_Chart_Axis $yAxis
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writePlotArea(PHPExcel_Chart_PlotArea $plotArea, PHPExcel_Chart_Title $xAxisLabel = null, PHPExcel_Chart_Title $yAxisLabel = null, $objWriter, PHPExcel_Worksheet $pSheet, PHPExcel_Chart_Axis $xAxis, PHPExcel_Chart_Axis $yAxis, PHPExcel_Chart_GridLines $majorGridlines, PHPExcel_Chart_GridLines $minorGridlines)
+ {
+ if (is_null($plotArea)) {
+ return;
+ }
+
+ $id1 = $id2 = 0;
+ $this->_seriesIndex = 0;
+ $objWriter->startElement('c:plotArea');
+
+ $layout = $plotArea->getLayout();
+
+ $this->writeLayout($layout, $objWriter);
+
+ $chartTypes = self::getChartType($plotArea);
+ $catIsMultiLevelSeries = $valIsMultiLevelSeries = false;
+ $plotGroupingType = '';
+ foreach ($chartTypes as $chartType) {
+ $objWriter->startElement('c:' . $chartType);
+
+ $groupCount = $plotArea->getPlotGroupCount();
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $plotGroup = $plotArea->getPlotGroupByIndex($i);
+ $groupType = $plotGroup->getPlotType();
+ if ($groupType == $chartType) {
+ $plotStyle = $plotGroup->getPlotStyle();
+ if ($groupType === PHPExcel_Chart_DataSeries::TYPE_RADARCHART) {
+ $objWriter->startElement('c:radarStyle');
+ $objWriter->writeAttribute('val', $plotStyle);
+ $objWriter->endElement();
+ } elseif ($groupType === PHPExcel_Chart_DataSeries::TYPE_SCATTERCHART) {
+ $objWriter->startElement('c:scatterStyle');
+ $objWriter->writeAttribute('val', $plotStyle);
+ $objWriter->endElement();
+ }
+
+ $this->writePlotGroup($plotGroup, $chartType, $objWriter, $catIsMultiLevelSeries, $valIsMultiLevelSeries, $plotGroupingType, $pSheet);
+ }
+ }
+
+ $this->writeDataLabels($objWriter, $layout);
+
+ if ($chartType === PHPExcel_Chart_DataSeries::TYPE_LINECHART) {
+ // Line only, Line3D can't be smoothed
+
+ $objWriter->startElement('c:smooth');
+ $objWriter->writeAttribute('val', (integer) $plotGroup->getSmoothLine());
+ $objWriter->endElement();
+ } elseif (($chartType === PHPExcel_Chart_DataSeries::TYPE_BARCHART) ||($chartType === PHPExcel_Chart_DataSeries::TYPE_BARCHART_3D)) {
+ $objWriter->startElement('c:gapWidth');
+ $objWriter->writeAttribute('val', 150);
+ $objWriter->endElement();
+
+ if ($plotGroupingType == 'percentStacked' || $plotGroupingType == 'stacked') {
+ $objWriter->startElement('c:overlap');
+ $objWriter->writeAttribute('val', 100);
+ $objWriter->endElement();
+ }
+ } elseif ($chartType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) {
+ $objWriter->startElement('c:bubbleScale');
+ $objWriter->writeAttribute('val', 25);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:showNegBubbles');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+ } elseif ($chartType === PHPExcel_Chart_DataSeries::TYPE_STOCKCHART) {
+ $objWriter->startElement('c:hiLowLines');
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:upDownBars');
+
+ $objWriter->startElement('c:gapWidth');
+ $objWriter->writeAttribute('val', 300);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:upBars');
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:downBars');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ // Generate 2 unique numbers to use for axId values
+ // $id1 = $id2 = rand(10000000,99999999);
+ // do {
+ // $id2 = rand(10000000,99999999);
+ // } while ($id1 == $id2);
+ $id1 = '75091328';
+ $id2 = '75089408';
+
+ if (($chartType !== PHPExcel_Chart_DataSeries::TYPE_PIECHART) && ($chartType !== PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) && ($chartType !== PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) {
+ $objWriter->startElement('c:axId');
+ $objWriter->writeAttribute('val', $id1);
+ $objWriter->endElement();
+ $objWriter->startElement('c:axId');
+ $objWriter->writeAttribute('val', $id2);
+ $objWriter->endElement();
+ } else {
+ $objWriter->startElement('c:firstSliceAng');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ if ($chartType === PHPExcel_Chart_DataSeries::TYPE_DONUTCHART) {
+ $objWriter->startElement('c:holeSize');
+ $objWriter->writeAttribute('val', 50);
+ $objWriter->endElement();
+ }
+ }
+
+ $objWriter->endElement();
+ }
+
+ if (($chartType !== PHPExcel_Chart_DataSeries::TYPE_PIECHART) && ($chartType !== PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) && ($chartType !== PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) {
+ if ($chartType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) {
+ $this->writeValueAxis($objWriter, $plotArea, $xAxisLabel, $chartType, $id1, $id2, $catIsMultiLevelSeries, $xAxis, $yAxis, $majorGridlines, $minorGridlines);
+ } else {
+ $this->writeCategoryAxis($objWriter, $plotArea, $xAxisLabel, $chartType, $id1, $id2, $catIsMultiLevelSeries, $xAxis, $yAxis);
+ }
+
+ $this->writeValueAxis($objWriter, $plotArea, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $xAxis, $yAxis, $majorGridlines, $minorGridlines);
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Data Labels
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Chart_Layout $chartLayout Chart layout
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDataLabels($objWriter, $chartLayout)
+ {
+ $objWriter->startElement('c:dLbls');
+
+ $objWriter->startElement('c:showLegendKey');
+ $showLegendKey = (empty($chartLayout)) ? 0 : $chartLayout->getShowLegendKey();
+ $objWriter->writeAttribute('val', ((empty($showLegendKey)) ? 0 : 1));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:showVal');
+ $showVal = (empty($chartLayout)) ? 0 : $chartLayout->getShowVal();
+ $objWriter->writeAttribute('val', ((empty($showVal)) ? 0 : 1));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:showCatName');
+ $showCatName = (empty($chartLayout)) ? 0 : $chartLayout->getShowCatName();
+ $objWriter->writeAttribute('val', ((empty($showCatName)) ? 0 : 1));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:showSerName');
+ $showSerName = (empty($chartLayout)) ? 0 : $chartLayout->getShowSerName();
+ $objWriter->writeAttribute('val', ((empty($showSerName)) ? 0 : 1));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:showPercent');
+ $showPercent = (empty($chartLayout)) ? 0 : $chartLayout->getShowPercent();
+ $objWriter->writeAttribute('val', ((empty($showPercent)) ? 0 : 1));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:showBubbleSize');
+ $showBubbleSize = (empty($chartLayout)) ? 0 : $chartLayout->getShowBubbleSize();
+ $objWriter->writeAttribute('val', ((empty($showBubbleSize)) ? 0 : 1));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:showLeaderLines');
+ $showLeaderLines = (empty($chartLayout)) ? 1 : $chartLayout->getShowLeaderLines();
+ $objWriter->writeAttribute('val', ((empty($showLeaderLines)) ? 0 : 1));
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Category Axis
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Chart_PlotArea $plotArea
+ * @param PHPExcel_Chart_Title $xAxisLabel
+ * @param string $groupType Chart type
+ * @param string $id1
+ * @param string $id2
+ * @param boolean $isMultiLevelSeries
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeCategoryAxis($objWriter, PHPExcel_Chart_PlotArea $plotArea, $xAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, $xAxis, $yAxis)
+ {
+ $objWriter->startElement('c:catAx');
+
+ if ($id1 > 0) {
+ $objWriter->startElement('c:axId');
+ $objWriter->writeAttribute('val', $id1);
+ $objWriter->endElement();
+ }
+
+ $objWriter->startElement('c:scaling');
+ $objWriter->startElement('c:orientation');
+ $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('orientation'));
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:delete');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:axPos');
+ $objWriter->writeAttribute('val', "b");
+ $objWriter->endElement();
+
+ if (!is_null($xAxisLabel)) {
+ $objWriter->startElement('c:title');
+ $objWriter->startElement('c:tx');
+ $objWriter->startElement('c:rich');
+
+ $objWriter->startElement('a:bodyPr');
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:lstStyle');
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:p');
+ $objWriter->startElement('a:r');
+
+ $caption = $xAxisLabel->getCaption();
+ if (is_array($caption)) {
+ $caption = $caption[0];
+ }
+ $objWriter->startElement('a:t');
+ // $objWriter->writeAttribute('xml:space', 'preserve');
+ $objWriter->writeRawData(PHPExcel_Shared_String::ControlCharacterPHP2OOXML($caption));
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $layout = $xAxisLabel->getLayout();
+ $this->writeLayout($layout, $objWriter);
+
+ $objWriter->startElement('c:overlay');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ $objWriter->startElement('c:numFmt');
+ $objWriter->writeAttribute('formatCode', $yAxis->getAxisNumberFormat());
+ $objWriter->writeAttribute('sourceLinked', $yAxis->getAxisNumberSourceLinked());
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:majorTickMark');
+ $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('major_tick_mark'));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:minorTickMark');
+ $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('minor_tick_mark'));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:tickLblPos');
+ $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('axis_labels'));
+ $objWriter->endElement();
+
+ if ($id2 > 0) {
+ $objWriter->startElement('c:crossAx');
+ $objWriter->writeAttribute('val', $id2);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:crosses');
+ $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('horizontal_crosses'));
+ $objWriter->endElement();
+ }
+
+ $objWriter->startElement('c:auto');
+ $objWriter->writeAttribute('val', 1);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:lblAlgn');
+ $objWriter->writeAttribute('val', "ctr");
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:lblOffset');
+ $objWriter->writeAttribute('val', 100);
+ $objWriter->endElement();
+
+ if ($isMultiLevelSeries) {
+ $objWriter->startElement('c:noMultiLvlLbl');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+ }
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Value Axis
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Chart_PlotArea $plotArea
+ * @param PHPExcel_Chart_Title $yAxisLabel
+ * @param string $groupType Chart type
+ * @param string $id1
+ * @param string $id2
+ * @param boolean $isMultiLevelSeries
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeValueAxis($objWriter, PHPExcel_Chart_PlotArea $plotArea, $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, $xAxis, $yAxis, $majorGridlines, $minorGridlines)
+ {
+ $objWriter->startElement('c:valAx');
+
+ if ($id2 > 0) {
+ $objWriter->startElement('c:axId');
+ $objWriter->writeAttribute('val', $id2);
+ $objWriter->endElement();
+ }
+
+ $objWriter->startElement('c:scaling');
+
+ if (!is_null($xAxis->getAxisOptionsProperty('maximum'))) {
+ $objWriter->startElement('c:max');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('maximum'));
+ $objWriter->endElement();
+ }
+
+ if (!is_null($xAxis->getAxisOptionsProperty('minimum'))) {
+ $objWriter->startElement('c:min');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('minimum'));
+ $objWriter->endElement();
+ }
+
+ $objWriter->startElement('c:orientation');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('orientation'));
+
+
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:delete');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:axPos');
+ $objWriter->writeAttribute('val', "l");
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:majorGridlines');
+ $objWriter->startElement('c:spPr');
+
+ if (!is_null($majorGridlines->getLineColorProperty('value'))) {
+ $objWriter->startElement('a:ln');
+ $objWriter->writeAttribute('w', $majorGridlines->getLineStyleProperty('width'));
+ $objWriter->startElement('a:solidFill');
+ $objWriter->startElement("a:{$majorGridlines->getLineColorProperty('type')}");
+ $objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('value'));
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('alpha'));
+ $objWriter->endElement(); //end alpha
+ $objWriter->endElement(); //end srgbClr
+ $objWriter->endElement(); //end solidFill
+
+ $objWriter->startElement('a:prstDash');
+ $objWriter->writeAttribute('val', $majorGridlines->getLineStyleProperty('dash'));
+ $objWriter->endElement();
+
+ if ($majorGridlines->getLineStyleProperty('join') == 'miter') {
+ $objWriter->startElement('a:miter');
+ $objWriter->writeAttribute('lim', '800000');
+ $objWriter->endElement();
+ } else {
+ $objWriter->startElement('a:bevel');
+ $objWriter->endElement();
+ }
+
+ if (!is_null($majorGridlines->getLineStyleProperty(array('arrow', 'head', 'type')))) {
+ $objWriter->startElement('a:headEnd');
+ $objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(array('arrow', 'head', 'type')));
+ $objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('head', 'w'));
+ $objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('head', 'len'));
+ $objWriter->endElement();
+ }
+
+ if (!is_null($majorGridlines->getLineStyleProperty(array('arrow', 'end', 'type')))) {
+ $objWriter->startElement('a:tailEnd');
+ $objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(array('arrow', 'end', 'type')));
+ $objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('end', 'w'));
+ $objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('end', 'len'));
+ $objWriter->endElement();
+ }
+ $objWriter->endElement(); //end ln
+ }
+ $objWriter->startElement('a:effectLst');
+
+ if (!is_null($majorGridlines->getGlowSize())) {
+ $objWriter->startElement('a:glow');
+ $objWriter->writeAttribute('rad', $majorGridlines->getGlowSize());
+ $objWriter->startElement("a:{$majorGridlines->getGlowColor('type')}");
+ $objWriter->writeAttribute('val', $majorGridlines->getGlowColor('value'));
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $majorGridlines->getGlowColor('alpha'));
+ $objWriter->endElement(); //end alpha
+ $objWriter->endElement(); //end schemeClr
+ $objWriter->endElement(); //end glow
+ }
+
+ if (!is_null($majorGridlines->getShadowProperty('presets'))) {
+ $objWriter->startElement("a:{$majorGridlines->getShadowProperty('effect')}");
+ if (!is_null($majorGridlines->getShadowProperty('blur'))) {
+ $objWriter->writeAttribute('blurRad', $majorGridlines->getShadowProperty('blur'));
+ }
+ if (!is_null($majorGridlines->getShadowProperty('distance'))) {
+ $objWriter->writeAttribute('dist', $majorGridlines->getShadowProperty('distance'));
+ }
+ if (!is_null($majorGridlines->getShadowProperty('direction'))) {
+ $objWriter->writeAttribute('dir', $majorGridlines->getShadowProperty('direction'));
+ }
+ if (!is_null($majorGridlines->getShadowProperty('algn'))) {
+ $objWriter->writeAttribute('algn', $majorGridlines->getShadowProperty('algn'));
+ }
+ if (!is_null($majorGridlines->getShadowProperty(array('size', 'sx')))) {
+ $objWriter->writeAttribute('sx', $majorGridlines->getShadowProperty(array('size', 'sx')));
+ }
+ if (!is_null($majorGridlines->getShadowProperty(array('size', 'sy')))) {
+ $objWriter->writeAttribute('sy', $majorGridlines->getShadowProperty(array('size', 'sy')));
+ }
+ if (!is_null($majorGridlines->getShadowProperty(array('size', 'kx')))) {
+ $objWriter->writeAttribute('kx', $majorGridlines->getShadowProperty(array('size', 'kx')));
+ }
+ if (!is_null($majorGridlines->getShadowProperty('rotWithShape'))) {
+ $objWriter->writeAttribute('rotWithShape', $majorGridlines->getShadowProperty('rotWithShape'));
+ }
+ $objWriter->startElement("a:{$majorGridlines->getShadowProperty(array('color', 'type'))}");
+ $objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(array('color', 'value')));
+
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(array('color', 'alpha')));
+ $objWriter->endElement(); //end alpha
+
+ $objWriter->endElement(); //end color:type
+ $objWriter->endElement(); //end shadow
+ }
+
+ if (!is_null($majorGridlines->getSoftEdgesSize())) {
+ $objWriter->startElement('a:softEdge');
+ $objWriter->writeAttribute('rad', $majorGridlines->getSoftEdgesSize());
+ $objWriter->endElement(); //end softEdge
+ }
+
+ $objWriter->endElement(); //end effectLst
+ $objWriter->endElement(); //end spPr
+ $objWriter->endElement(); //end majorGridLines
+
+ if ($minorGridlines->getObjectState()) {
+ $objWriter->startElement('c:minorGridlines');
+ $objWriter->startElement('c:spPr');
+
+ if (!is_null($minorGridlines->getLineColorProperty('value'))) {
+ $objWriter->startElement('a:ln');
+ $objWriter->writeAttribute('w', $minorGridlines->getLineStyleProperty('width'));
+ $objWriter->startElement('a:solidFill');
+ $objWriter->startElement("a:{$minorGridlines->getLineColorProperty('type')}");
+ $objWriter->writeAttribute('val', $minorGridlines->getLineColorProperty('value'));
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $minorGridlines->getLineColorProperty('alpha'));
+ $objWriter->endElement(); //end alpha
+ $objWriter->endElement(); //end srgbClr
+ $objWriter->endElement(); //end solidFill
+
+ $objWriter->startElement('a:prstDash');
+ $objWriter->writeAttribute('val', $minorGridlines->getLineStyleProperty('dash'));
+ $objWriter->endElement();
+
+ if ($minorGridlines->getLineStyleProperty('join') == 'miter') {
+ $objWriter->startElement('a:miter');
+ $objWriter->writeAttribute('lim', '800000');
+ $objWriter->endElement();
+ } else {
+ $objWriter->startElement('a:bevel');
+ $objWriter->endElement();
+ }
+
+ if (!is_null($minorGridlines->getLineStyleProperty(array('arrow', 'head', 'type')))) {
+ $objWriter->startElement('a:headEnd');
+ $objWriter->writeAttribute('type', $minorGridlines->getLineStyleProperty(array('arrow', 'head', 'type')));
+ $objWriter->writeAttribute('w', $minorGridlines->getLineStyleArrowParameters('head', 'w'));
+ $objWriter->writeAttribute('len', $minorGridlines->getLineStyleArrowParameters('head', 'len'));
+ $objWriter->endElement();
+ }
+
+ if (!is_null($minorGridlines->getLineStyleProperty(array('arrow', 'end', 'type')))) {
+ $objWriter->startElement('a:tailEnd');
+ $objWriter->writeAttribute('type', $minorGridlines->getLineStyleProperty(array('arrow', 'end', 'type')));
+ $objWriter->writeAttribute('w', $minorGridlines->getLineStyleArrowParameters('end', 'w'));
+ $objWriter->writeAttribute('len', $minorGridlines->getLineStyleArrowParameters('end', 'len'));
+ $objWriter->endElement();
+ }
+ $objWriter->endElement(); //end ln
+ }
+
+ $objWriter->startElement('a:effectLst');
+
+ if (!is_null($minorGridlines->getGlowSize())) {
+ $objWriter->startElement('a:glow');
+ $objWriter->writeAttribute('rad', $minorGridlines->getGlowSize());
+ $objWriter->startElement("a:{$minorGridlines->getGlowColor('type')}");
+ $objWriter->writeAttribute('val', $minorGridlines->getGlowColor('value'));
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $minorGridlines->getGlowColor('alpha'));
+ $objWriter->endElement(); //end alpha
+ $objWriter->endElement(); //end schemeClr
+ $objWriter->endElement(); //end glow
+ }
+
+ if (!is_null($minorGridlines->getShadowProperty('presets'))) {
+ $objWriter->startElement("a:{$minorGridlines->getShadowProperty('effect')}");
+ if (!is_null($minorGridlines->getShadowProperty('blur'))) {
+ $objWriter->writeAttribute('blurRad', $minorGridlines->getShadowProperty('blur'));
+ }
+ if (!is_null($minorGridlines->getShadowProperty('distance'))) {
+ $objWriter->writeAttribute('dist', $minorGridlines->getShadowProperty('distance'));
+ }
+ if (!is_null($minorGridlines->getShadowProperty('direction'))) {
+ $objWriter->writeAttribute('dir', $minorGridlines->getShadowProperty('direction'));
+ }
+ if (!is_null($minorGridlines->getShadowProperty('algn'))) {
+ $objWriter->writeAttribute('algn', $minorGridlines->getShadowProperty('algn'));
+ }
+ if (!is_null($minorGridlines->getShadowProperty(array('size', 'sx')))) {
+ $objWriter->writeAttribute('sx', $minorGridlines->getShadowProperty(array('size', 'sx')));
+ }
+ if (!is_null($minorGridlines->getShadowProperty(array('size', 'sy')))) {
+ $objWriter->writeAttribute('sy', $minorGridlines->getShadowProperty(array('size', 'sy')));
+ }
+ if (!is_null($minorGridlines->getShadowProperty(array('size', 'kx')))) {
+ $objWriter->writeAttribute('kx', $minorGridlines->getShadowProperty(array('size', 'kx')));
+ }
+ if (!is_null($minorGridlines->getShadowProperty('rotWithShape'))) {
+ $objWriter->writeAttribute('rotWithShape', $minorGridlines->getShadowProperty('rotWithShape'));
+ }
+ $objWriter->startElement("a:{$minorGridlines->getShadowProperty(array('color', 'type'))}");
+ $objWriter->writeAttribute('val', $minorGridlines->getShadowProperty(array('color', 'value')));
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $minorGridlines->getShadowProperty(array('color', 'alpha')));
+ $objWriter->endElement(); //end alpha
+ $objWriter->endElement(); //end color:type
+ $objWriter->endElement(); //end shadow
+ }
+
+ if (!is_null($minorGridlines->getSoftEdgesSize())) {
+ $objWriter->startElement('a:softEdge');
+ $objWriter->writeAttribute('rad', $minorGridlines->getSoftEdgesSize());
+ $objWriter->endElement(); //end softEdge
+ }
+
+ $objWriter->endElement(); //end effectLst
+ $objWriter->endElement(); //end spPr
+ $objWriter->endElement(); //end minorGridLines
+ }
+
+ if (!is_null($yAxisLabel)) {
+ $objWriter->startElement('c:title');
+ $objWriter->startElement('c:tx');
+ $objWriter->startElement('c:rich');
+
+ $objWriter->startElement('a:bodyPr');
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:lstStyle');
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:p');
+ $objWriter->startElement('a:r');
+
+ $caption = $yAxisLabel->getCaption();
+ if (is_array($caption)) {
+ $caption = $caption[0];
+ }
+
+ $objWriter->startElement('a:t');
+ // $objWriter->writeAttribute('xml:space', 'preserve');
+ $objWriter->writeRawData(PHPExcel_Shared_String::ControlCharacterPHP2OOXML($caption));
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ if ($groupType !== PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) {
+ $layout = $yAxisLabel->getLayout();
+ $this->writeLayout($layout, $objWriter);
+ }
+
+ $objWriter->startElement('c:overlay');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ $objWriter->startElement('c:numFmt');
+ $objWriter->writeAttribute('formatCode', $xAxis->getAxisNumberFormat());
+ $objWriter->writeAttribute('sourceLinked', $xAxis->getAxisNumberSourceLinked());
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:majorTickMark');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('major_tick_mark'));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:minorTickMark');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('minor_tick_mark'));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:tickLblPos');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('axis_labels'));
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:spPr');
+
+ if (!is_null($xAxis->getFillProperty('value'))) {
+ $objWriter->startElement('a:solidFill');
+ $objWriter->startElement("a:" . $xAxis->getFillProperty('type'));
+ $objWriter->writeAttribute('val', $xAxis->getFillProperty('value'));
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $xAxis->getFillProperty('alpha'));
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+
+ $objWriter->startElement('a:ln');
+
+ $objWriter->writeAttribute('w', $xAxis->getLineStyleProperty('width'));
+ $objWriter->writeAttribute('cap', $xAxis->getLineStyleProperty('cap'));
+ $objWriter->writeAttribute('cmpd', $xAxis->getLineStyleProperty('compound'));
+
+ if (!is_null($xAxis->getLineProperty('value'))) {
+ $objWriter->startElement('a:solidFill');
+ $objWriter->startElement("a:" . $xAxis->getLineProperty('type'));
+ $objWriter->writeAttribute('val', $xAxis->getLineProperty('value'));
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $xAxis->getLineProperty('alpha'));
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+
+ $objWriter->startElement('a:prstDash');
+ $objWriter->writeAttribute('val', $xAxis->getLineStyleProperty('dash'));
+ $objWriter->endElement();
+
+ if ($xAxis->getLineStyleProperty('join') == 'miter') {
+ $objWriter->startElement('a:miter');
+ $objWriter->writeAttribute('lim', '800000');
+ $objWriter->endElement();
+ } else {
+ $objWriter->startElement('a:bevel');
+ $objWriter->endElement();
+ }
+
+ if (!is_null($xAxis->getLineStyleProperty(array('arrow', 'head', 'type')))) {
+ $objWriter->startElement('a:headEnd');
+ $objWriter->writeAttribute('type', $xAxis->getLineStyleProperty(array('arrow', 'head', 'type')));
+ $objWriter->writeAttribute('w', $xAxis->getLineStyleArrowWidth('head'));
+ $objWriter->writeAttribute('len', $xAxis->getLineStyleArrowLength('head'));
+ $objWriter->endElement();
+ }
+
+ if (!is_null($xAxis->getLineStyleProperty(array('arrow', 'end', 'type')))) {
+ $objWriter->startElement('a:tailEnd');
+ $objWriter->writeAttribute('type', $xAxis->getLineStyleProperty(array('arrow', 'end', 'type')));
+ $objWriter->writeAttribute('w', $xAxis->getLineStyleArrowWidth('end'));
+ $objWriter->writeAttribute('len', $xAxis->getLineStyleArrowLength('end'));
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:effectLst');
+
+ if (!is_null($xAxis->getGlowProperty('size'))) {
+ $objWriter->startElement('a:glow');
+ $objWriter->writeAttribute('rad', $xAxis->getGlowProperty('size'));
+ $objWriter->startElement("a:{$xAxis->getGlowProperty(array('color','type'))}");
+ $objWriter->writeAttribute('val', $xAxis->getGlowProperty(array('color','value')));
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $xAxis->getGlowProperty(array('color','alpha')));
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+
+ if (!is_null($xAxis->getShadowProperty('presets'))) {
+ $objWriter->startElement("a:{$xAxis->getShadowProperty('effect')}");
+
+ if (!is_null($xAxis->getShadowProperty('blur'))) {
+ $objWriter->writeAttribute('blurRad', $xAxis->getShadowProperty('blur'));
+ }
+ if (!is_null($xAxis->getShadowProperty('distance'))) {
+ $objWriter->writeAttribute('dist', $xAxis->getShadowProperty('distance'));
+ }
+ if (!is_null($xAxis->getShadowProperty('direction'))) {
+ $objWriter->writeAttribute('dir', $xAxis->getShadowProperty('direction'));
+ }
+ if (!is_null($xAxis->getShadowProperty('algn'))) {
+ $objWriter->writeAttribute('algn', $xAxis->getShadowProperty('algn'));
+ }
+ if (!is_null($xAxis->getShadowProperty(array('size','sx')))) {
+ $objWriter->writeAttribute('sx', $xAxis->getShadowProperty(array('size','sx')));
+ }
+ if (!is_null($xAxis->getShadowProperty(array('size','sy')))) {
+ $objWriter->writeAttribute('sy', $xAxis->getShadowProperty(array('size','sy')));
+ }
+ if (!is_null($xAxis->getShadowProperty(array('size','kx')))) {
+ $objWriter->writeAttribute('kx', $xAxis->getShadowProperty(array('size','kx')));
+ }
+ if (!is_null($xAxis->getShadowProperty('rotWithShape'))) {
+ $objWriter->writeAttribute('rotWithShape', $xAxis->getShadowProperty('rotWithShape'));
+ }
+
+ $objWriter->startElement("a:{$xAxis->getShadowProperty(array('color','type'))}");
+ $objWriter->writeAttribute('val', $xAxis->getShadowProperty(array('color','value')));
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $xAxis->getShadowProperty(array('color','alpha')));
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ if (!is_null($xAxis->getSoftEdgesSize())) {
+ $objWriter->startElement('a:softEdge');
+ $objWriter->writeAttribute('rad', $xAxis->getSoftEdgesSize());
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement(); //effectList
+ $objWriter->endElement(); //end spPr
+
+ if ($id1 > 0) {
+ $objWriter->startElement('c:crossAx');
+ $objWriter->writeAttribute('val', $id2);
+ $objWriter->endElement();
+
+ if (!is_null($xAxis->getAxisOptionsProperty('horizontal_crosses_value'))) {
+ $objWriter->startElement('c:crossesAt');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('horizontal_crosses_value'));
+ $objWriter->endElement();
+ } else {
+ $objWriter->startElement('c:crosses');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('horizontal_crosses'));
+ $objWriter->endElement();
+ }
+
+ $objWriter->startElement('c:crossBetween');
+ $objWriter->writeAttribute('val', "midCat");
+ $objWriter->endElement();
+
+ if (!is_null($xAxis->getAxisOptionsProperty('major_unit'))) {
+ $objWriter->startElement('c:majorUnit');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('major_unit'));
+ $objWriter->endElement();
+ }
+
+ if (!is_null($xAxis->getAxisOptionsProperty('minor_unit'))) {
+ $objWriter->startElement('c:minorUnit');
+ $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('minor_unit'));
+ $objWriter->endElement();
+ }
+ }
+
+ if ($isMultiLevelSeries) {
+ if ($groupType !== PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) {
+ $objWriter->startElement('c:noMultiLvlLbl');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+ }
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Get the data series type(s) for a chart plot series
+ *
+ * @param PHPExcel_Chart_PlotArea $plotArea
+ *
+ * @return string|array
+ * @throws PHPExcel_Writer_Exception
+ */
+ private static function getChartType($plotArea)
+ {
+ $groupCount = $plotArea->getPlotGroupCount();
+
+ if ($groupCount == 1) {
+ $chartType = array($plotArea->getPlotGroupByIndex(0)->getPlotType());
+ } else {
+ $chartTypes = array();
+ for ($i = 0; $i < $groupCount; ++$i) {
+ $chartTypes[] = $plotArea->getPlotGroupByIndex($i)->getPlotType();
+ }
+ $chartType = array_unique($chartTypes);
+ if (count($chartTypes) == 0) {
+ throw new PHPExcel_Writer_Exception('Chart is not yet implemented');
+ }
+ }
+
+ return $chartType;
+ }
+
+ /**
+ * Write Plot Group (series of related plots)
+ *
+ * @param PHPExcel_Chart_DataSeries $plotGroup
+ * @param string $groupType Type of plot for dataseries
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param boolean &$catIsMultiLevelSeries Is category a multi-series category
+ * @param boolean &$valIsMultiLevelSeries Is value set a multi-series set
+ * @param string &$plotGroupingType Type of grouping for multi-series values
+ * @param PHPExcel_Worksheet $pSheet
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writePlotGroup($plotGroup, $groupType, $objWriter, &$catIsMultiLevelSeries, &$valIsMultiLevelSeries, &$plotGroupingType, PHPExcel_Worksheet $pSheet)
+ {
+ if (is_null($plotGroup)) {
+ return;
+ }
+
+ if (($groupType == PHPExcel_Chart_DataSeries::TYPE_BARCHART) || ($groupType == PHPExcel_Chart_DataSeries::TYPE_BARCHART_3D)) {
+ $objWriter->startElement('c:barDir');
+ $objWriter->writeAttribute('val', $plotGroup->getPlotDirection());
+ $objWriter->endElement();
+ }
+
+ if (!is_null($plotGroup->getPlotGrouping())) {
+ $plotGroupingType = $plotGroup->getPlotGrouping();
+ $objWriter->startElement('c:grouping');
+ $objWriter->writeAttribute('val', $plotGroupingType);
+ $objWriter->endElement();
+ }
+
+ // Get these details before the loop, because we can use the count to check for varyColors
+ $plotSeriesOrder = $plotGroup->getPlotOrder();
+ $plotSeriesCount = count($plotSeriesOrder);
+
+ if (($groupType !== PHPExcel_Chart_DataSeries::TYPE_RADARCHART) && ($groupType !== PHPExcel_Chart_DataSeries::TYPE_STOCKCHART)) {
+ if ($groupType !== PHPExcel_Chart_DataSeries::TYPE_LINECHART) {
+ if (($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART) || ($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) || ($groupType == PHPExcel_Chart_DataSeries::TYPE_DONUTCHART) || ($plotSeriesCount > 1)) {
+ $objWriter->startElement('c:varyColors');
+ $objWriter->writeAttribute('val', 1);
+ $objWriter->endElement();
+ } else {
+ $objWriter->startElement('c:varyColors');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+ }
+ }
+ }
+
+ foreach ($plotSeriesOrder as $plotSeriesIdx => $plotSeriesRef) {
+ $objWriter->startElement('c:ser');
+
+ $objWriter->startElement('c:idx');
+ $objWriter->writeAttribute('val', $this->_seriesIndex + $plotSeriesIdx);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:order');
+ $objWriter->writeAttribute('val', $this->_seriesIndex + $plotSeriesRef);
+ $objWriter->endElement();
+
+ if (($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART) || ($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) || ($groupType == PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) {
+ $objWriter->startElement('c:dPt');
+ $objWriter->startElement('c:idx');
+ $objWriter->writeAttribute('val', 3);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:bubble3D');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:spPr');
+ $objWriter->startElement('a:solidFill');
+ $objWriter->startElement('a:srgbClr');
+ $objWriter->writeAttribute('val', 'FF9900');
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+
+ // Labels
+ $plotSeriesLabel = $plotGroup->getPlotLabelByIndex($plotSeriesRef);
+ if ($plotSeriesLabel && ($plotSeriesLabel->getPointCount() > 0)) {
+ $objWriter->startElement('c:tx');
+ $objWriter->startElement('c:strRef');
+ $this->writePlotSeriesLabel($plotSeriesLabel, $objWriter);
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+
+ // Formatting for the points
+ if (($groupType == PHPExcel_Chart_DataSeries::TYPE_LINECHART) || ($groupType == PHPExcel_Chart_DataSeries::TYPE_STOCKCHART)) {
+ $objWriter->startElement('c:spPr');
+ $objWriter->startElement('a:ln');
+ $objWriter->writeAttribute('w', 12700);
+ if ($groupType == PHPExcel_Chart_DataSeries::TYPE_STOCKCHART) {
+ $objWriter->startElement('a:noFill');
+ $objWriter->endElement();
+ }
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+
+ $plotSeriesValues = $plotGroup->getPlotValuesByIndex($plotSeriesRef);
+ if ($plotSeriesValues) {
+ $plotSeriesMarker = $plotSeriesValues->getPointMarker();
+ if ($plotSeriesMarker) {
+ $objWriter->startElement('c:marker');
+ $objWriter->startElement('c:symbol');
+ $objWriter->writeAttribute('val', $plotSeriesMarker);
+ $objWriter->endElement();
+
+ if ($plotSeriesMarker !== 'none') {
+ $objWriter->startElement('c:size');
+ $objWriter->writeAttribute('val', 3);
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+ }
+
+ if (($groupType === PHPExcel_Chart_DataSeries::TYPE_BARCHART) || ($groupType === PHPExcel_Chart_DataSeries::TYPE_BARCHART_3D) || ($groupType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART)) {
+ $objWriter->startElement('c:invertIfNegative');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+ }
+
+ // Category Labels
+ $plotSeriesCategory = $plotGroup->getPlotCategoryByIndex($plotSeriesRef);
+ if ($plotSeriesCategory && ($plotSeriesCategory->getPointCount() > 0)) {
+ $catIsMultiLevelSeries = $catIsMultiLevelSeries || $plotSeriesCategory->isMultiLevelSeries();
+
+ if (($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART) || ($groupType == PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) || ($groupType == PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) {
+ if (!is_null($plotGroup->getPlotStyle())) {
+ $plotStyle = $plotGroup->getPlotStyle();
+ if ($plotStyle) {
+ $objWriter->startElement('c:explosion');
+ $objWriter->writeAttribute('val', 25);
+ $objWriter->endElement();
+ }
+ }
+ }
+
+ if (($groupType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) || ($groupType === PHPExcel_Chart_DataSeries::TYPE_SCATTERCHART)) {
+ $objWriter->startElement('c:xVal');
+ } else {
+ $objWriter->startElement('c:cat');
+ }
+
+ $this->writePlotSeriesValues($plotSeriesCategory, $objWriter, $groupType, 'str', $pSheet);
+ $objWriter->endElement();
+ }
+
+ // Values
+ if ($plotSeriesValues) {
+ $valIsMultiLevelSeries = $valIsMultiLevelSeries || $plotSeriesValues->isMultiLevelSeries();
+
+ if (($groupType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) || ($groupType === PHPExcel_Chart_DataSeries::TYPE_SCATTERCHART)) {
+ $objWriter->startElement('c:yVal');
+ } else {
+ $objWriter->startElement('c:val');
+ }
+
+ $this->writePlotSeriesValues($plotSeriesValues, $objWriter, $groupType, 'num', $pSheet);
+ $objWriter->endElement();
+ }
+
+ if ($groupType === PHPExcel_Chart_DataSeries::TYPE_BUBBLECHART) {
+ $this->writeBubbles($plotSeriesValues, $objWriter, $pSheet);
+ }
+
+ $objWriter->endElement();
+ }
+
+ $this->_seriesIndex += $plotSeriesIdx + 1;
+ }
+
+ /**
+ * Write Plot Series Label
+ *
+ * @param PHPExcel_Chart_DataSeriesValues $plotSeriesLabel
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writePlotSeriesLabel($plotSeriesLabel, $objWriter)
+ {
+ if (is_null($plotSeriesLabel)) {
+ return;
+ }
+
+ $objWriter->startElement('c:f');
+ $objWriter->writeRawData($plotSeriesLabel->getDataSource());
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:strCache');
+ $objWriter->startElement('c:ptCount');
+ $objWriter->writeAttribute('val', $plotSeriesLabel->getPointCount());
+ $objWriter->endElement();
+
+ foreach ($plotSeriesLabel->getDataValues() as $plotLabelKey => $plotLabelValue) {
+ $objWriter->startElement('c:pt');
+ $objWriter->writeAttribute('idx', $plotLabelKey);
+
+ $objWriter->startElement('c:v');
+ $objWriter->writeRawData($plotLabelValue);
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Plot Series Values
+ *
+ * @param PHPExcel_Chart_DataSeriesValues $plotSeriesValues
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param string $groupType Type of plot for dataseries
+ * @param string $dataType Datatype of series values
+ * @param PHPExcel_Worksheet $pSheet
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writePlotSeriesValues($plotSeriesValues, $objWriter, $groupType, $dataType = 'str', PHPExcel_Worksheet $pSheet)
+ {
+ if (is_null($plotSeriesValues)) {
+ return;
+ }
+
+ if ($plotSeriesValues->isMultiLevelSeries()) {
+ $levelCount = $plotSeriesValues->multiLevelCount();
+
+ $objWriter->startElement('c:multiLvlStrRef');
+
+ $objWriter->startElement('c:f');
+ $objWriter->writeRawData($plotSeriesValues->getDataSource());
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:multiLvlStrCache');
+
+ $objWriter->startElement('c:ptCount');
+ $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount());
+ $objWriter->endElement();
+
+ for ($level = 0; $level < $levelCount; ++$level) {
+ $objWriter->startElement('c:lvl');
+
+ foreach ($plotSeriesValues->getDataValues() as $plotSeriesKey => $plotSeriesValue) {
+ if (isset($plotSeriesValue[$level])) {
+ $objWriter->startElement('c:pt');
+ $objWriter->writeAttribute('idx', $plotSeriesKey);
+
+ $objWriter->startElement('c:v');
+ $objWriter->writeRawData($plotSeriesValue[$level]);
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+ }
+
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ } else {
+ $objWriter->startElement('c:' . $dataType . 'Ref');
+
+ $objWriter->startElement('c:f');
+ $objWriter->writeRawData($plotSeriesValues->getDataSource());
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:' . $dataType . 'Cache');
+
+ if (($groupType != PHPExcel_Chart_DataSeries::TYPE_PIECHART) && ($groupType != PHPExcel_Chart_DataSeries::TYPE_PIECHART_3D) && ($groupType != PHPExcel_Chart_DataSeries::TYPE_DONUTCHART)) {
+ if (($plotSeriesValues->getFormatCode() !== null) && ($plotSeriesValues->getFormatCode() !== '')) {
+ $objWriter->startElement('c:formatCode');
+ $objWriter->writeRawData($plotSeriesValues->getFormatCode());
+ $objWriter->endElement();
+ }
+ }
+
+ $objWriter->startElement('c:ptCount');
+ $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount());
+ $objWriter->endElement();
+
+ $dataValues = $plotSeriesValues->getDataValues();
+ if (!empty($dataValues)) {
+ if (is_array($dataValues)) {
+ foreach ($dataValues as $plotSeriesKey => $plotSeriesValue) {
+ $objWriter->startElement('c:pt');
+ $objWriter->writeAttribute('idx', $plotSeriesKey);
+
+ $objWriter->startElement('c:v');
+ $objWriter->writeRawData($plotSeriesValue);
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+ }
+ }
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write Bubble Chart Details
+ *
+ * @param PHPExcel_Chart_DataSeriesValues $plotSeriesValues
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeBubbles($plotSeriesValues, $objWriter, PHPExcel_Worksheet $pSheet)
+ {
+ if (is_null($plotSeriesValues)) {
+ return;
+ }
+
+ $objWriter->startElement('c:bubbleSize');
+ $objWriter->startElement('c:numLit');
+
+ $objWriter->startElement('c:formatCode');
+ $objWriter->writeRawData('General');
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:ptCount');
+ $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount());
+ $objWriter->endElement();
+
+ $dataValues = $plotSeriesValues->getDataValues();
+ if (!empty($dataValues)) {
+ if (is_array($dataValues)) {
+ foreach ($dataValues as $plotSeriesKey => $plotSeriesValue) {
+ $objWriter->startElement('c:pt');
+ $objWriter->writeAttribute('idx', $plotSeriesKey);
+ $objWriter->startElement('c:v');
+ $objWriter->writeRawData(1);
+ $objWriter->endElement();
+ $objWriter->endElement();
+ }
+ }
+ }
+
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:bubble3D');
+ $objWriter->writeAttribute('val', 0);
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Layout
+ *
+ * @param PHPExcel_Chart_Layout $layout
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeLayout(PHPExcel_Chart_Layout $layout = null, $objWriter)
+ {
+ $objWriter->startElement('c:layout');
+
+ if (!is_null($layout)) {
+ $objWriter->startElement('c:manualLayout');
+
+ $layoutTarget = $layout->getLayoutTarget();
+ if (!is_null($layoutTarget)) {
+ $objWriter->startElement('c:layoutTarget');
+ $objWriter->writeAttribute('val', $layoutTarget);
+ $objWriter->endElement();
+ }
+
+ $xMode = $layout->getXMode();
+ if (!is_null($xMode)) {
+ $objWriter->startElement('c:xMode');
+ $objWriter->writeAttribute('val', $xMode);
+ $objWriter->endElement();
+ }
+
+ $yMode = $layout->getYMode();
+ if (!is_null($yMode)) {
+ $objWriter->startElement('c:yMode');
+ $objWriter->writeAttribute('val', $yMode);
+ $objWriter->endElement();
+ }
+
+ $x = $layout->getXPosition();
+ if (!is_null($x)) {
+ $objWriter->startElement('c:x');
+ $objWriter->writeAttribute('val', $x);
+ $objWriter->endElement();
+ }
+
+ $y = $layout->getYPosition();
+ if (!is_null($y)) {
+ $objWriter->startElement('c:y');
+ $objWriter->writeAttribute('val', $y);
+ $objWriter->endElement();
+ }
+
+ $w = $layout->getWidth();
+ if (!is_null($w)) {
+ $objWriter->startElement('c:w');
+ $objWriter->writeAttribute('val', $w);
+ $objWriter->endElement();
+ }
+
+ $h = $layout->getHeight();
+ if (!is_null($h)) {
+ $objWriter->startElement('c:h');
+ $objWriter->writeAttribute('val', $h);
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Alternate Content block
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeAlternateContent($objWriter)
+ {
+ $objWriter->startElement('mc:AlternateContent');
+ $objWriter->writeAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006');
+
+ $objWriter->startElement('mc:Choice');
+ $objWriter->writeAttribute('xmlns:c14', 'http://schemas.microsoft.com/office/drawing/2007/8/2/chart');
+ $objWriter->writeAttribute('Requires', 'c14');
+
+ $objWriter->startElement('c14:style');
+ $objWriter->writeAttribute('val', '102');
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->startElement('mc:Fallback');
+ $objWriter->startElement('c:style');
+ $objWriter->writeAttribute('val', '2');
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Printer Settings
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ *
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writePrintSettings($objWriter)
+ {
+ $objWriter->startElement('c:printSettings');
+
+ $objWriter->startElement('c:headerFooter');
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:pageMargins');
+ $objWriter->writeAttribute('footer', 0.3);
+ $objWriter->writeAttribute('header', 0.3);
+ $objWriter->writeAttribute('r', 0.7);
+ $objWriter->writeAttribute('l', 0.7);
+ $objWriter->writeAttribute('t', 0.75);
+ $objWriter->writeAttribute('b', 0.75);
+ $objWriter->endElement();
+
+ $objWriter->startElement('c:pageSetup');
+ $objWriter->writeAttribute('orientation', "portrait");
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/Comments.php b/PHPExcel/Writer/Excel2007/Comments.php
new file mode 100644
index 0000000..7139f6c
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/Comments.php
@@ -0,0 +1,260 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Comments cache
+ $comments = $pWorksheet->getComments();
+
+ // Authors cache
+ $authors = array();
+ $authorId = 0;
+ foreach ($comments as $comment) {
+ if (!isset($authors[$comment->getAuthor()])) {
+ $authors[$comment->getAuthor()] = $authorId++;
+ }
+ }
+
+ // comments
+ $objWriter->startElement('comments');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+
+ // Loop through authors
+ $objWriter->startElement('authors');
+ foreach ($authors as $author => $index) {
+ $objWriter->writeElement('author', $author);
+ }
+ $objWriter->endElement();
+
+ // Loop through comments
+ $objWriter->startElement('commentList');
+ foreach ($comments as $key => $value) {
+ $this->writeComment($objWriter, $key, $value, $authors);
+ }
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write comment to XML format
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param string $pCellReference Cell reference
+ * @param PHPExcel_Comment $pComment Comment
+ * @param array $pAuthors Array of authors
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeComment(PHPExcel_Shared_XMLWriter $objWriter = null, $pCellReference = 'A1', PHPExcel_Comment $pComment = null, $pAuthors = null)
+ {
+ // comment
+ $objWriter->startElement('comment');
+ $objWriter->writeAttribute('ref', $pCellReference);
+ $objWriter->writeAttribute('authorId', $pAuthors[$pComment->getAuthor()]);
+
+ // text
+ $objWriter->startElement('text');
+ $this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $pComment->getText());
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write VML comments to XML format
+ *
+ * @param PHPExcel_Worksheet $pWorksheet
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeVMLComments(PHPExcel_Worksheet $pWorksheet = null)
+ {
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Comments cache
+ $comments = $pWorksheet->getComments();
+
+ // xml
+ $objWriter->startElement('xml');
+ $objWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml');
+ $objWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office');
+ $objWriter->writeAttribute('xmlns:x', 'urn:schemas-microsoft-com:office:excel');
+
+ // o:shapelayout
+ $objWriter->startElement('o:shapelayout');
+ $objWriter->writeAttribute('v:ext', 'edit');
+
+ // o:idmap
+ $objWriter->startElement('o:idmap');
+ $objWriter->writeAttribute('v:ext', 'edit');
+ $objWriter->writeAttribute('data', '1');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // v:shapetype
+ $objWriter->startElement('v:shapetype');
+ $objWriter->writeAttribute('id', '_x0000_t202');
+ $objWriter->writeAttribute('coordsize', '21600,21600');
+ $objWriter->writeAttribute('o:spt', '202');
+ $objWriter->writeAttribute('path', 'm,l,21600r21600,l21600,xe');
+
+ // v:stroke
+ $objWriter->startElement('v:stroke');
+ $objWriter->writeAttribute('joinstyle', 'miter');
+ $objWriter->endElement();
+
+ // v:path
+ $objWriter->startElement('v:path');
+ $objWriter->writeAttribute('gradientshapeok', 't');
+ $objWriter->writeAttribute('o:connecttype', 'rect');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // Loop through comments
+ foreach ($comments as $key => $value) {
+ $this->writeVMLComment($objWriter, $key, $value);
+ }
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write VML comment to XML format
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param string $pCellReference Cell reference
+ * @param PHPExcel_Comment $pComment Comment
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeVMLComment(PHPExcel_Shared_XMLWriter $objWriter = null, $pCellReference = 'A1', PHPExcel_Comment $pComment = null)
+ {
+ // Metadata
+ list($column, $row) = PHPExcel_Cell::coordinateFromString($pCellReference);
+ $column = PHPExcel_Cell::columnIndexFromString($column);
+ $id = 1024 + $column + $row;
+ $id = substr($id, 0, 4);
+
+ // v:shape
+ $objWriter->startElement('v:shape');
+ $objWriter->writeAttribute('id', '_x0000_s' . $id);
+ $objWriter->writeAttribute('type', '#_x0000_t202');
+ $objWriter->writeAttribute('style', 'position:absolute;margin-left:' . $pComment->getMarginLeft() . ';margin-top:' . $pComment->getMarginTop() . ';width:' . $pComment->getWidth() . ';height:' . $pComment->getHeight() . ';z-index:1;visibility:' . ($pComment->getVisible() ? 'visible' : 'hidden'));
+ $objWriter->writeAttribute('fillcolor', '#' . $pComment->getFillColor()->getRGB());
+ $objWriter->writeAttribute('o:insetmode', 'auto');
+
+ // v:fill
+ $objWriter->startElement('v:fill');
+ $objWriter->writeAttribute('color2', '#' . $pComment->getFillColor()->getRGB());
+ $objWriter->endElement();
+
+ // v:shadow
+ $objWriter->startElement('v:shadow');
+ $objWriter->writeAttribute('on', 't');
+ $objWriter->writeAttribute('color', 'black');
+ $objWriter->writeAttribute('obscured', 't');
+ $objWriter->endElement();
+
+ // v:path
+ $objWriter->startElement('v:path');
+ $objWriter->writeAttribute('o:connecttype', 'none');
+ $objWriter->endElement();
+
+ // v:textbox
+ $objWriter->startElement('v:textbox');
+ $objWriter->writeAttribute('style', 'mso-direction-alt:auto');
+
+ // div
+ $objWriter->startElement('div');
+ $objWriter->writeAttribute('style', 'text-align:left');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // x:ClientData
+ $objWriter->startElement('x:ClientData');
+ $objWriter->writeAttribute('ObjectType', 'Note');
+
+ // x:MoveWithCells
+ $objWriter->writeElement('x:MoveWithCells', '');
+
+ // x:SizeWithCells
+ $objWriter->writeElement('x:SizeWithCells', '');
+
+ // x:Anchor
+ //$objWriter->writeElement('x:Anchor', $column . ', 15, ' . ($row - 2) . ', 10, ' . ($column + 4) . ', 15, ' . ($row + 5) . ', 18');
+
+ // x:AutoFill
+ $objWriter->writeElement('x:AutoFill', 'False');
+
+ // x:Row
+ $objWriter->writeElement('x:Row', ($row - 1));
+
+ // x:Column
+ $objWriter->writeElement('x:Column', ($column - 1));
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/ContentTypes.php b/PHPExcel/Writer/Excel2007/ContentTypes.php
new file mode 100644
index 0000000..0d91189
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/ContentTypes.php
@@ -0,0 +1,240 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Types
+ $objWriter->startElement('Types');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/content-types');
+
+ // Theme
+ $this->writeOverrideContentType($objWriter, '/xl/theme/theme1.xml', 'application/vnd.openxmlformats-officedocument.theme+xml');
+
+ // Styles
+ $this->writeOverrideContentType($objWriter, '/xl/styles.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml');
+
+ // Rels
+ $this->writeDefaultContentType($objWriter, 'rels', 'application/vnd.openxmlformats-package.relationships+xml');
+
+ // XML
+ $this->writeDefaultContentType($objWriter, 'xml', 'application/xml');
+
+ // VML
+ $this->writeDefaultContentType($objWriter, 'vml', 'application/vnd.openxmlformats-officedocument.vmlDrawing');
+
+ // Workbook
+ if ($pPHPExcel->hasMacros()) { //Macros in workbook ?
+ // Yes : not standard content but "macroEnabled"
+ $this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.ms-excel.sheet.macroEnabled.main+xml');
+ //... and define a new type for the VBA project
+ $this->writeDefaultContentType($objWriter, 'bin', 'application/vnd.ms-office.vbaProject');
+ if ($pPHPExcel->hasMacrosCertificate()) {// signed macros ?
+ // Yes : add needed information
+ $this->writeOverrideContentType($objWriter, '/xl/vbaProjectSignature.bin', 'application/vnd.ms-office.vbaProjectSignature');
+ }
+ } else {// no macros in workbook, so standard type
+ $this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml');
+ }
+
+ // DocProps
+ $this->writeOverrideContentType($objWriter, '/docProps/app.xml', 'application/vnd.openxmlformats-officedocument.extended-properties+xml');
+
+ $this->writeOverrideContentType($objWriter, '/docProps/core.xml', 'application/vnd.openxmlformats-package.core-properties+xml');
+
+ $customPropertyList = $pPHPExcel->getProperties()->getCustomProperties();
+ if (!empty($customPropertyList)) {
+ $this->writeOverrideContentType($objWriter, '/docProps/custom.xml', 'application/vnd.openxmlformats-officedocument.custom-properties+xml');
+ }
+
+ // Worksheets
+ $sheetCount = $pPHPExcel->getSheetCount();
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ $this->writeOverrideContentType($objWriter, '/xl/worksheets/sheet' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml');
+ }
+
+ // Shared strings
+ $this->writeOverrideContentType($objWriter, '/xl/sharedStrings.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml');
+
+ // Add worksheet relationship content types
+ $chart = 1;
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ $drawings = $pPHPExcel->getSheet($i)->getDrawingCollection();
+ $drawingCount = count($drawings);
+ $chartCount = ($includeCharts) ? $pPHPExcel->getSheet($i)->getChartCount() : 0;
+
+ // We need a drawing relationship for the worksheet if we have either drawings or charts
+ if (($drawingCount > 0) || ($chartCount > 0)) {
+ $this->writeOverrideContentType($objWriter, '/xl/drawings/drawing' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.drawing+xml');
+ }
+
+ // If we have charts, then we need a chart relationship for every individual chart
+ if ($chartCount > 0) {
+ for ($c = 0; $c < $chartCount; ++$c) {
+ $this->writeOverrideContentType($objWriter, '/xl/charts/chart' . $chart++ . '.xml', 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml');
+ }
+ }
+ }
+
+ // Comments
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ if (count($pPHPExcel->getSheet($i)->getComments()) > 0) {
+ $this->writeOverrideContentType($objWriter, '/xl/comments' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml');
+ }
+ }
+
+ // Add media content-types
+ $aMediaContentTypes = array();
+ $mediaCount = $this->getParentWriter()->getDrawingHashTable()->count();
+ for ($i = 0; $i < $mediaCount; ++$i) {
+ $extension = '';
+ $mimeType = '';
+
+ if ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof PHPExcel_Worksheet_Drawing) {
+ $extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getExtension());
+ $mimeType = $this->getImageMimeType($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getPath());
+ } elseif ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof PHPExcel_Worksheet_MemoryDrawing) {
+ $extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType());
+ $extension = explode('/', $extension);
+ $extension = $extension[1];
+
+ $mimeType = $this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType();
+ }
+
+ if (!isset( $aMediaContentTypes[$extension])) {
+ $aMediaContentTypes[$extension] = $mimeType;
+
+ $this->writeDefaultContentType($objWriter, $extension, $mimeType);
+ }
+ }
+ if ($pPHPExcel->hasRibbonBinObjects()) {
+ // Some additional objects in the ribbon ?
+ // we need to write "Extension" but not already write for media content
+ $tabRibbonTypes=array_diff($pPHPExcel->getRibbonBinObjects('types'), array_keys($aMediaContentTypes));
+ foreach ($tabRibbonTypes as $aRibbonType) {
+ $mimeType='image/.'.$aRibbonType;//we wrote $mimeType like customUI Editor
+ $this->writeDefaultContentType($objWriter, $aRibbonType, $mimeType);
+ }
+ }
+ $sheetCount = $pPHPExcel->getSheetCount();
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ if (count($pPHPExcel->getSheet()->getHeaderFooter()->getImages()) > 0) {
+ foreach ($pPHPExcel->getSheet()->getHeaderFooter()->getImages() as $image) {
+ if (!isset( $aMediaContentTypes[strtolower($image->getExtension())])) {
+ $aMediaContentTypes[strtolower($image->getExtension())] = $this->getImageMimeType($image->getPath());
+
+ $this->writeDefaultContentType($objWriter, strtolower($image->getExtension()), $aMediaContentTypes[strtolower($image->getExtension())]);
+ }
+ }
+ }
+ }
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Get image mime type
+ *
+ * @param string $pFile Filename
+ * @return string Mime Type
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function getImageMimeType($pFile = '')
+ {
+ if (PHPExcel_Shared_File::file_exists($pFile)) {
+ $image = getimagesize($pFile);
+ return image_type_to_mime_type($image[2]);
+ } else {
+ throw new PHPExcel_Writer_Exception("File $pFile does not exist");
+ }
+ }
+
+ /**
+ * Write Default content type
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param string $pPartname Part name
+ * @param string $pContentType Content type
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDefaultContentType(PHPExcel_Shared_XMLWriter $objWriter = null, $pPartname = '', $pContentType = '')
+ {
+ if ($pPartname != '' && $pContentType != '') {
+ // Write content type
+ $objWriter->startElement('Default');
+ $objWriter->writeAttribute('Extension', $pPartname);
+ $objWriter->writeAttribute('ContentType', $pContentType);
+ $objWriter->endElement();
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid parameters passed.");
+ }
+ }
+
+ /**
+ * Write Override content type
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param string $pPartname Part name
+ * @param string $pContentType Content type
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeOverrideContentType(PHPExcel_Shared_XMLWriter $objWriter = null, $pPartname = '', $pContentType = '')
+ {
+ if ($pPartname != '' && $pContentType != '') {
+ // Write content type
+ $objWriter->startElement('Override');
+ $objWriter->writeAttribute('PartName', $pPartname);
+ $objWriter->writeAttribute('ContentType', $pContentType);
+ $objWriter->endElement();
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid parameters passed.");
+ }
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/DocProps.php b/PHPExcel/Writer/Excel2007/DocProps.php
new file mode 100644
index 0000000..fef3d93
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/DocProps.php
@@ -0,0 +1,262 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Properties
+ $objWriter->startElement('Properties');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties');
+ $objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
+
+ // Application
+ $objWriter->writeElement('Application', 'Microsoft Excel');
+
+ // DocSecurity
+ $objWriter->writeElement('DocSecurity', '0');
+
+ // ScaleCrop
+ $objWriter->writeElement('ScaleCrop', 'false');
+
+ // HeadingPairs
+ $objWriter->startElement('HeadingPairs');
+
+ // Vector
+ $objWriter->startElement('vt:vector');
+ $objWriter->writeAttribute('size', '2');
+ $objWriter->writeAttribute('baseType', 'variant');
+
+ // Variant
+ $objWriter->startElement('vt:variant');
+ $objWriter->writeElement('vt:lpstr', 'Worksheets');
+ $objWriter->endElement();
+
+ // Variant
+ $objWriter->startElement('vt:variant');
+ $objWriter->writeElement('vt:i4', $pPHPExcel->getSheetCount());
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // TitlesOfParts
+ $objWriter->startElement('TitlesOfParts');
+
+ // Vector
+ $objWriter->startElement('vt:vector');
+ $objWriter->writeAttribute('size', $pPHPExcel->getSheetCount());
+ $objWriter->writeAttribute('baseType', 'lpstr');
+
+ $sheetCount = $pPHPExcel->getSheetCount();
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ $objWriter->writeElement('vt:lpstr', $pPHPExcel->getSheet($i)->getTitle());
+ }
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // Company
+ $objWriter->writeElement('Company', $pPHPExcel->getProperties()->getCompany());
+
+ // Company
+ $objWriter->writeElement('Manager', $pPHPExcel->getProperties()->getManager());
+
+ // LinksUpToDate
+ $objWriter->writeElement('LinksUpToDate', 'false');
+
+ // SharedDoc
+ $objWriter->writeElement('SharedDoc', 'false');
+
+ // HyperlinksChanged
+ $objWriter->writeElement('HyperlinksChanged', 'false');
+
+ // AppVersion
+ $objWriter->writeElement('AppVersion', '12.0000');
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write docProps/core.xml to XML format
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeDocPropsCore(PHPExcel $pPHPExcel = null)
+ {
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // cp:coreProperties
+ $objWriter->startElement('cp:coreProperties');
+ $objWriter->writeAttribute('xmlns:cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
+ $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
+ $objWriter->writeAttribute('xmlns:dcterms', 'http://purl.org/dc/terms/');
+ $objWriter->writeAttribute('xmlns:dcmitype', 'http://purl.org/dc/dcmitype/');
+ $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
+
+ // dc:creator
+ $objWriter->writeElement('dc:creator', $pPHPExcel->getProperties()->getCreator());
+
+ // cp:lastModifiedBy
+ $objWriter->writeElement('cp:lastModifiedBy', $pPHPExcel->getProperties()->getLastModifiedBy());
+
+ // dcterms:created
+ $objWriter->startElement('dcterms:created');
+ $objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF');
+ $objWriter->writeRawData(date(DATE_W3C, $pPHPExcel->getProperties()->getCreated()));
+ $objWriter->endElement();
+
+ // dcterms:modified
+ $objWriter->startElement('dcterms:modified');
+ $objWriter->writeAttribute('xsi:type', 'dcterms:W3CDTF');
+ $objWriter->writeRawData(date(DATE_W3C, $pPHPExcel->getProperties()->getModified()));
+ $objWriter->endElement();
+
+ // dc:title
+ $objWriter->writeElement('dc:title', $pPHPExcel->getProperties()->getTitle());
+
+ // dc:description
+ $objWriter->writeElement('dc:description', $pPHPExcel->getProperties()->getDescription());
+
+ // dc:subject
+ $objWriter->writeElement('dc:subject', $pPHPExcel->getProperties()->getSubject());
+
+ // cp:keywords
+ $objWriter->writeElement('cp:keywords', $pPHPExcel->getProperties()->getKeywords());
+
+ // cp:category
+ $objWriter->writeElement('cp:category', $pPHPExcel->getProperties()->getCategory());
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write docProps/custom.xml to XML format
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeDocPropsCustom(PHPExcel $pPHPExcel = null)
+ {
+ $customPropertyList = $pPHPExcel->getProperties()->getCustomProperties();
+ if (empty($customPropertyList)) {
+ return;
+ }
+
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // cp:coreProperties
+ $objWriter->startElement('Properties');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/officeDocument/2006/custom-properties');
+ $objWriter->writeAttribute('xmlns:vt', 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
+
+
+ foreach ($customPropertyList as $key => $customProperty) {
+ $propertyValue = $pPHPExcel->getProperties()->getCustomPropertyValue($customProperty);
+ $propertyType = $pPHPExcel->getProperties()->getCustomPropertyType($customProperty);
+
+ $objWriter->startElement('property');
+ $objWriter->writeAttribute('fmtid', '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}');
+ $objWriter->writeAttribute('pid', $key+2);
+ $objWriter->writeAttribute('name', $customProperty);
+
+ switch ($propertyType) {
+ case 'i':
+ $objWriter->writeElement('vt:i4', $propertyValue);
+ break;
+ case 'f':
+ $objWriter->writeElement('vt:r8', $propertyValue);
+ break;
+ case 'b':
+ $objWriter->writeElement('vt:bool', ($propertyValue) ? 'true' : 'false');
+ break;
+ case 'd':
+ $objWriter->startElement('vt:filetime');
+ $objWriter->writeRawData(date(DATE_W3C, $propertyValue));
+ $objWriter->endElement();
+ break;
+ default:
+ $objWriter->writeElement('vt:lpwstr', $propertyValue);
+ break;
+ }
+
+ $objWriter->endElement();
+ }
+
+
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/Drawing.php b/PHPExcel/Writer/Excel2007/Drawing.php
new file mode 100644
index 0000000..2814388
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/Drawing.php
@@ -0,0 +1,589 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // xdr:wsDr
+ $objWriter->startElement('xdr:wsDr');
+ $objWriter->writeAttribute('xmlns:xdr', 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
+ $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
+
+ // Loop through images and write drawings
+ $i = 1;
+ $iterator = $pWorksheet->getDrawingCollection()->getIterator();
+ while ($iterator->valid()) {
+ $this->writeDrawing($objWriter, $iterator->current(), $i);
+
+ $iterator->next();
+ ++$i;
+ }
+
+ if ($includeCharts) {
+ $chartCount = $pWorksheet->getChartCount();
+ // Loop through charts and write the chart position
+ if ($chartCount > 0) {
+ for ($c = 0; $c < $chartCount; ++$c) {
+ $this->writeChart($objWriter, $pWorksheet->getChartByIndex($c), $c+$i);
+ }
+ }
+ }
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write drawings to XML format
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Chart $pChart
+ * @param int $pRelationId
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeChart(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Chart $pChart = null, $pRelationId = -1)
+ {
+ $tl = $pChart->getTopLeftPosition();
+ $tl['colRow'] = PHPExcel_Cell::coordinateFromString($tl['cell']);
+ $br = $pChart->getBottomRightPosition();
+ $br['colRow'] = PHPExcel_Cell::coordinateFromString($br['cell']);
+
+ $objWriter->startElement('xdr:twoCellAnchor');
+
+ $objWriter->startElement('xdr:from');
+ $objWriter->writeElement('xdr:col', PHPExcel_Cell::columnIndexFromString($tl['colRow'][0]) - 1);
+ $objWriter->writeElement('xdr:colOff', PHPExcel_Shared_Drawing::pixelsToEMU($tl['xOffset']));
+ $objWriter->writeElement('xdr:row', $tl['colRow'][1] - 1);
+ $objWriter->writeElement('xdr:rowOff', PHPExcel_Shared_Drawing::pixelsToEMU($tl['yOffset']));
+ $objWriter->endElement();
+ $objWriter->startElement('xdr:to');
+ $objWriter->writeElement('xdr:col', PHPExcel_Cell::columnIndexFromString($br['colRow'][0]) - 1);
+ $objWriter->writeElement('xdr:colOff', PHPExcel_Shared_Drawing::pixelsToEMU($br['xOffset']));
+ $objWriter->writeElement('xdr:row', $br['colRow'][1] - 1);
+ $objWriter->writeElement('xdr:rowOff', PHPExcel_Shared_Drawing::pixelsToEMU($br['yOffset']));
+ $objWriter->endElement();
+
+ $objWriter->startElement('xdr:graphicFrame');
+ $objWriter->writeAttribute('macro', '');
+ $objWriter->startElement('xdr:nvGraphicFramePr');
+ $objWriter->startElement('xdr:cNvPr');
+ $objWriter->writeAttribute('name', 'Chart '.$pRelationId);
+ $objWriter->writeAttribute('id', 1025 * $pRelationId);
+ $objWriter->endElement();
+ $objWriter->startElement('xdr:cNvGraphicFramePr');
+ $objWriter->startElement('a:graphicFrameLocks');
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->startElement('xdr:xfrm');
+ $objWriter->startElement('a:off');
+ $objWriter->writeAttribute('x', '0');
+ $objWriter->writeAttribute('y', '0');
+ $objWriter->endElement();
+ $objWriter->startElement('a:ext');
+ $objWriter->writeAttribute('cx', '0');
+ $objWriter->writeAttribute('cy', '0');
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->startElement('a:graphic');
+ $objWriter->startElement('a:graphicData');
+ $objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
+ $objWriter->startElement('c:chart');
+ $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
+ $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+ $objWriter->writeAttribute('r:id', 'rId'.$pRelationId);
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ $objWriter->startElement('xdr:clientData');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write drawings to XML format
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet_BaseDrawing $pDrawing
+ * @param int $pRelationId
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeDrawing(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet_BaseDrawing $pDrawing = null, $pRelationId = -1)
+ {
+ if ($pRelationId >= 0) {
+ // xdr:oneCellAnchor
+ $objWriter->startElement('xdr:oneCellAnchor');
+ // Image location
+ $aCoordinates = PHPExcel_Cell::coordinateFromString($pDrawing->getCoordinates());
+ $aCoordinates[0] = PHPExcel_Cell::columnIndexFromString($aCoordinates[0]);
+
+ // xdr:from
+ $objWriter->startElement('xdr:from');
+ $objWriter->writeElement('xdr:col', $aCoordinates[0] - 1);
+ $objWriter->writeElement('xdr:colOff', PHPExcel_Shared_Drawing::pixelsToEMU($pDrawing->getOffsetX()));
+ $objWriter->writeElement('xdr:row', $aCoordinates[1] - 1);
+ $objWriter->writeElement('xdr:rowOff', PHPExcel_Shared_Drawing::pixelsToEMU($pDrawing->getOffsetY()));
+ $objWriter->endElement();
+
+ // xdr:ext
+ $objWriter->startElement('xdr:ext');
+ $objWriter->writeAttribute('cx', PHPExcel_Shared_Drawing::pixelsToEMU($pDrawing->getWidth()));
+ $objWriter->writeAttribute('cy', PHPExcel_Shared_Drawing::pixelsToEMU($pDrawing->getHeight()));
+ $objWriter->endElement();
+
+ // xdr:pic
+ $objWriter->startElement('xdr:pic');
+
+ // xdr:nvPicPr
+ $objWriter->startElement('xdr:nvPicPr');
+
+ // xdr:cNvPr
+ $objWriter->startElement('xdr:cNvPr');
+ $objWriter->writeAttribute('id', $pRelationId);
+ $objWriter->writeAttribute('name', $pDrawing->getName());
+ $objWriter->writeAttribute('descr', $pDrawing->getDescription());
+ $objWriter->endElement();
+
+ // xdr:cNvPicPr
+ $objWriter->startElement('xdr:cNvPicPr');
+
+ // a:picLocks
+ $objWriter->startElement('a:picLocks');
+ $objWriter->writeAttribute('noChangeAspect', '1');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // xdr:blipFill
+ $objWriter->startElement('xdr:blipFill');
+
+ // a:blip
+ $objWriter->startElement('a:blip');
+ $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+ $objWriter->writeAttribute('r:embed', 'rId' . $pRelationId);
+ $objWriter->endElement();
+
+ // a:stretch
+ $objWriter->startElement('a:stretch');
+ $objWriter->writeElement('a:fillRect', null);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // xdr:spPr
+ $objWriter->startElement('xdr:spPr');
+
+ // a:xfrm
+ $objWriter->startElement('a:xfrm');
+ $objWriter->writeAttribute('rot', PHPExcel_Shared_Drawing::degreesToAngle($pDrawing->getRotation()));
+ $objWriter->endElement();
+
+ // a:prstGeom
+ $objWriter->startElement('a:prstGeom');
+ $objWriter->writeAttribute('prst', 'rect');
+
+ // a:avLst
+ $objWriter->writeElement('a:avLst', null);
+
+ $objWriter->endElement();
+
+// // a:solidFill
+// $objWriter->startElement('a:solidFill');
+
+// // a:srgbClr
+// $objWriter->startElement('a:srgbClr');
+// $objWriter->writeAttribute('val', 'FFFFFF');
+
+///* SHADE
+// // a:shade
+// $objWriter->startElement('a:shade');
+// $objWriter->writeAttribute('val', '85000');
+// $objWriter->endElement();
+//*/
+
+// $objWriter->endElement();
+
+// $objWriter->endElement();
+/*
+ // a:ln
+ $objWriter->startElement('a:ln');
+ $objWriter->writeAttribute('w', '88900');
+ $objWriter->writeAttribute('cap', 'sq');
+
+ // a:solidFill
+ $objWriter->startElement('a:solidFill');
+
+ // a:srgbClr
+ $objWriter->startElement('a:srgbClr');
+ $objWriter->writeAttribute('val', 'FFFFFF');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:miter
+ $objWriter->startElement('a:miter');
+ $objWriter->writeAttribute('lim', '800000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+*/
+
+ if ($pDrawing->getShadow()->getVisible()) {
+ // a:effectLst
+ $objWriter->startElement('a:effectLst');
+
+ // a:outerShdw
+ $objWriter->startElement('a:outerShdw');
+ $objWriter->writeAttribute('blurRad', PHPExcel_Shared_Drawing::pixelsToEMU($pDrawing->getShadow()->getBlurRadius()));
+ $objWriter->writeAttribute('dist', PHPExcel_Shared_Drawing::pixelsToEMU($pDrawing->getShadow()->getDistance()));
+ $objWriter->writeAttribute('dir', PHPExcel_Shared_Drawing::degreesToAngle($pDrawing->getShadow()->getDirection()));
+ $objWriter->writeAttribute('algn', $pDrawing->getShadow()->getAlignment());
+ $objWriter->writeAttribute('rotWithShape', '0');
+
+ // a:srgbClr
+ $objWriter->startElement('a:srgbClr');
+ $objWriter->writeAttribute('val', $pDrawing->getShadow()->getColor()->getRGB());
+
+ // a:alpha
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', $pDrawing->getShadow()->getAlpha() * 1000);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+/*
+
+ // a:scene3d
+ $objWriter->startElement('a:scene3d');
+
+ // a:camera
+ $objWriter->startElement('a:camera');
+ $objWriter->writeAttribute('prst', 'orthographicFront');
+ $objWriter->endElement();
+
+ // a:lightRig
+ $objWriter->startElement('a:lightRig');
+ $objWriter->writeAttribute('rig', 'twoPt');
+ $objWriter->writeAttribute('dir', 't');
+
+ // a:rot
+ $objWriter->startElement('a:rot');
+ $objWriter->writeAttribute('lat', '0');
+ $objWriter->writeAttribute('lon', '0');
+ $objWriter->writeAttribute('rev', '0');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+*/
+/*
+ // a:sp3d
+ $objWriter->startElement('a:sp3d');
+
+ // a:bevelT
+ $objWriter->startElement('a:bevelT');
+ $objWriter->writeAttribute('w', '25400');
+ $objWriter->writeAttribute('h', '19050');
+ $objWriter->endElement();
+
+ // a:contourClr
+ $objWriter->startElement('a:contourClr');
+
+ // a:srgbClr
+ $objWriter->startElement('a:srgbClr');
+ $objWriter->writeAttribute('val', 'FFFFFF');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+*/
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // xdr:clientData
+ $objWriter->writeElement('xdr:clientData', null);
+
+ $objWriter->endElement();
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid parameters passed.");
+ }
+ }
+
+ /**
+ * Write VML header/footer images to XML format
+ *
+ * @param PHPExcel_Worksheet $pWorksheet
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeVMLHeaderFooterImages(PHPExcel_Worksheet $pWorksheet = null)
+ {
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Header/footer images
+ $images = $pWorksheet->getHeaderFooter()->getImages();
+
+ // xml
+ $objWriter->startElement('xml');
+ $objWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml');
+ $objWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office');
+ $objWriter->writeAttribute('xmlns:x', 'urn:schemas-microsoft-com:office:excel');
+
+ // o:shapelayout
+ $objWriter->startElement('o:shapelayout');
+ $objWriter->writeAttribute('v:ext', 'edit');
+
+ // o:idmap
+ $objWriter->startElement('o:idmap');
+ $objWriter->writeAttribute('v:ext', 'edit');
+ $objWriter->writeAttribute('data', '1');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // v:shapetype
+ $objWriter->startElement('v:shapetype');
+ $objWriter->writeAttribute('id', '_x0000_t75');
+ $objWriter->writeAttribute('coordsize', '21600,21600');
+ $objWriter->writeAttribute('o:spt', '75');
+ $objWriter->writeAttribute('o:preferrelative', 't');
+ $objWriter->writeAttribute('path', 'm@4@5l@4@11@9@11@9@5xe');
+ $objWriter->writeAttribute('filled', 'f');
+ $objWriter->writeAttribute('stroked', 'f');
+
+ // v:stroke
+ $objWriter->startElement('v:stroke');
+ $objWriter->writeAttribute('joinstyle', 'miter');
+ $objWriter->endElement();
+
+ // v:formulas
+ $objWriter->startElement('v:formulas');
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'if lineDrawn pixelLineWidth 0');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'sum @0 1 0');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'sum 0 0 @1');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'prod @2 1 2');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'prod @3 21600 pixelWidth');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'prod @3 21600 pixelHeight');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'sum @0 0 1');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'prod @6 1 2');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'prod @7 21600 pixelWidth');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'sum @8 21600 0');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'prod @7 21600 pixelHeight');
+ $objWriter->endElement();
+
+ // v:f
+ $objWriter->startElement('v:f');
+ $objWriter->writeAttribute('eqn', 'sum @10 21600 0');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // v:path
+ $objWriter->startElement('v:path');
+ $objWriter->writeAttribute('o:extrusionok', 'f');
+ $objWriter->writeAttribute('gradientshapeok', 't');
+ $objWriter->writeAttribute('o:connecttype', 'rect');
+ $objWriter->endElement();
+
+ // o:lock
+ $objWriter->startElement('o:lock');
+ $objWriter->writeAttribute('v:ext', 'edit');
+ $objWriter->writeAttribute('aspectratio', 't');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // Loop through images
+ foreach ($images as $key => $value) {
+ $this->writeVMLHeaderFooterImage($objWriter, $key, $value);
+ }
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write VML comment to XML format
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param string $pReference Reference
+ * @param PHPExcel_Worksheet_HeaderFooterDrawing $pImage Image
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeVMLHeaderFooterImage(PHPExcel_Shared_XMLWriter $objWriter = null, $pReference = '', PHPExcel_Worksheet_HeaderFooterDrawing $pImage = null)
+ {
+ // Calculate object id
+ preg_match('{(\d+)}', md5($pReference), $m);
+ $id = 1500 + (substr($m[1], 0, 2) * 1);
+
+ // Calculate offset
+ $width = $pImage->getWidth();
+ $height = $pImage->getHeight();
+ $marginLeft = $pImage->getOffsetX();
+ $marginTop = $pImage->getOffsetY();
+
+ // v:shape
+ $objWriter->startElement('v:shape');
+ $objWriter->writeAttribute('id', $pReference);
+ $objWriter->writeAttribute('o:spid', '_x0000_s' . $id);
+ $objWriter->writeAttribute('type', '#_x0000_t75');
+ $objWriter->writeAttribute('style', "position:absolute;margin-left:{$marginLeft}px;margin-top:{$marginTop}px;width:{$width}px;height:{$height}px;z-index:1");
+
+ // v:imagedata
+ $objWriter->startElement('v:imagedata');
+ $objWriter->writeAttribute('o:relid', 'rId' . $pReference);
+ $objWriter->writeAttribute('o:title', $pImage->getName());
+ $objWriter->endElement();
+
+ // o:lock
+ $objWriter->startElement('o:lock');
+ $objWriter->writeAttribute('v:ext', 'edit');
+ $objWriter->writeAttribute('rotation', 't');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+
+ /**
+ * Get an array of all drawings
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return PHPExcel_Worksheet_Drawing[] All drawings in PHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function allDrawings(PHPExcel $pPHPExcel = null)
+ {
+ // Get an array of all drawings
+ $aDrawings = array();
+
+ // Loop through PHPExcel
+ $sheetCount = $pPHPExcel->getSheetCount();
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ // Loop through images and add to array
+ $iterator = $pPHPExcel->getSheet($i)->getDrawingCollection()->getIterator();
+ while ($iterator->valid()) {
+ $aDrawings[] = $iterator->current();
+
+ $iterator->next();
+ }
+ }
+
+ return $aDrawings;
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/Rels.php b/PHPExcel/Writer/Excel2007/Rels.php
new file mode 100644
index 0000000..14ff337
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/Rels.php
@@ -0,0 +1,424 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Relationships
+ $objWriter->startElement('Relationships');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+ $customPropertyList = $pPHPExcel->getProperties()->getCustomProperties();
+ if (!empty($customPropertyList)) {
+ // Relationship docProps/app.xml
+ $this->writeRelationship(
+ $objWriter,
+ 4,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties',
+ 'docProps/custom.xml'
+ );
+
+ }
+
+ // Relationship docProps/app.xml
+ $this->writeRelationship(
+ $objWriter,
+ 3,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties',
+ 'docProps/app.xml'
+ );
+
+ // Relationship docProps/core.xml
+ $this->writeRelationship(
+ $objWriter,
+ 2,
+ 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties',
+ 'docProps/core.xml'
+ );
+
+ // Relationship xl/workbook.xml
+ $this->writeRelationship(
+ $objWriter,
+ 1,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
+ 'xl/workbook.xml'
+ );
+ // a custom UI in workbook ?
+ if ($pPHPExcel->hasRibbon()) {
+ $this->writeRelationShip(
+ $objWriter,
+ 5,
+ 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility',
+ $pPHPExcel->getRibbonXMLData('target')
+ );
+ }
+
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write workbook relationships to XML format
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeWorkbookRelationships(PHPExcel $pPHPExcel = null)
+ {
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Relationships
+ $objWriter->startElement('Relationships');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+ // Relationship styles.xml
+ $this->writeRelationship(
+ $objWriter,
+ 1,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
+ 'styles.xml'
+ );
+
+ // Relationship theme/theme1.xml
+ $this->writeRelationship(
+ $objWriter,
+ 2,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme',
+ 'theme/theme1.xml'
+ );
+
+ // Relationship sharedStrings.xml
+ $this->writeRelationship(
+ $objWriter,
+ 3,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings',
+ 'sharedStrings.xml'
+ );
+
+ // Relationships with sheets
+ $sheetCount = $pPHPExcel->getSheetCount();
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ $this->writeRelationship(
+ $objWriter,
+ ($i + 1 + 3),
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet',
+ 'worksheets/sheet' . ($i + 1) . '.xml'
+ );
+ }
+ // Relationships for vbaProject if needed
+ // id : just after the last sheet
+ if ($pPHPExcel->hasMacros()) {
+ $this->writeRelationShip(
+ $objWriter,
+ ($i + 1 + 3),
+ 'http://schemas.microsoft.com/office/2006/relationships/vbaProject',
+ 'vbaProject.bin'
+ );
+ ++$i;//increment i if needed for an another relation
+ }
+
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write worksheet relationships to XML format
+ *
+ * Numbering is as follows:
+ * rId1 - Drawings
+ * rId_hyperlink_x - Hyperlinks
+ *
+ * @param PHPExcel_Worksheet $pWorksheet
+ * @param int $pWorksheetId
+ * @param boolean $includeCharts Flag indicating if we should write charts
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeWorksheetRelationships(PHPExcel_Worksheet $pWorksheet = null, $pWorksheetId = 1, $includeCharts = false)
+ {
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Relationships
+ $objWriter->startElement('Relationships');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+ // Write drawing relationships?
+ $d = 0;
+ if ($includeCharts) {
+ $charts = $pWorksheet->getChartCollection();
+ } else {
+ $charts = array();
+ }
+ if (($pWorksheet->getDrawingCollection()->count() > 0) ||
+ (count($charts) > 0)) {
+ $this->writeRelationship(
+ $objWriter,
+ ++$d,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing',
+ '../drawings/drawing' . $pWorksheetId . '.xml'
+ );
+ }
+
+ // Write chart relationships?
+// $chartCount = 0;
+// $charts = $pWorksheet->getChartCollection();
+// echo 'Chart Rels: ' , count($charts) , '
';
+// if (count($charts) > 0) {
+// foreach ($charts as $chart) {
+// $this->writeRelationship(
+// $objWriter,
+// ++$d,
+// 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
+// '../charts/chart' . ++$chartCount . '.xml'
+// );
+// }
+// }
+//
+ // Write hyperlink relationships?
+ $i = 1;
+ foreach ($pWorksheet->getHyperlinkCollection() as $hyperlink) {
+ if (!$hyperlink->isInternal()) {
+ $this->writeRelationship(
+ $objWriter,
+ '_hyperlink_' . $i,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
+ $hyperlink->getUrl(),
+ 'External'
+ );
+
+ ++$i;
+ }
+ }
+
+ // Write comments relationship?
+ $i = 1;
+ if (count($pWorksheet->getComments()) > 0) {
+ $this->writeRelationship(
+ $objWriter,
+ '_comments_vml' . $i,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing',
+ '../drawings/vmlDrawing' . $pWorksheetId . '.vml'
+ );
+
+ $this->writeRelationship(
+ $objWriter,
+ '_comments' . $i,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments',
+ '../comments' . $pWorksheetId . '.xml'
+ );
+ }
+
+ // Write header/footer relationship?
+ $i = 1;
+ if (count($pWorksheet->getHeaderFooter()->getImages()) > 0) {
+ $this->writeRelationship(
+ $objWriter,
+ '_headerfooter_vml' . $i,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing',
+ '../drawings/vmlDrawingHF' . $pWorksheetId . '.vml'
+ );
+ }
+
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write drawing relationships to XML format
+ *
+ * @param PHPExcel_Worksheet $pWorksheet
+ * @param int &$chartRef Chart ID
+ * @param boolean $includeCharts Flag indicating if we should write charts
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeDrawingRelationships(PHPExcel_Worksheet $pWorksheet = null, &$chartRef, $includeCharts = false)
+ {
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Relationships
+ $objWriter->startElement('Relationships');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+ // Loop through images and write relationships
+ $i = 1;
+ $iterator = $pWorksheet->getDrawingCollection()->getIterator();
+ while ($iterator->valid()) {
+ if ($iterator->current() instanceof PHPExcel_Worksheet_Drawing
+ || $iterator->current() instanceof PHPExcel_Worksheet_MemoryDrawing) {
+ // Write relationship for image drawing
+ $this->writeRelationship(
+ $objWriter,
+ $i,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
+ '../media/' . str_replace(' ', '', $iterator->current()->getIndexedFilename())
+ );
+ }
+
+ $iterator->next();
+ ++$i;
+ }
+
+ if ($includeCharts) {
+ // Loop through charts and write relationships
+ $chartCount = $pWorksheet->getChartCount();
+ if ($chartCount > 0) {
+ for ($c = 0; $c < $chartCount; ++$c) {
+ $this->writeRelationship(
+ $objWriter,
+ $i++,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
+ '../charts/chart' . ++$chartRef . '.xml'
+ );
+ }
+ }
+ }
+
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write header/footer drawing relationships to XML format
+ *
+ * @param PHPExcel_Worksheet $pWorksheet
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeHeaderFooterDrawingRelationships(PHPExcel_Worksheet $pWorksheet = null)
+ {
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Relationships
+ $objWriter->startElement('Relationships');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+
+ // Loop through images and write relationships
+ foreach ($pWorksheet->getHeaderFooter()->getImages() as $key => $value) {
+ // Write relationship for image drawing
+ $this->writeRelationship(
+ $objWriter,
+ $key,
+ 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
+ '../media/' . $value->getIndexedFilename()
+ );
+ }
+
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write Override content type
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param int $pId Relationship ID. rId will be prepended!
+ * @param string $pType Relationship type
+ * @param string $pTarget Relationship target
+ * @param string $pTargetMode Relationship target mode
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeRelationship(PHPExcel_Shared_XMLWriter $objWriter = null, $pId = 1, $pType = '', $pTarget = '', $pTargetMode = '')
+ {
+ if ($pType != '' && $pTarget != '') {
+ // Write relationship
+ $objWriter->startElement('Relationship');
+ $objWriter->writeAttribute('Id', 'rId' . $pId);
+ $objWriter->writeAttribute('Type', $pType);
+ $objWriter->writeAttribute('Target', $pTarget);
+
+ if ($pTargetMode != '') {
+ $objWriter->writeAttribute('TargetMode', $pTargetMode);
+ }
+
+ $objWriter->endElement();
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid parameters passed.");
+ }
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/RelsRibbon.php b/PHPExcel/Writer/Excel2007/RelsRibbon.php
new file mode 100644
index 0000000..5a4a9fc
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/RelsRibbon.php
@@ -0,0 +1,67 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Relationships
+ $objWriter->startElement('Relationships');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+ $localRels = $pPHPExcel->getRibbonBinObjects('names');
+ if (is_array($localRels)) {
+ foreach ($localRels as $aId => $aTarget) {
+ $objWriter->startElement('Relationship');
+ $objWriter->writeAttribute('Id', $aId);
+ $objWriter->writeAttribute('Type', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image');
+ $objWriter->writeAttribute('Target', $aTarget);
+ $objWriter->endElement();
+ }
+ }
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/RelsVBA.php b/PHPExcel/Writer/Excel2007/RelsVBA.php
new file mode 100644
index 0000000..bee0e64
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/RelsVBA.php
@@ -0,0 +1,63 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Relationships
+ $objWriter->startElement('Relationships');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
+ $objWriter->startElement('Relationship');
+ $objWriter->writeAttribute('Id', 'rId1');
+ $objWriter->writeAttribute('Type', 'http://schemas.microsoft.com/office/2006/relationships/vbaProjectSignature');
+ $objWriter->writeAttribute('Target', 'vbaProjectSignature.bin');
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/StringTable.php b/PHPExcel/Writer/Excel2007/StringTable.php
new file mode 100644
index 0000000..fccec66
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/StringTable.php
@@ -0,0 +1,313 @@
+flipStringTable($aStringTable);
+
+ // Loop through cells
+ foreach ($pSheet->getCellCollection() as $cellID) {
+ $cell = $pSheet->getCell($cellID);
+ $cellValue = $cell->getValue();
+ if (!is_object($cellValue) &&
+ ($cellValue !== null) &&
+ $cellValue !== '' &&
+ !isset($aFlippedStringTable[$cellValue]) &&
+ ($cell->getDataType() == PHPExcel_Cell_DataType::TYPE_STRING || $cell->getDataType() == PHPExcel_Cell_DataType::TYPE_STRING2 || $cell->getDataType() == PHPExcel_Cell_DataType::TYPE_NULL)) {
+ $aStringTable[] = $cellValue;
+ $aFlippedStringTable[$cellValue] = true;
+ } elseif ($cellValue instanceof PHPExcel_RichText &&
+ ($cellValue !== null) &&
+ !isset($aFlippedStringTable[$cellValue->getHashCode()])) {
+ $aStringTable[] = $cellValue;
+ $aFlippedStringTable[$cellValue->getHashCode()] = true;
+ }
+ }
+
+ return $aStringTable;
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid PHPExcel_Worksheet object passed.");
+ }
+ }
+
+ /**
+ * Write string table to XML format
+ *
+ * @param string[] $pStringTable
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeStringTable($pStringTable = null)
+ {
+ if ($pStringTable !== null) {
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // String table
+ $objWriter->startElement('sst');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+ $objWriter->writeAttribute('uniqueCount', count($pStringTable));
+
+ // Loop through string table
+ foreach ($pStringTable as $textElement) {
+ $objWriter->startElement('si');
+
+ if (! $textElement instanceof PHPExcel_RichText) {
+ $textToWrite = PHPExcel_Shared_String::ControlCharacterPHP2OOXML($textElement);
+ $objWriter->startElement('t');
+ if ($textToWrite !== trim($textToWrite)) {
+ $objWriter->writeAttribute('xml:space', 'preserve');
+ }
+ $objWriter->writeRawData($textToWrite);
+ $objWriter->endElement();
+ } elseif ($textElement instanceof PHPExcel_RichText) {
+ $this->writeRichText($objWriter, $textElement);
+ }
+
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid string table array passed.");
+ }
+ }
+
+ /**
+ * Write Rich Text
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_RichText $pRichText Rich text
+ * @param string $prefix Optional Namespace prefix
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeRichText(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_RichText $pRichText = null, $prefix = null)
+ {
+ if ($prefix !== null) {
+ $prefix .= ':';
+ }
+
+ // Loop through rich text elements
+ $elements = $pRichText->getRichTextElements();
+ foreach ($elements as $element) {
+ // r
+ $objWriter->startElement($prefix.'r');
+
+ // rPr
+ if ($element instanceof PHPExcel_RichText_Run) {
+ // rPr
+ $objWriter->startElement($prefix.'rPr');
+
+ // rFont
+ $objWriter->startElement($prefix.'rFont');
+ $objWriter->writeAttribute('val', $element->getFont()->getName());
+ $objWriter->endElement();
+
+ // Bold
+ $objWriter->startElement($prefix.'b');
+ $objWriter->writeAttribute('val', ($element->getFont()->getBold() ? 'true' : 'false'));
+ $objWriter->endElement();
+
+ // Italic
+ $objWriter->startElement($prefix.'i');
+ $objWriter->writeAttribute('val', ($element->getFont()->getItalic() ? 'true' : 'false'));
+ $objWriter->endElement();
+
+ // Superscript / subscript
+ if ($element->getFont()->getSuperScript() || $element->getFont()->getSubScript()) {
+ $objWriter->startElement($prefix.'vertAlign');
+ if ($element->getFont()->getSuperScript()) {
+ $objWriter->writeAttribute('val', 'superscript');
+ } elseif ($element->getFont()->getSubScript()) {
+ $objWriter->writeAttribute('val', 'subscript');
+ }
+ $objWriter->endElement();
+ }
+
+ // Strikethrough
+ $objWriter->startElement($prefix.'strike');
+ $objWriter->writeAttribute('val', ($element->getFont()->getStrikethrough() ? 'true' : 'false'));
+ $objWriter->endElement();
+
+ // Color
+ $objWriter->startElement($prefix.'color');
+ $objWriter->writeAttribute('rgb', $element->getFont()->getColor()->getARGB());
+ $objWriter->endElement();
+
+ // Size
+ $objWriter->startElement($prefix.'sz');
+ $objWriter->writeAttribute('val', $element->getFont()->getSize());
+ $objWriter->endElement();
+
+ // Underline
+ $objWriter->startElement($prefix.'u');
+ $objWriter->writeAttribute('val', $element->getFont()->getUnderline());
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ // t
+ $objWriter->startElement($prefix.'t');
+ $objWriter->writeAttribute('xml:space', 'preserve');
+ $objWriter->writeRawData(PHPExcel_Shared_String::ControlCharacterPHP2OOXML($element->getText()));
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write Rich Text
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param string|PHPExcel_RichText $pRichText text string or Rich text
+ * @param string $prefix Optional Namespace prefix
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeRichTextForCharts(PHPExcel_Shared_XMLWriter $objWriter = null, $pRichText = null, $prefix = null)
+ {
+ if (!$pRichText instanceof PHPExcel_RichText) {
+ $textRun = $pRichText;
+ $pRichText = new PHPExcel_RichText();
+ $pRichText->createTextRun($textRun);
+ }
+
+ if ($prefix !== null) {
+ $prefix .= ':';
+ }
+
+ // Loop through rich text elements
+ $elements = $pRichText->getRichTextElements();
+ foreach ($elements as $element) {
+ // r
+ $objWriter->startElement($prefix.'r');
+
+ // rPr
+ $objWriter->startElement($prefix.'rPr');
+
+ // Bold
+ $objWriter->writeAttribute('b', ($element->getFont()->getBold() ? 1 : 0));
+ // Italic
+ $objWriter->writeAttribute('i', ($element->getFont()->getItalic() ? 1 : 0));
+ // Underline
+ $underlineType = $element->getFont()->getUnderline();
+ switch ($underlineType) {
+ case 'single':
+ $underlineType = 'sng';
+ break;
+ case 'double':
+ $underlineType = 'dbl';
+ break;
+ }
+ $objWriter->writeAttribute('u', $underlineType);
+ // Strikethrough
+ $objWriter->writeAttribute('strike', ($element->getFont()->getStrikethrough() ? 'sngStrike' : 'noStrike'));
+
+ // rFont
+ $objWriter->startElement($prefix.'latin');
+ $objWriter->writeAttribute('typeface', $element->getFont()->getName());
+ $objWriter->endElement();
+
+ // Superscript / subscript
+// if ($element->getFont()->getSuperScript() || $element->getFont()->getSubScript()) {
+// $objWriter->startElement($prefix.'vertAlign');
+// if ($element->getFont()->getSuperScript()) {
+// $objWriter->writeAttribute('val', 'superscript');
+// } elseif ($element->getFont()->getSubScript()) {
+// $objWriter->writeAttribute('val', 'subscript');
+// }
+// $objWriter->endElement();
+// }
+//
+ $objWriter->endElement();
+
+ // t
+ $objWriter->startElement($prefix.'t');
+// $objWriter->writeAttribute('xml:space', 'preserve'); // Excel2010 accepts, Excel2007 complains
+ $objWriter->writeRawData(PHPExcel_Shared_String::ControlCharacterPHP2OOXML($element->getText()));
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Flip string table (for index searching)
+ *
+ * @param array $stringTable Stringtable
+ * @return array
+ */
+ public function flipStringTable($stringTable = array())
+ {
+ // Return value
+ $returnValue = array();
+
+ // Loop through stringtable and add flipped items to $returnValue
+ foreach ($stringTable as $key => $value) {
+ if (! $value instanceof PHPExcel_RichText) {
+ $returnValue[$value] = $key;
+ } elseif ($value instanceof PHPExcel_RichText) {
+ $returnValue[$value->getHashCode()] = $key;
+ }
+ }
+
+ return $returnValue;
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/Style.php b/PHPExcel/Writer/Excel2007/Style.php
new file mode 100644
index 0000000..d3f0af7
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/Style.php
@@ -0,0 +1,696 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // styleSheet
+ $objWriter->startElement('styleSheet');
+ $objWriter->writeAttribute('xml:space', 'preserve');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+
+ // numFmts
+ $objWriter->startElement('numFmts');
+ $objWriter->writeAttribute('count', $this->getParentWriter()->getNumFmtHashTable()->count());
+
+ // numFmt
+ for ($i = 0; $i < $this->getParentWriter()->getNumFmtHashTable()->count(); ++$i) {
+ $this->writeNumFmt($objWriter, $this->getParentWriter()->getNumFmtHashTable()->getByIndex($i), $i);
+ }
+
+ $objWriter->endElement();
+
+ // fonts
+ $objWriter->startElement('fonts');
+ $objWriter->writeAttribute('count', $this->getParentWriter()->getFontHashTable()->count());
+
+ // font
+ for ($i = 0; $i < $this->getParentWriter()->getFontHashTable()->count(); ++$i) {
+ $this->writeFont($objWriter, $this->getParentWriter()->getFontHashTable()->getByIndex($i));
+ }
+
+ $objWriter->endElement();
+
+ // fills
+ $objWriter->startElement('fills');
+ $objWriter->writeAttribute('count', $this->getParentWriter()->getFillHashTable()->count());
+
+ // fill
+ for ($i = 0; $i < $this->getParentWriter()->getFillHashTable()->count(); ++$i) {
+ $this->writeFill($objWriter, $this->getParentWriter()->getFillHashTable()->getByIndex($i));
+ }
+
+ $objWriter->endElement();
+
+ // borders
+ $objWriter->startElement('borders');
+ $objWriter->writeAttribute('count', $this->getParentWriter()->getBordersHashTable()->count());
+
+ // border
+ for ($i = 0; $i < $this->getParentWriter()->getBordersHashTable()->count(); ++$i) {
+ $this->writeBorder($objWriter, $this->getParentWriter()->getBordersHashTable()->getByIndex($i));
+ }
+
+ $objWriter->endElement();
+
+ // cellStyleXfs
+ $objWriter->startElement('cellStyleXfs');
+ $objWriter->writeAttribute('count', 1);
+
+ // xf
+ $objWriter->startElement('xf');
+ $objWriter->writeAttribute('numFmtId', 0);
+ $objWriter->writeAttribute('fontId', 0);
+ $objWriter->writeAttribute('fillId', 0);
+ $objWriter->writeAttribute('borderId', 0);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // cellXfs
+ $objWriter->startElement('cellXfs');
+ $objWriter->writeAttribute('count', count($pPHPExcel->getCellXfCollection()));
+
+ // xf
+ foreach ($pPHPExcel->getCellXfCollection() as $cellXf) {
+ $this->writeCellStyleXf($objWriter, $cellXf, $pPHPExcel);
+ }
+
+ $objWriter->endElement();
+
+ // cellStyles
+ $objWriter->startElement('cellStyles');
+ $objWriter->writeAttribute('count', 1);
+
+ // cellStyle
+ $objWriter->startElement('cellStyle');
+ $objWriter->writeAttribute('name', 'Normal');
+ $objWriter->writeAttribute('xfId', 0);
+ $objWriter->writeAttribute('builtinId', 0);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // dxfs
+ $objWriter->startElement('dxfs');
+ $objWriter->writeAttribute('count', $this->getParentWriter()->getStylesConditionalHashTable()->count());
+
+ // dxf
+ for ($i = 0; $i < $this->getParentWriter()->getStylesConditionalHashTable()->count(); ++$i) {
+ $this->writeCellStyleDxf($objWriter, $this->getParentWriter()->getStylesConditionalHashTable()->getByIndex($i)->getStyle());
+ }
+
+ $objWriter->endElement();
+
+ // tableStyles
+ $objWriter->startElement('tableStyles');
+ $objWriter->writeAttribute('defaultTableStyle', 'TableStyleMedium9');
+ $objWriter->writeAttribute('defaultPivotStyle', 'PivotTableStyle1');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write Fill
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Style_Fill $pFill Fill style
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeFill(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Style_Fill $pFill = null)
+ {
+ // Check if this is a pattern type or gradient type
+ if ($pFill->getFillType() === PHPExcel_Style_Fill::FILL_GRADIENT_LINEAR ||
+ $pFill->getFillType() === PHPExcel_Style_Fill::FILL_GRADIENT_PATH) {
+ // Gradient fill
+ $this->writeGradientFill($objWriter, $pFill);
+ } elseif ($pFill->getFillType() !== null) {
+ // Pattern fill
+ $this->writePatternFill($objWriter, $pFill);
+ }
+ }
+
+ /**
+ * Write Gradient Fill
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Style_Fill $pFill Fill style
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeGradientFill(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Style_Fill $pFill = null)
+ {
+ // fill
+ $objWriter->startElement('fill');
+
+ // gradientFill
+ $objWriter->startElement('gradientFill');
+ $objWriter->writeAttribute('type', $pFill->getFillType());
+ $objWriter->writeAttribute('degree', $pFill->getRotation());
+
+ // stop
+ $objWriter->startElement('stop');
+ $objWriter->writeAttribute('position', '0');
+
+ // color
+ $objWriter->startElement('color');
+ $objWriter->writeAttribute('rgb', $pFill->getStartColor()->getARGB());
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // stop
+ $objWriter->startElement('stop');
+ $objWriter->writeAttribute('position', '1');
+
+ // color
+ $objWriter->startElement('color');
+ $objWriter->writeAttribute('rgb', $pFill->getEndColor()->getARGB());
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Pattern Fill
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Style_Fill $pFill Fill style
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writePatternFill(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Style_Fill $pFill = null)
+ {
+ // fill
+ $objWriter->startElement('fill');
+
+ // patternFill
+ $objWriter->startElement('patternFill');
+ $objWriter->writeAttribute('patternType', $pFill->getFillType());
+
+ if ($pFill->getFillType() !== PHPExcel_Style_Fill::FILL_NONE) {
+ // fgColor
+ if ($pFill->getStartColor()->getARGB()) {
+ $objWriter->startElement('fgColor');
+ $objWriter->writeAttribute('rgb', $pFill->getStartColor()->getARGB());
+ $objWriter->endElement();
+ }
+ }
+ if ($pFill->getFillType() !== PHPExcel_Style_Fill::FILL_NONE) {
+ // bgColor
+ if ($pFill->getEndColor()->getARGB()) {
+ $objWriter->startElement('bgColor');
+ $objWriter->writeAttribute('rgb', $pFill->getEndColor()->getARGB());
+ $objWriter->endElement();
+ }
+ }
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Font
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Style_Font $pFont Font style
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeFont(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Style_Font $pFont = null)
+ {
+ // font
+ $objWriter->startElement('font');
+ // Weird! The order of these elements actually makes a difference when opening Excel2007
+ // files in Excel2003 with the compatibility pack. It's not documented behaviour,
+ // and makes for a real WTF!
+
+ // Bold. We explicitly write this element also when false (like MS Office Excel 2007 does
+ // for conditional formatting). Otherwise it will apparently not be picked up in conditional
+ // formatting style dialog
+ if ($pFont->getBold() !== null) {
+ $objWriter->startElement('b');
+ $objWriter->writeAttribute('val', $pFont->getBold() ? '1' : '0');
+ $objWriter->endElement();
+ }
+
+ // Italic
+ if ($pFont->getItalic() !== null) {
+ $objWriter->startElement('i');
+ $objWriter->writeAttribute('val', $pFont->getItalic() ? '1' : '0');
+ $objWriter->endElement();
+ }
+
+ // Strikethrough
+ if ($pFont->getStrikethrough() !== null) {
+ $objWriter->startElement('strike');
+ $objWriter->writeAttribute('val', $pFont->getStrikethrough() ? '1' : '0');
+ $objWriter->endElement();
+ }
+
+ // Underline
+ if ($pFont->getUnderline() !== null) {
+ $objWriter->startElement('u');
+ $objWriter->writeAttribute('val', $pFont->getUnderline());
+ $objWriter->endElement();
+ }
+
+ // Superscript / subscript
+ if ($pFont->getSuperScript() === true || $pFont->getSubScript() === true) {
+ $objWriter->startElement('vertAlign');
+ if ($pFont->getSuperScript() === true) {
+ $objWriter->writeAttribute('val', 'superscript');
+ } elseif ($pFont->getSubScript() === true) {
+ $objWriter->writeAttribute('val', 'subscript');
+ }
+ $objWriter->endElement();
+ }
+
+ // Size
+ if ($pFont->getSize() !== null) {
+ $objWriter->startElement('sz');
+ $objWriter->writeAttribute('val', $pFont->getSize());
+ $objWriter->endElement();
+ }
+
+ // Foreground color
+ if ($pFont->getColor()->getARGB() !== null) {
+ $objWriter->startElement('color');
+ $objWriter->writeAttribute('rgb', $pFont->getColor()->getARGB());
+ $objWriter->endElement();
+ }
+
+ // Name
+ if ($pFont->getName() !== null) {
+ $objWriter->startElement('name');
+ $objWriter->writeAttribute('val', $pFont->getName());
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Border
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Style_Borders $pBorders Borders style
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeBorder(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Style_Borders $pBorders = null)
+ {
+ // Write border
+ $objWriter->startElement('border');
+ // Diagonal?
+ switch ($pBorders->getDiagonalDirection()) {
+ case PHPExcel_Style_Borders::DIAGONAL_UP:
+ $objWriter->writeAttribute('diagonalUp', 'true');
+ $objWriter->writeAttribute('diagonalDown', 'false');
+ break;
+ case PHPExcel_Style_Borders::DIAGONAL_DOWN:
+ $objWriter->writeAttribute('diagonalUp', 'false');
+ $objWriter->writeAttribute('diagonalDown', 'true');
+ break;
+ case PHPExcel_Style_Borders::DIAGONAL_BOTH:
+ $objWriter->writeAttribute('diagonalUp', 'true');
+ $objWriter->writeAttribute('diagonalDown', 'true');
+ break;
+ }
+
+ // BorderPr
+ $this->writeBorderPr($objWriter, 'left', $pBorders->getLeft());
+ $this->writeBorderPr($objWriter, 'right', $pBorders->getRight());
+ $this->writeBorderPr($objWriter, 'top', $pBorders->getTop());
+ $this->writeBorderPr($objWriter, 'bottom', $pBorders->getBottom());
+ $this->writeBorderPr($objWriter, 'diagonal', $pBorders->getDiagonal());
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Cell Style Xf
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Style $pStyle Style
+ * @param PHPExcel $pPHPExcel Workbook
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeCellStyleXf(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Style $pStyle = null, PHPExcel $pPHPExcel = null)
+ {
+ // xf
+ $objWriter->startElement('xf');
+ $objWriter->writeAttribute('xfId', 0);
+ $objWriter->writeAttribute('fontId', (int)$this->getParentWriter()->getFontHashTable()->getIndexForHashCode($pStyle->getFont()->getHashCode()));
+ if ($pStyle->getQuotePrefix()) {
+ $objWriter->writeAttribute('quotePrefix', 1);
+ }
+
+ if ($pStyle->getNumberFormat()->getBuiltInFormatCode() === false) {
+ $objWriter->writeAttribute('numFmtId', (int)($this->getParentWriter()->getNumFmtHashTable()->getIndexForHashCode($pStyle->getNumberFormat()->getHashCode()) + 164));
+ } else {
+ $objWriter->writeAttribute('numFmtId', (int)$pStyle->getNumberFormat()->getBuiltInFormatCode());
+ }
+
+ $objWriter->writeAttribute('fillId', (int)$this->getParentWriter()->getFillHashTable()->getIndexForHashCode($pStyle->getFill()->getHashCode()));
+ $objWriter->writeAttribute('borderId', (int)$this->getParentWriter()->getBordersHashTable()->getIndexForHashCode($pStyle->getBorders()->getHashCode()));
+
+ // Apply styles?
+ $objWriter->writeAttribute('applyFont', ($pPHPExcel->getDefaultStyle()->getFont()->getHashCode() != $pStyle->getFont()->getHashCode()) ? '1' : '0');
+ $objWriter->writeAttribute('applyNumberFormat', ($pPHPExcel->getDefaultStyle()->getNumberFormat()->getHashCode() != $pStyle->getNumberFormat()->getHashCode()) ? '1' : '0');
+ $objWriter->writeAttribute('applyFill', ($pPHPExcel->getDefaultStyle()->getFill()->getHashCode() != $pStyle->getFill()->getHashCode()) ? '1' : '0');
+ $objWriter->writeAttribute('applyBorder', ($pPHPExcel->getDefaultStyle()->getBorders()->getHashCode() != $pStyle->getBorders()->getHashCode()) ? '1' : '0');
+ $objWriter->writeAttribute('applyAlignment', ($pPHPExcel->getDefaultStyle()->getAlignment()->getHashCode() != $pStyle->getAlignment()->getHashCode()) ? '1' : '0');
+ if ($pStyle->getProtection()->getLocked() != PHPExcel_Style_Protection::PROTECTION_INHERIT || $pStyle->getProtection()->getHidden() != PHPExcel_Style_Protection::PROTECTION_INHERIT) {
+ $objWriter->writeAttribute('applyProtection', 'true');
+ }
+
+ // alignment
+ $objWriter->startElement('alignment');
+ $objWriter->writeAttribute('horizontal', $pStyle->getAlignment()->getHorizontal());
+ $objWriter->writeAttribute('vertical', $pStyle->getAlignment()->getVertical());
+
+ $textRotation = 0;
+ if ($pStyle->getAlignment()->getTextRotation() >= 0) {
+ $textRotation = $pStyle->getAlignment()->getTextRotation();
+ } elseif ($pStyle->getAlignment()->getTextRotation() < 0) {
+ $textRotation = 90 - $pStyle->getAlignment()->getTextRotation();
+ }
+ $objWriter->writeAttribute('textRotation', $textRotation);
+
+ $objWriter->writeAttribute('wrapText', ($pStyle->getAlignment()->getWrapText() ? 'true' : 'false'));
+ $objWriter->writeAttribute('shrinkToFit', ($pStyle->getAlignment()->getShrinkToFit() ? 'true' : 'false'));
+
+ if ($pStyle->getAlignment()->getIndent() > 0) {
+ $objWriter->writeAttribute('indent', $pStyle->getAlignment()->getIndent());
+ }
+ if ($pStyle->getAlignment()->getReadorder() > 0) {
+ $objWriter->writeAttribute('readingOrder', $pStyle->getAlignment()->getReadorder());
+ }
+ $objWriter->endElement();
+
+ // protection
+ if ($pStyle->getProtection()->getLocked() != PHPExcel_Style_Protection::PROTECTION_INHERIT || $pStyle->getProtection()->getHidden() != PHPExcel_Style_Protection::PROTECTION_INHERIT) {
+ $objWriter->startElement('protection');
+ if ($pStyle->getProtection()->getLocked() != PHPExcel_Style_Protection::PROTECTION_INHERIT) {
+ $objWriter->writeAttribute('locked', ($pStyle->getProtection()->getLocked() == PHPExcel_Style_Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
+ }
+ if ($pStyle->getProtection()->getHidden() != PHPExcel_Style_Protection::PROTECTION_INHERIT) {
+ $objWriter->writeAttribute('hidden', ($pStyle->getProtection()->getHidden() == PHPExcel_Style_Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
+ }
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Cell Style Dxf
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Style $pStyle Style
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeCellStyleDxf(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Style $pStyle = null)
+ {
+ // dxf
+ $objWriter->startElement('dxf');
+
+ // font
+ $this->writeFont($objWriter, $pStyle->getFont());
+
+ // numFmt
+ $this->writeNumFmt($objWriter, $pStyle->getNumberFormat());
+
+ // fill
+ $this->writeFill($objWriter, $pStyle->getFill());
+
+ // alignment
+ $objWriter->startElement('alignment');
+ if ($pStyle->getAlignment()->getHorizontal() !== null) {
+ $objWriter->writeAttribute('horizontal', $pStyle->getAlignment()->getHorizontal());
+ }
+ if ($pStyle->getAlignment()->getVertical() !== null) {
+ $objWriter->writeAttribute('vertical', $pStyle->getAlignment()->getVertical());
+ }
+
+ if ($pStyle->getAlignment()->getTextRotation() !== null) {
+ $textRotation = 0;
+ if ($pStyle->getAlignment()->getTextRotation() >= 0) {
+ $textRotation = $pStyle->getAlignment()->getTextRotation();
+ } elseif ($pStyle->getAlignment()->getTextRotation() < 0) {
+ $textRotation = 90 - $pStyle->getAlignment()->getTextRotation();
+ }
+ $objWriter->writeAttribute('textRotation', $textRotation);
+ }
+ $objWriter->endElement();
+
+ // border
+ $this->writeBorder($objWriter, $pStyle->getBorders());
+
+ // protection
+ if (($pStyle->getProtection()->getLocked() !== null) || ($pStyle->getProtection()->getHidden() !== null)) {
+ if ($pStyle->getProtection()->getLocked() !== PHPExcel_Style_Protection::PROTECTION_INHERIT ||
+ $pStyle->getProtection()->getHidden() !== PHPExcel_Style_Protection::PROTECTION_INHERIT) {
+ $objWriter->startElement('protection');
+ if (($pStyle->getProtection()->getLocked() !== null) &&
+ ($pStyle->getProtection()->getLocked() !== PHPExcel_Style_Protection::PROTECTION_INHERIT)) {
+ $objWriter->writeAttribute('locked', ($pStyle->getProtection()->getLocked() == PHPExcel_Style_Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
+ }
+ if (($pStyle->getProtection()->getHidden() !== null) &&
+ ($pStyle->getProtection()->getHidden() !== PHPExcel_Style_Protection::PROTECTION_INHERIT)) {
+ $objWriter->writeAttribute('hidden', ($pStyle->getProtection()->getHidden() == PHPExcel_Style_Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
+ }
+ $objWriter->endElement();
+ }
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write BorderPr
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param string $pName Element name
+ * @param PHPExcel_Style_Border $pBorder Border style
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeBorderPr(PHPExcel_Shared_XMLWriter $objWriter = null, $pName = 'left', PHPExcel_Style_Border $pBorder = null)
+ {
+ // Write BorderPr
+ if ($pBorder->getBorderStyle() != PHPExcel_Style_Border::BORDER_NONE) {
+ $objWriter->startElement($pName);
+ $objWriter->writeAttribute('style', $pBorder->getBorderStyle());
+
+ // color
+ $objWriter->startElement('color');
+ $objWriter->writeAttribute('rgb', $pBorder->getColor()->getARGB());
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write NumberFormat
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Style_NumberFormat $pNumberFormat Number Format
+ * @param int $pId Number Format identifier
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeNumFmt(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Style_NumberFormat $pNumberFormat = null, $pId = 0)
+ {
+ // Translate formatcode
+ $formatCode = $pNumberFormat->getFormatCode();
+
+ // numFmt
+ if ($formatCode !== null) {
+ $objWriter->startElement('numFmt');
+ $objWriter->writeAttribute('numFmtId', ($pId + 164));
+ $objWriter->writeAttribute('formatCode', $formatCode);
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Get an array of all styles
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return PHPExcel_Style[] All styles in PHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function allStyles(PHPExcel $pPHPExcel = null)
+ {
+ return $pPHPExcel->getCellXfCollection();
+ }
+
+ /**
+ * Get an array of all conditional styles
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return PHPExcel_Style_Conditional[] All conditional styles in PHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function allConditionalStyles(PHPExcel $pPHPExcel = null)
+ {
+ // Get an array of all styles
+ $aStyles = array();
+
+ $sheetCount = $pPHPExcel->getSheetCount();
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ foreach ($pPHPExcel->getSheet($i)->getConditionalStylesCollection() as $conditionalStyles) {
+ foreach ($conditionalStyles as $conditionalStyle) {
+ $aStyles[] = $conditionalStyle;
+ }
+ }
+ }
+
+ return $aStyles;
+ }
+
+ /**
+ * Get an array of all fills
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return PHPExcel_Style_Fill[] All fills in PHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function allFills(PHPExcel $pPHPExcel = null)
+ {
+ // Get an array of unique fills
+ $aFills = array();
+
+ // Two first fills are predefined
+ $fill0 = new PHPExcel_Style_Fill();
+ $fill0->setFillType(PHPExcel_Style_Fill::FILL_NONE);
+ $aFills[] = $fill0;
+
+ $fill1 = new PHPExcel_Style_Fill();
+ $fill1->setFillType(PHPExcel_Style_Fill::FILL_PATTERN_GRAY125);
+ $aFills[] = $fill1;
+ // The remaining fills
+ $aStyles = $this->allStyles($pPHPExcel);
+ foreach ($aStyles as $style) {
+ if (!array_key_exists($style->getFill()->getHashCode(), $aFills)) {
+ $aFills[ $style->getFill()->getHashCode() ] = $style->getFill();
+ }
+ }
+
+ return $aFills;
+ }
+
+ /**
+ * Get an array of all fonts
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return PHPExcel_Style_Font[] All fonts in PHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function allFonts(PHPExcel $pPHPExcel = null)
+ {
+ // Get an array of unique fonts
+ $aFonts = array();
+ $aStyles = $this->allStyles($pPHPExcel);
+
+ foreach ($aStyles as $style) {
+ if (!array_key_exists($style->getFont()->getHashCode(), $aFonts)) {
+ $aFonts[ $style->getFont()->getHashCode() ] = $style->getFont();
+ }
+ }
+
+ return $aFonts;
+ }
+
+ /**
+ * Get an array of all borders
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return PHPExcel_Style_Borders[] All borders in PHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function allBorders(PHPExcel $pPHPExcel = null)
+ {
+ // Get an array of unique borders
+ $aBorders = array();
+ $aStyles = $this->allStyles($pPHPExcel);
+
+ foreach ($aStyles as $style) {
+ if (!array_key_exists($style->getBorders()->getHashCode(), $aBorders)) {
+ $aBorders[ $style->getBorders()->getHashCode() ] = $style->getBorders();
+ }
+ }
+
+ return $aBorders;
+ }
+
+ /**
+ * Get an array of all number formats
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return PHPExcel_Style_NumberFormat[] All number formats in PHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function allNumberFormats(PHPExcel $pPHPExcel = null)
+ {
+ // Get an array of unique number formats
+ $aNumFmts = array();
+ $aStyles = $this->allStyles($pPHPExcel);
+
+ foreach ($aStyles as $style) {
+ if ($style->getNumberFormat()->getBuiltInFormatCode() === false && !array_key_exists($style->getNumberFormat()->getHashCode(), $aNumFmts)) {
+ $aNumFmts[ $style->getNumberFormat()->getHashCode() ] = $style->getNumberFormat();
+ }
+ }
+
+ return $aNumFmts;
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/Theme.php b/PHPExcel/Writer/Excel2007/Theme.php
new file mode 100644
index 0000000..223e402
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/Theme.php
@@ -0,0 +1,869 @@
+ 'MS Pゴシック',
+ 'Hang' => '맑은 고딕',
+ 'Hans' => '宋体',
+ 'Hant' => '新細明體',
+ 'Arab' => 'Times New Roman',
+ 'Hebr' => 'Times New Roman',
+ 'Thai' => 'Tahoma',
+ 'Ethi' => 'Nyala',
+ 'Beng' => 'Vrinda',
+ 'Gujr' => 'Shruti',
+ 'Khmr' => 'MoolBoran',
+ 'Knda' => 'Tunga',
+ 'Guru' => 'Raavi',
+ 'Cans' => 'Euphemia',
+ 'Cher' => 'Plantagenet Cherokee',
+ 'Yiii' => 'Microsoft Yi Baiti',
+ 'Tibt' => 'Microsoft Himalaya',
+ 'Thaa' => 'MV Boli',
+ 'Deva' => 'Mangal',
+ 'Telu' => 'Gautami',
+ 'Taml' => 'Latha',
+ 'Syrc' => 'Estrangelo Edessa',
+ 'Orya' => 'Kalinga',
+ 'Mlym' => 'Kartika',
+ 'Laoo' => 'DokChampa',
+ 'Sinh' => 'Iskoola Pota',
+ 'Mong' => 'Mongolian Baiti',
+ 'Viet' => 'Times New Roman',
+ 'Uigh' => 'Microsoft Uighur',
+ 'Geor' => 'Sylfaen',
+ );
+
+ /**
+ * Map of Minor fonts to write
+ * @static array of string
+ *
+ */
+ private static $minorFonts = array(
+ 'Jpan' => 'MS Pゴシック',
+ 'Hang' => '맑은 고딕',
+ 'Hans' => '宋体',
+ 'Hant' => '新細明體',
+ 'Arab' => 'Arial',
+ 'Hebr' => 'Arial',
+ 'Thai' => 'Tahoma',
+ 'Ethi' => 'Nyala',
+ 'Beng' => 'Vrinda',
+ 'Gujr' => 'Shruti',
+ 'Khmr' => 'DaunPenh',
+ 'Knda' => 'Tunga',
+ 'Guru' => 'Raavi',
+ 'Cans' => 'Euphemia',
+ 'Cher' => 'Plantagenet Cherokee',
+ 'Yiii' => 'Microsoft Yi Baiti',
+ 'Tibt' => 'Microsoft Himalaya',
+ 'Thaa' => 'MV Boli',
+ 'Deva' => 'Mangal',
+ 'Telu' => 'Gautami',
+ 'Taml' => 'Latha',
+ 'Syrc' => 'Estrangelo Edessa',
+ 'Orya' => 'Kalinga',
+ 'Mlym' => 'Kartika',
+ 'Laoo' => 'DokChampa',
+ 'Sinh' => 'Iskoola Pota',
+ 'Mong' => 'Mongolian Baiti',
+ 'Viet' => 'Arial',
+ 'Uigh' => 'Microsoft Uighur',
+ 'Geor' => 'Sylfaen',
+ );
+
+ /**
+ * Map of core colours
+ * @static array of string
+ *
+ */
+ private static $colourScheme = array(
+ 'dk2' => '1F497D',
+ 'lt2' => 'EEECE1',
+ 'accent1' => '4F81BD',
+ 'accent2' => 'C0504D',
+ 'accent3' => '9BBB59',
+ 'accent4' => '8064A2',
+ 'accent5' => '4BACC6',
+ 'accent6' => 'F79646',
+ 'hlink' => '0000FF',
+ 'folHlink' => '800080',
+ );
+
+ /**
+ * Write theme to XML format
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function writeTheme(PHPExcel $pPHPExcel = null)
+ {
+ // Create XML writer
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // a:theme
+ $objWriter->startElement('a:theme');
+ $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
+ $objWriter->writeAttribute('name', 'Office Theme');
+
+ // a:themeElements
+ $objWriter->startElement('a:themeElements');
+
+ // a:clrScheme
+ $objWriter->startElement('a:clrScheme');
+ $objWriter->writeAttribute('name', 'Office');
+
+ // a:dk1
+ $objWriter->startElement('a:dk1');
+
+ // a:sysClr
+ $objWriter->startElement('a:sysClr');
+ $objWriter->writeAttribute('val', 'windowText');
+ $objWriter->writeAttribute('lastClr', '000000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:lt1
+ $objWriter->startElement('a:lt1');
+
+ // a:sysClr
+ $objWriter->startElement('a:sysClr');
+ $objWriter->writeAttribute('val', 'window');
+ $objWriter->writeAttribute('lastClr', 'FFFFFF');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:dk2
+ $this->writeColourScheme($objWriter);
+
+ $objWriter->endElement();
+
+ // a:fontScheme
+ $objWriter->startElement('a:fontScheme');
+ $objWriter->writeAttribute('name', 'Office');
+
+ // a:majorFont
+ $objWriter->startElement('a:majorFont');
+ $this->writeFonts($objWriter, 'Cambria', self::$majorFonts);
+ $objWriter->endElement();
+
+ // a:minorFont
+ $objWriter->startElement('a:minorFont');
+ $this->writeFonts($objWriter, 'Calibri', self::$minorFonts);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:fmtScheme
+ $objWriter->startElement('a:fmtScheme');
+ $objWriter->writeAttribute('name', 'Office');
+
+ // a:fillStyleLst
+ $objWriter->startElement('a:fillStyleLst');
+
+ // a:solidFill
+ $objWriter->startElement('a:solidFill');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gradFill
+ $objWriter->startElement('a:gradFill');
+ $objWriter->writeAttribute('rotWithShape', '1');
+
+ // a:gsLst
+ $objWriter->startElement('a:gsLst');
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '0');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:tint
+ $objWriter->startElement('a:tint');
+ $objWriter->writeAttribute('val', '50000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '300000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '35000');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:tint
+ $objWriter->startElement('a:tint');
+ $objWriter->writeAttribute('val', '37000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '300000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '100000');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:tint
+ $objWriter->startElement('a:tint');
+ $objWriter->writeAttribute('val', '15000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '350000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:lin
+ $objWriter->startElement('a:lin');
+ $objWriter->writeAttribute('ang', '16200000');
+ $objWriter->writeAttribute('scaled', '1');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gradFill
+ $objWriter->startElement('a:gradFill');
+ $objWriter->writeAttribute('rotWithShape', '1');
+
+ // a:gsLst
+ $objWriter->startElement('a:gsLst');
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '0');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:shade
+ $objWriter->startElement('a:shade');
+ $objWriter->writeAttribute('val', '51000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '130000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '80000');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:shade
+ $objWriter->startElement('a:shade');
+ $objWriter->writeAttribute('val', '93000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '130000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '100000');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:shade
+ $objWriter->startElement('a:shade');
+ $objWriter->writeAttribute('val', '94000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '135000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:lin
+ $objWriter->startElement('a:lin');
+ $objWriter->writeAttribute('ang', '16200000');
+ $objWriter->writeAttribute('scaled', '0');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:lnStyleLst
+ $objWriter->startElement('a:lnStyleLst');
+
+ // a:ln
+ $objWriter->startElement('a:ln');
+ $objWriter->writeAttribute('w', '9525');
+ $objWriter->writeAttribute('cap', 'flat');
+ $objWriter->writeAttribute('cmpd', 'sng');
+ $objWriter->writeAttribute('algn', 'ctr');
+
+ // a:solidFill
+ $objWriter->startElement('a:solidFill');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:shade
+ $objWriter->startElement('a:shade');
+ $objWriter->writeAttribute('val', '95000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '105000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:prstDash
+ $objWriter->startElement('a:prstDash');
+ $objWriter->writeAttribute('val', 'solid');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:ln
+ $objWriter->startElement('a:ln');
+ $objWriter->writeAttribute('w', '25400');
+ $objWriter->writeAttribute('cap', 'flat');
+ $objWriter->writeAttribute('cmpd', 'sng');
+ $objWriter->writeAttribute('algn', 'ctr');
+
+ // a:solidFill
+ $objWriter->startElement('a:solidFill');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:prstDash
+ $objWriter->startElement('a:prstDash');
+ $objWriter->writeAttribute('val', 'solid');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:ln
+ $objWriter->startElement('a:ln');
+ $objWriter->writeAttribute('w', '38100');
+ $objWriter->writeAttribute('cap', 'flat');
+ $objWriter->writeAttribute('cmpd', 'sng');
+ $objWriter->writeAttribute('algn', 'ctr');
+
+ // a:solidFill
+ $objWriter->startElement('a:solidFill');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:prstDash
+ $objWriter->startElement('a:prstDash');
+ $objWriter->writeAttribute('val', 'solid');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+
+
+ // a:effectStyleLst
+ $objWriter->startElement('a:effectStyleLst');
+
+ // a:effectStyle
+ $objWriter->startElement('a:effectStyle');
+
+ // a:effectLst
+ $objWriter->startElement('a:effectLst');
+
+ // a:outerShdw
+ $objWriter->startElement('a:outerShdw');
+ $objWriter->writeAttribute('blurRad', '40000');
+ $objWriter->writeAttribute('dist', '20000');
+ $objWriter->writeAttribute('dir', '5400000');
+ $objWriter->writeAttribute('rotWithShape', '0');
+
+ // a:srgbClr
+ $objWriter->startElement('a:srgbClr');
+ $objWriter->writeAttribute('val', '000000');
+
+ // a:alpha
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', '38000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:effectStyle
+ $objWriter->startElement('a:effectStyle');
+
+ // a:effectLst
+ $objWriter->startElement('a:effectLst');
+
+ // a:outerShdw
+ $objWriter->startElement('a:outerShdw');
+ $objWriter->writeAttribute('blurRad', '40000');
+ $objWriter->writeAttribute('dist', '23000');
+ $objWriter->writeAttribute('dir', '5400000');
+ $objWriter->writeAttribute('rotWithShape', '0');
+
+ // a:srgbClr
+ $objWriter->startElement('a:srgbClr');
+ $objWriter->writeAttribute('val', '000000');
+
+ // a:alpha
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', '35000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:effectStyle
+ $objWriter->startElement('a:effectStyle');
+
+ // a:effectLst
+ $objWriter->startElement('a:effectLst');
+
+ // a:outerShdw
+ $objWriter->startElement('a:outerShdw');
+ $objWriter->writeAttribute('blurRad', '40000');
+ $objWriter->writeAttribute('dist', '23000');
+ $objWriter->writeAttribute('dir', '5400000');
+ $objWriter->writeAttribute('rotWithShape', '0');
+
+ // a:srgbClr
+ $objWriter->startElement('a:srgbClr');
+ $objWriter->writeAttribute('val', '000000');
+
+ // a:alpha
+ $objWriter->startElement('a:alpha');
+ $objWriter->writeAttribute('val', '35000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:scene3d
+ $objWriter->startElement('a:scene3d');
+
+ // a:camera
+ $objWriter->startElement('a:camera');
+ $objWriter->writeAttribute('prst', 'orthographicFront');
+
+ // a:rot
+ $objWriter->startElement('a:rot');
+ $objWriter->writeAttribute('lat', '0');
+ $objWriter->writeAttribute('lon', '0');
+ $objWriter->writeAttribute('rev', '0');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:lightRig
+ $objWriter->startElement('a:lightRig');
+ $objWriter->writeAttribute('rig', 'threePt');
+ $objWriter->writeAttribute('dir', 't');
+
+ // a:rot
+ $objWriter->startElement('a:rot');
+ $objWriter->writeAttribute('lat', '0');
+ $objWriter->writeAttribute('lon', '0');
+ $objWriter->writeAttribute('rev', '1200000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:sp3d
+ $objWriter->startElement('a:sp3d');
+
+ // a:bevelT
+ $objWriter->startElement('a:bevelT');
+ $objWriter->writeAttribute('w', '63500');
+ $objWriter->writeAttribute('h', '25400');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:bgFillStyleLst
+ $objWriter->startElement('a:bgFillStyleLst');
+
+ // a:solidFill
+ $objWriter->startElement('a:solidFill');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gradFill
+ $objWriter->startElement('a:gradFill');
+ $objWriter->writeAttribute('rotWithShape', '1');
+
+ // a:gsLst
+ $objWriter->startElement('a:gsLst');
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '0');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:tint
+ $objWriter->startElement('a:tint');
+ $objWriter->writeAttribute('val', '40000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '350000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '40000');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:tint
+ $objWriter->startElement('a:tint');
+ $objWriter->writeAttribute('val', '45000');
+ $objWriter->endElement();
+
+ // a:shade
+ $objWriter->startElement('a:shade');
+ $objWriter->writeAttribute('val', '99000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '350000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '100000');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:shade
+ $objWriter->startElement('a:shade');
+ $objWriter->writeAttribute('val', '20000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '255000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:path
+ $objWriter->startElement('a:path');
+ $objWriter->writeAttribute('path', 'circle');
+
+ // a:fillToRect
+ $objWriter->startElement('a:fillToRect');
+ $objWriter->writeAttribute('l', '50000');
+ $objWriter->writeAttribute('t', '-80000');
+ $objWriter->writeAttribute('r', '50000');
+ $objWriter->writeAttribute('b', '180000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gradFill
+ $objWriter->startElement('a:gradFill');
+ $objWriter->writeAttribute('rotWithShape', '1');
+
+ // a:gsLst
+ $objWriter->startElement('a:gsLst');
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '0');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:tint
+ $objWriter->startElement('a:tint');
+ $objWriter->writeAttribute('val', '80000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '300000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:gs
+ $objWriter->startElement('a:gs');
+ $objWriter->writeAttribute('pos', '100000');
+
+ // a:schemeClr
+ $objWriter->startElement('a:schemeClr');
+ $objWriter->writeAttribute('val', 'phClr');
+
+ // a:shade
+ $objWriter->startElement('a:shade');
+ $objWriter->writeAttribute('val', '30000');
+ $objWriter->endElement();
+
+ // a:satMod
+ $objWriter->startElement('a:satMod');
+ $objWriter->writeAttribute('val', '200000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:path
+ $objWriter->startElement('a:path');
+ $objWriter->writeAttribute('path', 'circle');
+
+ // a:fillToRect
+ $objWriter->startElement('a:fillToRect');
+ $objWriter->writeAttribute('l', '50000');
+ $objWriter->writeAttribute('t', '50000');
+ $objWriter->writeAttribute('r', '50000');
+ $objWriter->writeAttribute('b', '50000');
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+
+ // a:objectDefaults
+ $objWriter->writeElement('a:objectDefaults', null);
+
+ // a:extraClrSchemeLst
+ $objWriter->writeElement('a:extraClrSchemeLst', null);
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write fonts to XML format
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter
+ * @param string $latinFont
+ * @param array of string $fontSet
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeFonts($objWriter, $latinFont, $fontSet)
+ {
+ // a:latin
+ $objWriter->startElement('a:latin');
+ $objWriter->writeAttribute('typeface', $latinFont);
+ $objWriter->endElement();
+
+ // a:ea
+ $objWriter->startElement('a:ea');
+ $objWriter->writeAttribute('typeface', '');
+ $objWriter->endElement();
+
+ // a:cs
+ $objWriter->startElement('a:cs');
+ $objWriter->writeAttribute('typeface', '');
+ $objWriter->endElement();
+
+ foreach ($fontSet as $fontScript => $typeface) {
+ $objWriter->startElement('a:font');
+ $objWriter->writeAttribute('script', $fontScript);
+ $objWriter->writeAttribute('typeface', $typeface);
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write colour scheme to XML format
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeColourScheme($objWriter)
+ {
+ foreach (self::$colourScheme as $colourName => $colourValue) {
+ $objWriter->startElement('a:'.$colourName);
+
+ $objWriter->startElement('a:srgbClr');
+ $objWriter->writeAttribute('val', $colourValue);
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/Workbook.php b/PHPExcel/Writer/Excel2007/Workbook.php
new file mode 100644
index 0000000..87c877e
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/Workbook.php
@@ -0,0 +1,448 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // workbook
+ $objWriter->startElement('workbook');
+ $objWriter->writeAttribute('xml:space', 'preserve');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+ $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+ // fileVersion
+ $this->writeFileVersion($objWriter);
+
+ // workbookPr
+ $this->writeWorkbookPr($objWriter);
+
+ // workbookProtection
+ $this->writeWorkbookProtection($objWriter, $pPHPExcel);
+
+ // bookViews
+ if ($this->getParentWriter()->getOffice2003Compatibility() === false) {
+ $this->writeBookViews($objWriter, $pPHPExcel);
+ }
+
+ // sheets
+ $this->writeSheets($objWriter, $pPHPExcel);
+
+ // definedNames
+ $this->writeDefinedNames($objWriter, $pPHPExcel);
+
+ // calcPr
+ $this->writeCalcPr($objWriter, $recalcRequired);
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write file version
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeFileVersion(PHPExcel_Shared_XMLWriter $objWriter = null)
+ {
+ $objWriter->startElement('fileVersion');
+ $objWriter->writeAttribute('appName', 'xl');
+ $objWriter->writeAttribute('lastEdited', '4');
+ $objWriter->writeAttribute('lowestEdited', '4');
+ $objWriter->writeAttribute('rupBuild', '4505');
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write WorkbookPr
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeWorkbookPr(PHPExcel_Shared_XMLWriter $objWriter = null)
+ {
+ $objWriter->startElement('workbookPr');
+
+ if (PHPExcel_Shared_Date::getExcelCalendar() == PHPExcel_Shared_Date::CALENDAR_MAC_1904) {
+ $objWriter->writeAttribute('date1904', '1');
+ }
+
+ $objWriter->writeAttribute('codeName', 'ThisWorkbook');
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write BookViews
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel $pPHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeBookViews(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel $pPHPExcel = null)
+ {
+ // bookViews
+ $objWriter->startElement('bookViews');
+
+ // workbookView
+ $objWriter->startElement('workbookView');
+
+ $objWriter->writeAttribute('activeTab', $pPHPExcel->getActiveSheetIndex());
+ $objWriter->writeAttribute('autoFilterDateGrouping', '1');
+ $objWriter->writeAttribute('firstSheet', '0');
+ $objWriter->writeAttribute('minimized', '0');
+ $objWriter->writeAttribute('showHorizontalScroll', '1');
+ $objWriter->writeAttribute('showSheetTabs', '1');
+ $objWriter->writeAttribute('showVerticalScroll', '1');
+ $objWriter->writeAttribute('tabRatio', '600');
+ $objWriter->writeAttribute('visibility', 'visible');
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write WorkbookProtection
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel $pPHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeWorkbookProtection(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel $pPHPExcel = null)
+ {
+ if ($pPHPExcel->getSecurity()->isSecurityEnabled()) {
+ $objWriter->startElement('workbookProtection');
+ $objWriter->writeAttribute('lockRevision', ($pPHPExcel->getSecurity()->getLockRevision() ? 'true' : 'false'));
+ $objWriter->writeAttribute('lockStructure', ($pPHPExcel->getSecurity()->getLockStructure() ? 'true' : 'false'));
+ $objWriter->writeAttribute('lockWindows', ($pPHPExcel->getSecurity()->getLockWindows() ? 'true' : 'false'));
+
+ if ($pPHPExcel->getSecurity()->getRevisionsPassword() != '') {
+ $objWriter->writeAttribute('revisionsPassword', $pPHPExcel->getSecurity()->getRevisionsPassword());
+ }
+
+ if ($pPHPExcel->getSecurity()->getWorkbookPassword() != '') {
+ $objWriter->writeAttribute('workbookPassword', $pPHPExcel->getSecurity()->getWorkbookPassword());
+ }
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write calcPr
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param boolean $recalcRequired Indicate whether formulas should be recalculated before writing
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeCalcPr(PHPExcel_Shared_XMLWriter $objWriter = null, $recalcRequired = true)
+ {
+ $objWriter->startElement('calcPr');
+
+ // Set the calcid to a higher value than Excel itself will use, otherwise Excel will always recalc
+ // If MS Excel does do a recalc, then users opening a file in MS Excel will be prompted to save on exit
+ // because the file has changed
+ $objWriter->writeAttribute('calcId', '999999');
+ $objWriter->writeAttribute('calcMode', 'auto');
+ // fullCalcOnLoad isn't needed if we've recalculating for the save
+ $objWriter->writeAttribute('calcCompleted', ($recalcRequired) ? 1 : 0);
+ $objWriter->writeAttribute('fullCalcOnLoad', ($recalcRequired) ? 0 : 1);
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write sheets
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel $pPHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeSheets(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel $pPHPExcel = null)
+ {
+ // Write sheets
+ $objWriter->startElement('sheets');
+ $sheetCount = $pPHPExcel->getSheetCount();
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ // sheet
+ $this->writeSheet(
+ $objWriter,
+ $pPHPExcel->getSheet($i)->getTitle(),
+ ($i + 1),
+ ($i + 1 + 3),
+ $pPHPExcel->getSheet($i)->getSheetState()
+ );
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write sheet
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param string $pSheetname Sheet name
+ * @param int $pSheetId Sheet id
+ * @param int $pRelId Relationship ID
+ * @param string $sheetState Sheet state (visible, hidden, veryHidden)
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeSheet(PHPExcel_Shared_XMLWriter $objWriter = null, $pSheetname = '', $pSheetId = 1, $pRelId = 1, $sheetState = 'visible')
+ {
+ if ($pSheetname != '') {
+ // Write sheet
+ $objWriter->startElement('sheet');
+ $objWriter->writeAttribute('name', $pSheetname);
+ $objWriter->writeAttribute('sheetId', $pSheetId);
+ if ($sheetState != 'visible' && $sheetState != '') {
+ $objWriter->writeAttribute('state', $sheetState);
+ }
+ $objWriter->writeAttribute('r:id', 'rId' . $pRelId);
+ $objWriter->endElement();
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid parameters passed.");
+ }
+ }
+
+ /**
+ * Write Defined Names
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel $pPHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDefinedNames(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel $pPHPExcel = null)
+ {
+ // Write defined names
+ $objWriter->startElement('definedNames');
+
+ // Named ranges
+ if (count($pPHPExcel->getNamedRanges()) > 0) {
+ // Named ranges
+ $this->writeNamedRanges($objWriter, $pPHPExcel);
+ }
+
+ // Other defined names
+ $sheetCount = $pPHPExcel->getSheetCount();
+ for ($i = 0; $i < $sheetCount; ++$i) {
+ // definedName for autoFilter
+ $this->writeDefinedNameForAutofilter($objWriter, $pPHPExcel->getSheet($i), $i);
+
+ // definedName for Print_Titles
+ $this->writeDefinedNameForPrintTitles($objWriter, $pPHPExcel->getSheet($i), $i);
+
+ // definedName for Print_Area
+ $this->writeDefinedNameForPrintArea($objWriter, $pPHPExcel->getSheet($i), $i);
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write named ranges
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel $pPHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeNamedRanges(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel $pPHPExcel)
+ {
+ // Loop named ranges
+ $namedRanges = $pPHPExcel->getNamedRanges();
+ foreach ($namedRanges as $namedRange) {
+ $this->writeDefinedNameForNamedRange($objWriter, $namedRange);
+ }
+ }
+
+ /**
+ * Write Defined Name for named range
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_NamedRange $pNamedRange
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDefinedNameForNamedRange(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_NamedRange $pNamedRange)
+ {
+ // definedName for named range
+ $objWriter->startElement('definedName');
+ $objWriter->writeAttribute('name', $pNamedRange->getName());
+ if ($pNamedRange->getLocalOnly()) {
+ $objWriter->writeAttribute('localSheetId', $pNamedRange->getScope()->getParent()->getIndex($pNamedRange->getScope()));
+ }
+
+ // Create absolute coordinate and write as raw text
+ $range = PHPExcel_Cell::splitRange($pNamedRange->getRange());
+ for ($i = 0; $i < count($range); $i++) {
+ $range[$i][0] = '\'' . str_replace("'", "''", $pNamedRange->getWorksheet()->getTitle()) . '\'!' . PHPExcel_Cell::absoluteReference($range[$i][0]);
+ if (isset($range[$i][1])) {
+ $range[$i][1] = PHPExcel_Cell::absoluteReference($range[$i][1]);
+ }
+ }
+ $range = PHPExcel_Cell::buildRange($range);
+
+ $objWriter->writeRawData($range);
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Defined Name for autoFilter
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet
+ * @param int $pSheetId
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDefinedNameForAutofilter(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null, $pSheetId = 0)
+ {
+ // definedName for autoFilter
+ $autoFilterRange = $pSheet->getAutoFilter()->getRange();
+ if (!empty($autoFilterRange)) {
+ $objWriter->startElement('definedName');
+ $objWriter->writeAttribute('name', '_xlnm._FilterDatabase');
+ $objWriter->writeAttribute('localSheetId', $pSheetId);
+ $objWriter->writeAttribute('hidden', '1');
+
+ // Create absolute coordinate and write as raw text
+ $range = PHPExcel_Cell::splitRange($autoFilterRange);
+ $range = $range[0];
+ // Strip any worksheet ref so we can make the cell ref absolute
+ if (strpos($range[0], '!') !== false) {
+ list($ws, $range[0]) = explode('!', $range[0]);
+ }
+
+ $range[0] = PHPExcel_Cell::absoluteCoordinate($range[0]);
+ $range[1] = PHPExcel_Cell::absoluteCoordinate($range[1]);
+ $range = implode(':', $range);
+
+ $objWriter->writeRawData('\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . $range);
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write Defined Name for PrintTitles
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet
+ * @param int $pSheetId
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDefinedNameForPrintTitles(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null, $pSheetId = 0)
+ {
+ // definedName for PrintTitles
+ if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
+ $objWriter->startElement('definedName');
+ $objWriter->writeAttribute('name', '_xlnm.Print_Titles');
+ $objWriter->writeAttribute('localSheetId', $pSheetId);
+
+ // Setting string
+ $settingString = '';
+
+ // Columns to repeat
+ if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
+ $repeat = $pSheet->getPageSetup()->getColumnsToRepeatAtLeft();
+
+ $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
+ }
+
+ // Rows to repeat
+ if ($pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) {
+ if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {
+ $settingString .= ',';
+ }
+
+ $repeat = $pSheet->getPageSetup()->getRowsToRepeatAtTop();
+
+ $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];
+ }
+
+ $objWriter->writeRawData($settingString);
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write Defined Name for PrintTitles
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet
+ * @param int $pSheetId
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDefinedNameForPrintArea(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null, $pSheetId = 0)
+ {
+ // definedName for PrintArea
+ if ($pSheet->getPageSetup()->isPrintAreaSet()) {
+ $objWriter->startElement('definedName');
+ $objWriter->writeAttribute('name', '_xlnm.Print_Area');
+ $objWriter->writeAttribute('localSheetId', $pSheetId);
+
+ // Setting string
+ $settingString = '';
+
+ // Print area
+ $printArea = PHPExcel_Cell::splitRange($pSheet->getPageSetup()->getPrintArea());
+
+ $chunks = array();
+ foreach ($printArea as $printAreaRect) {
+ $printAreaRect[0] = PHPExcel_Cell::absoluteReference($printAreaRect[0]);
+ $printAreaRect[1] = PHPExcel_Cell::absoluteReference($printAreaRect[1]);
+ $chunks[] = '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . implode(':', $printAreaRect);
+ }
+
+ $objWriter->writeRawData(implode(',', $chunks));
+
+ $objWriter->endElement();
+ }
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/Worksheet.php b/PHPExcel/Writer/Excel2007/Worksheet.php
new file mode 100644
index 0000000..c211403
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/Worksheet.php
@@ -0,0 +1,1219 @@
+getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8', 'yes');
+
+ // Worksheet
+ $objWriter->startElement('worksheet');
+ $objWriter->writeAttribute('xml:space', 'preserve');
+ $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+ $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+ // sheetPr
+ $this->writeSheetPr($objWriter, $pSheet);
+
+ // Dimension
+ $this->writeDimension($objWriter, $pSheet);
+
+ // sheetViews
+ $this->writeSheetViews($objWriter, $pSheet);
+
+ // sheetFormatPr
+ $this->writeSheetFormatPr($objWriter, $pSheet);
+
+ // cols
+ $this->writeCols($objWriter, $pSheet);
+
+ // sheetData
+ $this->writeSheetData($objWriter, $pSheet, $pStringTable);
+
+ // sheetProtection
+ $this->writeSheetProtection($objWriter, $pSheet);
+
+ // protectedRanges
+ $this->writeProtectedRanges($objWriter, $pSheet);
+
+ // autoFilter
+ $this->writeAutoFilter($objWriter, $pSheet);
+
+ // mergeCells
+ $this->writeMergeCells($objWriter, $pSheet);
+
+ // conditionalFormatting
+ $this->writeConditionalFormatting($objWriter, $pSheet);
+
+ // dataValidations
+ $this->writeDataValidations($objWriter, $pSheet);
+
+ // hyperlinks
+ $this->writeHyperlinks($objWriter, $pSheet);
+
+ // Print options
+ $this->writePrintOptions($objWriter, $pSheet);
+
+ // Page margins
+ $this->writePageMargins($objWriter, $pSheet);
+
+ // Page setup
+ $this->writePageSetup($objWriter, $pSheet);
+
+ // Header / footer
+ $this->writeHeaderFooter($objWriter, $pSheet);
+
+ // Breaks
+ $this->writeBreaks($objWriter, $pSheet);
+
+ // Drawings and/or Charts
+ $this->writeDrawings($objWriter, $pSheet, $includeCharts);
+
+ // LegacyDrawing
+ $this->writeLegacyDrawing($objWriter, $pSheet);
+
+ // LegacyDrawingHF
+ $this->writeLegacyDrawingHF($objWriter, $pSheet);
+
+ $objWriter->endElement();
+
+ // Return
+ return $objWriter->getData();
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid PHPExcel_Worksheet object passed.");
+ }
+ }
+
+ /**
+ * Write SheetPr
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeSheetPr(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // sheetPr
+ $objWriter->startElement('sheetPr');
+ //$objWriter->writeAttribute('codeName', $pSheet->getTitle());
+ if ($pSheet->getParent()->hasMacros()) {//if the workbook have macros, we need to have codeName for the sheet
+ if ($pSheet->hasCodeName()==false) {
+ $pSheet->setCodeName($pSheet->getTitle());
+ }
+ $objWriter->writeAttribute('codeName', $pSheet->getCodeName());
+ }
+ $autoFilterRange = $pSheet->getAutoFilter()->getRange();
+ if (!empty($autoFilterRange)) {
+ $objWriter->writeAttribute('filterMode', 1);
+ $pSheet->getAutoFilter()->showHideRows();
+ }
+
+ // tabColor
+ if ($pSheet->isTabColorSet()) {
+ $objWriter->startElement('tabColor');
+ $objWriter->writeAttribute('rgb', $pSheet->getTabColor()->getARGB());
+ $objWriter->endElement();
+ }
+
+ // outlinePr
+ $objWriter->startElement('outlinePr');
+ $objWriter->writeAttribute('summaryBelow', ($pSheet->getShowSummaryBelow() ? '1' : '0'));
+ $objWriter->writeAttribute('summaryRight', ($pSheet->getShowSummaryRight() ? '1' : '0'));
+ $objWriter->endElement();
+
+ // pageSetUpPr
+ if ($pSheet->getPageSetup()->getFitToPage()) {
+ $objWriter->startElement('pageSetUpPr');
+ $objWriter->writeAttribute('fitToPage', '1');
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Dimension
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDimension(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // dimension
+ $objWriter->startElement('dimension');
+ $objWriter->writeAttribute('ref', $pSheet->calculateWorksheetDimension());
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write SheetViews
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeSheetViews(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // sheetViews
+ $objWriter->startElement('sheetViews');
+
+ // Sheet selected?
+ $sheetSelected = false;
+ if ($this->getParentWriter()->getPHPExcel()->getIndex($pSheet) == $this->getParentWriter()->getPHPExcel()->getActiveSheetIndex()) {
+ $sheetSelected = true;
+ }
+
+ // sheetView
+ $objWriter->startElement('sheetView');
+ $objWriter->writeAttribute('tabSelected', $sheetSelected ? '1' : '0');
+ $objWriter->writeAttribute('workbookViewId', '0');
+
+ // Zoom scales
+ if ($pSheet->getSheetView()->getZoomScale() != 100) {
+ $objWriter->writeAttribute('zoomScale', $pSheet->getSheetView()->getZoomScale());
+ }
+ if ($pSheet->getSheetView()->getZoomScaleNormal() != 100) {
+ $objWriter->writeAttribute('zoomScaleNormal', $pSheet->getSheetView()->getZoomScaleNormal());
+ }
+
+ // View Layout Type
+ if ($pSheet->getSheetView()->getView() !== PHPExcel_Worksheet_SheetView::SHEETVIEW_NORMAL) {
+ $objWriter->writeAttribute('view', $pSheet->getSheetView()->getView());
+ }
+
+ // Gridlines
+ if ($pSheet->getShowGridlines()) {
+ $objWriter->writeAttribute('showGridLines', 'true');
+ } else {
+ $objWriter->writeAttribute('showGridLines', 'false');
+ }
+
+ // Row and column headers
+ if ($pSheet->getShowRowColHeaders()) {
+ $objWriter->writeAttribute('showRowColHeaders', '1');
+ } else {
+ $objWriter->writeAttribute('showRowColHeaders', '0');
+ }
+
+ // Right-to-left
+ if ($pSheet->getRightToLeft()) {
+ $objWriter->writeAttribute('rightToLeft', 'true');
+ }
+
+ $activeCell = $pSheet->getActiveCell();
+
+ // Pane
+ $pane = '';
+ $topLeftCell = $pSheet->getFreezePane();
+ if (($topLeftCell != '') && ($topLeftCell != 'A1')) {
+ $activeCell = empty($activeCell) ? $topLeftCell : $activeCell;
+ // Calculate freeze coordinates
+ $xSplit = $ySplit = 0;
+
+ list($xSplit, $ySplit) = PHPExcel_Cell::coordinateFromString($topLeftCell);
+ $xSplit = PHPExcel_Cell::columnIndexFromString($xSplit);
+
+ // pane
+ $pane = 'topRight';
+ $objWriter->startElement('pane');
+ if ($xSplit > 1) {
+ $objWriter->writeAttribute('xSplit', $xSplit - 1);
+ }
+ if ($ySplit > 1) {
+ $objWriter->writeAttribute('ySplit', $ySplit - 1);
+ $pane = ($xSplit > 1) ? 'bottomRight' : 'bottomLeft';
+ }
+ $objWriter->writeAttribute('topLeftCell', $topLeftCell);
+ $objWriter->writeAttribute('activePane', $pane);
+ $objWriter->writeAttribute('state', 'frozen');
+ $objWriter->endElement();
+
+ if (($xSplit > 1) && ($ySplit > 1)) {
+ // Write additional selections if more than two panes (ie both an X and a Y split)
+ $objWriter->startElement('selection');
+ $objWriter->writeAttribute('pane', 'topRight');
+ $objWriter->endElement();
+ $objWriter->startElement('selection');
+ $objWriter->writeAttribute('pane', 'bottomLeft');
+ $objWriter->endElement();
+ }
+ }
+
+ // Selection
+// if ($pane != '') {
+ // Only need to write selection element if we have a split pane
+ // We cheat a little by over-riding the active cell selection, setting it to the split cell
+ $objWriter->startElement('selection');
+ if ($pane != '') {
+ $objWriter->writeAttribute('pane', $pane);
+ }
+ $objWriter->writeAttribute('activeCell', $activeCell);
+ $objWriter->writeAttribute('sqref', $activeCell);
+ $objWriter->endElement();
+// }
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write SheetFormatPr
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeSheetFormatPr(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // sheetFormatPr
+ $objWriter->startElement('sheetFormatPr');
+
+ // Default row height
+ if ($pSheet->getDefaultRowDimension()->getRowHeight() >= 0) {
+ $objWriter->writeAttribute('customHeight', 'true');
+ $objWriter->writeAttribute('defaultRowHeight', PHPExcel_Shared_String::FormatNumber($pSheet->getDefaultRowDimension()->getRowHeight()));
+ } else {
+ $objWriter->writeAttribute('defaultRowHeight', '14.4');
+ }
+
+ // Set Zero Height row
+ if ((string)$pSheet->getDefaultRowDimension()->getZeroHeight() == '1' ||
+ strtolower((string)$pSheet->getDefaultRowDimension()->getZeroHeight()) == 'true') {
+ $objWriter->writeAttribute('zeroHeight', '1');
+ }
+
+ // Default column width
+ if ($pSheet->getDefaultColumnDimension()->getWidth() >= 0) {
+ $objWriter->writeAttribute('defaultColWidth', PHPExcel_Shared_String::FormatNumber($pSheet->getDefaultColumnDimension()->getWidth()));
+ }
+
+ // Outline level - row
+ $outlineLevelRow = 0;
+ foreach ($pSheet->getRowDimensions() as $dimension) {
+ if ($dimension->getOutlineLevel() > $outlineLevelRow) {
+ $outlineLevelRow = $dimension->getOutlineLevel();
+ }
+ }
+ $objWriter->writeAttribute('outlineLevelRow', (int)$outlineLevelRow);
+
+ // Outline level - column
+ $outlineLevelCol = 0;
+ foreach ($pSheet->getColumnDimensions() as $dimension) {
+ if ($dimension->getOutlineLevel() > $outlineLevelCol) {
+ $outlineLevelCol = $dimension->getOutlineLevel();
+ }
+ }
+ $objWriter->writeAttribute('outlineLevelCol', (int)$outlineLevelCol);
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Cols
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeCols(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // cols
+ if (count($pSheet->getColumnDimensions()) > 0) {
+ $objWriter->startElement('cols');
+
+ $pSheet->calculateColumnWidths();
+
+ // Loop through column dimensions
+ foreach ($pSheet->getColumnDimensions() as $colDimension) {
+ // col
+ $objWriter->startElement('col');
+ $objWriter->writeAttribute('min', PHPExcel_Cell::columnIndexFromString($colDimension->getColumnIndex()));
+ $objWriter->writeAttribute('max', PHPExcel_Cell::columnIndexFromString($colDimension->getColumnIndex()));
+
+ if ($colDimension->getWidth() < 0) {
+ // No width set, apply default of 10
+ $objWriter->writeAttribute('width', '9.10');
+ } else {
+ // Width set
+ $objWriter->writeAttribute('width', PHPExcel_Shared_String::FormatNumber($colDimension->getWidth()));
+ }
+
+ // Column visibility
+ if ($colDimension->getVisible() == false) {
+ $objWriter->writeAttribute('hidden', 'true');
+ }
+
+ // Auto size?
+ if ($colDimension->getAutoSize()) {
+ $objWriter->writeAttribute('bestFit', 'true');
+ }
+
+ // Custom width?
+ if ($colDimension->getWidth() != $pSheet->getDefaultColumnDimension()->getWidth()) {
+ $objWriter->writeAttribute('customWidth', 'true');
+ }
+
+ // Collapsed
+ if ($colDimension->getCollapsed() == true) {
+ $objWriter->writeAttribute('collapsed', 'true');
+ }
+
+ // Outline level
+ if ($colDimension->getOutlineLevel() > 0) {
+ $objWriter->writeAttribute('outlineLevel', $colDimension->getOutlineLevel());
+ }
+
+ // Style
+ $objWriter->writeAttribute('style', $colDimension->getXfIndex());
+
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write SheetProtection
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeSheetProtection(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // sheetProtection
+ $objWriter->startElement('sheetProtection');
+
+ if ($pSheet->getProtection()->getPassword() != '') {
+ $objWriter->writeAttribute('password', $pSheet->getProtection()->getPassword());
+ }
+
+ $objWriter->writeAttribute('sheet', ($pSheet->getProtection()->getSheet() ? 'true' : 'false'));
+ $objWriter->writeAttribute('objects', ($pSheet->getProtection()->getObjects() ? 'true' : 'false'));
+ $objWriter->writeAttribute('scenarios', ($pSheet->getProtection()->getScenarios() ? 'true' : 'false'));
+ $objWriter->writeAttribute('formatCells', ($pSheet->getProtection()->getFormatCells() ? 'true' : 'false'));
+ $objWriter->writeAttribute('formatColumns', ($pSheet->getProtection()->getFormatColumns() ? 'true' : 'false'));
+ $objWriter->writeAttribute('formatRows', ($pSheet->getProtection()->getFormatRows() ? 'true' : 'false'));
+ $objWriter->writeAttribute('insertColumns', ($pSheet->getProtection()->getInsertColumns() ? 'true' : 'false'));
+ $objWriter->writeAttribute('insertRows', ($pSheet->getProtection()->getInsertRows() ? 'true' : 'false'));
+ $objWriter->writeAttribute('insertHyperlinks', ($pSheet->getProtection()->getInsertHyperlinks() ? 'true' : 'false'));
+ $objWriter->writeAttribute('deleteColumns', ($pSheet->getProtection()->getDeleteColumns() ? 'true' : 'false'));
+ $objWriter->writeAttribute('deleteRows', ($pSheet->getProtection()->getDeleteRows() ? 'true' : 'false'));
+ $objWriter->writeAttribute('selectLockedCells', ($pSheet->getProtection()->getSelectLockedCells() ? 'true' : 'false'));
+ $objWriter->writeAttribute('sort', ($pSheet->getProtection()->getSort() ? 'true' : 'false'));
+ $objWriter->writeAttribute('autoFilter', ($pSheet->getProtection()->getAutoFilter() ? 'true' : 'false'));
+ $objWriter->writeAttribute('pivotTables', ($pSheet->getProtection()->getPivotTables() ? 'true' : 'false'));
+ $objWriter->writeAttribute('selectUnlockedCells', ($pSheet->getProtection()->getSelectUnlockedCells() ? 'true' : 'false'));
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write ConditionalFormatting
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeConditionalFormatting(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // Conditional id
+ $id = 1;
+
+ // Loop through styles in the current worksheet
+ foreach ($pSheet->getConditionalStylesCollection() as $cellCoordinate => $conditionalStyles) {
+ foreach ($conditionalStyles as $conditional) {
+ // WHY was this again?
+ // if ($this->getParentWriter()->getStylesConditionalHashTable()->getIndexForHashCode($conditional->getHashCode()) == '') {
+ // continue;
+ // }
+ if ($conditional->getConditionType() != PHPExcel_Style_Conditional::CONDITION_NONE) {
+ // conditionalFormatting
+ $objWriter->startElement('conditionalFormatting');
+ $objWriter->writeAttribute('sqref', $cellCoordinate);
+
+ // cfRule
+ $objWriter->startElement('cfRule');
+ $objWriter->writeAttribute('type', $conditional->getConditionType());
+ $objWriter->writeAttribute('dxfId', $this->getParentWriter()->getStylesConditionalHashTable()->getIndexForHashCode($conditional->getHashCode()));
+ $objWriter->writeAttribute('priority', $id++);
+
+ if (($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS || $conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CONTAINSTEXT)
+ && $conditional->getOperatorType() != PHPExcel_Style_Conditional::OPERATOR_NONE) {
+ $objWriter->writeAttribute('operator', $conditional->getOperatorType());
+ }
+
+ if ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CONTAINSTEXT
+ && !is_null($conditional->getText())) {
+ $objWriter->writeAttribute('text', $conditional->getText());
+ }
+
+ if ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CONTAINSTEXT
+ && $conditional->getOperatorType() == PHPExcel_Style_Conditional::OPERATOR_CONTAINSTEXT
+ && !is_null($conditional->getText())) {
+ $objWriter->writeElement('formula', 'NOT(ISERROR(SEARCH("' . $conditional->getText() . '",' . $cellCoordinate . ')))');
+ } elseif ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CONTAINSTEXT
+ && $conditional->getOperatorType() == PHPExcel_Style_Conditional::OPERATOR_BEGINSWITH
+ && !is_null($conditional->getText())) {
+ $objWriter->writeElement('formula', 'LEFT(' . $cellCoordinate . ',' . strlen($conditional->getText()) . ')="' . $conditional->getText() . '"');
+ } elseif ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CONTAINSTEXT
+ && $conditional->getOperatorType() == PHPExcel_Style_Conditional::OPERATOR_ENDSWITH
+ && !is_null($conditional->getText())) {
+ $objWriter->writeElement('formula', 'RIGHT(' . $cellCoordinate . ',' . strlen($conditional->getText()) . ')="' . $conditional->getText() . '"');
+ } elseif ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CONTAINSTEXT
+ && $conditional->getOperatorType() == PHPExcel_Style_Conditional::OPERATOR_NOTCONTAINS
+ && !is_null($conditional->getText())) {
+ $objWriter->writeElement('formula', 'ISERROR(SEARCH("' . $conditional->getText() . '",' . $cellCoordinate . '))');
+ } elseif ($conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CELLIS
+ || $conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_CONTAINSTEXT
+ || $conditional->getConditionType() == PHPExcel_Style_Conditional::CONDITION_EXPRESSION) {
+ foreach ($conditional->getConditions() as $formula) {
+ // Formula
+ $objWriter->writeElement('formula', $formula);
+ }
+ }
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+ }
+ }
+ }
+
+ /**
+ * Write DataValidations
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDataValidations(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // Datavalidation collection
+ $dataValidationCollection = $pSheet->getDataValidationCollection();
+
+ // Write data validations?
+ if (!empty($dataValidationCollection)) {
+ $objWriter->startElement('dataValidations');
+ $objWriter->writeAttribute('count', count($dataValidationCollection));
+
+ foreach ($dataValidationCollection as $coordinate => $dv) {
+ $objWriter->startElement('dataValidation');
+
+ if ($dv->getType() != '') {
+ $objWriter->writeAttribute('type', $dv->getType());
+ }
+
+ if ($dv->getErrorStyle() != '') {
+ $objWriter->writeAttribute('errorStyle', $dv->getErrorStyle());
+ }
+
+ if ($dv->getOperator() != '') {
+ $objWriter->writeAttribute('operator', $dv->getOperator());
+ }
+
+ $objWriter->writeAttribute('allowBlank', ($dv->getAllowBlank() ? '1' : '0'));
+ $objWriter->writeAttribute('showDropDown', (!$dv->getShowDropDown() ? '1' : '0'));
+ $objWriter->writeAttribute('showInputMessage', ($dv->getShowInputMessage() ? '1' : '0'));
+ $objWriter->writeAttribute('showErrorMessage', ($dv->getShowErrorMessage() ? '1' : '0'));
+
+ if ($dv->getErrorTitle() !== '') {
+ $objWriter->writeAttribute('errorTitle', $dv->getErrorTitle());
+ }
+ if ($dv->getError() !== '') {
+ $objWriter->writeAttribute('error', $dv->getError());
+ }
+ if ($dv->getPromptTitle() !== '') {
+ $objWriter->writeAttribute('promptTitle', $dv->getPromptTitle());
+ }
+ if ($dv->getPrompt() !== '') {
+ $objWriter->writeAttribute('prompt', $dv->getPrompt());
+ }
+
+ $objWriter->writeAttribute('sqref', $coordinate);
+
+ if ($dv->getFormula1() !== '') {
+ $objWriter->writeElement('formula1', $dv->getFormula1());
+ }
+ if ($dv->getFormula2() !== '') {
+ $objWriter->writeElement('formula2', $dv->getFormula2());
+ }
+
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write Hyperlinks
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeHyperlinks(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // Hyperlink collection
+ $hyperlinkCollection = $pSheet->getHyperlinkCollection();
+
+ // Relation ID
+ $relationId = 1;
+
+ // Write hyperlinks?
+ if (!empty($hyperlinkCollection)) {
+ $objWriter->startElement('hyperlinks');
+
+ foreach ($hyperlinkCollection as $coordinate => $hyperlink) {
+ $objWriter->startElement('hyperlink');
+
+ $objWriter->writeAttribute('ref', $coordinate);
+ if (!$hyperlink->isInternal()) {
+ $objWriter->writeAttribute('r:id', 'rId_hyperlink_' . $relationId);
+ ++$relationId;
+ } else {
+ $objWriter->writeAttribute('location', str_replace('sheet://', '', $hyperlink->getUrl()));
+ }
+
+ if ($hyperlink->getTooltip() != '') {
+ $objWriter->writeAttribute('tooltip', $hyperlink->getTooltip());
+ }
+
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write ProtectedRanges
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeProtectedRanges(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ if (count($pSheet->getProtectedCells()) > 0) {
+ // protectedRanges
+ $objWriter->startElement('protectedRanges');
+
+ // Loop protectedRanges
+ foreach ($pSheet->getProtectedCells() as $protectedCell => $passwordHash) {
+ // protectedRange
+ $objWriter->startElement('protectedRange');
+ $objWriter->writeAttribute('name', 'p' . md5($protectedCell));
+ $objWriter->writeAttribute('sqref', $protectedCell);
+ if (!empty($passwordHash)) {
+ $objWriter->writeAttribute('password', $passwordHash);
+ }
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write MergeCells
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeMergeCells(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ if (count($pSheet->getMergeCells()) > 0) {
+ // mergeCells
+ $objWriter->startElement('mergeCells');
+
+ // Loop mergeCells
+ foreach ($pSheet->getMergeCells() as $mergeCell) {
+ // mergeCell
+ $objWriter->startElement('mergeCell');
+ $objWriter->writeAttribute('ref', $mergeCell);
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write PrintOptions
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writePrintOptions(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // printOptions
+ $objWriter->startElement('printOptions');
+
+ $objWriter->writeAttribute('gridLines', ($pSheet->getPrintGridlines() ? 'true': 'false'));
+ $objWriter->writeAttribute('gridLinesSet', 'true');
+
+ if ($pSheet->getPageSetup()->getHorizontalCentered()) {
+ $objWriter->writeAttribute('horizontalCentered', 'true');
+ }
+
+ if ($pSheet->getPageSetup()->getVerticalCentered()) {
+ $objWriter->writeAttribute('verticalCentered', 'true');
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write PageMargins
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writePageMargins(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // pageMargins
+ $objWriter->startElement('pageMargins');
+ $objWriter->writeAttribute('left', PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getLeft()));
+ $objWriter->writeAttribute('right', PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getRight()));
+ $objWriter->writeAttribute('top', PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getTop()));
+ $objWriter->writeAttribute('bottom', PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getBottom()));
+ $objWriter->writeAttribute('header', PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getHeader()));
+ $objWriter->writeAttribute('footer', PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getFooter()));
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write AutoFilter
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeAutoFilter(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ $autoFilterRange = $pSheet->getAutoFilter()->getRange();
+ if (!empty($autoFilterRange)) {
+ // autoFilter
+ $objWriter->startElement('autoFilter');
+
+ // Strip any worksheet reference from the filter coordinates
+ $range = PHPExcel_Cell::splitRange($autoFilterRange);
+ $range = $range[0];
+ // Strip any worksheet ref
+ if (strpos($range[0], '!') !== false) {
+ list($ws, $range[0]) = explode('!', $range[0]);
+ }
+ $range = implode(':', $range);
+
+ $objWriter->writeAttribute('ref', str_replace('$', '', $range));
+
+ $columns = $pSheet->getAutoFilter()->getColumns();
+ if (count($columns > 0)) {
+ foreach ($columns as $columnID => $column) {
+ $rules = $column->getRules();
+ if (count($rules) > 0) {
+ $objWriter->startElement('filterColumn');
+ $objWriter->writeAttribute('colId', $pSheet->getAutoFilter()->getColumnOffset($columnID));
+
+ $objWriter->startElement($column->getFilterType());
+ if ($column->getJoin() == PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_AND) {
+ $objWriter->writeAttribute('and', 1);
+ }
+
+ foreach ($rules as $rule) {
+ if (($column->getFilterType() === PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_FILTER) &&
+ ($rule->getOperator() === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL) &&
+ ($rule->getValue() === '')) {
+ // Filter rule for Blanks
+ $objWriter->writeAttribute('blank', 1);
+ } elseif ($rule->getRuleType() === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER) {
+ // Dynamic Filter Rule
+ $objWriter->writeAttribute('type', $rule->getGrouping());
+ $val = $column->getAttribute('val');
+ if ($val !== null) {
+ $objWriter->writeAttribute('val', $val);
+ }
+ $maxVal = $column->getAttribute('maxVal');
+ if ($maxVal !== null) {
+ $objWriter->writeAttribute('maxVal', $maxVal);
+ }
+ } elseif ($rule->getRuleType() === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_TOPTENFILTER) {
+ // Top 10 Filter Rule
+ $objWriter->writeAttribute('val', $rule->getValue());
+ $objWriter->writeAttribute('percent', (($rule->getOperator() === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) ? '1' : '0'));
+ $objWriter->writeAttribute('top', (($rule->getGrouping() === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) ? '1': '0'));
+ } else {
+ // Filter, DateGroupItem or CustomFilter
+ $objWriter->startElement($rule->getRuleType());
+
+ if ($rule->getOperator() !== PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL) {
+ $objWriter->writeAttribute('operator', $rule->getOperator());
+ }
+ if ($rule->getRuleType() === PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_DATEGROUP) {
+ // Date Group filters
+ foreach ($rule->getValue() as $key => $value) {
+ if ($value > '') {
+ $objWriter->writeAttribute($key, $value);
+ }
+ }
+ $objWriter->writeAttribute('dateTimeGrouping', $rule->getGrouping());
+ } else {
+ $objWriter->writeAttribute('val', $rule->getValue());
+ }
+
+ $objWriter->endElement();
+ }
+ }
+
+ $objWriter->endElement();
+
+ $objWriter->endElement();
+ }
+ }
+ }
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write PageSetup
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writePageSetup(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // pageSetup
+ $objWriter->startElement('pageSetup');
+ $objWriter->writeAttribute('paperSize', $pSheet->getPageSetup()->getPaperSize());
+ $objWriter->writeAttribute('orientation', $pSheet->getPageSetup()->getOrientation());
+
+ if (!is_null($pSheet->getPageSetup()->getScale())) {
+ $objWriter->writeAttribute('scale', $pSheet->getPageSetup()->getScale());
+ }
+ if (!is_null($pSheet->getPageSetup()->getFitToHeight())) {
+ $objWriter->writeAttribute('fitToHeight', $pSheet->getPageSetup()->getFitToHeight());
+ } else {
+ $objWriter->writeAttribute('fitToHeight', '0');
+ }
+ if (!is_null($pSheet->getPageSetup()->getFitToWidth())) {
+ $objWriter->writeAttribute('fitToWidth', $pSheet->getPageSetup()->getFitToWidth());
+ } else {
+ $objWriter->writeAttribute('fitToWidth', '0');
+ }
+ if (!is_null($pSheet->getPageSetup()->getFirstPageNumber())) {
+ $objWriter->writeAttribute('firstPageNumber', $pSheet->getPageSetup()->getFirstPageNumber());
+ $objWriter->writeAttribute('useFirstPageNumber', '1');
+ }
+
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Header / Footer
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeHeaderFooter(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // headerFooter
+ $objWriter->startElement('headerFooter');
+ $objWriter->writeAttribute('differentOddEven', ($pSheet->getHeaderFooter()->getDifferentOddEven() ? 'true' : 'false'));
+ $objWriter->writeAttribute('differentFirst', ($pSheet->getHeaderFooter()->getDifferentFirst() ? 'true' : 'false'));
+ $objWriter->writeAttribute('scaleWithDoc', ($pSheet->getHeaderFooter()->getScaleWithDocument() ? 'true' : 'false'));
+ $objWriter->writeAttribute('alignWithMargins', ($pSheet->getHeaderFooter()->getAlignWithMargins() ? 'true' : 'false'));
+
+ $objWriter->writeElement('oddHeader', $pSheet->getHeaderFooter()->getOddHeader());
+ $objWriter->writeElement('oddFooter', $pSheet->getHeaderFooter()->getOddFooter());
+ $objWriter->writeElement('evenHeader', $pSheet->getHeaderFooter()->getEvenHeader());
+ $objWriter->writeElement('evenFooter', $pSheet->getHeaderFooter()->getEvenFooter());
+ $objWriter->writeElement('firstHeader', $pSheet->getHeaderFooter()->getFirstHeader());
+ $objWriter->writeElement('firstFooter', $pSheet->getHeaderFooter()->getFirstFooter());
+ $objWriter->endElement();
+ }
+
+ /**
+ * Write Breaks
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeBreaks(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // Get row and column breaks
+ $aRowBreaks = array();
+ $aColumnBreaks = array();
+ foreach ($pSheet->getBreaks() as $cell => $breakType) {
+ if ($breakType == PHPExcel_Worksheet::BREAK_ROW) {
+ $aRowBreaks[] = $cell;
+ } elseif ($breakType == PHPExcel_Worksheet::BREAK_COLUMN) {
+ $aColumnBreaks[] = $cell;
+ }
+ }
+
+ // rowBreaks
+ if (!empty($aRowBreaks)) {
+ $objWriter->startElement('rowBreaks');
+ $objWriter->writeAttribute('count', count($aRowBreaks));
+ $objWriter->writeAttribute('manualBreakCount', count($aRowBreaks));
+
+ foreach ($aRowBreaks as $cell) {
+ $coords = PHPExcel_Cell::coordinateFromString($cell);
+
+ $objWriter->startElement('brk');
+ $objWriter->writeAttribute('id', $coords[1]);
+ $objWriter->writeAttribute('man', '1');
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+
+ // Second, write column breaks
+ if (!empty($aColumnBreaks)) {
+ $objWriter->startElement('colBreaks');
+ $objWriter->writeAttribute('count', count($aColumnBreaks));
+ $objWriter->writeAttribute('manualBreakCount', count($aColumnBreaks));
+
+ foreach ($aColumnBreaks as $cell) {
+ $coords = PHPExcel_Cell::coordinateFromString($cell);
+
+ $objWriter->startElement('brk');
+ $objWriter->writeAttribute('id', PHPExcel_Cell::columnIndexFromString($coords[0]) - 1);
+ $objWriter->writeAttribute('man', '1');
+ $objWriter->endElement();
+ }
+
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write SheetData
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @param string[] $pStringTable String table
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeSheetData(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null, $pStringTable = null)
+ {
+ if (is_array($pStringTable)) {
+ // Flipped stringtable, for faster index searching
+ $aFlippedStringTable = $this->getParentWriter()->getWriterPart('stringtable')->flipStringTable($pStringTable);
+
+ // sheetData
+ $objWriter->startElement('sheetData');
+
+ // Get column count
+ $colCount = PHPExcel_Cell::columnIndexFromString($pSheet->getHighestColumn());
+
+ // Highest row number
+ $highestRow = $pSheet->getHighestRow();
+
+ // Loop through cells
+ $cellsByRow = array();
+ foreach ($pSheet->getCellCollection() as $cellID) {
+ $cellAddress = PHPExcel_Cell::coordinateFromString($cellID);
+ $cellsByRow[$cellAddress[1]][] = $cellID;
+ }
+
+ $currentRow = 0;
+ while ($currentRow++ < $highestRow) {
+ // Get row dimension
+ $rowDimension = $pSheet->getRowDimension($currentRow);
+
+ // Write current row?
+ $writeCurrentRow = isset($cellsByRow[$currentRow]) || $rowDimension->getRowHeight() >= 0 || $rowDimension->getVisible() == false || $rowDimension->getCollapsed() == true || $rowDimension->getOutlineLevel() > 0 || $rowDimension->getXfIndex() !== null;
+
+ if ($writeCurrentRow) {
+ // Start a new row
+ $objWriter->startElement('row');
+ $objWriter->writeAttribute('r', $currentRow);
+ $objWriter->writeAttribute('spans', '1:' . $colCount);
+
+ // Row dimensions
+ if ($rowDimension->getRowHeight() >= 0) {
+ $objWriter->writeAttribute('customHeight', '1');
+ $objWriter->writeAttribute('ht', PHPExcel_Shared_String::FormatNumber($rowDimension->getRowHeight()));
+ }
+
+ // Row visibility
+ if ($rowDimension->getVisible() == false) {
+ $objWriter->writeAttribute('hidden', 'true');
+ }
+
+ // Collapsed
+ if ($rowDimension->getCollapsed() == true) {
+ $objWriter->writeAttribute('collapsed', 'true');
+ }
+
+ // Outline level
+ if ($rowDimension->getOutlineLevel() > 0) {
+ $objWriter->writeAttribute('outlineLevel', $rowDimension->getOutlineLevel());
+ }
+
+ // Style
+ if ($rowDimension->getXfIndex() !== null) {
+ $objWriter->writeAttribute('s', $rowDimension->getXfIndex());
+ $objWriter->writeAttribute('customFormat', '1');
+ }
+
+ // Write cells
+ if (isset($cellsByRow[$currentRow])) {
+ foreach ($cellsByRow[$currentRow] as $cellAddress) {
+ // Write cell
+ $this->writeCell($objWriter, $pSheet, $cellAddress, $pStringTable, $aFlippedStringTable);
+ }
+ }
+
+ // End row
+ $objWriter->endElement();
+ }
+ }
+
+ $objWriter->endElement();
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid parameters passed.");
+ }
+ }
+
+ /**
+ * Write Cell
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @param PHPExcel_Cell $pCellAddress Cell Address
+ * @param string[] $pStringTable String table
+ * @param string[] $pFlippedStringTable String table (flipped), for faster index searching
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeCell(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null, $pCellAddress = null, $pStringTable = null, $pFlippedStringTable = null)
+ {
+ if (is_array($pStringTable) && is_array($pFlippedStringTable)) {
+ // Cell
+ $pCell = $pSheet->getCell($pCellAddress);
+ $objWriter->startElement('c');
+ $objWriter->writeAttribute('r', $pCellAddress);
+
+ // Sheet styles
+ if ($pCell->getXfIndex() != '') {
+ $objWriter->writeAttribute('s', $pCell->getXfIndex());
+ }
+
+ // If cell value is supplied, write cell value
+ $cellValue = $pCell->getValue();
+ if (is_object($cellValue) || $cellValue !== '') {
+ // Map type
+ $mappedType = $pCell->getDataType();
+
+ // Write data type depending on its type
+ switch (strtolower($mappedType)) {
+ case 'inlinestr': // Inline string
+ case 's': // String
+ case 'b': // Boolean
+ $objWriter->writeAttribute('t', $mappedType);
+ break;
+ case 'f': // Formula
+ $calculatedValue = ($this->getParentWriter()->getPreCalculateFormulas()) ?
+ $pCell->getCalculatedValue() :
+ $cellValue;
+ if (is_string($calculatedValue)) {
+ $objWriter->writeAttribute('t', 'str');
+ }
+ break;
+ case 'e': // Error
+ $objWriter->writeAttribute('t', $mappedType);
+ }
+
+ // Write data depending on its type
+ switch (strtolower($mappedType)) {
+ case 'inlinestr': // Inline string
+ if (! $cellValue instanceof PHPExcel_RichText) {
+ $objWriter->writeElement('t', PHPExcel_Shared_String::ControlCharacterPHP2OOXML(htmlspecialchars($cellValue)));
+ } elseif ($cellValue instanceof PHPExcel_RichText) {
+ $objWriter->startElement('is');
+ $this->getParentWriter()->getWriterPart('stringtable')->writeRichText($objWriter, $cellValue);
+ $objWriter->endElement();
+ }
+
+ break;
+ case 's': // String
+ if (! $cellValue instanceof PHPExcel_RichText) {
+ if (isset($pFlippedStringTable[$cellValue])) {
+ $objWriter->writeElement('v', $pFlippedStringTable[$cellValue]);
+ }
+ } elseif ($cellValue instanceof PHPExcel_RichText) {
+ $objWriter->writeElement('v', $pFlippedStringTable[$cellValue->getHashCode()]);
+ }
+
+ break;
+ case 'f': // Formula
+ $attributes = $pCell->getFormulaAttributes();
+ if ($attributes['t'] == 'array') {
+ $objWriter->startElement('f');
+ $objWriter->writeAttribute('t', 'array');
+ $objWriter->writeAttribute('ref', $pCellAddress);
+ $objWriter->writeAttribute('aca', '1');
+ $objWriter->writeAttribute('ca', '1');
+ $objWriter->text(substr($cellValue, 1));
+ $objWriter->endElement();
+ } else {
+ $objWriter->writeElement('f', substr($cellValue, 1));
+ }
+ if ($this->getParentWriter()->getOffice2003Compatibility() === false) {
+ if ($this->getParentWriter()->getPreCalculateFormulas()) {
+// $calculatedValue = $pCell->getCalculatedValue();
+ if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) != '#') {
+ $objWriter->writeElement('v', PHPExcel_Shared_String::FormatNumber($calculatedValue));
+ } else {
+ $objWriter->writeElement('v', '0');
+ }
+ } else {
+ $objWriter->writeElement('v', '0');
+ }
+ }
+ break;
+ case 'n': // Numeric
+ // force point as decimal separator in case current locale uses comma
+ $objWriter->writeElement('v', str_replace(',', '.', $cellValue));
+ break;
+ case 'b': // Boolean
+ $objWriter->writeElement('v', ($cellValue ? '1' : '0'));
+ break;
+ case 'e': // Error
+ if (substr($cellValue, 0, 1) == '=') {
+ $objWriter->writeElement('f', substr($cellValue, 1));
+ $objWriter->writeElement('v', substr($cellValue, 1));
+ } else {
+ $objWriter->writeElement('v', $cellValue);
+ }
+
+ break;
+ }
+ }
+
+ $objWriter->endElement();
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid parameters passed.");
+ }
+ }
+
+ /**
+ * Write Drawings
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @param boolean $includeCharts Flag indicating if we should include drawing details for charts
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeDrawings(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null, $includeCharts = false)
+ {
+ $chartCount = ($includeCharts) ? $pSheet->getChartCollection()->count() : 0;
+ // If sheet contains drawings, add the relationships
+ if (($pSheet->getDrawingCollection()->count() > 0) ||
+ ($chartCount > 0)) {
+ $objWriter->startElement('drawing');
+ $objWriter->writeAttribute('r:id', 'rId1');
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write LegacyDrawing
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeLegacyDrawing(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // If sheet contains comments, add the relationships
+ if (count($pSheet->getComments()) > 0) {
+ $objWriter->startElement('legacyDrawing');
+ $objWriter->writeAttribute('r:id', 'rId_comments_vml1');
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write LegacyDrawingHF
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter XML Writer
+ * @param PHPExcel_Worksheet $pSheet Worksheet
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeLegacyDrawingHF(PHPExcel_Shared_XMLWriter $objWriter = null, PHPExcel_Worksheet $pSheet = null)
+ {
+ // If sheet contains images, add the relationships
+ if (count($pSheet->getHeaderFooter()->getImages()) > 0) {
+ $objWriter->startElement('legacyDrawingHF');
+ $objWriter->writeAttribute('r:id', 'rId_headerfooter_vml1');
+ $objWriter->endElement();
+ }
+ }
+}
diff --git a/PHPExcel/Writer/Excel2007/WriterPart.php b/PHPExcel/Writer/Excel2007/WriterPart.php
new file mode 100644
index 0000000..806ebe5
--- /dev/null
+++ b/PHPExcel/Writer/Excel2007/WriterPart.php
@@ -0,0 +1,75 @@
+parentWriter = $pWriter;
+ }
+
+ /**
+ * Get parent IWriter object
+ *
+ * @return PHPExcel_Writer_IWriter
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function getParentWriter()
+ {
+ if (!is_null($this->parentWriter)) {
+ return $this->parentWriter;
+ } else {
+ throw new PHPExcel_Writer_Exception("No parent PHPExcel_Writer_IWriter assigned.");
+ }
+ }
+
+ /**
+ * Set parent IWriter object
+ *
+ * @param PHPExcel_Writer_IWriter $pWriter
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function __construct(PHPExcel_Writer_IWriter $pWriter = null)
+ {
+ if (!is_null($pWriter)) {
+ $this->parentWriter = $pWriter;
+ }
+ }
+}
diff --git a/PHPExcel/Writer/Excel5.php b/PHPExcel/Writer/Excel5.php
new file mode 100644
index 0000000..2dede81
--- /dev/null
+++ b/PHPExcel/Writer/Excel5.php
@@ -0,0 +1,904 @@
+phpExcel = $phpExcel;
+
+ $this->parser = new PHPExcel_Writer_Excel5_Parser();
+ }
+
+ /**
+ * Save PHPExcel to file
+ *
+ * @param string $pFilename
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function save($pFilename = null)
+ {
+
+ // garbage collect
+ $this->phpExcel->garbageCollect();
+
+ $saveDebugLog = PHPExcel_Calculation::getInstance($this->phpExcel)->getDebugLog()->getWriteDebugLog();
+ PHPExcel_Calculation::getInstance($this->phpExcel)->getDebugLog()->setWriteDebugLog(false);
+ $saveDateReturnType = PHPExcel_Calculation_Functions::getReturnDateType();
+ PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL);
+
+ // initialize colors array
+ $this->colors = array();
+
+ // Initialise workbook writer
+ $this->writerWorkbook = new PHPExcel_Writer_Excel5_Workbook($this->phpExcel, $this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser);
+
+ // Initialise worksheet writers
+ $countSheets = $this->phpExcel->getSheetCount();
+ for ($i = 0; $i < $countSheets; ++$i) {
+ $this->writerWorksheets[$i] = new PHPExcel_Writer_Excel5_Worksheet($this->strTotal, $this->strUnique, $this->strTable, $this->colors, $this->parser, $this->preCalculateFormulas, $this->phpExcel->getSheet($i));
+ }
+
+ // build Escher objects. Escher objects for workbooks needs to be build before Escher object for workbook.
+ $this->buildWorksheetEschers();
+ $this->buildWorkbookEscher();
+
+ // add 15 identical cell style Xfs
+ // for now, we use the first cellXf instead of cellStyleXf
+ $cellXfCollection = $this->phpExcel->getCellXfCollection();
+ for ($i = 0; $i < 15; ++$i) {
+ $this->writerWorkbook->addXfWriter($cellXfCollection[0], true);
+ }
+
+ // add all the cell Xfs
+ foreach ($this->phpExcel->getCellXfCollection() as $style) {
+ $this->writerWorkbook->addXfWriter($style, false);
+ }
+
+ // add fonts from rich text eleemnts
+ for ($i = 0; $i < $countSheets; ++$i) {
+ foreach ($this->writerWorksheets[$i]->phpSheet->getCellCollection() as $cellID) {
+ $cell = $this->writerWorksheets[$i]->phpSheet->getCell($cellID);
+ $cVal = $cell->getValue();
+ if ($cVal instanceof PHPExcel_RichText) {
+ $elements = $cVal->getRichTextElements();
+ foreach ($elements as $element) {
+ if ($element instanceof PHPExcel_RichText_Run) {
+ $font = $element->getFont();
+ $this->writerWorksheets[$i]->fontHashIndex[$font->getHashCode()] = $this->writerWorkbook->addFont($font);
+ }
+ }
+ }
+ }
+ }
+
+ // initialize OLE file
+ $workbookStreamName = 'Workbook';
+ $OLE = new PHPExcel_Shared_OLE_PPS_File(PHPExcel_Shared_OLE::Asc2Ucs($workbookStreamName));
+
+ // Write the worksheet streams before the global workbook stream,
+ // because the byte sizes of these are needed in the global workbook stream
+ $worksheetSizes = array();
+ for ($i = 0; $i < $countSheets; ++$i) {
+ $this->writerWorksheets[$i]->close();
+ $worksheetSizes[] = $this->writerWorksheets[$i]->_datasize;
+ }
+
+ // add binary data for global workbook stream
+ $OLE->append($this->writerWorkbook->writeWorkbook($worksheetSizes));
+
+ // add binary data for sheet streams
+ for ($i = 0; $i < $countSheets; ++$i) {
+ $OLE->append($this->writerWorksheets[$i]->getData());
+ }
+
+ $this->documentSummaryInformation = $this->writeDocumentSummaryInformation();
+ // initialize OLE Document Summary Information
+ if (isset($this->documentSummaryInformation) && !empty($this->documentSummaryInformation)) {
+ $OLE_DocumentSummaryInformation = new PHPExcel_Shared_OLE_PPS_File(PHPExcel_Shared_OLE::Asc2Ucs(chr(5) . 'DocumentSummaryInformation'));
+ $OLE_DocumentSummaryInformation->append($this->documentSummaryInformation);
+ }
+
+ $this->summaryInformation = $this->writeSummaryInformation();
+ // initialize OLE Summary Information
+ if (isset($this->summaryInformation) && !empty($this->summaryInformation)) {
+ $OLE_SummaryInformation = new PHPExcel_Shared_OLE_PPS_File(PHPExcel_Shared_OLE::Asc2Ucs(chr(5) . 'SummaryInformation'));
+ $OLE_SummaryInformation->append($this->summaryInformation);
+ }
+
+ // define OLE Parts
+ $arrRootData = array($OLE);
+ // initialize OLE Properties file
+ if (isset($OLE_SummaryInformation)) {
+ $arrRootData[] = $OLE_SummaryInformation;
+ }
+ // initialize OLE Extended Properties file
+ if (isset($OLE_DocumentSummaryInformation)) {
+ $arrRootData[] = $OLE_DocumentSummaryInformation;
+ }
+
+ $root = new PHPExcel_Shared_OLE_PPS_Root(time(), time(), $arrRootData);
+ // save the OLE file
+ $res = $root->save($pFilename);
+
+ PHPExcel_Calculation_Functions::setReturnDateType($saveDateReturnType);
+ PHPExcel_Calculation::getInstance($this->phpExcel)->getDebugLog()->setWriteDebugLog($saveDebugLog);
+ }
+
+ /**
+ * Set temporary storage directory
+ *
+ * @deprecated
+ * @param string $pValue Temporary storage directory
+ * @throws PHPExcel_Writer_Exception when directory does not exist
+ * @return PHPExcel_Writer_Excel5
+ */
+ public function setTempDir($pValue = '')
+ {
+ return $this;
+ }
+
+ /**
+ * Build the Worksheet Escher objects
+ *
+ */
+ private function buildWorksheetEschers()
+ {
+ // 1-based index to BstoreContainer
+ $blipIndex = 0;
+ $lastReducedSpId = 0;
+ $lastSpId = 0;
+
+ foreach ($this->phpExcel->getAllsheets() as $sheet) {
+ // sheet index
+ $sheetIndex = $sheet->getParent()->getIndex($sheet);
+
+ $escher = null;
+
+ // check if there are any shapes for this sheet
+ $filterRange = $sheet->getAutoFilter()->getRange();
+ if (count($sheet->getDrawingCollection()) == 0 && empty($filterRange)) {
+ continue;
+ }
+
+ // create intermediate Escher object
+ $escher = new PHPExcel_Shared_Escher();
+
+ // dgContainer
+ $dgContainer = new PHPExcel_Shared_Escher_DgContainer();
+
+ // set the drawing index (we use sheet index + 1)
+ $dgId = $sheet->getParent()->getIndex($sheet) + 1;
+ $dgContainer->setDgId($dgId);
+ $escher->setDgContainer($dgContainer);
+
+ // spgrContainer
+ $spgrContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer();
+ $dgContainer->setSpgrContainer($spgrContainer);
+
+ // add one shape which is the group shape
+ $spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
+ $spContainer->setSpgr(true);
+ $spContainer->setSpType(0);
+ $spContainer->setSpId(($sheet->getParent()->getIndex($sheet) + 1) << 10);
+ $spgrContainer->addChild($spContainer);
+
+ // add the shapes
+
+ $countShapes[$sheetIndex] = 0; // count number of shapes (minus group shape), in sheet
+
+ foreach ($sheet->getDrawingCollection() as $drawing) {
+ ++$blipIndex;
+
+ ++$countShapes[$sheetIndex];
+
+ // add the shape
+ $spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
+
+ // set the shape type
+ $spContainer->setSpType(0x004B);
+ // set the shape flag
+ $spContainer->setSpFlag(0x02);
+
+ // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
+ $reducedSpId = $countShapes[$sheetIndex];
+ $spId = $reducedSpId
+ | ($sheet->getParent()->getIndex($sheet) + 1) << 10;
+ $spContainer->setSpId($spId);
+
+ // keep track of last reducedSpId
+ $lastReducedSpId = $reducedSpId;
+
+ // keep track of last spId
+ $lastSpId = $spId;
+
+ // set the BLIP index
+ $spContainer->setOPT(0x4104, $blipIndex);
+
+ // set coordinates and offsets, client anchor
+ $coordinates = $drawing->getCoordinates();
+ $offsetX = $drawing->getOffsetX();
+ $offsetY = $drawing->getOffsetY();
+ $width = $drawing->getWidth();
+ $height = $drawing->getHeight();
+
+ $twoAnchor = PHPExcel_Shared_Excel5::oneAnchor2twoAnchor($sheet, $coordinates, $offsetX, $offsetY, $width, $height);
+
+ $spContainer->setStartCoordinates($twoAnchor['startCoordinates']);
+ $spContainer->setStartOffsetX($twoAnchor['startOffsetX']);
+ $spContainer->setStartOffsetY($twoAnchor['startOffsetY']);
+ $spContainer->setEndCoordinates($twoAnchor['endCoordinates']);
+ $spContainer->setEndOffsetX($twoAnchor['endOffsetX']);
+ $spContainer->setEndOffsetY($twoAnchor['endOffsetY']);
+
+ $spgrContainer->addChild($spContainer);
+ }
+
+ // AutoFilters
+ if (!empty($filterRange)) {
+ $rangeBounds = PHPExcel_Cell::rangeBoundaries($filterRange);
+ $iNumColStart = $rangeBounds[0][0];
+ $iNumColEnd = $rangeBounds[1][0];
+
+ $iInc = $iNumColStart;
+ while ($iInc <= $iNumColEnd) {
+ ++$countShapes[$sheetIndex];
+
+ // create an Drawing Object for the dropdown
+ $oDrawing = new PHPExcel_Worksheet_BaseDrawing();
+ // get the coordinates of drawing
+ $cDrawing = PHPExcel_Cell::stringFromColumnIndex($iInc - 1) . $rangeBounds[0][1];
+ $oDrawing->setCoordinates($cDrawing);
+ $oDrawing->setWorksheet($sheet);
+
+ // add the shape
+ $spContainer = new PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer();
+ // set the shape type
+ $spContainer->setSpType(0x00C9);
+ // set the shape flag
+ $spContainer->setSpFlag(0x01);
+
+ // set the shape index (we combine 1-based sheet index and $countShapes to create unique shape index)
+ $reducedSpId = $countShapes[$sheetIndex];
+ $spId = $reducedSpId
+ | ($sheet->getParent()->getIndex($sheet) + 1) << 10;
+ $spContainer->setSpId($spId);
+
+ // keep track of last reducedSpId
+ $lastReducedSpId = $reducedSpId;
+
+ // keep track of last spId
+ $lastSpId = $spId;
+
+ $spContainer->setOPT(0x007F, 0x01040104); // Protection -> fLockAgainstGrouping
+ $spContainer->setOPT(0x00BF, 0x00080008); // Text -> fFitTextToShape
+ $spContainer->setOPT(0x01BF, 0x00010000); // Fill Style -> fNoFillHitTest
+ $spContainer->setOPT(0x01FF, 0x00080000); // Line Style -> fNoLineDrawDash
+ $spContainer->setOPT(0x03BF, 0x000A0000); // Group Shape -> fPrint
+
+ // set coordinates and offsets, client anchor
+ $endCoordinates = PHPExcel_Cell::stringFromColumnIndex(PHPExcel_Cell::stringFromColumnIndex($iInc - 1));
+ $endCoordinates .= $rangeBounds[0][1] + 1;
+
+ $spContainer->setStartCoordinates($cDrawing);
+ $spContainer->setStartOffsetX(0);
+ $spContainer->setStartOffsetY(0);
+ $spContainer->setEndCoordinates($endCoordinates);
+ $spContainer->setEndOffsetX(0);
+ $spContainer->setEndOffsetY(0);
+
+ $spgrContainer->addChild($spContainer);
+ $iInc++;
+ }
+ }
+
+ // identifier clusters, used for workbook Escher object
+ $this->IDCLs[$dgId] = $lastReducedSpId;
+
+ // set last shape index
+ $dgContainer->setLastSpId($lastSpId);
+
+ // set the Escher object
+ $this->writerWorksheets[$sheetIndex]->setEscher($escher);
+ }
+ }
+
+ /**
+ * Build the Escher object corresponding to the MSODRAWINGGROUP record
+ */
+ private function buildWorkbookEscher()
+ {
+ $escher = null;
+
+ // any drawings in this workbook?
+ $found = false;
+ foreach ($this->phpExcel->getAllSheets() as $sheet) {
+ if (count($sheet->getDrawingCollection()) > 0) {
+ $found = true;
+ break;
+ }
+ }
+
+ // nothing to do if there are no drawings
+ if (!$found) {
+ return;
+ }
+
+ // if we reach here, then there are drawings in the workbook
+ $escher = new PHPExcel_Shared_Escher();
+
+ // dggContainer
+ $dggContainer = new PHPExcel_Shared_Escher_DggContainer();
+ $escher->setDggContainer($dggContainer);
+
+ // set IDCLs (identifier clusters)
+ $dggContainer->setIDCLs($this->IDCLs);
+
+ // this loop is for determining maximum shape identifier of all drawing
+ $spIdMax = 0;
+ $totalCountShapes = 0;
+ $countDrawings = 0;
+
+ foreach ($this->phpExcel->getAllsheets() as $sheet) {
+ $sheetCountShapes = 0; // count number of shapes (minus group shape), in sheet
+
+ if (count($sheet->getDrawingCollection()) > 0) {
+ ++$countDrawings;
+
+ foreach ($sheet->getDrawingCollection() as $drawing) {
+ ++$sheetCountShapes;
+ ++$totalCountShapes;
+
+ $spId = $sheetCountShapes | ($this->phpExcel->getIndex($sheet) + 1) << 10;
+ $spIdMax = max($spId, $spIdMax);
+ }
+ }
+ }
+
+ $dggContainer->setSpIdMax($spIdMax + 1);
+ $dggContainer->setCDgSaved($countDrawings);
+ $dggContainer->setCSpSaved($totalCountShapes + $countDrawings); // total number of shapes incl. one group shapes per drawing
+
+ // bstoreContainer
+ $bstoreContainer = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer();
+ $dggContainer->setBstoreContainer($bstoreContainer);
+
+ // the BSE's (all the images)
+ foreach ($this->phpExcel->getAllsheets() as $sheet) {
+ foreach ($sheet->getDrawingCollection() as $drawing) {
+ if ($drawing instanceof PHPExcel_Worksheet_Drawing) {
+ $filename = $drawing->getPath();
+
+ list($imagesx, $imagesy, $imageFormat) = getimagesize($filename);
+
+ switch ($imageFormat) {
+ case 1: // GIF, not supported by BIFF8, we convert to PNG
+ $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
+ ob_start();
+ imagepng(imagecreatefromgif($filename));
+ $blipData = ob_get_contents();
+ ob_end_clean();
+ break;
+ case 2: // JPEG
+ $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG;
+ $blipData = file_get_contents($filename);
+ break;
+ case 3: // PNG
+ $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
+ $blipData = file_get_contents($filename);
+ break;
+ case 6: // Windows DIB (BMP), we convert to PNG
+ $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
+ ob_start();
+ imagepng(PHPExcel_Shared_Drawing::imagecreatefrombmp($filename));
+ $blipData = ob_get_contents();
+ ob_end_clean();
+ break;
+ default:
+ continue 2;
+ }
+
+ $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
+ $blip->setData($blipData);
+
+ $BSE = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
+ $BSE->setBlipType($blipType);
+ $BSE->setBlip($blip);
+
+ $bstoreContainer->addBSE($BSE);
+ } elseif ($drawing instanceof PHPExcel_Worksheet_MemoryDrawing) {
+ switch ($drawing->getRenderingFunction()) {
+ case PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG:
+ $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG;
+ $renderingFunction = 'imagejpeg';
+ break;
+ case PHPExcel_Worksheet_MemoryDrawing::RENDERING_GIF:
+ case PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG:
+ case PHPExcel_Worksheet_MemoryDrawing::RENDERING_DEFAULT:
+ $blipType = PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG;
+ $renderingFunction = 'imagepng';
+ break;
+ }
+
+ ob_start();
+ call_user_func($renderingFunction, $drawing->getImageResource());
+ $blipData = ob_get_contents();
+ ob_end_clean();
+
+ $blip = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE_Blip();
+ $blip->setData($blipData);
+
+ $BSE = new PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE();
+ $BSE->setBlipType($blipType);
+ $BSE->setBlip($blip);
+
+ $bstoreContainer->addBSE($BSE);
+ }
+ }
+ }
+
+ // Set the Escher object
+ $this->writerWorkbook->setEscher($escher);
+ }
+
+ /**
+ * Build the OLE Part for DocumentSummary Information
+ * @return string
+ */
+ private function writeDocumentSummaryInformation()
+ {
+ // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
+ $data = pack('v', 0xFFFE);
+ // offset: 2; size: 2;
+ $data .= pack('v', 0x0000);
+ // offset: 4; size: 2; OS version
+ $data .= pack('v', 0x0106);
+ // offset: 6; size: 2; OS indicator
+ $data .= pack('v', 0x0002);
+ // offset: 8; size: 16
+ $data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00);
+ // offset: 24; size: 4; section count
+ $data .= pack('V', 0x0001);
+
+ // offset: 28; size: 16; first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
+ $data .= pack('vvvvvvvv', 0xD502, 0xD5CD, 0x2E9C, 0x101B, 0x9793, 0x0008, 0x2C2B, 0xAEF9);
+ // offset: 44; size: 4; offset of the start
+ $data .= pack('V', 0x30);
+
+ // SECTION
+ $dataSection = array();
+ $dataSection_NumProps = 0;
+ $dataSection_Summary = '';
+ $dataSection_Content = '';
+
+ // GKPIDDSI_CODEPAGE: CodePage
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x01),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x02), // 2 byte signed integer
+ 'data' => array('data' => 1252));
+ $dataSection_NumProps++;
+
+ // GKPIDDSI_CATEGORY : Category
+ if ($this->phpExcel->getProperties()->getCategory()) {
+ $dataProp = $this->phpExcel->getProperties()->getCategory();
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x02),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x1E),
+ 'data' => array('data' => $dataProp, 'length' => strlen($dataProp)));
+ $dataSection_NumProps++;
+ }
+ // GKPIDDSI_VERSION :Version of the application that wrote the property storage
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x17),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x03),
+ 'data' => array('pack' => 'V', 'data' => 0x000C0000));
+ $dataSection_NumProps++;
+ // GKPIDDSI_SCALE : FALSE
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0B),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x0B),
+ 'data' => array('data' => false));
+ $dataSection_NumProps++;
+ // GKPIDDSI_LINKSDIRTY : True if any of the values for the linked properties have changed outside of the application
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x10),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x0B),
+ 'data' => array('data' => false));
+ $dataSection_NumProps++;
+ // GKPIDDSI_SHAREDOC : FALSE
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x13),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x0B),
+ 'data' => array('data' => false));
+ $dataSection_NumProps++;
+ // GKPIDDSI_HYPERLINKSCHANGED : True if any of the values for the _PID_LINKS (hyperlink text) have changed outside of the application
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x16),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x0B),
+ 'data' => array('data' => false));
+ $dataSection_NumProps++;
+
+ // GKPIDDSI_DOCSPARTS
+ // MS-OSHARED p75 (2.3.3.2.2.1)
+ // Structure is VtVecUnalignedLpstrValue (2.3.3.1.9)
+ // cElements
+ $dataProp = pack('v', 0x0001);
+ $dataProp .= pack('v', 0x0000);
+ // array of UnalignedLpstr
+ // cch
+ $dataProp .= pack('v', 0x000A);
+ $dataProp .= pack('v', 0x0000);
+ // value
+ $dataProp .= 'Worksheet'.chr(0);
+
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0D),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x101E),
+ 'data' => array('data' => $dataProp, 'length' => strlen($dataProp)));
+ $dataSection_NumProps++;
+
+ // GKPIDDSI_HEADINGPAIR
+ // VtVecHeadingPairValue
+ // cElements
+ $dataProp = pack('v', 0x0002);
+ $dataProp .= pack('v', 0x0000);
+ // Array of vtHeadingPair
+ // vtUnalignedString - headingString
+ // stringType
+ $dataProp .= pack('v', 0x001E);
+ // padding
+ $dataProp .= pack('v', 0x0000);
+ // UnalignedLpstr
+ // cch
+ $dataProp .= pack('v', 0x0013);
+ $dataProp .= pack('v', 0x0000);
+ // value
+ $dataProp .= 'Feuilles de calcul';
+ // vtUnalignedString - headingParts
+ // wType : 0x0003 = 32 bit signed integer
+ $dataProp .= pack('v', 0x0300);
+ // padding
+ $dataProp .= pack('v', 0x0000);
+ // value
+ $dataProp .= pack('v', 0x0100);
+ $dataProp .= pack('v', 0x0000);
+ $dataProp .= pack('v', 0x0000);
+ $dataProp .= pack('v', 0x0000);
+
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0C),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x100C),
+ 'data' => array('data' => $dataProp, 'length' => strlen($dataProp)));
+ $dataSection_NumProps++;
+
+ // 4 Section Length
+ // 4 Property count
+ // 8 * $dataSection_NumProps (8 = ID (4) + OffSet(4))
+ $dataSection_Content_Offset = 8 + $dataSection_NumProps * 8;
+ foreach ($dataSection as $dataProp) {
+ // Summary
+ $dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']);
+ // Offset
+ $dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset);
+ // DataType
+ $dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']);
+ // Data
+ if ($dataProp['type']['data'] == 0x02) { // 2 byte signed integer
+ $dataSection_Content .= pack('V', $dataProp['data']['data']);
+
+ $dataSection_Content_Offset += 4 + 4;
+ } elseif ($dataProp['type']['data'] == 0x03) { // 4 byte signed integer
+ $dataSection_Content .= pack('V', $dataProp['data']['data']);
+
+ $dataSection_Content_Offset += 4 + 4;
+ } elseif ($dataProp['type']['data'] == 0x0B) { // Boolean
+ if ($dataProp['data']['data'] == false) {
+ $dataSection_Content .= pack('V', 0x0000);
+ } else {
+ $dataSection_Content .= pack('V', 0x0001);
+ }
+ $dataSection_Content_Offset += 4 + 4;
+ } elseif ($dataProp['type']['data'] == 0x1E) { // null-terminated string prepended by dword string length
+ // Null-terminated string
+ $dataProp['data']['data'] .= chr(0);
+ $dataProp['data']['length'] += 1;
+ // Complete the string with null string for being a %4
+ $dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4)==4 ? 0 : (4 - $dataProp['data']['length'] % 4));
+ $dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT);
+
+ $dataSection_Content .= pack('V', $dataProp['data']['length']);
+ $dataSection_Content .= $dataProp['data']['data'];
+
+ $dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']);
+ } elseif ($dataProp['type']['data'] == 0x40) { // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+ $dataSection_Content .= $dataProp['data']['data'];
+
+ $dataSection_Content_Offset += 4 + 8;
+ } else {
+ // Data Type Not Used at the moment
+ $dataSection_Content .= $dataProp['data']['data'];
+
+ $dataSection_Content_Offset += 4 + $dataProp['data']['length'];
+ }
+ }
+ // Now $dataSection_Content_Offset contains the size of the content
+
+ // section header
+ // offset: $secOffset; size: 4; section length
+ // + x Size of the content (summary + content)
+ $data .= pack('V', $dataSection_Content_Offset);
+ // offset: $secOffset+4; size: 4; property count
+ $data .= pack('V', $dataSection_NumProps);
+ // Section Summary
+ $data .= $dataSection_Summary;
+ // Section Content
+ $data .= $dataSection_Content;
+
+ return $data;
+ }
+
+ /**
+ * Build the OLE Part for Summary Information
+ * @return string
+ */
+ private function writeSummaryInformation()
+ {
+ // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
+ $data = pack('v', 0xFFFE);
+ // offset: 2; size: 2;
+ $data .= pack('v', 0x0000);
+ // offset: 4; size: 2; OS version
+ $data .= pack('v', 0x0106);
+ // offset: 6; size: 2; OS indicator
+ $data .= pack('v', 0x0002);
+ // offset: 8; size: 16
+ $data .= pack('VVVV', 0x00, 0x00, 0x00, 0x00);
+ // offset: 24; size: 4; section count
+ $data .= pack('V', 0x0001);
+
+ // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
+ $data .= pack('vvvvvvvv', 0x85E0, 0xF29F, 0x4FF9, 0x1068, 0x91AB, 0x0008, 0x272B, 0xD9B3);
+ // offset: 44; size: 4; offset of the start
+ $data .= pack('V', 0x30);
+
+ // SECTION
+ $dataSection = array();
+ $dataSection_NumProps = 0;
+ $dataSection_Summary = '';
+ $dataSection_Content = '';
+
+ // CodePage : CP-1252
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x01),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x02), // 2 byte signed integer
+ 'data' => array('data' => 1252));
+ $dataSection_NumProps++;
+
+ // Title
+ if ($this->phpExcel->getProperties()->getTitle()) {
+ $dataProp = $this->phpExcel->getProperties()->getTitle();
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x02),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
+ 'data' => array('data' => $dataProp, 'length' => strlen($dataProp)));
+ $dataSection_NumProps++;
+ }
+ // Subject
+ if ($this->phpExcel->getProperties()->getSubject()) {
+ $dataProp = $this->phpExcel->getProperties()->getSubject();
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x03),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
+ 'data' => array('data' => $dataProp, 'length' => strlen($dataProp)));
+ $dataSection_NumProps++;
+ }
+ // Author (Creator)
+ if ($this->phpExcel->getProperties()->getCreator()) {
+ $dataProp = $this->phpExcel->getProperties()->getCreator();
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x04),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
+ 'data' => array('data' => $dataProp, 'length' => strlen($dataProp)));
+ $dataSection_NumProps++;
+ }
+ // Keywords
+ if ($this->phpExcel->getProperties()->getKeywords()) {
+ $dataProp = $this->phpExcel->getProperties()->getKeywords();
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x05),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
+ 'data' => array('data' => $dataProp, 'length' => strlen($dataProp)));
+ $dataSection_NumProps++;
+ }
+ // Comments (Description)
+ if ($this->phpExcel->getProperties()->getDescription()) {
+ $dataProp = $this->phpExcel->getProperties()->getDescription();
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x06),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
+ 'data' => array('data' => $dataProp, 'length' => strlen($dataProp)));
+ $dataSection_NumProps++;
+ }
+ // Last Saved By (LastModifiedBy)
+ if ($this->phpExcel->getProperties()->getLastModifiedBy()) {
+ $dataProp = $this->phpExcel->getProperties()->getLastModifiedBy();
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x08),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x1E), // null-terminated string prepended by dword string length
+ 'data' => array('data' => $dataProp, 'length' => strlen($dataProp)));
+ $dataSection_NumProps++;
+ }
+ // Created Date/Time
+ if ($this->phpExcel->getProperties()->getCreated()) {
+ $dataProp = $this->phpExcel->getProperties()->getCreated();
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0C),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x40), // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+ 'data' => array('data' => PHPExcel_Shared_OLE::LocalDate2OLE($dataProp)));
+ $dataSection_NumProps++;
+ }
+ // Modified Date/Time
+ if ($this->phpExcel->getProperties()->getModified()) {
+ $dataProp = $this->phpExcel->getProperties()->getModified();
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x0D),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x40), // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+ 'data' => array('data' => PHPExcel_Shared_OLE::LocalDate2OLE($dataProp)));
+ $dataSection_NumProps++;
+ }
+ // Security
+ $dataSection[] = array('summary'=> array('pack' => 'V', 'data' => 0x13),
+ 'offset' => array('pack' => 'V'),
+ 'type' => array('pack' => 'V', 'data' => 0x03), // 4 byte signed integer
+ 'data' => array('data' => 0x00));
+ $dataSection_NumProps++;
+
+
+ // 4 Section Length
+ // 4 Property count
+ // 8 * $dataSection_NumProps (8 = ID (4) + OffSet(4))
+ $dataSection_Content_Offset = 8 + $dataSection_NumProps * 8;
+ foreach ($dataSection as $dataProp) {
+ // Summary
+ $dataSection_Summary .= pack($dataProp['summary']['pack'], $dataProp['summary']['data']);
+ // Offset
+ $dataSection_Summary .= pack($dataProp['offset']['pack'], $dataSection_Content_Offset);
+ // DataType
+ $dataSection_Content .= pack($dataProp['type']['pack'], $dataProp['type']['data']);
+ // Data
+ if ($dataProp['type']['data'] == 0x02) { // 2 byte signed integer
+ $dataSection_Content .= pack('V', $dataProp['data']['data']);
+
+ $dataSection_Content_Offset += 4 + 4;
+ } elseif ($dataProp['type']['data'] == 0x03) { // 4 byte signed integer
+ $dataSection_Content .= pack('V', $dataProp['data']['data']);
+
+ $dataSection_Content_Offset += 4 + 4;
+ } elseif ($dataProp['type']['data'] == 0x1E) { // null-terminated string prepended by dword string length
+ // Null-terminated string
+ $dataProp['data']['data'] .= chr(0);
+ $dataProp['data']['length'] += 1;
+ // Complete the string with null string for being a %4
+ $dataProp['data']['length'] = $dataProp['data']['length'] + ((4 - $dataProp['data']['length'] % 4)==4 ? 0 : (4 - $dataProp['data']['length'] % 4));
+ $dataProp['data']['data'] = str_pad($dataProp['data']['data'], $dataProp['data']['length'], chr(0), STR_PAD_RIGHT);
+
+ $dataSection_Content .= pack('V', $dataProp['data']['length']);
+ $dataSection_Content .= $dataProp['data']['data'];
+
+ $dataSection_Content_Offset += 4 + 4 + strlen($dataProp['data']['data']);
+ } elseif ($dataProp['type']['data'] == 0x40) { // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
+ $dataSection_Content .= $dataProp['data']['data'];
+
+ $dataSection_Content_Offset += 4 + 8;
+ } else {
+ // Data Type Not Used at the moment
+ }
+ }
+ // Now $dataSection_Content_Offset contains the size of the content
+
+ // section header
+ // offset: $secOffset; size: 4; section length
+ // + x Size of the content (summary + content)
+ $data .= pack('V', $dataSection_Content_Offset);
+ // offset: $secOffset+4; size: 4; property count
+ $data .= pack('V', $dataSection_NumProps);
+ // Section Summary
+ $data .= $dataSection_Summary;
+ // Section Content
+ $data .= $dataSection_Content;
+
+ return $data;
+ }
+}
diff --git a/PHPExcel/Writer/Excel5/BIFFwriter.php b/PHPExcel/Writer/Excel5/BIFFwriter.php
new file mode 100644
index 0000000..a795752
--- /dev/null
+++ b/PHPExcel/Writer/Excel5/BIFFwriter.php
@@ -0,0 +1,246 @@
+
+// *
+// * 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
+// * ';
+ for ($col = 'A'; $col != $colMax; ++$col) {
+ $html .= ' ';
+ }
+ return $html;
+ }
+
+
+ /**
+ * Generate image tag in cell
+ *
+ * @param PHPExcel_Worksheet $pSheet PHPExcel_Worksheet
+ * @param string $coordinates Cell coordinates
+ * @return string
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeImageInCell(PHPExcel_Worksheet $pSheet, $coordinates)
+ {
+ // Construct HTML
+ $html = '';
+
+ // Write images
+ foreach ($pSheet->getDrawingCollection() as $drawing) {
+ if ($drawing instanceof PHPExcel_Worksheet_Drawing) {
+ if ($drawing->getCoordinates() == $coordinates) {
+ $filename = $drawing->getPath();
+
+ // Strip off eventual '.'
+ if (substr($filename, 0, 1) == '.') {
+ $filename = substr($filename, 1);
+ }
+
+ // Prepend images root
+ $filename = $this->getImagesRoot() . $filename;
+
+ // Strip off eventual '.'
+ if (substr($filename, 0, 1) == '.' && substr($filename, 0, 2) != './') {
+ $filename = substr($filename, 1);
+ }
+
+ // Convert UTF8 data to PCDATA
+ $filename = htmlspecialchars($filename);
+
+ $html .= PHP_EOL;
+ if ((!$this->embedImages) || ($this->isPdf)) {
+ $imageData = $filename;
+ } else {
+ $imageDetails = getimagesize($filename);
+ if ($fp = fopen($filename, "rb", 0)) {
+ $picture = fread($fp, filesize($filename));
+ fclose($fp);
+ // base64 encode the binary data, then break it
+ // into chunks according to RFC 2045 semantics
+ $base64 = chunk_split(base64_encode($picture));
+ $imageData = 'data:'.$imageDetails['mime'].';base64,' . $base64;
+ } else {
+ $imageData = $filename;
+ }
+ }
+
+ $html .= '';
+ $html .= $this->writeImageInCell($pSheet, $col.$row);
+ if ($this->includeCharts) {
+ $html .= $this->writeChartInCell($pSheet, $col.$row);
+ }
+ $html .= ' ';
+ }
+ ++$row;
+ $html .= '' . PHP_EOL;
+ } else {
+ $style = isset($this->cssStyles['table']) ?
+ $this->assembleCSS($this->cssStyles['table']) : '';
+
+ if ($this->isPdf && $pSheet->getShowGridlines()) {
+ $html .= '
' . PHP_EOL;
+ } else {
+ $html .= '
+ $html .= $this->generateTableFooter();
+
+ // insert page break
+ $html .= '';
+
+ // open table again: ' . PHP_EOL;
+ }
+ }
+
+ // Write
' . PHP_EOL;
+
+ return $html;
+ }
+
+ /**
+ * Generate row
+ *
+ * @param PHPExcel_Worksheet $pSheet PHPExcel_Worksheet
+ * @param array $pValues Array containing cells in a row
+ * @param int $pRow Row number (0-based)
+ * @return string
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function generateRow(PHPExcel_Worksheet $pSheet, $pValues = null, $pRow = 0, $cellType = 'td')
+ {
+ if (is_array($pValues)) {
+ // Construct HTML
+ $html = '';
+
+ // Sheet index
+ $sheetIndex = $pSheet->getParent()->getIndex($pSheet);
+
+ // DomPDF and breaks
+ if ($this->isPdf && count($pSheet->getBreaks()) > 0) {
+ $breaks = $pSheet->getBreaks();
+
+ // check if a break is needed before this row
+ if (isset($breaks['A' . $pRow])) {
+ // close table: +
' . PHP_EOL;
+ } else {
+ $style = isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow])
+ ? $this->assembleCSS($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]) : '';
+
+ $html .= ' ' . PHP_EOL;
+ }
+
+ // Write cells
+ $colNum = 0;
+ foreach ($pValues as $cellAddress) {
+ $cell = ($cellAddress > '') ? $pSheet->getCell($cellAddress) : '';
+ $coordinate = PHPExcel_Cell::stringFromColumnIndex($colNum) . ($pRow + 1);
+ if (!$this->useInlineCss) {
+ $cssClass = '';
+ $cssClass = 'column' . $colNum;
+ } else {
+ $cssClass = array();
+ if ($cellType == 'th') {
+ if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum])) {
+ $this->cssStyles['table.sheet' . $sheetIndex . ' th.column' . $colNum];
+ }
+ } else {
+ if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum])) {
+ $this->cssStyles['table.sheet' . $sheetIndex . ' td.column' . $colNum];
+ }
+ }
+ }
+ $colSpan = 1;
+ $rowSpan = 1;
+
+ // initialize
+ $cellData = ' ';
+
+ // PHPExcel_Cell
+ if ($cell instanceof PHPExcel_Cell) {
+ $cellData = '';
+ if (is_null($cell->getParent())) {
+ $cell->attach($pSheet);
+ }
+ // Value
+ if ($cell->getValue() instanceof PHPExcel_RichText) {
+ // Loop through rich text elements
+ $elements = $cell->getValue()->getRichTextElements();
+ foreach ($elements as $element) {
+ // Rich text start?
+ if ($element instanceof PHPExcel_RichText_Run) {
+ $cellData .= '';
+
+ if ($element->getFont()->getSuperScript()) {
+ $cellData .= '';
+ } elseif ($element->getFont()->getSubScript()) {
+ $cellData .= '';
+ }
+ }
+
+ // Convert UTF8 data to PCDATA
+ $cellText = $element->getText();
+ $cellData .= htmlspecialchars($cellText);
+
+ if ($element instanceof PHPExcel_RichText_Run) {
+ if ($element->getFont()->getSuperScript()) {
+ $cellData .= '';
+ } elseif ($element->getFont()->getSubScript()) {
+ $cellData .= '';
+ }
+
+ $cellData .= '';
+ }
+ }
+ } else {
+ if ($this->preCalculateFormulas) {
+ $cellData = PHPExcel_Style_NumberFormat::toFormattedString(
+ $cell->getCalculatedValue(),
+ $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
+ array($this, 'formatColor')
+ );
+ } else {
+ $cellData = PHPExcel_Style_NumberFormat::toFormattedString(
+ $cell->getValue(),
+ $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode(),
+ array($this, 'formatColor')
+ );
+ }
+ $cellData = htmlspecialchars($cellData);
+ if ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSuperScript()) {
+ $cellData = ''.$cellData.'';
+ } elseif ($pSheet->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont()->getSubScript()) {
+ $cellData = ''.$cellData.'';
+ }
+ }
+
+ // Converts the cell content so that spaces occuring at beginning of each new line are replaced by
+ // Example: " Hello\n to the world" is converted to " Hello\n to the world"
+ $cellData = preg_replace("/(?m)(?:^|\\G) /", ' ', $cellData);
+
+ // convert newline "\n" to '
'
+ $cellData = nl2br($cellData);
+
+ // Extend CSS class?
+ if (!$this->useInlineCss) {
+ $cssClass .= ' style' . $cell->getXfIndex();
+ $cssClass .= ' ' . $cell->getDataType();
+ } else {
+ if ($cellType == 'th') {
+ if (isset($this->cssStyles['th.style' . $cell->getXfIndex()])) {
+ $cssClass = array_merge($cssClass, $this->cssStyles['th.style' . $cell->getXfIndex()]);
+ }
+ } else {
+ if (isset($this->cssStyles['td.style' . $cell->getXfIndex()])) {
+ $cssClass = array_merge($cssClass, $this->cssStyles['td.style' . $cell->getXfIndex()]);
+ }
+ }
+
+ // General horizontal alignment: Actual horizontal alignment depends on dataType
+ $sharedStyle = $pSheet->getParent()->getCellXfByIndex($cell->getXfIndex());
+ if ($sharedStyle->getAlignment()->getHorizontal() == PHPExcel_Style_Alignment::HORIZONTAL_GENERAL
+ && isset($this->cssStyles['.' . $cell->getDataType()]['text-align'])) {
+ $cssClass['text-align'] = $this->cssStyles['.' . $cell->getDataType()]['text-align'];
+ }
+ }
+ }
+
+ // Hyperlink?
+ if ($pSheet->hyperlinkExists($coordinate) && !$pSheet->getHyperlink($coordinate)->isInternal()) {
+ $cellData = '' . $cellData . '';
+ }
+
+ // Should the cell be written or is it swallowed by a rowspan or colspan?
+ $writeCell = !(isset($this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])
+ && $this->isSpannedCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum]);
+
+ // Colspan and Rowspan
+ $colspan = 1;
+ $rowspan = 1;
+ if (isset($this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum])) {
+ $spans = $this->isBaseCell[$pSheet->getParent()->getIndex($pSheet)][$pRow + 1][$colNum];
+ $rowSpan = $spans['rowspan'];
+ $colSpan = $spans['colspan'];
+
+ // Also apply style from last cell in merge to fix borders -
+ // relies on !important for non-none border declarations in createCSSStyleBorder
+ $endCellCoord = PHPExcel_Cell::stringFromColumnIndex($colNum + $colSpan - 1) . ($pRow + $rowSpan);
+ if (!$this->useInlineCss) {
+ $cssClass .= ' style' . $pSheet->getCell($endCellCoord)->getXfIndex();
+ }
+ }
+
+ // Write
+ if ($writeCell) {
+ // Column start
+ $html .= ' <' . $cellType;
+ if (!$this->useInlineCss) {
+ $html .= ' class="' . $cssClass . '"';
+ } else {
+ //** Necessary redundant code for the sake of PHPExcel_Writer_PDF **
+ // We must explicitly write the width of the element because TCPDF
+ // does not recognize e.g. element because TCPDF
+ // does not recognize e.g.
+ if (isset($this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'])) {
+ $height = $this->cssStyles['table.sheet' . $sheetIndex . ' tr.row' . $pRow]['height'];
+ $cssClass['height'] = $height;
+ }
+ //** end of redundant code **
+
+ $html .= ' style="' . $this->assembleCSS($cssClass) . '"';
+ }
+ if ($colSpan > 1) {
+ $html .= ' colspan="' . $colSpan . '"';
+ }
+ if ($rowSpan > 1) {
+ $html .= ' rowspan="' . $rowSpan . '"';
+ }
+ $html .= '>';
+
+ // Image?
+ $html .= $this->writeImageInCell($pSheet, $coordinate);
+
+ // Chart?
+ if ($this->includeCharts) {
+ $html .= $this->writeChartInCell($pSheet, $coordinate);
+ }
+
+ // Cell data
+ $html .= $cellData;
+
+ // Column end
+ $html .= ''.$cellType.'>' . PHP_EOL;
+ }
+
+ // Next column
+ ++$colNum;
+ }
+
+ // Write row end
+ $html .= ' ' . PHP_EOL;
+
+ // Return
+ return $html;
+ } else {
+ throw new PHPExcel_Writer_Exception("Invalid parameters passed.");
+ }
+ }
+
+ /**
+ * Takes array where of CSS properties / values and converts to CSS string
+ *
+ * @param array
+ * @return string
+ */
+ private function assembleCSS($pValue = array())
+ {
+ $pairs = array();
+ foreach ($pValue as $property => $value) {
+ $pairs[] = $property . ':' . $value;
+ }
+ $string = implode('; ', $pairs);
+
+ return $string;
+ }
+
+ /**
+ * Get images root
+ *
+ * @return string
+ */
+ public function getImagesRoot()
+ {
+ return $this->imagesRoot;
+ }
+
+ /**
+ * Set images root
+ *
+ * @param string $pValue
+ * @return PHPExcel_Writer_HTML
+ */
+ public function setImagesRoot($pValue = '.')
+ {
+ $this->imagesRoot = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get embed images
+ *
+ * @return boolean
+ */
+ public function getEmbedImages()
+ {
+ return $this->embedImages;
+ }
+
+ /**
+ * Set embed images
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Writer_HTML
+ */
+ public function setEmbedImages($pValue = '.')
+ {
+ $this->embedImages = $pValue;
+ return $this;
+ }
+
+ /**
+ * Get use inline CSS?
+ *
+ * @return boolean
+ */
+ public function getUseInlineCss()
+ {
+ return $this->useInlineCss;
+ }
+
+ /**
+ * Set use inline CSS?
+ *
+ * @param boolean $pValue
+ * @return PHPExcel_Writer_HTML
+ */
+ public function setUseInlineCss($pValue = false)
+ {
+ $this->useInlineCss = $pValue;
+ return $this;
+ }
+
+ /**
+ * Add color to formatted string as inline style
+ *
+ * @param string $pValue Plain formatted value without color
+ * @param string $pFormat Format code
+ * @return string
+ */
+ public function formatColor($pValue, $pFormat)
+ {
+ // Color information, e.g. [Red] is always at the beginning
+ $color = null; // initialize
+ $matches = array();
+
+ $color_regex = '/^\\[[a-zA-Z]+\\]/';
+ if (preg_match($color_regex, $pFormat, $matches)) {
+ $color = str_replace('[', '', $matches[0]);
+ $color = str_replace(']', '', $color);
+ $color = strtolower($color);
+ }
+
+ // convert to PCDATA
+ $value = htmlspecialchars($pValue);
+
+ // color span tag
+ if ($color !== null) {
+ $value = '' . $value . '';
+ }
+
+ return $value;
+ }
+
+ /**
+ * Calculate information about HTML colspan and rowspan which is not always the same as Excel's
+ */
+ private function calculateSpans()
+ {
+ // Identify all cells that should be omitted in HTML due to cell merge.
+ // In HTML only the upper-left cell should be written and it should have
+ // appropriate rowspan / colspan attribute
+ $sheetIndexes = $this->sheetIndex !== null ?
+ array($this->sheetIndex) : range(0, $this->phpExcel->getSheetCount() - 1);
+
+ foreach ($sheetIndexes as $sheetIndex) {
+ $sheet = $this->phpExcel->getSheet($sheetIndex);
+
+ $candidateSpannedRow = array();
+
+ // loop through all Excel merged cells
+ foreach ($sheet->getMergeCells() as $cells) {
+ list($cells,) = PHPExcel_Cell::splitRange($cells);
+ $first = $cells[0];
+ $last = $cells[1];
+
+ list($fc, $fr) = PHPExcel_Cell::coordinateFromString($first);
+ $fc = PHPExcel_Cell::columnIndexFromString($fc) - 1;
+
+ list($lc, $lr) = PHPExcel_Cell::coordinateFromString($last);
+ $lc = PHPExcel_Cell::columnIndexFromString($lc) - 1;
+
+ // loop through the individual cells in the individual merge
+ $r = $fr - 1;
+ while ($r++ < $lr) {
+ // also, flag this row as a HTML row that is candidate to be omitted
+ $candidateSpannedRow[$r] = $r;
+
+ $c = $fc - 1;
+ while ($c++ < $lc) {
+ if (!($c == $fc && $r == $fr)) {
+ // not the upper-left cell (should not be written in HTML)
+ $this->isSpannedCell[$sheetIndex][$r][$c] = array(
+ 'baseCell' => array($fr, $fc),
+ );
+ } else {
+ // upper-left is the base cell that should hold the colspan/rowspan attribute
+ $this->isBaseCell[$sheetIndex][$r][$c] = array(
+ 'xlrowspan' => $lr - $fr + 1, // Excel rowspan
+ 'rowspan' => $lr - $fr + 1, // HTML rowspan, value may change
+ 'xlcolspan' => $lc - $fc + 1, // Excel colspan
+ 'colspan' => $lc - $fc + 1, // HTML colspan, value may change
+ );
+ }
+ }
+ }
+ }
+
+ // Identify which rows should be omitted in HTML. These are the rows where all the cells
+ // participate in a merge and the where base cells are somewhere above.
+ $countColumns = PHPExcel_Cell::columnIndexFromString($sheet->getHighestColumn());
+ foreach ($candidateSpannedRow as $rowIndex) {
+ if (isset($this->isSpannedCell[$sheetIndex][$rowIndex])) {
+ if (count($this->isSpannedCell[$sheetIndex][$rowIndex]) == $countColumns) {
+ $this->isSpannedRow[$sheetIndex][$rowIndex] = $rowIndex;
+ };
+ }
+ }
+
+ // For each of the omitted rows we found above, the affected rowspans should be subtracted by 1
+ if (isset($this->isSpannedRow[$sheetIndex])) {
+ foreach ($this->isSpannedRow[$sheetIndex] as $rowIndex) {
+ $adjustedBaseCells = array();
+ $c = -1;
+ $e = $countColumns - 1;
+ while ($c++ < $e) {
+ $baseCell = $this->isSpannedCell[$sheetIndex][$rowIndex][$c]['baseCell'];
+
+ if (!in_array($baseCell, $adjustedBaseCells)) {
+ // subtract rowspan by 1
+ --$this->isBaseCell[$sheetIndex][ $baseCell[0] ][ $baseCell[1] ]['rowspan'];
+ $adjustedBaseCells[] = $baseCell;
+ }
+ }
+ }
+ }
+
+ // TODO: Same for columns
+ }
+
+ // We have calculated the spans
+ $this->spansAreCalculated = true;
+ }
+
+ private function setMargins(PHPExcel_Worksheet $pSheet)
+ {
+ $htmlPage = '@page { ';
+ $htmlBody = 'body { ';
+
+ $left = PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getLeft()) . 'in; ';
+ $htmlPage .= 'margin-left: ' . $left;
+ $htmlBody .= 'margin-left: ' . $left;
+ $right = PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getRight()) . 'in; ';
+ $htmlPage .= 'margin-right: ' . $right;
+ $htmlBody .= 'margin-right: ' . $right;
+ $top = PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getTop()) . 'in; ';
+ $htmlPage .= 'margin-top: ' . $top;
+ $htmlBody .= 'margin-top: ' . $top;
+ $bottom = PHPExcel_Shared_String::FormatNumber($pSheet->getPageMargins()->getBottom()) . 'in; ';
+ $htmlPage .= 'margin-bottom: ' . $bottom;
+ $htmlBody .= 'margin-bottom: ' . $bottom;
+
+ $htmlPage .= "}\n";
+ $htmlBody .= "}\n";
+
+ return "\n";
+ }
+}
diff --git a/PHPExcel/Writer/IWriter.php b/PHPExcel/Writer/IWriter.php
new file mode 100644
index 0000000..c7cfe74
--- /dev/null
+++ b/PHPExcel/Writer/IWriter.php
@@ -0,0 +1,37 @@
+setPHPExcel($pPHPExcel);
+
+ $writerPartsArray = array(
+ 'content' => 'PHPExcel_Writer_OpenDocument_Content',
+ 'meta' => 'PHPExcel_Writer_OpenDocument_Meta',
+ 'meta_inf' => 'PHPExcel_Writer_OpenDocument_MetaInf',
+ 'mimetype' => 'PHPExcel_Writer_OpenDocument_Mimetype',
+ 'settings' => 'PHPExcel_Writer_OpenDocument_Settings',
+ 'styles' => 'PHPExcel_Writer_OpenDocument_Styles',
+ 'thumbnails' => 'PHPExcel_Writer_OpenDocument_Thumbnails'
+ );
+
+ foreach ($writerPartsArray as $writer => $class) {
+ $this->writerParts[$writer] = new $class($this);
+ }
+ }
+
+ /**
+ * Get writer part
+ *
+ * @param string $pPartName Writer part name
+ * @return PHPExcel_Writer_Excel2007_WriterPart
+ */
+ public function getWriterPart($pPartName = '')
+ {
+ if ($pPartName != '' && isset($this->writerParts[strtolower($pPartName)])) {
+ return $this->writerParts[strtolower($pPartName)];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Save PHPExcel to file
+ *
+ * @param string $pFilename
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function save($pFilename = null)
+ {
+ if (!$this->spreadSheet) {
+ throw new PHPExcel_Writer_Exception('PHPExcel object unassigned.');
+ }
+
+ // garbage collect
+ $this->spreadSheet->garbageCollect();
+
+ // If $pFilename is php://output or php://stdout, make it a temporary file...
+ $originalFilename = $pFilename;
+ if (strtolower($pFilename) == 'php://output' || strtolower($pFilename) == 'php://stdout') {
+ $pFilename = @tempnam(PHPExcel_Shared_File::sys_get_temp_dir(), 'phpxltmp');
+ if ($pFilename == '') {
+ $pFilename = $originalFilename;
+ }
+ }
+
+ $objZip = $this->createZip($pFilename);
+
+ $objZip->addFromString('META-INF/manifest.xml', $this->getWriterPart('meta_inf')->writeManifest());
+ $objZip->addFromString('Thumbnails/thumbnail.png', $this->getWriterPart('thumbnails')->writeThumbnail());
+ $objZip->addFromString('content.xml', $this->getWriterPart('content')->write());
+ $objZip->addFromString('meta.xml', $this->getWriterPart('meta')->write());
+ $objZip->addFromString('mimetype', $this->getWriterPart('mimetype')->write());
+ $objZip->addFromString('settings.xml', $this->getWriterPart('settings')->write());
+ $objZip->addFromString('styles.xml', $this->getWriterPart('styles')->write());
+
+ // Close file
+ if ($objZip->close() === false) {
+ throw new PHPExcel_Writer_Exception("Could not close zip file $pFilename.");
+ }
+
+ // If a temporary file was used, copy it to the correct file stream
+ if ($originalFilename != $pFilename) {
+ if (copy($pFilename, $originalFilename) === false) {
+ throw new PHPExcel_Writer_Exception("Could not copy temporary zip file $pFilename to $originalFilename.");
+ }
+ @unlink($pFilename);
+ }
+ }
+
+ /**
+ * Create zip object
+ *
+ * @param string $pFilename
+ * @throws PHPExcel_Writer_Exception
+ * @return ZipArchive
+ */
+ private function createZip($pFilename)
+ {
+ // Create new ZIP file and open it for writing
+ $zipClass = PHPExcel_Settings::getZipClass();
+ $objZip = new $zipClass();
+
+ // Retrieve OVERWRITE and CREATE constants from the instantiated zip class
+ // This method of accessing constant values from a dynamic class should work with all appropriate versions of PHP
+ $ro = new ReflectionObject($objZip);
+ $zipOverWrite = $ro->getConstant('OVERWRITE');
+ $zipCreate = $ro->getConstant('CREATE');
+
+ if (file_exists($pFilename)) {
+ unlink($pFilename);
+ }
+ // Try opening the ZIP file
+ if ($objZip->open($pFilename, $zipOverWrite) !== true) {
+ if ($objZip->open($pFilename, $zipCreate) !== true) {
+ throw new PHPExcel_Writer_Exception("Could not open $pFilename for writing.");
+ }
+ }
+
+ return $objZip;
+ }
+
+ /**
+ * Get PHPExcel object
+ *
+ * @return PHPExcel
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function getPHPExcel()
+ {
+ if ($this->spreadSheet !== null) {
+ return $this->spreadSheet;
+ } else {
+ throw new PHPExcel_Writer_Exception('No PHPExcel assigned.');
+ }
+ }
+
+ /**
+ * Set PHPExcel object
+ *
+ * @param PHPExcel $pPHPExcel PHPExcel object
+ * @throws PHPExcel_Writer_Exception
+ * @return PHPExcel_Writer_Excel2007
+ */
+ public function setPHPExcel(PHPExcel $pPHPExcel = null)
+ {
+ $this->spreadSheet = $pPHPExcel;
+ return $this;
+ }
+}
diff --git a/PHPExcel/Writer/OpenDocument/Cell/Comment.php b/PHPExcel/Writer/OpenDocument/Cell/Comment.php
new file mode 100644
index 0000000..f1e98a1
--- /dev/null
+++ b/PHPExcel/Writer/OpenDocument/Cell/Comment.php
@@ -0,0 +1,63 @@
+
+ */
+class PHPExcel_Writer_OpenDocument_Cell_Comment
+{
+ public static function write(PHPExcel_Shared_XMLWriter $objWriter, PHPExcel_Cell $cell)
+ {
+ $comments = $cell->getWorksheet()->getComments();
+ if (!isset($comments[$cell->getCoordinate()])) {
+ return;
+ }
+ $comment = $comments[$cell->getCoordinate()];
+
+ $objWriter->startElement('office:annotation');
+ //$objWriter->writeAttribute('draw:style-name', 'gr1');
+ //$objWriter->writeAttribute('draw:text-style-name', 'P1');
+ $objWriter->writeAttribute('svg:width', $comment->getWidth());
+ $objWriter->writeAttribute('svg:height', $comment->getHeight());
+ $objWriter->writeAttribute('svg:x', $comment->getMarginLeft());
+ $objWriter->writeAttribute('svg:y', $comment->getMarginTop());
+ //$objWriter->writeAttribute('draw:caption-point-x', $comment->getMarginLeft());
+ //$objWriter->writeAttribute('draw:caption-point-y', $comment->getMarginTop());
+ $objWriter->writeElement('dc:creator', $comment->getAuthor());
+ // TODO: Not realized in PHPExcel_Comment yet.
+ //$objWriter->writeElement('dc:date', $comment->getDate());
+ $objWriter->writeElement('text:p', $comment->getText()->getPlainText());
+ //$objWriter->writeAttribute('draw:text-style-name', 'P1');
+ $objWriter->endElement();
+ }
+}
diff --git a/PHPExcel/Writer/OpenDocument/Content.php b/PHPExcel/Writer/OpenDocument/Content.php
new file mode 100644
index 0000000..a34b167
--- /dev/null
+++ b/PHPExcel/Writer/OpenDocument/Content.php
@@ -0,0 +1,272 @@
+
+ */
+class PHPExcel_Writer_OpenDocument_Content extends PHPExcel_Writer_OpenDocument_WriterPart
+{
+ const NUMBER_COLS_REPEATED_MAX = 1024;
+ const NUMBER_ROWS_REPEATED_MAX = 1048576;
+
+ /**
+ * Write content.xml to XML format
+ *
+ * @param PHPExcel $pPHPExcel
+ * @return string XML Output
+ * @throws PHPExcel_Writer_Exception
+ */
+ public function write(PHPExcel $pPHPExcel = null)
+ {
+ if (!$pPHPExcel) {
+ $pPHPExcel = $this->getParentWriter()->getPHPExcel(); /* @var $pPHPExcel PHPExcel */
+ }
+
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8');
+
+ // Content
+ $objWriter->startElement('office:document-content');
+ $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
+ $objWriter->writeAttribute('xmlns:style', 'urn:oasis:names:tc:opendocument:xmlns:style:1.0');
+ $objWriter->writeAttribute('xmlns:text', 'urn:oasis:names:tc:opendocument:xmlns:text:1.0');
+ $objWriter->writeAttribute('xmlns:table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0');
+ $objWriter->writeAttribute('xmlns:draw', 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0');
+ $objWriter->writeAttribute('xmlns:fo', 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0');
+ $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
+ $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
+ $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0');
+ $objWriter->writeAttribute('xmlns:number', 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0');
+ $objWriter->writeAttribute('xmlns:presentation', 'urn:oasis:names:tc:opendocument:xmlns:presentation:1.0');
+ $objWriter->writeAttribute('xmlns:svg', 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0');
+ $objWriter->writeAttribute('xmlns:chart', 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0');
+ $objWriter->writeAttribute('xmlns:dr3d', 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0');
+ $objWriter->writeAttribute('xmlns:math', 'http://www.w3.org/1998/Math/MathML');
+ $objWriter->writeAttribute('xmlns:form', 'urn:oasis:names:tc:opendocument:xmlns:form:1.0');
+ $objWriter->writeAttribute('xmlns:script', 'urn:oasis:names:tc:opendocument:xmlns:script:1.0');
+ $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office');
+ $objWriter->writeAttribute('xmlns:ooow', 'http://openoffice.org/2004/writer');
+ $objWriter->writeAttribute('xmlns:oooc', 'http://openoffice.org/2004/calc');
+ $objWriter->writeAttribute('xmlns:dom', 'http://www.w3.org/2001/xml-events');
+ $objWriter->writeAttribute('xmlns:xforms', 'http://www.w3.org/2002/xforms');
+ $objWriter->writeAttribute('xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
+ $objWriter->writeAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
+ $objWriter->writeAttribute('xmlns:rpt', 'http://openoffice.org/2005/report');
+ $objWriter->writeAttribute('xmlns:of', 'urn:oasis:names:tc:opendocument:xmlns:of:1.2');
+ $objWriter->writeAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
+ $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#');
+ $objWriter->writeAttribute('xmlns:tableooo', 'http://openoffice.org/2009/table');
+ $objWriter->writeAttribute('xmlns:field', 'urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0');
+ $objWriter->writeAttribute('xmlns:formx', 'urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0');
+ $objWriter->writeAttribute('xmlns:css3t', 'http://www.w3.org/TR/css3-text/');
+ $objWriter->writeAttribute('office:version', '1.2');
+
+ $objWriter->writeElement('office:scripts');
+ $objWriter->writeElement('office:font-face-decls');
+ $objWriter->writeElement('office:automatic-styles');
+
+ $objWriter->startElement('office:body');
+ $objWriter->startElement('office:spreadsheet');
+ $objWriter->writeElement('table:calculation-settings');
+ $this->writeSheets($objWriter);
+ $objWriter->writeElement('table:named-expressions');
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $objWriter->endElement();
+
+ return $objWriter->getData();
+ }
+
+ /**
+ * Write sheets
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter
+ */
+ private function writeSheets(PHPExcel_Shared_XMLWriter $objWriter)
+ {
+ $pPHPExcel = $this->getParentWriter()->getPHPExcel(); /* @var $pPHPExcel PHPExcel */
+
+ $sheet_count = $pPHPExcel->getSheetCount();
+ for ($i = 0; $i < $sheet_count; $i++) {
+ //$this->getWriterPart('Worksheet')->writeWorksheet());
+ $objWriter->startElement('table:table');
+ $objWriter->writeAttribute('table:name', $pPHPExcel->getSheet($i)->getTitle());
+ $objWriter->writeElement('office:forms');
+ $objWriter->startElement('table:table-column');
+ $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
+ $objWriter->endElement();
+ $this->writeRows($objWriter, $pPHPExcel->getSheet($i));
+ $objWriter->endElement();
+ }
+ }
+
+ /**
+ * Write rows of the specified sheet
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter
+ * @param PHPExcel_Worksheet $sheet
+ */
+ private function writeRows(PHPExcel_Shared_XMLWriter $objWriter, PHPExcel_Worksheet $sheet)
+ {
+ $number_rows_repeated = self::NUMBER_ROWS_REPEATED_MAX;
+ $span_row = 0;
+ $rows = $sheet->getRowIterator();
+ while ($rows->valid()) {
+ $number_rows_repeated--;
+ $row = $rows->current();
+ if ($row->getCellIterator()->valid()) {
+ if ($span_row) {
+ $objWriter->startElement('table:table-row');
+ if ($span_row > 1) {
+ $objWriter->writeAttribute('table:number-rows-repeated', $span_row);
+ }
+ $objWriter->startElement('table:table-cell');
+ $objWriter->writeAttribute('table:number-columns-repeated', self::NUMBER_COLS_REPEATED_MAX);
+ $objWriter->endElement();
+ $objWriter->endElement();
+ $span_row = 0;
+ }
+ $objWriter->startElement('table:table-row');
+ $this->writeCells($objWriter, $row);
+ $objWriter->endElement();
+ } else {
+ $span_row++;
+ }
+ $rows->next();
+ }
+ }
+
+ /**
+ * Write cells of the specified row
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter
+ * @param PHPExcel_Worksheet_Row $row
+ * @throws PHPExcel_Writer_Exception
+ */
+ private function writeCells(PHPExcel_Shared_XMLWriter $objWriter, PHPExcel_Worksheet_Row $row)
+ {
+ $number_cols_repeated = self::NUMBER_COLS_REPEATED_MAX;
+ $prev_column = -1;
+ $cells = $row->getCellIterator();
+ while ($cells->valid()) {
+ $cell = $cells->current();
+ $column = PHPExcel_Cell::columnIndexFromString($cell->getColumn()) - 1;
+
+ $this->writeCellSpan($objWriter, $column, $prev_column);
+ $objWriter->startElement('table:table-cell');
+
+ switch ($cell->getDataType()) {
+ case PHPExcel_Cell_DataType::TYPE_BOOL:
+ $objWriter->writeAttribute('office:value-type', 'boolean');
+ $objWriter->writeAttribute('office:value', $cell->getValue());
+ $objWriter->writeElement('text:p', $cell->getValue());
+ break;
+
+ case PHPExcel_Cell_DataType::TYPE_ERROR:
+ throw new PHPExcel_Writer_Exception('Writing of error not implemented yet.');
+ break;
+
+ case PHPExcel_Cell_DataType::TYPE_FORMULA:
+ try {
+ $formula_value = $cell->getCalculatedValue();
+ } catch (Exception $e) {
+ $formula_value = $cell->getValue();
+ }
+ $objWriter->writeAttribute('table:formula', 'of:' . $cell->getValue());
+ if (is_numeric($formula_value)) {
+ $objWriter->writeAttribute('office:value-type', 'float');
+ } else {
+ $objWriter->writeAttribute('office:value-type', 'string');
+ }
+ $objWriter->writeAttribute('office:value', $formula_value);
+ $objWriter->writeElement('text:p', $formula_value);
+ break;
+
+ case PHPExcel_Cell_DataType::TYPE_INLINE:
+ throw new PHPExcel_Writer_Exception('Writing of inline not implemented yet.');
+ break;
+
+ case PHPExcel_Cell_DataType::TYPE_NUMERIC:
+ $objWriter->writeAttribute('office:value-type', 'float');
+ $objWriter->writeAttribute('office:value', $cell->getValue());
+ $objWriter->writeElement('text:p', $cell->getValue());
+ break;
+
+ case PHPExcel_Cell_DataType::TYPE_STRING:
+ $objWriter->writeAttribute('office:value-type', 'string');
+ $objWriter->writeElement('text:p', $cell->getValue());
+ break;
+ }
+ PHPExcel_Writer_OpenDocument_Cell_Comment::write($objWriter, $cell);
+ $objWriter->endElement();
+ $prev_column = $column;
+ $cells->next();
+ }
+ $number_cols_repeated = $number_cols_repeated - $prev_column - 1;
+ if ($number_cols_repeated > 0) {
+ if ($number_cols_repeated > 1) {
+ $objWriter->startElement('table:table-cell');
+ $objWriter->writeAttribute('table:number-columns-repeated', $number_cols_repeated);
+ $objWriter->endElement();
+ } else {
+ $objWriter->writeElement('table:table-cell');
+ }
+ }
+ }
+
+ /**
+ * Write span
+ *
+ * @param PHPExcel_Shared_XMLWriter $objWriter
+ * @param integer $curColumn
+ * @param integer $prevColumn
+ */
+ private function writeCellSpan(PHPExcel_Shared_XMLWriter $objWriter, $curColumn, $prevColumn)
+ {
+ $diff = $curColumn - $prevColumn - 1;
+ if (1 === $diff) {
+ $objWriter->writeElement('table:table-cell');
+ } elseif ($diff > 1) {
+ $objWriter->startElement('table:table-cell');
+ $objWriter->writeAttribute('table:number-columns-repeated', $diff);
+ $objWriter->endElement();
+ }
+ }
+}
diff --git a/PHPExcel/Writer/OpenDocument/Meta.php b/PHPExcel/Writer/OpenDocument/Meta.php
new file mode 100644
index 0000000..1a5c5fd
--- /dev/null
+++ b/PHPExcel/Writer/OpenDocument/Meta.php
@@ -0,0 +1,95 @@
+getParentWriter()->getPHPExcel();
+ }
+
+ $objWriter = null;
+ if ($this->getParentWriter()->getUseDiskCaching()) {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
+ } else {
+ $objWriter = new PHPExcel_Shared_XMLWriter(PHPExcel_Shared_XMLWriter::STORAGE_MEMORY);
+ }
+
+ // XML header
+ $objWriter->startDocument('1.0', 'UTF-8');
+
+ // Meta
+ $objWriter->startElement('office:document-meta');
+
+ $objWriter->writeAttribute('xmlns:office', 'urn:oasis:names:tc:opendocument:xmlns:office:1.0');
+ $objWriter->writeAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
+ $objWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
+ $objWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0');
+ $objWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office');
+ $objWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#');
+ $objWriter->writeAttribute('office:version', '1.2');
+
+ $objWriter->startElement('office:meta');
+
+ $objWriter->writeElement('meta:initial-creator', $pPHPExcel->getProperties()->getCreator());
+ $objWriter->writeElement('dc:creator', $pPHPExcel->getProperties()->getCreator());
+ $objWriter->writeElement('meta:creation-date', date(DATE_W3C, $pPHPExcel->getProperties()->getCreated()));
+ $objWriter->writeElement('dc:date', date(DATE_W3C, $pPHPExcel->getProperties()->getCreated()));
+ $objWriter->writeElement('dc:title', $pPHPExcel->getProperties()->getTitle());
+ $objWriter->writeElement('dc:description', $pPHPExcel->getProperties()->getDescription());
+ $objWriter->writeElement('dc:subject', $pPHPExcel->getProperties()->getSubject());
+ $keywords = explode(' ', $pPHPExcel->getProperties()->getKeywords());
+ foreach ($keywords as $keyword) {
+ $objWriter->writeElement('meta:keyword', $keyword);
+ }
+
+ //
";
unlink($taskDir.'/'.$taskfiles[0]);