Update to phpspreadsheet 1.8.2
This commit is contained in:
parent
04536f9ec8
commit
76c3229c97
@ -23,7 +23,7 @@ PEAR Mail_MIME 1.8.9 BSD Yes
|
||||
ParseDown 1.6 MIT License Yes Markdown parser
|
||||
PCLZip 2.8.4 LGPL-3+ Yes Library to zip/unzip files
|
||||
PHPDebugBar 1.15.1 MIT License Yes Used only by the module "debugbar" for developers
|
||||
PHPSpreadSheet ? LGPL-2.1+ Yes Read/Write XLS files, read ODS files
|
||||
PHPSpreadSheet 1.8.2 LGPL-2.1+ Yes Read/Write XLS files, read ODS files
|
||||
php-iban 1.4.7 LGPL-3+ Yes Parse and validate IBAN (and IIBAN) bank account information in PHP
|
||||
PHPoAuthLib 0.8.2 MIT License Yes Library to provide oauth1 and oauth2 to different service
|
||||
PHPPrintIPP 1.3 GPL-2+ Yes Library to send print IPP requests
|
||||
|
||||
57
composer.lock
generated
57
composer.lock
generated
@ -328,6 +328,63 @@
|
||||
},
|
||||
"time": "2020-03-23T09:12:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpexcel",
|
||||
"version": "1.8.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/PHPExcel.git",
|
||||
"reference": "1441011fb7ecdd8cc689878f54f8b58a6805f870"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/1441011fb7ecdd8cc689878f54f8b58a6805f870",
|
||||
"reference": "1441011fb7ecdd8cc689878f54f8b58a6805f870",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"php": "^5.2|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"squizlabs/php_codesniffer": "2.*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maarten Balliauw",
|
||||
"homepage": "http://blog.maartenballiauw.be"
|
||||
},
|
||||
{
|
||||
"name": "Erik Tilt"
|
||||
},
|
||||
{
|
||||
"name": "Franck Lefevre",
|
||||
"homepage": "http://rootslabs.net"
|
||||
},
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"homepage": "http://markbakeruk.net"
|
||||
}
|
||||
],
|
||||
"description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||
"homepage": "https://github.com/PHPOffice/PHPExcel",
|
||||
"keywords": [
|
||||
"OpenXML",
|
||||
"excel",
|
||||
"xlsx"
|
||||
],
|
||||
"abandoned": "phpoffice/phpspreadsheet",
|
||||
"time": "2018-11-22T23:07:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "restler/framework",
|
||||
"version": "3.0.0-RC6",
|
||||
|
||||
@ -117,7 +117,7 @@ class Calculation
|
||||
/**
|
||||
* An array of the nested cell references accessed by the calculation engine, used for the debug log.
|
||||
*
|
||||
* @var array of string
|
||||
* @var CyclicReferenceStack
|
||||
*/
|
||||
private $cyclicReferenceStack;
|
||||
|
||||
@ -1945,6 +1945,11 @@ class Calculation
|
||||
'functionCall' => [MathTrig::class, 'SUMXMY2'],
|
||||
'argumentCount' => '2',
|
||||
],
|
||||
'SWITCH' => [
|
||||
'category' => Category::CATEGORY_LOGICAL,
|
||||
'functionCall' => [Logical::class, 'statementSwitch'],
|
||||
'argumentCount' => '3+',
|
||||
],
|
||||
'SYD' => [
|
||||
'category' => Category::CATEGORY_FINANCIAL,
|
||||
'functionCall' => [Financial::class, 'SYD'],
|
||||
@ -2207,8 +2212,8 @@ class Calculation
|
||||
private static function loadLocales()
|
||||
{
|
||||
$localeFileDirectory = __DIR__ . '/locale/';
|
||||
foreach (glob($localeFileDirectory . '/*', GLOB_ONLYDIR) as $filename) {
|
||||
$filename = substr($filename, strlen($localeFileDirectory) + 1);
|
||||
foreach (glob($localeFileDirectory . '*', GLOB_ONLYDIR) as $filename) {
|
||||
$filename = substr($filename, strlen($localeFileDirectory));
|
||||
if ($filename != 'en') {
|
||||
self::$validLocaleLanguages[] = $filename;
|
||||
}
|
||||
@ -2413,7 +2418,6 @@ class Calculation
|
||||
if (strpos($locale, '_') !== false) {
|
||||
list($language) = explode('_', $locale);
|
||||
}
|
||||
|
||||
if (count(self::$validLocaleLanguages) == 1) {
|
||||
self::loadLocales();
|
||||
}
|
||||
@ -2704,7 +2708,7 @@ class Calculation
|
||||
* @param Cell $pCell Cell to calculate
|
||||
* @param bool $resetLog Flag indicating whether the debug log should be reset or not
|
||||
*
|
||||
* @throws Exception
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@ -2808,7 +2812,7 @@ class Calculation
|
||||
* @param string $cellID Address of the cell to calculate
|
||||
* @param Cell $pCell Cell to calculate
|
||||
*
|
||||
* @throws Exception
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@ -2892,6 +2896,15 @@ class Calculation
|
||||
{
|
||||
$cellValue = null;
|
||||
|
||||
// Quote-Prefixed cell values cannot be formulae, but are treated as strings
|
||||
if ($pCell !== null && $pCell->getStyle()->getQuotePrefix() === true) {
|
||||
return self::wrapResult((string) $formula);
|
||||
}
|
||||
|
||||
if (preg_match('/^=\s*cmd\s*\|/miu', $formula) !== 0) {
|
||||
return self::wrapResult($formula);
|
||||
}
|
||||
|
||||
// Basic validation that this is indeed a formula
|
||||
// We simply return the cell value if not
|
||||
$formula = trim($formula);
|
||||
|
||||
@ -273,6 +273,60 @@ class Logical
|
||||
return ($condition) ? $returnIfTrue : $returnIfFalse;
|
||||
}
|
||||
|
||||
/**
|
||||
* STATEMENT_SWITCH.
|
||||
*
|
||||
* Returns corresponding with first match (any data type such as a string, numeric, date, etc).
|
||||
*
|
||||
* Excel Function:
|
||||
* =SWITCH (expression, value1, result1, value2, result2, ... value_n, result_n [, default])
|
||||
*
|
||||
* Expression
|
||||
* The expression to compare to a list of values.
|
||||
* value1, value2, ... value_n
|
||||
* A list of values that are compared to expression. The SWITCH function is looking for the first value that matches the expression.
|
||||
* result1, result2, ... result_n
|
||||
* A list of results. The SWITCH function returns the corresponding result when a value matches expression.
|
||||
* default
|
||||
* Optional. It is the default to return if expression does not match any of the values (value1, value2, ... value_n).
|
||||
*
|
||||
* @category Logical Functions
|
||||
*
|
||||
* @param mixed $arguments Statement arguments
|
||||
*
|
||||
* @return mixed The value of matched expression
|
||||
*/
|
||||
public static function statementSwitch(...$arguments)
|
||||
{
|
||||
$result = Functions::VALUE();
|
||||
|
||||
if (count($arguments) > 0) {
|
||||
$targetValue = Functions::flattenSingleValue($arguments[0]);
|
||||
$argc = count($arguments) - 1;
|
||||
$switchCount = floor($argc / 2);
|
||||
$switchSatisfied = false;
|
||||
$hasDefaultClause = $argc % 2 !== 0;
|
||||
$defaultClause = $argc % 2 === 0 ? null : $arguments[count($arguments) - 1];
|
||||
|
||||
if ($switchCount) {
|
||||
for ($index = 0; $index < $switchCount; ++$index) {
|
||||
if ($targetValue == $arguments[$index * 2 + 1]) {
|
||||
$result = $arguments[$index * 2 + 2];
|
||||
$switchSatisfied = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$switchSatisfied) {
|
||||
$result = $hasDefaultClause ? $defaultClause : Functions::NA();
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* IFERROR.
|
||||
*
|
||||
|
||||
@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class LookupRef
|
||||
@ -473,8 +474,9 @@ class LookupRef
|
||||
$lookupValue = Functions::flattenSingleValue($lookupValue);
|
||||
$matchType = ($matchType === null) ? 1 : (int) Functions::flattenSingleValue($matchType);
|
||||
|
||||
$initialLookupValue = $lookupValue;
|
||||
// MATCH is not case sensitive
|
||||
$lookupValue = strtolower($lookupValue);
|
||||
$lookupValue = StringHelper::strToLower($lookupValue);
|
||||
|
||||
// Lookup_value type has to be number, text, or logical values
|
||||
if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
|
||||
@ -502,7 +504,7 @@ class LookupRef
|
||||
}
|
||||
// Convert strings to lowercase for case-insensitive testing
|
||||
if (is_string($lookupArrayValue)) {
|
||||
$lookupArray[$i] = strtolower($lookupArrayValue);
|
||||
$lookupArray[$i] = StringHelper::strToLower($lookupArrayValue);
|
||||
}
|
||||
if (($lookupArrayValue === null) && (($matchType == 1) || ($matchType == -1))) {
|
||||
$lookupArray = array_slice($lookupArray, 0, $i - 1);
|
||||
@ -522,9 +524,13 @@ class LookupRef
|
||||
|
||||
if ($matchType == 0 || $matchType == 1) {
|
||||
foreach ($lookupArray as $i => $lookupArrayValue) {
|
||||
if (($matchType == 0) && ($lookupArrayValue == $lookupValue)) {
|
||||
$onlyNumeric = is_numeric($lookupArrayValue) && is_numeric($lookupValue);
|
||||
$onlyNumericExactMatch = $onlyNumeric && $lookupArrayValue == $lookupValue;
|
||||
$nonOnlyNumericExactMatch = !$onlyNumeric && $lookupArrayValue === $lookupValue;
|
||||
$exactMatch = $onlyNumericExactMatch || $nonOnlyNumericExactMatch;
|
||||
if (($matchType == 0) && $exactMatch) {
|
||||
// exact match
|
||||
return ++$i;
|
||||
return $i + 1;
|
||||
} elseif (($matchType == 1) && ($lookupArrayValue <= $lookupValue)) {
|
||||
$i = array_search($i, $keySet);
|
||||
|
||||
@ -661,7 +667,9 @@ class LookupRef
|
||||
{
|
||||
reset($a);
|
||||
$firstColumn = key($a);
|
||||
if (($aLower = strtolower($a[$firstColumn])) == ($bLower = strtolower($b[$firstColumn]))) {
|
||||
$aLower = StringHelper::strToLower($a[$firstColumn]);
|
||||
$bLower = StringHelper::strToLower($b[$firstColumn]);
|
||||
if ($aLower == $bLower) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -707,11 +715,14 @@ class LookupRef
|
||||
uasort($lookup_array, ['self', 'vlookupSort']);
|
||||
}
|
||||
|
||||
$lookupLower = StringHelper::strToLower($lookup_value);
|
||||
$rowNumber = $rowValue = false;
|
||||
foreach ($lookup_array as $rowKey => $rowData) {
|
||||
$firstLower = StringHelper::strToLower($rowData[$firstColumn]);
|
||||
|
||||
// break if we have passed possible keys
|
||||
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)))) {
|
||||
(!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && ($firstLower > $lookupLower))) {
|
||||
break;
|
||||
}
|
||||
// remember the last key, but only if datatypes match
|
||||
@ -719,17 +730,15 @@ class LookupRef
|
||||
(!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]))) {
|
||||
if ($not_exact_match) {
|
||||
$rowNumber = $rowKey;
|
||||
$rowValue = $rowData[$firstColumn];
|
||||
|
||||
continue;
|
||||
} elseif ((strtolower($rowData[$firstColumn]) == strtolower($lookup_value))
|
||||
} elseif (($firstLower == $lookupLower)
|
||||
// Spreadsheets software returns first exact match,
|
||||
// we have sorted and we might have broken key orders
|
||||
// we want the first one (by its initial index)
|
||||
&& (($rowNumber == false) || ($rowKey < $rowNumber))
|
||||
) {
|
||||
$rowNumber = $rowKey;
|
||||
$rowValue = $rowData[$firstColumn];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -782,8 +791,11 @@ class LookupRef
|
||||
// break if we have passed possible keys
|
||||
$bothNumeric = is_numeric($lookup_value) && is_numeric($rowData);
|
||||
$bothNotNumeric = !is_numeric($lookup_value) && !is_numeric($rowData);
|
||||
$lookupLower = StringHelper::strToLower($lookup_value);
|
||||
$rowDataLower = StringHelper::strToLower($rowData);
|
||||
|
||||
if (($bothNumeric && $rowData > $lookup_value) ||
|
||||
($bothNotNumeric && strtolower($rowData) > strtolower($lookup_value))) {
|
||||
($bothNotNumeric && $rowDataLower > $lookupLower)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -793,7 +805,7 @@ class LookupRef
|
||||
$rowNumber = $rowKey;
|
||||
|
||||
continue;
|
||||
} elseif (strtolower($rowData) === strtolower($lookup_value)
|
||||
} elseif ($rowDataLower === $lookupLower
|
||||
&& ($rowNumber === null || $rowKey < $rowNumber)
|
||||
) {
|
||||
$rowNumber = $rowKey;
|
||||
|
||||
@ -1224,11 +1224,12 @@ class MathTrig
|
||||
}
|
||||
|
||||
$testCondition = '=' . $arg . $condition;
|
||||
$sumValue = array_key_exists($key, $sumArgs) ? $sumArgs[$key] : 0;
|
||||
|
||||
if (is_numeric($sumArgs[$key]) &&
|
||||
if (is_numeric($sumValue) &&
|
||||
Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
|
||||
// Is it a value within our criteria and only numeric can be added to the result
|
||||
$returnValue += $sumArgs[$key];
|
||||
$returnValue += $sumValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -337,6 +337,7 @@ SUMSQ
|
||||
SUMX2MY2
|
||||
SUMX2PY2
|
||||
SUMXMY2
|
||||
SWITCH
|
||||
SYD
|
||||
T
|
||||
TAN
|
||||
|
||||
@ -16,6 +16,8 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
|
||||
* @param Cell $cell Cell to bind value to
|
||||
* @param mixed $value Value to bind in cell
|
||||
*
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function bindValue(Cell $cell, $value = null)
|
||||
|
||||
@ -523,7 +523,7 @@ class Cell
|
||||
/**
|
||||
* If this cell is in a merge range, then return the range.
|
||||
*
|
||||
* @return string
|
||||
* @return false|string
|
||||
*/
|
||||
public function getMergeRange()
|
||||
{
|
||||
|
||||
@ -157,14 +157,12 @@ abstract class Coordinate
|
||||
}
|
||||
|
||||
// Build range
|
||||
$imploded = [];
|
||||
$counter = count($pRange);
|
||||
for ($i = 0; $i < $counter; ++$i) {
|
||||
$pRange[$i] = implode(':', $pRange[$i]);
|
||||
}
|
||||
$imploded = implode(',', $pRange);
|
||||
|
||||
return $imploded;
|
||||
return implode(',', $pRange);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -14,6 +14,8 @@ class DefaultValueBinder implements IValueBinder
|
||||
* @param Cell $cell Cell to bind value to
|
||||
* @param mixed $value Value to bind in cell
|
||||
*
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function bindValue(Cell $cell, $value)
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Cell;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class StringValueBinder implements IValueBinder
|
||||
{
|
||||
/**
|
||||
* Bind value to a cell.
|
||||
*
|
||||
* @param Cell $cell Cell to bind value to
|
||||
* @param mixed $value Value to bind in cell
|
||||
*
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function bindValue(Cell $cell, $value)
|
||||
{
|
||||
// sanitize UTF-8 strings
|
||||
if (is_string($value)) {
|
||||
$value = StringHelper::sanitizeUTF8($value);
|
||||
}
|
||||
|
||||
$cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
|
||||
|
||||
// Done!
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -365,10 +365,10 @@ class Axis extends Properties
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Shadow Properties from Maped Values.
|
||||
* Set Shadow Properties from Mapped Values.
|
||||
*
|
||||
* @param array $properties_map
|
||||
* @param * $reference
|
||||
* @param mixed &$reference
|
||||
*
|
||||
* @return Axis
|
||||
*/
|
||||
|
||||
@ -319,7 +319,7 @@ class GridLines extends Properties
|
||||
* Set Shadow Properties Values.
|
||||
*
|
||||
* @param array $properties_map
|
||||
* @param * $reference
|
||||
* @param mixed &$reference
|
||||
*
|
||||
* @return GridLines
|
||||
*/
|
||||
@ -439,7 +439,7 @@ class GridLines extends Properties
|
||||
{
|
||||
if ($size !== null) {
|
||||
$this->activateObject();
|
||||
$softEdges['size'] = (string) $this->getExcelPointsWidth($size);
|
||||
$this->softEdges['size'] = (string) $this->getExcelPointsWidth($size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -175,13 +175,13 @@ class Layout
|
||||
/**
|
||||
* Set X-Mode.
|
||||
*
|
||||
* @param X-Mode $value
|
||||
* @param string $value
|
||||
*
|
||||
* @return Layout
|
||||
*/
|
||||
public function setXMode($value)
|
||||
{
|
||||
$this->xMode = $value;
|
||||
$this->xMode = (string) $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -199,13 +199,13 @@ class Layout
|
||||
/**
|
||||
* Set Y-Mode.
|
||||
*
|
||||
* @param Y-Mode $value
|
||||
* @param string $value
|
||||
*
|
||||
* @return Layout
|
||||
*/
|
||||
public function setYMode($value)
|
||||
{
|
||||
$this->yMode = $value;
|
||||
$this->yMode = (string) $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -223,13 +223,13 @@ class Layout
|
||||
/**
|
||||
* Set X-Position.
|
||||
*
|
||||
* @param X-Position $value
|
||||
* @param float $value
|
||||
*
|
||||
* @return Layout
|
||||
*/
|
||||
public function setXPosition($value)
|
||||
{
|
||||
$this->xPos = $value;
|
||||
$this->xPos = (float) $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -247,13 +247,13 @@ class Layout
|
||||
/**
|
||||
* Set Y-Position.
|
||||
*
|
||||
* @param Y-Position $value
|
||||
* @param float $value
|
||||
*
|
||||
* @return Layout
|
||||
*/
|
||||
public function setYPosition($value)
|
||||
{
|
||||
$this->yPos = $value;
|
||||
$this->yPos = (float) $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -241,7 +241,7 @@ class Cells
|
||||
*/
|
||||
public function getHighestColumn($row = null)
|
||||
{
|
||||
if ($row == null) {
|
||||
if ($row === null) {
|
||||
$colRow = $this->getHighestRowAndColumn();
|
||||
|
||||
return $colRow['column'];
|
||||
@ -259,7 +259,7 @@ class Cells
|
||||
$columnList[] = Coordinate::columnIndexFromString($c);
|
||||
}
|
||||
|
||||
return Coordinate::stringFromColumnIndex(max($columnList) + 1);
|
||||
return Coordinate::stringFromColumnIndex(max($columnList));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,7 +272,7 @@ class Cells
|
||||
*/
|
||||
public function getHighestRow($column = null)
|
||||
{
|
||||
if ($column == null) {
|
||||
if ($column === null) {
|
||||
$colRow = $this->getHighestRowAndColumn();
|
||||
|
||||
return $colRow['row'];
|
||||
|
||||
@ -418,7 +418,7 @@ class Properties
|
||||
*
|
||||
* @param string $propertyName
|
||||
*
|
||||
* @return string
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomPropertyValue($propertyName)
|
||||
{
|
||||
|
||||
@ -55,27 +55,16 @@ abstract class BaseReader implements IReader
|
||||
*/
|
||||
protected $securityScanner;
|
||||
|
||||
/**
|
||||
* Read data only?
|
||||
* If this is true, then the Reader will only read data values for cells, it will not read any formatting information.
|
||||
* If false (the default) it will read data and formatting.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
}
|
||||
|
||||
public function getReadDataOnly()
|
||||
{
|
||||
return $this->readDataOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read data only
|
||||
* Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting information.
|
||||
* Set to false (the default) to advise the Reader to read both data and formatting for cells.
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setReadDataOnly($pValue)
|
||||
{
|
||||
$this->readDataOnly = (bool) $pValue;
|
||||
@ -83,27 +72,11 @@ abstract class BaseReader implements IReader
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read empty cells?
|
||||
* If this is true (the default), then the Reader will read data values for all cells, irrespective of value.
|
||||
* If false it will not read data for cells containing a null value or an empty string.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getReadEmptyCells()
|
||||
{
|
||||
return $this->readEmptyCells;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read empty cells
|
||||
* Set to true (the default) to advise the Reader read data values for all cells, irrespective of value.
|
||||
* Set to false to advise the Reader to ignore cells containing a null value or an empty string.
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setReadEmptyCells($pValue)
|
||||
{
|
||||
$this->readEmptyCells = (bool) $pValue;
|
||||
@ -111,29 +84,11 @@ abstract class BaseReader implements IReader
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read charts in workbook?
|
||||
* If this is true, then the Reader will include any charts that exist in the workbook.
|
||||
* Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
|
||||
* If false (the default) it will ignore any charts defined in the workbook file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIncludeCharts()
|
||||
{
|
||||
return $this->includeCharts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read charts in workbook
|
||||
* Set to true, to advise the Reader to include any charts that exist in the workbook.
|
||||
* Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
|
||||
* Set to false (the default) to discard charts.
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setIncludeCharts($pValue)
|
||||
{
|
||||
$this->includeCharts = (bool) $pValue;
|
||||
@ -141,27 +96,11 @@ abstract class BaseReader implements IReader
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get which sheets to load
|
||||
* Returns either an array of worksheet names (the list of worksheets that should be loaded), or a null
|
||||
* indicating that all worksheets in the workbook should be loaded.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLoadSheetsOnly()
|
||||
{
|
||||
return $this->loadSheetsOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set which sheets to load.
|
||||
*
|
||||
* @param mixed $value
|
||||
* This should be either an array of worksheet names to be loaded, or a string containing a single worksheet name.
|
||||
* If NULL, then it tells the Reader to read all worksheets in the workbook
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setLoadSheetsOnly($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
@ -173,12 +112,6 @@ abstract class BaseReader implements IReader
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all sheets to load
|
||||
* Tells the Reader to load all worksheets from the workbook.
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setLoadAllSheets()
|
||||
{
|
||||
$this->loadSheetsOnly = null;
|
||||
@ -186,23 +119,11 @@ abstract class BaseReader implements IReader
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read filter.
|
||||
*
|
||||
* @return IReadFilter
|
||||
*/
|
||||
public function getReadFilter()
|
||||
{
|
||||
return $this->readFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read filter.
|
||||
*
|
||||
* @param IReadFilter $pValue
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setReadFilter(IReadFilter $pValue)
|
||||
{
|
||||
$this->readFilter = $pValue;
|
||||
@ -210,7 +131,7 @@ abstract class BaseReader implements IReader
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSecuritySCanner()
|
||||
public function getSecurityScanner()
|
||||
{
|
||||
if (property_exists($this, 'securityScanner')) {
|
||||
return $this->securityScanner;
|
||||
|
||||
@ -62,7 +62,7 @@ class Csv extends BaseReader
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,7 +143,7 @@ class Csv extends BaseReader
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->skipBOM();
|
||||
$this->skipBOM();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,7 +155,7 @@ class Csv extends BaseReader
|
||||
return;
|
||||
}
|
||||
|
||||
$potentialDelimiters = [',', ';', "\t", '|', ':', ' '];
|
||||
$potentialDelimiters = [',', ';', "\t", '|', ':', ' ', '~'];
|
||||
$counts = [];
|
||||
foreach ($potentialDelimiters as $delimiter) {
|
||||
$counts[$delimiter] = [];
|
||||
@ -184,8 +184,9 @@ class Csv extends BaseReader
|
||||
// If number of lines is 0, nothing to infer : fall back to the default
|
||||
if ($numberLines === 0) {
|
||||
$this->delimiter = reset($potentialDelimiters);
|
||||
$this->skipBOM();
|
||||
|
||||
return $this->skipBOM();
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the mean square deviations for each delimiter (ignoring delimiters that haven't been found consistently)
|
||||
@ -230,7 +231,7 @@ class Csv extends BaseReader
|
||||
$this->delimiter = reset($potentialDelimiters);
|
||||
}
|
||||
|
||||
return $this->skipBOM();
|
||||
$this->skipBOM();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -254,15 +255,13 @@ class Csv extends BaseReader
|
||||
$line = $line . $newLine;
|
||||
|
||||
// Drop everything that is enclosed to avoid counting false positives in enclosures
|
||||
$enclosure = preg_quote($this->enclosure, '/');
|
||||
$line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/U', '', $line);
|
||||
$enclosure = '(?<!' . preg_quote($this->escapeCharacter, '/') . ')'
|
||||
. preg_quote($this->enclosure, '/');
|
||||
$line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line);
|
||||
|
||||
// See if we have any enclosures left in the line
|
||||
$matches = [];
|
||||
preg_match('/(' . $enclosure . ')/', $line, $matches);
|
||||
|
||||
// if we still have an enclosure then we need to read the next line aswell
|
||||
if (count($matches) > 0) {
|
||||
// if we still have an enclosure then we need to read the next line as well
|
||||
if (preg_match('/(' . $enclosure . ')/', $line) > 0) {
|
||||
$line = $this->getNextLine($line);
|
||||
}
|
||||
|
||||
@ -547,7 +546,8 @@ class Csv extends BaseReader
|
||||
fclose($this->fileHandle);
|
||||
|
||||
// Trust file extension if any
|
||||
if (strtolower(pathinfo($pFilename, PATHINFO_EXTENSION)) === 'csv') {
|
||||
$extension = strtolower(pathinfo($pFilename, PATHINFO_EXTENSION));
|
||||
if (in_array($extension, ['csv', 'tsv'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ class Gnumeric extends BaseReader
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
parent::__construct();
|
||||
$this->referenceHelper = ReferenceHelper::getInstance();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
}
|
||||
|
||||
@ -12,6 +12,9 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Color;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Font;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
/** PhpSpreadsheet root directory */
|
||||
@ -96,6 +99,26 @@ class Html extends BaseReader
|
||||
],
|
||||
],
|
||||
], // Bottom border
|
||||
'strong' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
],
|
||||
], // Bold
|
||||
'b' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
],
|
||||
], // Bold
|
||||
'i' => [
|
||||
'font' => [
|
||||
'italic' => true,
|
||||
],
|
||||
], // Italic
|
||||
'em' => [
|
||||
'font' => [
|
||||
'italic' => true,
|
||||
],
|
||||
], // Italic
|
||||
];
|
||||
|
||||
protected $rowspan = [];
|
||||
@ -105,7 +128,7 @@ class Html extends BaseReader
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
parent::__construct();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
}
|
||||
|
||||
@ -295,11 +318,9 @@ class Html extends BaseReader
|
||||
switch ($child->nodeName) {
|
||||
case 'meta':
|
||||
foreach ($attributeArray as $attributeName => $attributeValue) {
|
||||
switch ($attributeName) {
|
||||
case 'content':
|
||||
// TODO
|
||||
// Extract character set, so we can convert to UTF-8 if required
|
||||
break;
|
||||
// Extract character set, so we can convert to UTF-8 if required
|
||||
if ($attributeName === 'charset') {
|
||||
$this->setInputEncoding($attributeValue);
|
||||
}
|
||||
}
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
@ -334,6 +355,10 @@ class Html extends BaseReader
|
||||
$cellContent .= ' ';
|
||||
}
|
||||
|
||||
if (isset($this->formats[$child->nodeName])) {
|
||||
$sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'hr':
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
@ -423,6 +448,10 @@ class Html extends BaseReader
|
||||
$column = 'A';
|
||||
}
|
||||
|
||||
break;
|
||||
case 'img':
|
||||
$this->insertImage($sheet, $column, $row, $attributeArray);
|
||||
|
||||
break;
|
||||
case 'table':
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
@ -448,6 +477,11 @@ class Html extends BaseReader
|
||||
$column = $this->getTableStartColumn();
|
||||
$cellContent = '';
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
|
||||
if (isset($attributeArray['height'])) {
|
||||
$sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
|
||||
}
|
||||
|
||||
++$row;
|
||||
|
||||
break;
|
||||
@ -501,6 +535,27 @@ class Html extends BaseReader
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($attributeArray['width'])) {
|
||||
$sheet->getColumnDimension($column)->setWidth($attributeArray['width']);
|
||||
}
|
||||
|
||||
if (isset($attributeArray['height'])) {
|
||||
$sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
|
||||
}
|
||||
|
||||
if (isset($attributeArray['align'])) {
|
||||
$sheet->getStyle($column . $row)->getAlignment()->setHorizontal($attributeArray['align']);
|
||||
}
|
||||
|
||||
if (isset($attributeArray['valign'])) {
|
||||
$sheet->getStyle($column . $row)->getAlignment()->setVertical($attributeArray['valign']);
|
||||
}
|
||||
|
||||
if (isset($attributeArray['data-format'])) {
|
||||
$sheet->getStyle($column . $row)->getNumberFormat()->setFormatCode($attributeArray['data-format']);
|
||||
}
|
||||
|
||||
++$column;
|
||||
|
||||
break;
|
||||
@ -608,36 +663,271 @@ class Html extends BaseReader
|
||||
return;
|
||||
}
|
||||
|
||||
$supported_styles = ['background-color', 'color'];
|
||||
$cellStyle = $sheet->getStyle($column . $row);
|
||||
|
||||
// add color styles (background & text) from dom element,currently support : td & th, using ONLY inline css style with RGB color
|
||||
$styles = explode(';', $attributeArray['style']);
|
||||
foreach ($styles as $st) {
|
||||
$value = explode(':', $st);
|
||||
$styleName = isset($value[0]) ? trim($value[0]) : null;
|
||||
$styleValue = isset($value[1]) ? trim($value[1]) : null;
|
||||
|
||||
if (empty(trim($value[0])) || !in_array(trim($value[0]), $supported_styles)) {
|
||||
if (!$styleName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//check if has #, so we can get clean hex
|
||||
if (substr(trim($value[1]), 0, 1) == '#') {
|
||||
$style_color = substr(trim($value[1]), 1);
|
||||
}
|
||||
|
||||
if (empty($style_color)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (trim($value[0])) {
|
||||
switch ($styleName) {
|
||||
case 'background':
|
||||
case 'background-color':
|
||||
$sheet->getStyle($column . $row)->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => "{$style_color}"]]]);
|
||||
$styleColor = $this->getStyleColor($styleValue);
|
||||
|
||||
if (!$styleColor) {
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$cellStyle->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => $styleColor]]]);
|
||||
|
||||
break;
|
||||
case 'color':
|
||||
$sheet->getStyle($column . $row)->applyFromArray(['font' => ['color' => ['rgb' => "{$style_color}"]]]);
|
||||
$styleColor = $this->getStyleColor($styleValue);
|
||||
|
||||
if (!$styleColor) {
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$cellStyle->applyFromArray(['font' => ['color' => ['rgb' => $styleColor]]]);
|
||||
|
||||
break;
|
||||
|
||||
case 'border':
|
||||
$this->setBorderStyle($cellStyle, $styleValue, 'allBorders');
|
||||
|
||||
break;
|
||||
|
||||
case 'border-top':
|
||||
$this->setBorderStyle($cellStyle, $styleValue, 'top');
|
||||
|
||||
break;
|
||||
|
||||
case 'border-bottom':
|
||||
$this->setBorderStyle($cellStyle, $styleValue, 'bottom');
|
||||
|
||||
break;
|
||||
|
||||
case 'border-left':
|
||||
$this->setBorderStyle($cellStyle, $styleValue, 'left');
|
||||
|
||||
break;
|
||||
|
||||
case 'border-right':
|
||||
$this->setBorderStyle($cellStyle, $styleValue, 'right');
|
||||
|
||||
break;
|
||||
|
||||
case 'font-size':
|
||||
$cellStyle->getFont()->setSize(
|
||||
(float) $styleValue
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case 'font-weight':
|
||||
if ($styleValue === 'bold' || $styleValue >= 500) {
|
||||
$cellStyle->getFont()->setBold(true);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'font-style':
|
||||
if ($styleValue === 'italic') {
|
||||
$cellStyle->getFont()->setItalic(true);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'font-family':
|
||||
$cellStyle->getFont()->setName(str_replace('\'', '', $styleValue));
|
||||
|
||||
break;
|
||||
|
||||
case 'text-decoration':
|
||||
switch ($styleValue) {
|
||||
case 'underline':
|
||||
$cellStyle->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
|
||||
|
||||
break;
|
||||
case 'line-through':
|
||||
$cellStyle->getFont()->setStrikethrough(true);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'text-align':
|
||||
$cellStyle->getAlignment()->setHorizontal($styleValue);
|
||||
|
||||
break;
|
||||
|
||||
case 'vertical-align':
|
||||
$cellStyle->getAlignment()->setVertical($styleValue);
|
||||
|
||||
break;
|
||||
|
||||
case 'width':
|
||||
$sheet->getColumnDimension($column)->setWidth(
|
||||
str_replace('px', '', $styleValue)
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case 'height':
|
||||
$sheet->getRowDimension($row)->setRowHeight(
|
||||
str_replace('px', '', $styleValue)
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case 'word-wrap':
|
||||
$cellStyle->getAlignment()->setWrapText(
|
||||
$styleValue === 'break-word'
|
||||
);
|
||||
|
||||
break;
|
||||
|
||||
case 'text-indent':
|
||||
$cellStyle->getAlignment()->setIndent(
|
||||
(int) str_replace(['px'], '', $styleValue)
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if has #, so we can get clean hex.
|
||||
*
|
||||
* @param $value
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getStyleColor($value)
|
||||
{
|
||||
if (strpos($value, '#') === 0) {
|
||||
return substr($value, 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Worksheet $sheet
|
||||
* @param string $column
|
||||
* @param int $row
|
||||
* @param array $attributes
|
||||
*
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
||||
*/
|
||||
private function insertImage(Worksheet $sheet, $column, $row, array $attributes)
|
||||
{
|
||||
if (!isset($attributes['src'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$src = urldecode($attributes['src']);
|
||||
$width = isset($attributes['width']) ? (float) $attributes['width'] : null;
|
||||
$height = isset($attributes['height']) ? (float) $attributes['height'] : null;
|
||||
$name = isset($attributes['alt']) ? (float) $attributes['alt'] : null;
|
||||
|
||||
$drawing = new Drawing();
|
||||
$drawing->setPath($src);
|
||||
$drawing->setWorksheet($sheet);
|
||||
$drawing->setCoordinates($column . $row);
|
||||
$drawing->setOffsetX(0);
|
||||
$drawing->setOffsetY(10);
|
||||
$drawing->setResizeProportional(true);
|
||||
|
||||
if ($name) {
|
||||
$drawing->setName($name);
|
||||
}
|
||||
|
||||
if ($width) {
|
||||
$drawing->setWidth((int) $width);
|
||||
}
|
||||
|
||||
if ($height) {
|
||||
$drawing->setHeight((int) $height);
|
||||
}
|
||||
|
||||
$sheet->getColumnDimension($column)->setWidth(
|
||||
$drawing->getWidth() / 6
|
||||
);
|
||||
|
||||
$sheet->getRowDimension($row)->setRowHeight(
|
||||
$drawing->getHeight() * 0.9
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map html border style to PhpSpreadsheet border style.
|
||||
*
|
||||
* @param string $style
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getBorderStyle($style)
|
||||
{
|
||||
switch ($style) {
|
||||
case 'solid':
|
||||
return Border::BORDER_THIN;
|
||||
case 'dashed':
|
||||
return Border::BORDER_DASHED;
|
||||
case 'dotted':
|
||||
return Border::BORDER_DOTTED;
|
||||
case 'medium':
|
||||
return Border::BORDER_MEDIUM;
|
||||
case 'thick':
|
||||
return Border::BORDER_THICK;
|
||||
case 'none':
|
||||
return Border::BORDER_NONE;
|
||||
case 'dash-dot':
|
||||
return Border::BORDER_DASHDOT;
|
||||
case 'dash-dot-dot':
|
||||
return Border::BORDER_DASHDOTDOT;
|
||||
case 'double':
|
||||
return Border::BORDER_DOUBLE;
|
||||
case 'hair':
|
||||
return Border::BORDER_HAIR;
|
||||
case 'medium-dash-dot':
|
||||
return Border::BORDER_MEDIUMDASHDOT;
|
||||
case 'medium-dash-dot-dot':
|
||||
return Border::BORDER_MEDIUMDASHDOTDOT;
|
||||
case 'medium-dashed':
|
||||
return Border::BORDER_MEDIUMDASHED;
|
||||
case 'slant-dash-dot':
|
||||
return Border::BORDER_SLANTDASHDOT;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Style $cellStyle
|
||||
* @param string $styleValue
|
||||
* @param string $type
|
||||
*/
|
||||
private function setBorderStyle(Style $cellStyle, $styleValue, $type)
|
||||
{
|
||||
list(, $borderStyle, $color) = explode(' ', $styleValue);
|
||||
|
||||
$cellStyle->applyFromArray([
|
||||
'borders' => [
|
||||
$type => [
|
||||
'borderStyle' => $this->getBorderStyle($borderStyle),
|
||||
'color' => ['rgb' => $this->getStyleColor($color)],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,112 @@ interface IReader
|
||||
*/
|
||||
public function canRead($pFilename);
|
||||
|
||||
/**
|
||||
* Read data only?
|
||||
* If this is true, then the Reader will only read data values for cells, it will not read any formatting information.
|
||||
* If false (the default) it will read data and formatting.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getReadDataOnly();
|
||||
|
||||
/**
|
||||
* Set read data only
|
||||
* Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting information.
|
||||
* Set to false (the default) to advise the Reader to read both data and formatting for cells.
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setReadDataOnly($pValue);
|
||||
|
||||
/**
|
||||
* Read empty cells?
|
||||
* If this is true (the default), then the Reader will read data values for all cells, irrespective of value.
|
||||
* If false it will not read data for cells containing a null value or an empty string.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getReadEmptyCells();
|
||||
|
||||
/**
|
||||
* Set read empty cells
|
||||
* Set to true (the default) to advise the Reader read data values for all cells, irrespective of value.
|
||||
* Set to false to advise the Reader to ignore cells containing a null value or an empty string.
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setReadEmptyCells($pValue);
|
||||
|
||||
/**
|
||||
* Read charts in workbook?
|
||||
* If this is true, then the Reader will include any charts that exist in the workbook.
|
||||
* Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
|
||||
* If false (the default) it will ignore any charts defined in the workbook file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIncludeCharts();
|
||||
|
||||
/**
|
||||
* Set read charts in workbook
|
||||
* Set to true, to advise the Reader to include any charts that exist in the workbook.
|
||||
* Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
|
||||
* Set to false (the default) to discard charts.
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setIncludeCharts($pValue);
|
||||
|
||||
/**
|
||||
* Get which sheets to load
|
||||
* Returns either an array of worksheet names (the list of worksheets that should be loaded), or a null
|
||||
* indicating that all worksheets in the workbook should be loaded.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLoadSheetsOnly();
|
||||
|
||||
/**
|
||||
* Set which sheets to load.
|
||||
*
|
||||
* @param mixed $value
|
||||
* This should be either an array of worksheet names to be loaded, or a string containing a single worksheet name.
|
||||
* If NULL, then it tells the Reader to read all worksheets in the workbook
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setLoadSheetsOnly($value);
|
||||
|
||||
/**
|
||||
* Set all sheets to load
|
||||
* Tells the Reader to load all worksheets from the workbook.
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setLoadAllSheets();
|
||||
|
||||
/**
|
||||
* Read filter.
|
||||
*
|
||||
* @return IReadFilter
|
||||
*/
|
||||
public function getReadFilter();
|
||||
|
||||
/**
|
||||
* Set read filter.
|
||||
*
|
||||
* @param IReadFilter $pValue
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setReadFilter(IReadFilter $pValue);
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from file.
|
||||
*
|
||||
|
||||
@ -7,7 +7,7 @@ use DateTimeZone;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\Document\Properties;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
@ -25,7 +25,7 @@ class Ods extends BaseReader
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
parent::__construct();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ class Ods extends BaseReader
|
||||
$stat = $zip->statName('mimetype');
|
||||
if ($stat && ($stat['size'] <= 255)) {
|
||||
$mimeType = $zip->getFromName($stat['name']);
|
||||
} elseif ($stat = $zip->statName('META-INF/manifest.xml')) {
|
||||
} elseif ($zip->statName('META-INF/manifest.xml')) {
|
||||
$xml = simplexml_load_string(
|
||||
$this->securityScanner->scan($zip->getFromName('META-INF/manifest.xml')),
|
||||
'SimpleXMLElement',
|
||||
@ -265,7 +265,7 @@ class Ods extends BaseReader
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if (!$zip->open($pFilename)) {
|
||||
throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.');
|
||||
throw new Exception("Could not open {$pFilename} for reading! Error opening file.");
|
||||
}
|
||||
|
||||
// Meta
|
||||
@ -275,97 +275,13 @@ class Ods extends BaseReader
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
if ($xml === false) {
|
||||
throw new Exception('Unable to read data from {$pFilename}');
|
||||
}
|
||||
|
||||
$namespacesMeta = $xml->getNamespaces(true);
|
||||
|
||||
$docProps = $spreadsheet->getProperties();
|
||||
$officeProperty = $xml->children($namespacesMeta['office']);
|
||||
foreach ($officeProperty as $officePropertyData) {
|
||||
$officePropertyDC = [];
|
||||
if (isset($namespacesMeta['dc'])) {
|
||||
$officePropertyDC = $officePropertyData->children($namespacesMeta['dc']);
|
||||
}
|
||||
foreach ($officePropertyDC as $propertyName => $propertyValue) {
|
||||
$propertyValue = (string) $propertyValue;
|
||||
switch ($propertyName) {
|
||||
case 'title':
|
||||
$docProps->setTitle($propertyValue);
|
||||
|
||||
break;
|
||||
case 'subject':
|
||||
$docProps->setSubject($propertyValue);
|
||||
|
||||
break;
|
||||
case 'creator':
|
||||
$docProps->setCreator($propertyValue);
|
||||
$docProps->setLastModifiedBy($propertyValue);
|
||||
|
||||
break;
|
||||
case 'date':
|
||||
$creationDate = strtotime($propertyValue);
|
||||
$docProps->setCreated($creationDate);
|
||||
$docProps->setModified($creationDate);
|
||||
|
||||
break;
|
||||
case 'description':
|
||||
$docProps->setDescription($propertyValue);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$officePropertyMeta = [];
|
||||
if (isset($namespacesMeta['dc'])) {
|
||||
$officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
|
||||
}
|
||||
foreach ($officePropertyMeta as $propertyName => $propertyValue) {
|
||||
$propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']);
|
||||
$propertyValue = (string) $propertyValue;
|
||||
switch ($propertyName) {
|
||||
case 'initial-creator':
|
||||
$docProps->setCreator($propertyValue);
|
||||
|
||||
break;
|
||||
case 'keyword':
|
||||
$docProps->setKeywords($propertyValue);
|
||||
|
||||
break;
|
||||
case 'creation-date':
|
||||
$creationDate = strtotime($propertyValue);
|
||||
$docProps->setCreated($creationDate);
|
||||
|
||||
break;
|
||||
case 'user-defined':
|
||||
$propertyValueType = Properties::PROPERTY_TYPE_STRING;
|
||||
foreach ($propertyValueAttributes as $key => $value) {
|
||||
if ($key == 'name') {
|
||||
$propertyValueName = (string) $value;
|
||||
} elseif ($key == 'value-type') {
|
||||
switch ($value) {
|
||||
case 'date':
|
||||
$propertyValue = Properties::convertProperty($propertyValue, 'date');
|
||||
$propertyValueType = Properties::PROPERTY_TYPE_DATE;
|
||||
|
||||
break;
|
||||
case 'boolean':
|
||||
$propertyValue = Properties::convertProperty($propertyValue, 'bool');
|
||||
$propertyValueType = Properties::PROPERTY_TYPE_BOOLEAN;
|
||||
|
||||
break;
|
||||
case 'float':
|
||||
$propertyValue = Properties::convertProperty($propertyValue, 'r4');
|
||||
$propertyValueType = Properties::PROPERTY_TYPE_FLOAT;
|
||||
|
||||
break;
|
||||
default:
|
||||
$propertyValueType = Properties::PROPERTY_TYPE_STRING;
|
||||
}
|
||||
}
|
||||
}
|
||||
$docProps->setCustomProperty($propertyValueName, $propertyValue, $propertyValueType);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
(new DocumentProperties($spreadsheet))->load($xml, $namespacesMeta);
|
||||
|
||||
// Content
|
||||
|
||||
@ -513,7 +429,7 @@ class Ods extends BaseReader
|
||||
foreach ($paragraphs as $pData) {
|
||||
$dataArray[] = $this->scanElementForText($pData);
|
||||
}
|
||||
$allCellDataText = implode($dataArray, "\n");
|
||||
$allCellDataText = implode("\n", $dataArray);
|
||||
|
||||
$type = $cellData->getAttributeNS($officeNs, 'value-type');
|
||||
|
||||
@ -580,12 +496,12 @@ class Ods extends BaseReader
|
||||
);
|
||||
|
||||
$dataValue = Date::formattedPHPToExcel(
|
||||
$year,
|
||||
$month,
|
||||
$day,
|
||||
$hour,
|
||||
$minute,
|
||||
$second
|
||||
(int) $year,
|
||||
(int) $month,
|
||||
(int) $day,
|
||||
(int) $hour,
|
||||
(int) $minute,
|
||||
(int) $second
|
||||
);
|
||||
|
||||
if ($dataValue != floor($dataValue)) {
|
||||
|
||||
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Ods;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
|
||||
class Properties
|
||||
{
|
||||
private $spreadsheet;
|
||||
|
||||
public function __construct(Spreadsheet $spreadsheet)
|
||||
{
|
||||
$this->spreadsheet = $spreadsheet;
|
||||
}
|
||||
|
||||
public function load(\SimpleXMLElement $xml, $namespacesMeta)
|
||||
{
|
||||
$docProps = $this->spreadsheet->getProperties();
|
||||
$officeProperty = $xml->children($namespacesMeta['office']);
|
||||
foreach ($officeProperty as $officePropertyData) {
|
||||
/** @var \SimpleXMLElement $officePropertyData */
|
||||
$officePropertiesDC = (object) [];
|
||||
if (isset($namespacesMeta['dc'])) {
|
||||
$officePropertiesDC = $officePropertyData->children($namespacesMeta['dc']);
|
||||
}
|
||||
$this->setCoreProperties($docProps, $officePropertiesDC);
|
||||
|
||||
$officePropertyMeta = (object) [];
|
||||
if (isset($namespacesMeta['dc'])) {
|
||||
$officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
|
||||
}
|
||||
foreach ($officePropertyMeta as $propertyName => $propertyValue) {
|
||||
$this->setMetaProperties($namespacesMeta, $propertyValue, $propertyName, $docProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setCoreProperties(DocumentProperties $docProps, \SimpleXMLElement $officePropertyDC)
|
||||
{
|
||||
foreach ($officePropertyDC as $propertyName => $propertyValue) {
|
||||
$propertyValue = (string) $propertyValue;
|
||||
switch ($propertyName) {
|
||||
case 'title':
|
||||
$docProps->setTitle($propertyValue);
|
||||
|
||||
break;
|
||||
case 'subject':
|
||||
$docProps->setSubject($propertyValue);
|
||||
|
||||
break;
|
||||
case 'creator':
|
||||
$docProps->setCreator($propertyValue);
|
||||
$docProps->setLastModifiedBy($propertyValue);
|
||||
|
||||
break;
|
||||
case 'creation-date':
|
||||
$creationDate = strtotime($propertyValue);
|
||||
$docProps->setCreated($creationDate);
|
||||
$docProps->setModified($creationDate);
|
||||
|
||||
break;
|
||||
case 'keyword':
|
||||
$docProps->setKeywords($propertyValue);
|
||||
|
||||
break;
|
||||
case 'description':
|
||||
$docProps->setDescription($propertyValue);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setMetaProperties(
|
||||
$namespacesMeta,
|
||||
\SimpleXMLElement $propertyValue,
|
||||
$propertyName,
|
||||
DocumentProperties $docProps
|
||||
) {
|
||||
$propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']);
|
||||
$propertyValue = (string) $propertyValue;
|
||||
switch ($propertyName) {
|
||||
case 'initial-creator':
|
||||
$docProps->setCreator($propertyValue);
|
||||
|
||||
break;
|
||||
case 'keyword':
|
||||
$docProps->setKeywords($propertyValue);
|
||||
|
||||
break;
|
||||
case 'creation-date':
|
||||
$creationDate = strtotime($propertyValue);
|
||||
$docProps->setCreated($creationDate);
|
||||
|
||||
break;
|
||||
case 'user-defined':
|
||||
$this->setUserDefinedProperty($propertyValueAttributes, $propertyValue, $docProps);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function setUserDefinedProperty($propertyValueAttributes, $propertyValue, DocumentProperties $docProps)
|
||||
{
|
||||
$propertyValueName = '';
|
||||
$propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
|
||||
foreach ($propertyValueAttributes as $key => $value) {
|
||||
if ($key == 'name') {
|
||||
$propertyValueName = (string) $value;
|
||||
} elseif ($key == 'value-type') {
|
||||
switch ($value) {
|
||||
case 'date':
|
||||
$propertyValue = DocumentProperties::convertProperty($propertyValue, 'date');
|
||||
$propertyValueType = DocumentProperties::PROPERTY_TYPE_DATE;
|
||||
|
||||
break;
|
||||
case 'boolean':
|
||||
$propertyValue = DocumentProperties::convertProperty($propertyValue, 'bool');
|
||||
$propertyValueType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
|
||||
|
||||
break;
|
||||
case 'float':
|
||||
$propertyValue = DocumentProperties::convertProperty($propertyValue, 'r4');
|
||||
$propertyValueType = DocumentProperties::PROPERTY_TYPE_FLOAT;
|
||||
|
||||
break;
|
||||
default:
|
||||
$propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$docProps->setCustomProperty($propertyValueName, $propertyValue, $propertyValueType);
|
||||
}
|
||||
}
|
||||
@ -3,16 +3,10 @@
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Security;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
|
||||
class XmlScanner
|
||||
{
|
||||
/**
|
||||
* Identifies whether the thread-safe libxmlDisableEntityLoader() function is available.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $libxmlDisableEntityLoader = false;
|
||||
|
||||
/**
|
||||
* String used to identify risky xml elements.
|
||||
*
|
||||
@ -22,10 +16,16 @@ class XmlScanner
|
||||
|
||||
private $callback;
|
||||
|
||||
private function __construct($pattern = '<!DOCTYPE')
|
||||
private static $libxmlDisableEntityLoaderValue;
|
||||
|
||||
public function __construct($pattern = '<!DOCTYPE')
|
||||
{
|
||||
$this->pattern = $pattern;
|
||||
$this->libxmlDisableEntityLoader = $this->identifyLibxmlDisableEntityLoaderAvailability();
|
||||
|
||||
$this->disableEntityLoaderCheck();
|
||||
|
||||
// A fatal error will bypass the destructor, so we register a shutdown here
|
||||
register_shutdown_function([__CLASS__, 'shutdown']);
|
||||
}
|
||||
|
||||
public static function getInstance(Reader\IReader $reader)
|
||||
@ -43,7 +43,7 @@ class XmlScanner
|
||||
}
|
||||
}
|
||||
|
||||
private function identifyLibxmlDisableEntityLoaderAvailability()
|
||||
public static function threadSafeLibxmlDisableEntityLoaderAvailability()
|
||||
{
|
||||
if (PHP_MAJOR_VERSION == 7) {
|
||||
switch (PHP_MINOR_VERSION) {
|
||||
@ -61,11 +61,54 @@ class XmlScanner
|
||||
return false;
|
||||
}
|
||||
|
||||
private function disableEntityLoaderCheck()
|
||||
{
|
||||
if (Settings::getLibXmlDisableEntityLoader()) {
|
||||
$libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
|
||||
|
||||
if (self::$libxmlDisableEntityLoaderValue === null) {
|
||||
self::$libxmlDisableEntityLoaderValue = $libxmlDisableEntityLoaderValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function shutdown()
|
||||
{
|
||||
if (self::$libxmlDisableEntityLoaderValue !== null) {
|
||||
libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue);
|
||||
self::$libxmlDisableEntityLoaderValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
self::shutdown();
|
||||
}
|
||||
|
||||
public function setAdditionalCallback(callable $callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
private function toUtf8($xml)
|
||||
{
|
||||
$pattern = '/encoding="(.*?)"/';
|
||||
$result = preg_match($pattern, $xml, $matches);
|
||||
$charset = strtoupper($result ? $matches[1] : 'UTF-8');
|
||||
|
||||
if ($charset !== 'UTF-8') {
|
||||
$xml = mb_convert_encoding($xml, 'UTF-8', $charset);
|
||||
|
||||
$result = preg_match($pattern, $xml, $matches);
|
||||
$charset = strtoupper($result ? $matches[1] : 'UTF-8');
|
||||
if ($charset !== 'UTF-8') {
|
||||
throw new Reader\Exception('Suspicious Double-encoded XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
|
||||
}
|
||||
}
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the XML for use of <!ENTITY to prevent XXE/XEE attacks.
|
||||
*
|
||||
@ -77,33 +120,19 @@ class XmlScanner
|
||||
*/
|
||||
public function scan($xml)
|
||||
{
|
||||
if ($this->libxmlDisableEntityLoader) {
|
||||
$previousLibxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
|
||||
}
|
||||
$this->disableEntityLoaderCheck();
|
||||
|
||||
$pattern = '/encoding="(.*?)"/';
|
||||
$result = preg_match($pattern, $xml, $matches);
|
||||
$charset = $result ? $matches[1] : 'UTF-8';
|
||||
|
||||
if ($charset !== 'UTF-8') {
|
||||
$xml = mb_convert_encoding($xml, 'UTF-8', $charset);
|
||||
}
|
||||
$xml = $this->toUtf8($xml);
|
||||
|
||||
// Don't rely purely on libxml_disable_entity_loader()
|
||||
$pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/';
|
||||
|
||||
try {
|
||||
if (preg_match($pattern, $xml)) {
|
||||
throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
|
||||
}
|
||||
if (preg_match($pattern, $xml)) {
|
||||
throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
|
||||
}
|
||||
|
||||
if ($this->callback !== null && is_callable($this->callback)) {
|
||||
$xml = call_user_func($this->callback, $xml);
|
||||
}
|
||||
} finally {
|
||||
if (isset($previousLibxmlDisableEntityLoaderValue)) {
|
||||
libxml_disable_entity_loader($previousLibxmlDisableEntityLoaderValue);
|
||||
}
|
||||
if ($this->callback !== null && is_callable($this->callback)) {
|
||||
$xml = call_user_func($this->callback, $xml);
|
||||
}
|
||||
|
||||
return $xml;
|
||||
|
||||
@ -43,7 +43,7 @@ class Slk extends BaseReader
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -412,7 +412,7 @@ class Xls extends BaseReader
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -4,10 +4,19 @@ namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
|
||||
use PhpOffice\PhpSpreadsheet\Document\Properties;
|
||||
use PhpOffice\PhpSpreadsheet\NamedRange;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\AutoFilter;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Chart;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\ColumnAndRowAttributes;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\ConditionalStyles;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\DataValidations;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Hyperlinks;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\PageSetup;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Properties as PropertyReader;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViewOptions;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViews;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Styles;
|
||||
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
@ -20,11 +29,9 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Borders;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Color;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Protection;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
@ -52,7 +59,7 @@ class Xlsx extends BaseReader
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
parent::__construct();
|
||||
$this->referenceHelper = ReferenceHelper::getInstance();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
}
|
||||
@ -323,60 +330,6 @@ class Xlsx extends BaseReader
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Worksheet column attributes by attributes array passed.
|
||||
*
|
||||
* @param Worksheet $docSheet
|
||||
* @param string $column A, B, ... DX, ...
|
||||
* @param array $columnAttributes array of attributes (indexes are attribute name, values are value)
|
||||
* 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ?
|
||||
*/
|
||||
private function setColumnAttributes(Worksheet $docSheet, $column, array $columnAttributes)
|
||||
{
|
||||
if (isset($columnAttributes['xfIndex'])) {
|
||||
$docSheet->getColumnDimension($column)->setXfIndex($columnAttributes['xfIndex']);
|
||||
}
|
||||
if (isset($columnAttributes['visible'])) {
|
||||
$docSheet->getColumnDimension($column)->setVisible($columnAttributes['visible']);
|
||||
}
|
||||
if (isset($columnAttributes['collapsed'])) {
|
||||
$docSheet->getColumnDimension($column)->setCollapsed($columnAttributes['collapsed']);
|
||||
}
|
||||
if (isset($columnAttributes['outlineLevel'])) {
|
||||
$docSheet->getColumnDimension($column)->setOutlineLevel($columnAttributes['outlineLevel']);
|
||||
}
|
||||
if (isset($columnAttributes['width'])) {
|
||||
$docSheet->getColumnDimension($column)->setWidth($columnAttributes['width']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Worksheet row attributes by attributes array passed.
|
||||
*
|
||||
* @param Worksheet $docSheet
|
||||
* @param int $row 1, 2, 3, ... 99, ...
|
||||
* @param array $rowAttributes array of attributes (indexes are attribute name, values are value)
|
||||
* 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
|
||||
*/
|
||||
private function setRowAttributes(Worksheet $docSheet, $row, array $rowAttributes)
|
||||
{
|
||||
if (isset($rowAttributes['xfIndex'])) {
|
||||
$docSheet->getRowDimension($row)->setXfIndex($rowAttributes['xfIndex']);
|
||||
}
|
||||
if (isset($rowAttributes['visible'])) {
|
||||
$docSheet->getRowDimension($row)->setVisible($rowAttributes['visible']);
|
||||
}
|
||||
if (isset($rowAttributes['collapsed'])) {
|
||||
$docSheet->getRowDimension($row)->setCollapsed($rowAttributes['collapsed']);
|
||||
}
|
||||
if (isset($rowAttributes['outlineLevel'])) {
|
||||
$docSheet->getRowDimension($row)->setOutlineLevel($rowAttributes['outlineLevel']);
|
||||
}
|
||||
if (isset($rowAttributes['rowHeight'])) {
|
||||
$docSheet->getRowDimension($row)->setRowHeight($rowAttributes['rowHeight']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Spreadsheet from file.
|
||||
*
|
||||
@ -456,70 +409,20 @@ class Xlsx extends BaseReader
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
|
||||
$propertyReader = new PropertyReader($this->securityScanner, $excel->getProperties());
|
||||
foreach ($rels->Relationship as $rel) {
|
||||
switch ($rel['Type']) {
|
||||
case 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties':
|
||||
$xmlCore = simplexml_load_string(
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
if (is_object($xmlCore)) {
|
||||
$xmlCore->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
|
||||
$xmlCore->registerXPathNamespace('dcterms', 'http://purl.org/dc/terms/');
|
||||
$xmlCore->registerXPathNamespace('cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
|
||||
$docProps = $excel->getProperties();
|
||||
$docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
|
||||
$docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
|
||||
$docProps->setCreated(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:created')))); //! respect xsi:type
|
||||
$docProps->setModified(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:modified')))); //! respect xsi:type
|
||||
$docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
|
||||
$docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
|
||||
$docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
|
||||
$docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
|
||||
$docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
|
||||
}
|
||||
$propertyReader->readCoreProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
|
||||
|
||||
break;
|
||||
case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties':
|
||||
$xmlCore = simplexml_load_string(
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
if (is_object($xmlCore)) {
|
||||
$docProps = $excel->getProperties();
|
||||
if (isset($xmlCore->Company)) {
|
||||
$docProps->setCompany((string) $xmlCore->Company);
|
||||
}
|
||||
if (isset($xmlCore->Manager)) {
|
||||
$docProps->setManager((string) $xmlCore->Manager);
|
||||
}
|
||||
}
|
||||
$propertyReader->readExtendedProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
|
||||
|
||||
break;
|
||||
case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties':
|
||||
$xmlCore = simplexml_load_string(
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}")),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
if (is_object($xmlCore)) {
|
||||
$docProps = $excel->getProperties();
|
||||
/** @var SimpleXMLElement $xmlProperty */
|
||||
foreach ($xmlCore as $xmlProperty) {
|
||||
$cellDataOfficeAttributes = $xmlProperty->attributes();
|
||||
if (isset($cellDataOfficeAttributes['name'])) {
|
||||
$propertyName = (string) $cellDataOfficeAttributes['name'];
|
||||
$cellDataOfficeChildren = $xmlProperty->children('http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
|
||||
$attributeType = $cellDataOfficeChildren->getName();
|
||||
$attributeValue = (string) $cellDataOfficeChildren->{$attributeType};
|
||||
$attributeValue = Properties::convertProperty($attributeValue, $attributeType);
|
||||
$attributeType = Properties::convertPropertyType($attributeType);
|
||||
$docProps->setCustomProperty($propertyName, $attributeValue, $attributeType);
|
||||
}
|
||||
}
|
||||
}
|
||||
$propertyReader->readCustomProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
|
||||
|
||||
break;
|
||||
//Ribbon
|
||||
@ -586,8 +489,7 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
}
|
||||
}
|
||||
$styles = [];
|
||||
$cellStyles = [];
|
||||
|
||||
$xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']"));
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
$xmlStyles = simplexml_load_string(
|
||||
@ -595,6 +497,9 @@ class Xlsx extends BaseReader
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
|
||||
$styles = [];
|
||||
$cellStyles = [];
|
||||
$numFmts = null;
|
||||
if ($xmlStyles && $xmlStyles->numFmts[0]) {
|
||||
$numFmts = $xmlStyles->numFmts[0];
|
||||
@ -674,31 +579,10 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
}
|
||||
|
||||
$dxfs = [];
|
||||
if (!$this->readDataOnly && $xmlStyles) {
|
||||
// Conditional Styles
|
||||
if ($xmlStyles->dxfs) {
|
||||
foreach ($xmlStyles->dxfs->dxf as $dxf) {
|
||||
$style = new Style(false, true);
|
||||
self::readStyle($style, $dxf);
|
||||
$dxfs[] = $style;
|
||||
}
|
||||
}
|
||||
// Cell Styles
|
||||
if ($xmlStyles->cellStyles) {
|
||||
foreach ($xmlStyles->cellStyles->cellStyle as $cellStyle) {
|
||||
if ((int) ($cellStyle['builtinId']) == 0) {
|
||||
if (isset($cellStyles[(int) ($cellStyle['xfId'])])) {
|
||||
// Set default style
|
||||
$style = new Style();
|
||||
self::readStyle($style, $cellStyles[(int) ($cellStyle['xfId'])]);
|
||||
|
||||
// normal style, currently not using it for anything
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$styleReader = new Styles($xmlStyles);
|
||||
$styleReader->setStyleBaseData(self::$theme, $styles, $cellStyles);
|
||||
$dxfs = $styleReader->dxfs($this->readDataOnly);
|
||||
$styles = $styleReader->styles();
|
||||
|
||||
//~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
|
||||
$xmlWorkbook = simplexml_load_string(
|
||||
@ -765,134 +649,19 @@ class Xlsx extends BaseReader
|
||||
$docSheet->setSheetState((string) $eleSheet['state']);
|
||||
}
|
||||
|
||||
if (isset($xmlSheet->sheetViews, $xmlSheet->sheetViews->sheetView)) {
|
||||
if (isset($xmlSheet->sheetViews->sheetView['zoomScale'])) {
|
||||
$zoomScale = (int) ($xmlSheet->sheetViews->sheetView['zoomScale']);
|
||||
if ($zoomScale <= 0) {
|
||||
// setZoomScale will throw an Exception if the scale is less than or equals 0
|
||||
// that is OK when manually creating documents, but we should be able to read all documents
|
||||
$zoomScale = 100;
|
||||
}
|
||||
|
||||
$docSheet->getSheetView()->setZoomScale($zoomScale);
|
||||
}
|
||||
if (isset($xmlSheet->sheetViews->sheetView['zoomScaleNormal'])) {
|
||||
$zoomScaleNormal = (int) ($xmlSheet->sheetViews->sheetView['zoomScaleNormal']);
|
||||
if ($zoomScaleNormal <= 0) {
|
||||
// setZoomScaleNormal will throw an Exception if the scale is less than or equals 0
|
||||
// that is OK when manually creating documents, but we should be able to read all documents
|
||||
$zoomScaleNormal = 100;
|
||||
}
|
||||
|
||||
$docSheet->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
|
||||
}
|
||||
if (isset($xmlSheet->sheetViews->sheetView['view'])) {
|
||||
$docSheet->getSheetView()->setView((string) $xmlSheet->sheetViews->sheetView['view']);
|
||||
}
|
||||
if (isset($xmlSheet->sheetViews->sheetView['showGridLines'])) {
|
||||
$docSheet->setShowGridLines(self::boolean((string) $xmlSheet->sheetViews->sheetView['showGridLines']));
|
||||
}
|
||||
if (isset($xmlSheet->sheetViews->sheetView['showRowColHeaders'])) {
|
||||
$docSheet->setShowRowColHeaders(self::boolean((string) $xmlSheet->sheetViews->sheetView['showRowColHeaders']));
|
||||
}
|
||||
if (isset($xmlSheet->sheetViews->sheetView['rightToLeft'])) {
|
||||
$docSheet->setRightToLeft(self::boolean((string) $xmlSheet->sheetViews->sheetView['rightToLeft']));
|
||||
}
|
||||
if (isset($xmlSheet->sheetViews->sheetView->pane)) {
|
||||
$xSplit = 0;
|
||||
$ySplit = 0;
|
||||
$topLeftCell = null;
|
||||
|
||||
if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
|
||||
$xSplit = (int) ($xmlSheet->sheetViews->sheetView->pane['xSplit']);
|
||||
}
|
||||
|
||||
if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
|
||||
$ySplit = (int) ($xmlSheet->sheetViews->sheetView->pane['ySplit']);
|
||||
}
|
||||
|
||||
if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
|
||||
$topLeftCell = (string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell'];
|
||||
}
|
||||
|
||||
$docSheet->freezePane(Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1), $topLeftCell);
|
||||
if ($xmlSheet) {
|
||||
if (isset($xmlSheet->sheetViews, $xmlSheet->sheetViews->sheetView)) {
|
||||
$sheetViews = new SheetViews($xmlSheet->sheetViews->sheetView, $docSheet);
|
||||
$sheetViews->load();
|
||||
}
|
||||
|
||||
if (isset($xmlSheet->sheetViews->sheetView->selection)) {
|
||||
if (isset($xmlSheet->sheetViews->sheetView->selection['sqref'])) {
|
||||
$sqref = (string) $xmlSheet->sheetViews->sheetView->selection['sqref'];
|
||||
$sqref = explode(' ', $sqref);
|
||||
$sqref = $sqref[0];
|
||||
$docSheet->setSelectedCells($sqref);
|
||||
}
|
||||
}
|
||||
$sheetViewOptions = new SheetViewOptions($docSheet, $xmlSheet);
|
||||
$sheetViewOptions->load($this->getReadDataOnly());
|
||||
|
||||
(new ColumnAndRowAttributes($docSheet, $xmlSheet))
|
||||
->load($this->getReadFilter(), $this->getReadDataOnly());
|
||||
}
|
||||
|
||||
if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->tabColor)) {
|
||||
if (isset($xmlSheet->sheetPr->tabColor['rgb'])) {
|
||||
$docSheet->getTabColor()->setARGB((string) $xmlSheet->sheetPr->tabColor['rgb']);
|
||||
}
|
||||
}
|
||||
if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr['codeName'])) {
|
||||
$docSheet->setCodeName((string) $xmlSheet->sheetPr['codeName'], false);
|
||||
}
|
||||
if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->outlinePr)) {
|
||||
if (isset($xmlSheet->sheetPr->outlinePr['summaryRight']) &&
|
||||
!self::boolean((string) $xmlSheet->sheetPr->outlinePr['summaryRight'])) {
|
||||
$docSheet->setShowSummaryRight(false);
|
||||
} else {
|
||||
$docSheet->setShowSummaryRight(true);
|
||||
}
|
||||
|
||||
if (isset($xmlSheet->sheetPr->outlinePr['summaryBelow']) &&
|
||||
!self::boolean((string) $xmlSheet->sheetPr->outlinePr['summaryBelow'])) {
|
||||
$docSheet->setShowSummaryBelow(false);
|
||||
} else {
|
||||
$docSheet->setShowSummaryBelow(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->pageSetUpPr)) {
|
||||
if (isset($xmlSheet->sheetPr->pageSetUpPr['fitToPage']) &&
|
||||
!self::boolean((string) $xmlSheet->sheetPr->pageSetUpPr['fitToPage'])) {
|
||||
$docSheet->getPageSetup()->setFitToPage(false);
|
||||
} else {
|
||||
$docSheet->getPageSetup()->setFitToPage(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($xmlSheet->sheetFormatPr)) {
|
||||
if (isset($xmlSheet->sheetFormatPr['customHeight']) &&
|
||||
self::boolean((string) $xmlSheet->sheetFormatPr['customHeight']) &&
|
||||
isset($xmlSheet->sheetFormatPr['defaultRowHeight'])) {
|
||||
$docSheet->getDefaultRowDimension()->setRowHeight((float) $xmlSheet->sheetFormatPr['defaultRowHeight']);
|
||||
}
|
||||
if (isset($xmlSheet->sheetFormatPr['defaultColWidth'])) {
|
||||
$docSheet->getDefaultColumnDimension()->setWidth((float) $xmlSheet->sheetFormatPr['defaultColWidth']);
|
||||
}
|
||||
if (isset($xmlSheet->sheetFormatPr['zeroHeight']) &&
|
||||
((string) $xmlSheet->sheetFormatPr['zeroHeight'] == '1')) {
|
||||
$docSheet->getDefaultRowDimension()->setZeroHeight(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($xmlSheet->printOptions) && !$this->readDataOnly) {
|
||||
if (self::boolean((string) $xmlSheet->printOptions['gridLinesSet'])) {
|
||||
$docSheet->setShowGridlines(true);
|
||||
}
|
||||
if (self::boolean((string) $xmlSheet->printOptions['gridLines'])) {
|
||||
$docSheet->setPrintGridlines(true);
|
||||
}
|
||||
if (self::boolean((string) $xmlSheet->printOptions['horizontalCentered'])) {
|
||||
$docSheet->getPageSetup()->setHorizontalCentered(true);
|
||||
}
|
||||
if (self::boolean((string) $xmlSheet->printOptions['verticalCentered'])) {
|
||||
$docSheet->getPageSetup()->setVerticalCentered(true);
|
||||
}
|
||||
}
|
||||
|
||||
$this->readColumnsAndRowsAttributes($xmlSheet, $docSheet);
|
||||
|
||||
if ($xmlSheet && $xmlSheet->sheetData && $xmlSheet->sheetData->row) {
|
||||
$cIndex = 1; // Cell Start from 1
|
||||
foreach ($xmlSheet->sheetData->row as $row) {
|
||||
@ -972,38 +741,39 @@ class Xlsx extends BaseReader
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for numeric values
|
||||
if (is_numeric($value) && $cellDataType != 's') {
|
||||
if ($value == (int) $value) {
|
||||
$value = (int) $value;
|
||||
} elseif ($value == (float) $value) {
|
||||
$value = (float) $value;
|
||||
} elseif ($value == (float) $value) {
|
||||
$value = (float) $value;
|
||||
// read empty cells or the cells are not empty
|
||||
if ($this->readEmptyCells || ($value !== null && $value !== '')) {
|
||||
// Check for numeric values
|
||||
if (is_numeric($value) && $cellDataType != 's') {
|
||||
if ($value == (int) $value) {
|
||||
$value = (int) $value;
|
||||
} elseif ($value == (float) $value) {
|
||||
$value = (float) $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rich text?
|
||||
if ($value instanceof RichText && $this->readDataOnly) {
|
||||
$value = $value->getPlainText();
|
||||
}
|
||||
// Rich text?
|
||||
if ($value instanceof RichText && $this->readDataOnly) {
|
||||
$value = $value->getPlainText();
|
||||
}
|
||||
|
||||
$cell = $docSheet->getCell($r);
|
||||
// Assign value
|
||||
if ($cellDataType != '') {
|
||||
$cell->setValueExplicit($value, $cellDataType);
|
||||
} else {
|
||||
$cell->setValue($value);
|
||||
}
|
||||
if ($calculatedValue !== null) {
|
||||
$cell->setCalculatedValue($calculatedValue);
|
||||
}
|
||||
$cell = $docSheet->getCell($r);
|
||||
// Assign value
|
||||
if ($cellDataType != '') {
|
||||
$cell->setValueExplicit($value, $cellDataType);
|
||||
} else {
|
||||
$cell->setValue($value);
|
||||
}
|
||||
if ($calculatedValue !== null) {
|
||||
$cell->setCalculatedValue($calculatedValue);
|
||||
}
|
||||
|
||||
// Style information?
|
||||
if ($c['s'] && !$this->readDataOnly) {
|
||||
// no style index means 0, it seems
|
||||
$cell->setXfIndex(isset($styles[(int) ($c['s'])]) ?
|
||||
(int) ($c['s']) : 0);
|
||||
// Style information?
|
||||
if ($c['s'] && !$this->readDataOnly) {
|
||||
// no style index means 0, it seems
|
||||
$cell->setXfIndex(isset($styles[(int) ($c['s'])]) ?
|
||||
(int) ($c['s']) : 0);
|
||||
}
|
||||
}
|
||||
$rowIndex += 1;
|
||||
}
|
||||
@ -1011,49 +781,8 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
}
|
||||
|
||||
$conditionals = [];
|
||||
if (!$this->readDataOnly && $xmlSheet && $xmlSheet->conditionalFormatting) {
|
||||
foreach ($xmlSheet->conditionalFormatting as $conditional) {
|
||||
foreach ($conditional->cfRule as $cfRule) {
|
||||
if (((string) $cfRule['type'] == Conditional::CONDITION_NONE || (string) $cfRule['type'] == Conditional::CONDITION_CELLIS || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION) && isset($dxfs[(int) ($cfRule['dxfId'])])) {
|
||||
$conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($conditionals as $ref => $cfRules) {
|
||||
ksort($cfRules);
|
||||
$conditionalStyles = [];
|
||||
foreach ($cfRules as $cfRule) {
|
||||
$objConditional = new Conditional();
|
||||
$objConditional->setConditionType((string) $cfRule['type']);
|
||||
$objConditional->setOperatorType((string) $cfRule['operator']);
|
||||
|
||||
if ((string) $cfRule['text'] != '') {
|
||||
$objConditional->setText((string) $cfRule['text']);
|
||||
}
|
||||
|
||||
if (isset($cfRule['stopIfTrue']) && (int) $cfRule['stopIfTrue'] === 1) {
|
||||
$objConditional->setStopIfTrue(true);
|
||||
}
|
||||
|
||||
if (count($cfRule->formula) > 1) {
|
||||
foreach ($cfRule->formula as $formula) {
|
||||
$objConditional->addCondition((string) $formula);
|
||||
}
|
||||
} else {
|
||||
$objConditional->addCondition((string) $cfRule->formula);
|
||||
}
|
||||
$objConditional->setStyle(clone $dxfs[(int) ($cfRule['dxfId'])]);
|
||||
$conditionalStyles[] = $objConditional;
|
||||
}
|
||||
|
||||
// Extract all cell references in $ref
|
||||
$cellBlocks = explode(' ', str_replace('$', '', strtoupper($ref)));
|
||||
foreach ($cellBlocks as $cellBlock) {
|
||||
$docSheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
|
||||
}
|
||||
}
|
||||
(new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->load();
|
||||
}
|
||||
|
||||
$aKeys = ['sheet', 'objects', 'scenarios', 'formatCells', 'formatColumns', 'formatRows', 'insertColumns', 'insertRows', 'insertHyperlinks', 'deleteColumns', 'deleteRows', 'selectLockedCells', 'sort', 'autoFilter', 'pivotTables', 'selectUnlockedCells'];
|
||||
@ -1074,103 +803,7 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
|
||||
if ($xmlSheet && $xmlSheet->autoFilter && !$this->readDataOnly) {
|
||||
$autoFilterRange = (string) $xmlSheet->autoFilter['ref'];
|
||||
if (strpos($autoFilterRange, ':') !== false) {
|
||||
$autoFilter = $docSheet->getAutoFilter();
|
||||
$autoFilter->setRange($autoFilterRange);
|
||||
|
||||
foreach ($xmlSheet->autoFilter->filterColumn as $filterColumn) {
|
||||
$column = $autoFilter->getColumnByOffset((int) $filterColumn['colId']);
|
||||
// Check for standard filters
|
||||
if ($filterColumn->filters) {
|
||||
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
|
||||
$filters = $filterColumn->filters;
|
||||
if ((isset($filters['blank'])) && ($filters['blank'] == 1)) {
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
$column->createRule()->setRule(null, '')->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_FILTER);
|
||||
}
|
||||
// Standard filters are always an OR join, so no join rule needs to be set
|
||||
// Entries can be either filter elements
|
||||
foreach ($filters->filter as $filterRule) {
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
$column->createRule()->setRule(null, (string) $filterRule['val'])->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_FILTER);
|
||||
}
|
||||
// Or Date Group elements
|
||||
foreach ($filters->dateGroupItem as $dateGroupItem) {
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
$column->createRule()->setRule(
|
||||
null,
|
||||
[
|
||||
'year' => (string) $dateGroupItem['year'],
|
||||
'month' => (string) $dateGroupItem['month'],
|
||||
'day' => (string) $dateGroupItem['day'],
|
||||
'hour' => (string) $dateGroupItem['hour'],
|
||||
'minute' => (string) $dateGroupItem['minute'],
|
||||
'second' => (string) $dateGroupItem['second'],
|
||||
],
|
||||
(string) $dateGroupItem['dateTimeGrouping']
|
||||
)
|
||||
->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP);
|
||||
}
|
||||
}
|
||||
// Check for custom filters
|
||||
if ($filterColumn->customFilters) {
|
||||
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
|
||||
$customFilters = $filterColumn->customFilters;
|
||||
// Custom filters can an AND or an OR join;
|
||||
// and there should only ever be one or two entries
|
||||
if ((isset($customFilters['and'])) && ($customFilters['and'] == 1)) {
|
||||
$column->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
|
||||
}
|
||||
foreach ($customFilters->customFilter as $filterRule) {
|
||||
$column->createRule()->setRule(
|
||||
(string) $filterRule['operator'],
|
||||
(string) $filterRule['val']
|
||||
)
|
||||
->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
|
||||
}
|
||||
}
|
||||
// Check for dynamic filters
|
||||
if ($filterColumn->dynamicFilter) {
|
||||
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
|
||||
// We should only ever have one dynamic filter
|
||||
foreach ($filterColumn->dynamicFilter as $filterRule) {
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
$column->createRule()->setRule(
|
||||
null,
|
||||
(string) $filterRule['val'],
|
||||
(string) $filterRule['type']
|
||||
)
|
||||
->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
|
||||
if (isset($filterRule['val'])) {
|
||||
$column->setAttribute('val', (string) $filterRule['val']);
|
||||
}
|
||||
if (isset($filterRule['maxVal'])) {
|
||||
$column->setAttribute('maxVal', (string) $filterRule['maxVal']);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for dynamic filters
|
||||
if ($filterColumn->top10) {
|
||||
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER);
|
||||
// We should only ever have one top10 filter
|
||||
foreach ($filterColumn->top10 as $filterRule) {
|
||||
$column->createRule()->setRule(
|
||||
(((isset($filterRule['percent'])) && ($filterRule['percent'] == 1))
|
||||
? Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
|
||||
: Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
|
||||
),
|
||||
(string) $filterRule['val'],
|
||||
(((isset($filterRule['top'])) && ($filterRule['top'] == 1))
|
||||
? Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
|
||||
: Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
|
||||
)
|
||||
)
|
||||
->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_TOPTENFILTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(new AutoFilter($docSheet, $xmlSheet))->load();
|
||||
}
|
||||
|
||||
if ($xmlSheet && $xmlSheet->mergeCells && $xmlSheet->mergeCells->mergeCell && !$this->readDataOnly) {
|
||||
@ -1182,124 +815,12 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
}
|
||||
|
||||
if ($xmlSheet && $xmlSheet->pageMargins && !$this->readDataOnly) {
|
||||
$docPageMargins = $docSheet->getPageMargins();
|
||||
$docPageMargins->setLeft((float) ($xmlSheet->pageMargins['left']));
|
||||
$docPageMargins->setRight((float) ($xmlSheet->pageMargins['right']));
|
||||
$docPageMargins->setTop((float) ($xmlSheet->pageMargins['top']));
|
||||
$docPageMargins->setBottom((float) ($xmlSheet->pageMargins['bottom']));
|
||||
$docPageMargins->setHeader((float) ($xmlSheet->pageMargins['header']));
|
||||
$docPageMargins->setFooter((float) ($xmlSheet->pageMargins['footer']));
|
||||
}
|
||||
|
||||
if ($xmlSheet && $xmlSheet->pageSetup && !$this->readDataOnly) {
|
||||
$docPageSetup = $docSheet->getPageSetup();
|
||||
|
||||
if (isset($xmlSheet->pageSetup['orientation'])) {
|
||||
$docPageSetup->setOrientation((string) $xmlSheet->pageSetup['orientation']);
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['paperSize'])) {
|
||||
$docPageSetup->setPaperSize((int) ($xmlSheet->pageSetup['paperSize']));
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['scale'])) {
|
||||
$docPageSetup->setScale((int) ($xmlSheet->pageSetup['scale']), false);
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['fitToHeight']) && (int) ($xmlSheet->pageSetup['fitToHeight']) >= 0) {
|
||||
$docPageSetup->setFitToHeight((int) ($xmlSheet->pageSetup['fitToHeight']), false);
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['fitToWidth']) && (int) ($xmlSheet->pageSetup['fitToWidth']) >= 0) {
|
||||
$docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) &&
|
||||
self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) {
|
||||
$docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
|
||||
}
|
||||
|
||||
$relAttributes = $xmlSheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
|
||||
if (isset($relAttributes['id'])) {
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['pageSetupRelId'] = (string) $relAttributes['id'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($xmlSheet && $xmlSheet->headerFooter && !$this->readDataOnly) {
|
||||
$docHeaderFooter = $docSheet->getHeaderFooter();
|
||||
|
||||
if (isset($xmlSheet->headerFooter['differentOddEven']) &&
|
||||
self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])) {
|
||||
$docHeaderFooter->setDifferentOddEven(true);
|
||||
} else {
|
||||
$docHeaderFooter->setDifferentOddEven(false);
|
||||
}
|
||||
if (isset($xmlSheet->headerFooter['differentFirst']) &&
|
||||
self::boolean((string) $xmlSheet->headerFooter['differentFirst'])) {
|
||||
$docHeaderFooter->setDifferentFirst(true);
|
||||
} else {
|
||||
$docHeaderFooter->setDifferentFirst(false);
|
||||
}
|
||||
if (isset($xmlSheet->headerFooter['scaleWithDoc']) &&
|
||||
!self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])) {
|
||||
$docHeaderFooter->setScaleWithDocument(false);
|
||||
} else {
|
||||
$docHeaderFooter->setScaleWithDocument(true);
|
||||
}
|
||||
if (isset($xmlSheet->headerFooter['alignWithMargins']) &&
|
||||
!self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])) {
|
||||
$docHeaderFooter->setAlignWithMargins(false);
|
||||
} else {
|
||||
$docHeaderFooter->setAlignWithMargins(true);
|
||||
}
|
||||
|
||||
$docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
|
||||
$docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
|
||||
$docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
|
||||
$docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
|
||||
$docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
|
||||
$docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
|
||||
}
|
||||
|
||||
if ($xmlSheet && $xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk && !$this->readDataOnly) {
|
||||
foreach ($xmlSheet->rowBreaks->brk as $brk) {
|
||||
if ($brk['man']) {
|
||||
$docSheet->setBreak("A$brk[id]", Worksheet::BREAK_ROW);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($xmlSheet && $xmlSheet->colBreaks && $xmlSheet->colBreaks->brk && !$this->readDataOnly) {
|
||||
foreach ($xmlSheet->colBreaks->brk as $brk) {
|
||||
if ($brk['man']) {
|
||||
$docSheet->setBreak(Coordinate::stringFromColumnIndex((string) $brk['id'] + 1) . '1', Worksheet::BREAK_COLUMN);
|
||||
}
|
||||
}
|
||||
if ($xmlSheet && !$this->readDataOnly) {
|
||||
$unparsedLoadedData = (new PageSetup($docSheet, $xmlSheet))->load($unparsedLoadedData);
|
||||
}
|
||||
|
||||
if ($xmlSheet && $xmlSheet->dataValidations && !$this->readDataOnly) {
|
||||
foreach ($xmlSheet->dataValidations->dataValidation as $dataValidation) {
|
||||
// Uppercase coordinate
|
||||
$range = strtoupper($dataValidation['sqref']);
|
||||
$rangeSet = explode(' ', $range);
|
||||
foreach ($rangeSet as $range) {
|
||||
$stRange = $docSheet->shrinkRangeToFit($range);
|
||||
|
||||
// Extract all cell references in $range
|
||||
foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $reference) {
|
||||
// Create validation
|
||||
$docValidation = $docSheet->getCell($reference)->getDataValidation();
|
||||
$docValidation->setType((string) $dataValidation['type']);
|
||||
$docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
|
||||
$docValidation->setOperator((string) $dataValidation['operator']);
|
||||
$docValidation->setAllowBlank($dataValidation['allowBlank'] != 0);
|
||||
$docValidation->setShowDropDown($dataValidation['showDropDown'] == 0);
|
||||
$docValidation->setShowInputMessage($dataValidation['showInputMessage'] != 0);
|
||||
$docValidation->setShowErrorMessage($dataValidation['showErrorMessage'] != 0);
|
||||
$docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
|
||||
$docValidation->setError((string) $dataValidation['error']);
|
||||
$docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
|
||||
$docValidation->setPrompt((string) $dataValidation['prompt']);
|
||||
$docValidation->setFormula1((string) $dataValidation->formula1);
|
||||
$docValidation->setFormula2((string) $dataValidation->formula2);
|
||||
}
|
||||
}
|
||||
}
|
||||
(new DataValidations($docSheet, $xmlSheet))->load();
|
||||
}
|
||||
|
||||
// unparsed sheet AlternateContent
|
||||
@ -1313,50 +834,25 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
|
||||
// Add hyperlinks
|
||||
$hyperlinks = [];
|
||||
if (!$this->readDataOnly) {
|
||||
$hyperlinkReader = new Hyperlinks($docSheet);
|
||||
// Locate hyperlink relations
|
||||
if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
|
||||
$relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
|
||||
if ($zip->locateName($relationsFileName)) {
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$relsWorksheet = simplexml_load_string(
|
||||
$this->securityScanner->scan(
|
||||
$this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
|
||||
$this->getFromZipArchive($zip, $relationsFileName)
|
||||
),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
foreach ($relsWorksheet->Relationship as $ele) {
|
||||
if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
|
||||
$hyperlinks[(string) $ele['Id']] = (string) $ele['Target'];
|
||||
}
|
||||
}
|
||||
$hyperlinkReader->readHyperlinks($relsWorksheet);
|
||||
}
|
||||
|
||||
// Loop through hyperlinks
|
||||
if ($xmlSheet && $xmlSheet->hyperlinks) {
|
||||
/** @var SimpleXMLElement $hyperlink */
|
||||
foreach ($xmlSheet->hyperlinks->hyperlink as $hyperlink) {
|
||||
// Link url
|
||||
$linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
|
||||
|
||||
foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
|
||||
$cell = $docSheet->getCell($cellReference);
|
||||
if (isset($linkRel['id'])) {
|
||||
$hyperlinkUrl = $hyperlinks[(string) $linkRel['id']];
|
||||
if (isset($hyperlink['location'])) {
|
||||
$hyperlinkUrl .= '#' . (string) $hyperlink['location'];
|
||||
}
|
||||
$cell->getHyperlink()->setUrl($hyperlinkUrl);
|
||||
} elseif (isset($hyperlink['location'])) {
|
||||
$cell->getHyperlink()->setUrl('sheet://' . (string) $hyperlink['location']);
|
||||
}
|
||||
|
||||
// Tooltip
|
||||
if (isset($hyperlink['tooltip'])) {
|
||||
$cell->getHyperlink()->setTooltip((string) $hyperlink['tooltip']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$hyperlinkReader->setHyperlinks($xmlSheet->hyperlinks);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1418,12 +914,18 @@ class Xlsx extends BaseReader
|
||||
foreach ($vmlComments as $relName => $relPath) {
|
||||
// Load VML comments file
|
||||
$relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
|
||||
$vmlCommentsFile = simplexml_load_string(
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, $relPath)),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
$vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
|
||||
|
||||
try {
|
||||
$vmlCommentsFile = simplexml_load_string(
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, $relPath)),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
$vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
|
||||
} catch (\Throwable $ex) {
|
||||
//Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData
|
||||
continue;
|
||||
}
|
||||
|
||||
$shapes = $vmlCommentsFile->xpath('//v:shape');
|
||||
foreach ($shapes as $shape) {
|
||||
@ -1597,8 +1099,10 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
}
|
||||
if ($xmlSheet->drawing && !$this->readDataOnly) {
|
||||
$unparsedDrawings = [];
|
||||
foreach ($xmlSheet->drawing as $drawing) {
|
||||
$fileDrawing = $drawings[(string) self::getArrayItem($drawing->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
|
||||
$drawingRelId = (string) self::getArrayItem($drawing->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id');
|
||||
$fileDrawing = $drawings[$drawingRelId];
|
||||
//~ http://schemas.openxmlformats.org/package/2006/relationships"
|
||||
$relsDrawing = simplexml_load_string(
|
||||
$this->securityScanner->scan(
|
||||
@ -1630,10 +1134,11 @@ class Xlsx extends BaseReader
|
||||
$this->securityScanner->scan($this->getFromZipArchive($zip, $fileDrawing)),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
)->children('http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
|
||||
);
|
||||
$xmlDrawingChildren = $xmlDrawing->children('http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
|
||||
|
||||
if ($xmlDrawing->oneCellAnchor) {
|
||||
foreach ($xmlDrawing->oneCellAnchor as $oneCellAnchor) {
|
||||
if ($xmlDrawingChildren->oneCellAnchor) {
|
||||
foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) {
|
||||
if ($oneCellAnchor->pic->blipFill) {
|
||||
/** @var SimpleXMLElement $blip */
|
||||
$blip = $oneCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
|
||||
@ -1667,12 +1172,13 @@ class Xlsx extends BaseReader
|
||||
if ($outerShdw) {
|
||||
$shadow = $objDrawing->getShadow();
|
||||
$shadow->setVisible(true);
|
||||
$shadow->setBlurRadius(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
|
||||
$shadow->setDistance(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
|
||||
$shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
|
||||
$shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
|
||||
$shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
|
||||
$shadow->setAlignment((string) self::getArrayItem($outerShdw->attributes(), 'algn'));
|
||||
$shadow->getColor()->setRGB(self::getArrayItem($outerShdw->srgbClr->attributes(), 'val'));
|
||||
$shadow->setAlpha(self::getArrayItem($outerShdw->srgbClr->alpha->attributes(), 'val') / 1000);
|
||||
$clr = isset($outerShdw->srgbClr) ? $outerShdw->srgbClr : $outerShdw->prstClr;
|
||||
$shadow->getColor()->setRGB(self::getArrayItem($clr->attributes(), 'val'));
|
||||
$shadow->setAlpha(self::getArrayItem($clr->alpha->attributes(), 'val') / 1000);
|
||||
}
|
||||
|
||||
$this->readHyperLinkDrawing($objDrawing, $oneCellAnchor, $hyperlinks);
|
||||
@ -1688,8 +1194,8 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($xmlDrawing->twoCellAnchor) {
|
||||
foreach ($xmlDrawing->twoCellAnchor as $twoCellAnchor) {
|
||||
if ($xmlDrawingChildren->twoCellAnchor) {
|
||||
foreach ($xmlDrawingChildren->twoCellAnchor as $twoCellAnchor) {
|
||||
if ($twoCellAnchor->pic->blipFill) {
|
||||
$blip = $twoCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
|
||||
$xfrm = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
|
||||
@ -1719,12 +1225,13 @@ class Xlsx extends BaseReader
|
||||
if ($outerShdw) {
|
||||
$shadow = $objDrawing->getShadow();
|
||||
$shadow->setVisible(true);
|
||||
$shadow->setBlurRadius(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
|
||||
$shadow->setDistance(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
|
||||
$shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
|
||||
$shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
|
||||
$shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
|
||||
$shadow->setAlignment((string) self::getArrayItem($outerShdw->attributes(), 'algn'));
|
||||
$shadow->getColor()->setRGB(self::getArrayItem($outerShdw->srgbClr->attributes(), 'val'));
|
||||
$shadow->setAlpha(self::getArrayItem($outerShdw->srgbClr->alpha->attributes(), 'val') / 1000);
|
||||
$clr = isset($outerShdw->srgbClr) ? $outerShdw->srgbClr : $outerShdw->prstClr;
|
||||
$shadow->getColor()->setRGB(self::getArrayItem($clr->attributes(), 'val'));
|
||||
$shadow->setAlpha(self::getArrayItem($clr->alpha->attributes(), 'val') / 1000);
|
||||
}
|
||||
|
||||
$this->readHyperLinkDrawing($objDrawing, $twoCellAnchor, $hyperlinks);
|
||||
@ -1754,13 +1261,21 @@ class Xlsx extends BaseReader
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($relsDrawing === false && $xmlDrawing->count() == 0) {
|
||||
// Save Drawing without rels and children as unparsed
|
||||
$unparsedDrawings[$drawingRelId] = $xmlDrawing->asXML();
|
||||
}
|
||||
}
|
||||
|
||||
// store original rId of drawing files
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'] = [];
|
||||
foreach ($relsWorksheet->Relationship as $ele) {
|
||||
if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing') {
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = (string) $ele['Id'];
|
||||
$drawingRelId = (string) $ele['Id'];
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = $drawingRelId;
|
||||
if (isset($unparsedDrawings[$drawingRelId])) {
|
||||
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['Drawings'][$drawingRelId] = $unparsedDrawings[$drawingRelId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1905,7 +1420,7 @@ class Xlsx extends BaseReader
|
||||
if (strpos((string) $definedName, '!') !== false) {
|
||||
// Extract sheet name
|
||||
$extractedSheetName = Worksheet::extractSheetTitle((string) $definedName, true);
|
||||
$extractedSheetName = $extractedSheetName[0];
|
||||
$extractedSheetName = trim($extractedSheetName[0], "'");
|
||||
|
||||
// Locate sheet
|
||||
$locatedSheet = $excel->getSheetByName($extractedSheetName);
|
||||
@ -2511,94 +2026,4 @@ class Xlsx extends BaseReader
|
||||
|
||||
return (bool) $xsdBoolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read columns and rows attributes from XML and set them on the worksheet.
|
||||
*
|
||||
* @param SimpleXMLElement $xmlSheet
|
||||
* @param Worksheet $docSheet
|
||||
*/
|
||||
private function readColumnsAndRowsAttributes(SimpleXMLElement $xmlSheet, Worksheet $docSheet)
|
||||
{
|
||||
$columnsAttributes = [];
|
||||
$rowsAttributes = [];
|
||||
if (isset($xmlSheet->cols) && !$this->readDataOnly) {
|
||||
foreach ($xmlSheet->cols->col as $col) {
|
||||
for ($i = (int) ($col['min']); $i <= (int) ($col['max']); ++$i) {
|
||||
if ($col['style'] && !$this->readDataOnly) {
|
||||
$columnsAttributes[Coordinate::stringFromColumnIndex($i)]['xfIndex'] = (int) $col['style'];
|
||||
}
|
||||
if (self::boolean($col['hidden'])) {
|
||||
$columnsAttributes[Coordinate::stringFromColumnIndex($i)]['visible'] = false;
|
||||
}
|
||||
if (self::boolean($col['collapsed'])) {
|
||||
$columnsAttributes[Coordinate::stringFromColumnIndex($i)]['collapsed'] = true;
|
||||
}
|
||||
if ($col['outlineLevel'] > 0) {
|
||||
$columnsAttributes[Coordinate::stringFromColumnIndex($i)]['outlineLevel'] = (int) $col['outlineLevel'];
|
||||
}
|
||||
$columnsAttributes[Coordinate::stringFromColumnIndex($i)]['width'] = (float) $col['width'];
|
||||
|
||||
if ((int) ($col['max']) == 16384) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($xmlSheet && $xmlSheet->sheetData && $xmlSheet->sheetData->row) {
|
||||
foreach ($xmlSheet->sheetData->row as $row) {
|
||||
if ($row['ht'] && !$this->readDataOnly) {
|
||||
$rowsAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
|
||||
}
|
||||
if (self::boolean($row['hidden']) && !$this->readDataOnly) {
|
||||
$rowsAttributes[(int) $row['r']]['visible'] = false;
|
||||
}
|
||||
if (self::boolean($row['collapsed'])) {
|
||||
$rowsAttributes[(int) $row['r']]['collapsed'] = true;
|
||||
}
|
||||
if ($row['outlineLevel'] > 0) {
|
||||
$rowsAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel'];
|
||||
}
|
||||
if ($row['s'] && !$this->readDataOnly) {
|
||||
$rowsAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$readFilter = (\get_class($this->getReadFilter()) !== DefaultReadFilter::class ? $this->getReadFilter() : null);
|
||||
|
||||
// set columns/rows attributes
|
||||
$columnsAttributesSet = [];
|
||||
$rowsAttributesSet = [];
|
||||
foreach ($columnsAttributes as $coordColumn => $columnAttributes) {
|
||||
if ($readFilter !== null) {
|
||||
foreach ($rowsAttributes as $coordRow => $rowAttributes) {
|
||||
if (!$readFilter->readCell($coordColumn, $coordRow, $docSheet->getTitle())) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($columnsAttributesSet[$coordColumn])) {
|
||||
$this->setColumnAttributes($docSheet, $coordColumn, $columnAttributes);
|
||||
$columnsAttributesSet[$coordColumn] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($rowsAttributes as $coordRow => $rowAttributes) {
|
||||
if ($readFilter !== null) {
|
||||
foreach ($columnsAttributes as $coordColumn => $columnAttributes) {
|
||||
if (!$readFilter->readCell($coordColumn, $coordRow, $docSheet->getTitle())) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($rowsAttributesSet[$coordRow])) {
|
||||
$this->setRowAttributes($docSheet, $coordRow, $rowAttributes);
|
||||
$rowsAttributesSet[$coordRow] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class AutoFilter
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $worksheetXml;
|
||||
|
||||
public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml)
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
$this->worksheetXml = $worksheetXml;
|
||||
}
|
||||
|
||||
public function load()
|
||||
{
|
||||
$autoFilterRange = (string) $this->worksheetXml->autoFilter['ref'];
|
||||
if (strpos($autoFilterRange, ':') !== false) {
|
||||
$this->readAutoFilter($autoFilterRange, $this->worksheetXml);
|
||||
}
|
||||
}
|
||||
|
||||
private function readAutoFilter($autoFilterRange, $xmlSheet)
|
||||
{
|
||||
$autoFilter = $this->worksheet->getAutoFilter();
|
||||
$autoFilter->setRange($autoFilterRange);
|
||||
|
||||
foreach ($xmlSheet->autoFilter->filterColumn as $filterColumn) {
|
||||
$column = $autoFilter->getColumnByOffset((int) $filterColumn['colId']);
|
||||
// Check for standard filters
|
||||
if ($filterColumn->filters) {
|
||||
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
|
||||
$filters = $filterColumn->filters;
|
||||
if ((isset($filters['blank'])) && ($filters['blank'] == 1)) {
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
$column->createRule()->setRule(null, '')->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
|
||||
}
|
||||
// Standard filters are always an OR join, so no join rule needs to be set
|
||||
// Entries can be either filter elements
|
||||
foreach ($filters->filter as $filterRule) {
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
$column->createRule()->setRule(null, (string) $filterRule['val'])->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
|
||||
}
|
||||
|
||||
// Or Date Group elements
|
||||
$this->readDateRangeAutoFilter($filters, $column);
|
||||
}
|
||||
|
||||
// Check for custom filters
|
||||
$this->readCustomAutoFilter($filterColumn, $column);
|
||||
// Check for dynamic filters
|
||||
$this->readDynamicAutoFilter($filterColumn, $column);
|
||||
// Check for dynamic filters
|
||||
$this->readTopTenAutoFilter($filterColumn, $column);
|
||||
}
|
||||
}
|
||||
|
||||
private function readDateRangeAutoFilter(\SimpleXMLElement $filters, Column $column)
|
||||
{
|
||||
foreach ($filters->dateGroupItem as $dateGroupItem) {
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
$column->createRule()->setRule(
|
||||
null,
|
||||
[
|
||||
'year' => (string) $dateGroupItem['year'],
|
||||
'month' => (string) $dateGroupItem['month'],
|
||||
'day' => (string) $dateGroupItem['day'],
|
||||
'hour' => (string) $dateGroupItem['hour'],
|
||||
'minute' => (string) $dateGroupItem['minute'],
|
||||
'second' => (string) $dateGroupItem['second'],
|
||||
],
|
||||
(string) $dateGroupItem['dateTimeGrouping']
|
||||
)->setRuleType(Rule::AUTOFILTER_RULETYPE_DATEGROUP);
|
||||
}
|
||||
}
|
||||
|
||||
private function readCustomAutoFilter(\SimpleXMLElement $filterColumn, Column $column)
|
||||
{
|
||||
if ($filterColumn->customFilters) {
|
||||
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
|
||||
$customFilters = $filterColumn->customFilters;
|
||||
// Custom filters can an AND or an OR join;
|
||||
// and there should only ever be one or two entries
|
||||
if ((isset($customFilters['and'])) && ($customFilters['and'] == 1)) {
|
||||
$column->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
|
||||
}
|
||||
foreach ($customFilters->customFilter as $filterRule) {
|
||||
$column->createRule()->setRule(
|
||||
(string) $filterRule['operator'],
|
||||
(string) $filterRule['val']
|
||||
)->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function readDynamicAutoFilter(\SimpleXMLElement $filterColumn, Column $column)
|
||||
{
|
||||
if ($filterColumn->dynamicFilter) {
|
||||
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
|
||||
// We should only ever have one dynamic filter
|
||||
foreach ($filterColumn->dynamicFilter as $filterRule) {
|
||||
// Operator is undefined, but always treated as EQUAL
|
||||
$column->createRule()->setRule(
|
||||
null,
|
||||
(string) $filterRule['val'],
|
||||
(string) $filterRule['type']
|
||||
)->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
|
||||
if (isset($filterRule['val'])) {
|
||||
$column->setAttribute('val', (string) $filterRule['val']);
|
||||
}
|
||||
if (isset($filterRule['maxVal'])) {
|
||||
$column->setAttribute('maxVal', (string) $filterRule['maxVal']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function readTopTenAutoFilter(\SimpleXMLElement $filterColumn, Column $column)
|
||||
{
|
||||
if ($filterColumn->top10) {
|
||||
$column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER);
|
||||
// We should only ever have one top10 filter
|
||||
foreach ($filterColumn->top10 as $filterRule) {
|
||||
$column->createRule()->setRule(
|
||||
(((isset($filterRule['percent'])) && ($filterRule['percent'] == 1))
|
||||
? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
|
||||
: Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
|
||||
),
|
||||
(string) $filterRule['val'],
|
||||
(((isset($filterRule['top'])) && ($filterRule['top'] == 1))
|
||||
? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
|
||||
: Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
|
||||
)
|
||||
)->setRuleType(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
class BaseParserClass
|
||||
{
|
||||
protected static function boolean($value)
|
||||
{
|
||||
if (is_object($value)) {
|
||||
$value = (string) $value;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return (bool) $value;
|
||||
}
|
||||
|
||||
return $value === strtolower('true');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class ColumnAndRowAttributes extends BaseParserClass
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $worksheetXml;
|
||||
|
||||
public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml = null)
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
$this->worksheetXml = $worksheetXml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Worksheet column attributes by attributes array passed.
|
||||
*
|
||||
* @param string $columnAddress A, B, ... DX, ...
|
||||
* @param array $columnAttributes array of attributes (indexes are attribute name, values are value)
|
||||
* 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ?
|
||||
*/
|
||||
private function setColumnAttributes($columnAddress, array $columnAttributes)
|
||||
{
|
||||
if (isset($columnAttributes['xfIndex'])) {
|
||||
$this->worksheet->getColumnDimension($columnAddress)->setXfIndex($columnAttributes['xfIndex']);
|
||||
}
|
||||
if (isset($columnAttributes['visible'])) {
|
||||
$this->worksheet->getColumnDimension($columnAddress)->setVisible($columnAttributes['visible']);
|
||||
}
|
||||
if (isset($columnAttributes['collapsed'])) {
|
||||
$this->worksheet->getColumnDimension($columnAddress)->setCollapsed($columnAttributes['collapsed']);
|
||||
}
|
||||
if (isset($columnAttributes['outlineLevel'])) {
|
||||
$this->worksheet->getColumnDimension($columnAddress)->setOutlineLevel($columnAttributes['outlineLevel']);
|
||||
}
|
||||
if (isset($columnAttributes['width'])) {
|
||||
$this->worksheet->getColumnDimension($columnAddress)->setWidth($columnAttributes['width']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Worksheet row attributes by attributes array passed.
|
||||
*
|
||||
* @param int $rowNumber 1, 2, 3, ... 99, ...
|
||||
* @param array $rowAttributes array of attributes (indexes are attribute name, values are value)
|
||||
* 'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
|
||||
*/
|
||||
private function setRowAttributes($rowNumber, array $rowAttributes)
|
||||
{
|
||||
if (isset($rowAttributes['xfIndex'])) {
|
||||
$this->worksheet->getRowDimension($rowNumber)->setXfIndex($rowAttributes['xfIndex']);
|
||||
}
|
||||
if (isset($rowAttributes['visible'])) {
|
||||
$this->worksheet->getRowDimension($rowNumber)->setVisible($rowAttributes['visible']);
|
||||
}
|
||||
if (isset($rowAttributes['collapsed'])) {
|
||||
$this->worksheet->getRowDimension($rowNumber)->setCollapsed($rowAttributes['collapsed']);
|
||||
}
|
||||
if (isset($rowAttributes['outlineLevel'])) {
|
||||
$this->worksheet->getRowDimension($rowNumber)->setOutlineLevel($rowAttributes['outlineLevel']);
|
||||
}
|
||||
if (isset($rowAttributes['rowHeight'])) {
|
||||
$this->worksheet->getRowDimension($rowNumber)->setRowHeight($rowAttributes['rowHeight']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IReadFilter $readFilter
|
||||
* @param bool $readDataOnly
|
||||
*/
|
||||
public function load(IReadFilter $readFilter = null, $readDataOnly = false)
|
||||
{
|
||||
if ($this->worksheetXml === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$columnsAttributes = [];
|
||||
$rowsAttributes = [];
|
||||
if (isset($this->worksheetXml->cols)) {
|
||||
$columnsAttributes = $this->readColumnAttributes($this->worksheetXml->cols, $readDataOnly);
|
||||
}
|
||||
|
||||
if ($this->worksheetXml->sheetData && $this->worksheetXml->sheetData->row) {
|
||||
$rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly);
|
||||
}
|
||||
|
||||
// set columns/rows attributes
|
||||
$columnsAttributesAreSet = [];
|
||||
foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
|
||||
if ($readFilter === null ||
|
||||
!$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes)) {
|
||||
if (!isset($columnsAttributesAreSet[$columnCoordinate])) {
|
||||
$this->setColumnAttributes($columnCoordinate, $columnAttributes);
|
||||
$columnsAttributesAreSet[$columnCoordinate] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rowsAttributesAreSet = [];
|
||||
foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
|
||||
if ($readFilter === null ||
|
||||
!$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes)) {
|
||||
if (!isset($rowsAttributesAreSet[$rowCoordinate])) {
|
||||
$this->setRowAttributes($rowCoordinate, $rowAttributes);
|
||||
$rowsAttributesAreSet[$rowCoordinate] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isFilteredColumn(IReadFilter $readFilter, $columnCoordinate, array $rowsAttributes)
|
||||
{
|
||||
foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
|
||||
if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function readColumnAttributes(\SimpleXMLElement $worksheetCols, $readDataOnly)
|
||||
{
|
||||
$columnAttributes = [];
|
||||
|
||||
foreach ($worksheetCols->col as $column) {
|
||||
$startColumn = Coordinate::stringFromColumnIndex((int) $column['min']);
|
||||
$endColumn = Coordinate::stringFromColumnIndex((int) $column['max']);
|
||||
++$endColumn;
|
||||
for ($columnAddress = $startColumn; $columnAddress !== $endColumn; ++$columnAddress) {
|
||||
$columnAttributes[$columnAddress] = $this->readColumnRangeAttributes($column, $readDataOnly);
|
||||
|
||||
if ((int) ($column['max']) == 16384) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $columnAttributes;
|
||||
}
|
||||
|
||||
private function readColumnRangeAttributes(\SimpleXMLElement $column, $readDataOnly)
|
||||
{
|
||||
$columnAttributes = [];
|
||||
|
||||
if ($column['style'] && !$readDataOnly) {
|
||||
$columnAttributes['xfIndex'] = (int) $column['style'];
|
||||
}
|
||||
if (self::boolean($column['hidden'])) {
|
||||
$columnAttributes['visible'] = false;
|
||||
}
|
||||
if (self::boolean($column['collapsed'])) {
|
||||
$columnAttributes['collapsed'] = true;
|
||||
}
|
||||
if (((int) $column['outlineLevel']) > 0) {
|
||||
$columnAttributes['outlineLevel'] = (int) $column['outlineLevel'];
|
||||
}
|
||||
$columnAttributes['width'] = (float) $column['width'];
|
||||
|
||||
return $columnAttributes;
|
||||
}
|
||||
|
||||
private function isFilteredRow(IReadFilter $readFilter, $rowCoordinate, array $columnsAttributes)
|
||||
{
|
||||
foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
|
||||
if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function readRowAttributes(\SimpleXMLElement $worksheetRow, $readDataOnly)
|
||||
{
|
||||
$rowAttributes = [];
|
||||
|
||||
foreach ($worksheetRow as $row) {
|
||||
if ($row['ht'] && !$readDataOnly) {
|
||||
$rowAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
|
||||
}
|
||||
if (self::boolean($row['hidden'])) {
|
||||
$rowAttributes[(int) $row['r']]['visible'] = false;
|
||||
}
|
||||
if (self::boolean($row['collapsed'])) {
|
||||
$rowAttributes[(int) $row['r']]['collapsed'] = true;
|
||||
}
|
||||
if ((int) $row['outlineLevel'] > 0) {
|
||||
$rowAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel'];
|
||||
}
|
||||
if ($row['s'] && !$readDataOnly) {
|
||||
$rowAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s'];
|
||||
}
|
||||
}
|
||||
|
||||
return $rowAttributes;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class ConditionalStyles
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $worksheetXml;
|
||||
|
||||
private $dxfs;
|
||||
|
||||
public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml, array $dxfs = [])
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
$this->worksheetXml = $worksheetXml;
|
||||
$this->dxfs = $dxfs;
|
||||
}
|
||||
|
||||
public function load()
|
||||
{
|
||||
$this->setConditionalStyles(
|
||||
$this->worksheet,
|
||||
$this->readConditionalStyles($this->worksheetXml)
|
||||
);
|
||||
}
|
||||
|
||||
private function readConditionalStyles($xmlSheet)
|
||||
{
|
||||
$conditionals = [];
|
||||
foreach ($xmlSheet->conditionalFormatting as $conditional) {
|
||||
foreach ($conditional->cfRule as $cfRule) {
|
||||
if (((string) $cfRule['type'] == Conditional::CONDITION_NONE
|
||||
|| (string) $cfRule['type'] == Conditional::CONDITION_CELLIS
|
||||
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT
|
||||
|| (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION)
|
||||
&& isset($this->dxfs[(int) ($cfRule['dxfId'])])) {
|
||||
$conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $conditionals;
|
||||
}
|
||||
|
||||
private function setConditionalStyles(Worksheet $worksheet, array $conditionals)
|
||||
{
|
||||
foreach ($conditionals as $ref => $cfRules) {
|
||||
ksort($cfRules);
|
||||
$conditionalStyles = $this->readStyleRules($cfRules);
|
||||
|
||||
// Extract all cell references in $ref
|
||||
$cellBlocks = explode(' ', str_replace('$', '', strtoupper($ref)));
|
||||
foreach ($cellBlocks as $cellBlock) {
|
||||
$worksheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function readStyleRules($cfRules)
|
||||
{
|
||||
$conditionalStyles = [];
|
||||
foreach ($cfRules as $cfRule) {
|
||||
$objConditional = new Conditional();
|
||||
$objConditional->setConditionType((string) $cfRule['type']);
|
||||
$objConditional->setOperatorType((string) $cfRule['operator']);
|
||||
|
||||
if ((string) $cfRule['text'] != '') {
|
||||
$objConditional->setText((string) $cfRule['text']);
|
||||
}
|
||||
|
||||
if (isset($cfRule['stopIfTrue']) && (int) $cfRule['stopIfTrue'] === 1) {
|
||||
$objConditional->setStopIfTrue(true);
|
||||
}
|
||||
|
||||
if (count($cfRule->formula) > 1) {
|
||||
foreach ($cfRule->formula as $formula) {
|
||||
$objConditional->addCondition((string) $formula);
|
||||
}
|
||||
} else {
|
||||
$objConditional->addCondition((string) $cfRule->formula);
|
||||
}
|
||||
$objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);
|
||||
$conditionalStyles[] = $objConditional;
|
||||
}
|
||||
|
||||
return $conditionalStyles;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class DataValidations
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $worksheetXml;
|
||||
|
||||
public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml)
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
$this->worksheetXml = $worksheetXml;
|
||||
}
|
||||
|
||||
public function load()
|
||||
{
|
||||
foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
|
||||
// Uppercase coordinate
|
||||
$range = strtoupper($dataValidation['sqref']);
|
||||
$rangeSet = explode(' ', $range);
|
||||
foreach ($rangeSet as $range) {
|
||||
$stRange = $this->worksheet->shrinkRangeToFit($range);
|
||||
|
||||
// Extract all cell references in $range
|
||||
foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $reference) {
|
||||
// Create validation
|
||||
$docValidation = $this->worksheet->getCell($reference)->getDataValidation();
|
||||
$docValidation->setType((string) $dataValidation['type']);
|
||||
$docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
|
||||
$docValidation->setOperator((string) $dataValidation['operator']);
|
||||
$docValidation->setAllowBlank($dataValidation['allowBlank'] != 0);
|
||||
$docValidation->setShowDropDown($dataValidation['showDropDown'] == 0);
|
||||
$docValidation->setShowInputMessage($dataValidation['showInputMessage'] != 0);
|
||||
$docValidation->setShowErrorMessage($dataValidation['showErrorMessage'] != 0);
|
||||
$docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
|
||||
$docValidation->setError((string) $dataValidation['error']);
|
||||
$docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
|
||||
$docValidation->setPrompt((string) $dataValidation['prompt']);
|
||||
$docValidation->setFormula1((string) $dataValidation->formula1);
|
||||
$docValidation->setFormula2((string) $dataValidation->formula2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class Hyperlinks
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $hyperlinks = [];
|
||||
|
||||
public function __construct(Worksheet $workSheet)
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
}
|
||||
|
||||
public function readHyperlinks(\SimpleXMLElement $relsWorksheet)
|
||||
{
|
||||
foreach ($relsWorksheet->Relationship as $element) {
|
||||
if ($element['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
|
||||
$this->hyperlinks[(string) $element['Id']] = (string) $element['Target'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setHyperlinks(\SimpleXMLElement $worksheetXml)
|
||||
{
|
||||
foreach ($worksheetXml->hyperlink as $hyperlink) {
|
||||
$this->setHyperlink($hyperlink, $this->worksheet);
|
||||
}
|
||||
}
|
||||
|
||||
private function setHyperlink(\SimpleXMLElement $hyperlink, Worksheet $worksheet)
|
||||
{
|
||||
// Link url
|
||||
$linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
|
||||
|
||||
foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
|
||||
$cell = $worksheet->getCell($cellReference);
|
||||
if (isset($linkRel['id'])) {
|
||||
$hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']];
|
||||
if (isset($hyperlink['location'])) {
|
||||
$hyperlinkUrl .= '#' . (string) $hyperlink['location'];
|
||||
}
|
||||
$cell->getHyperlink()->setUrl($hyperlinkUrl);
|
||||
} elseif (isset($hyperlink['location'])) {
|
||||
$cell->getHyperlink()->setUrl('sheet://' . (string) $hyperlink['location']);
|
||||
}
|
||||
|
||||
// Tooltip
|
||||
if (isset($hyperlink['tooltip'])) {
|
||||
$cell->getHyperlink()->setTooltip((string) $hyperlink['tooltip']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class PageSetup extends BaseParserClass
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $worksheetXml;
|
||||
|
||||
public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml = null)
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
$this->worksheetXml = $worksheetXml;
|
||||
}
|
||||
|
||||
public function load(array $unparsedLoadedData)
|
||||
{
|
||||
if (!$this->worksheetXml) {
|
||||
return $unparsedLoadedData;
|
||||
}
|
||||
|
||||
$this->margins($this->worksheetXml, $this->worksheet);
|
||||
$unparsedLoadedData = $this->pageSetup($this->worksheetXml, $this->worksheet, $unparsedLoadedData);
|
||||
$this->headerFooter($this->worksheetXml, $this->worksheet);
|
||||
$this->pageBreaks($this->worksheetXml, $this->worksheet);
|
||||
|
||||
return $unparsedLoadedData;
|
||||
}
|
||||
|
||||
private function margins(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
|
||||
{
|
||||
if ($xmlSheet->pageMargins) {
|
||||
$docPageMargins = $worksheet->getPageMargins();
|
||||
$docPageMargins->setLeft((float) ($xmlSheet->pageMargins['left']));
|
||||
$docPageMargins->setRight((float) ($xmlSheet->pageMargins['right']));
|
||||
$docPageMargins->setTop((float) ($xmlSheet->pageMargins['top']));
|
||||
$docPageMargins->setBottom((float) ($xmlSheet->pageMargins['bottom']));
|
||||
$docPageMargins->setHeader((float) ($xmlSheet->pageMargins['header']));
|
||||
$docPageMargins->setFooter((float) ($xmlSheet->pageMargins['footer']));
|
||||
}
|
||||
}
|
||||
|
||||
private function pageSetup(\SimpleXMLElement $xmlSheet, Worksheet $worksheet, array $unparsedLoadedData)
|
||||
{
|
||||
if ($xmlSheet->pageSetup) {
|
||||
$docPageSetup = $worksheet->getPageSetup();
|
||||
|
||||
if (isset($xmlSheet->pageSetup['orientation'])) {
|
||||
$docPageSetup->setOrientation((string) $xmlSheet->pageSetup['orientation']);
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['paperSize'])) {
|
||||
$docPageSetup->setPaperSize((int) ($xmlSheet->pageSetup['paperSize']));
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['scale'])) {
|
||||
$docPageSetup->setScale((int) ($xmlSheet->pageSetup['scale']), false);
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['fitToHeight']) && (int) ($xmlSheet->pageSetup['fitToHeight']) >= 0) {
|
||||
$docPageSetup->setFitToHeight((int) ($xmlSheet->pageSetup['fitToHeight']), false);
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['fitToWidth']) && (int) ($xmlSheet->pageSetup['fitToWidth']) >= 0) {
|
||||
$docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
|
||||
}
|
||||
if (isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) &&
|
||||
self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) {
|
||||
$docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
|
||||
}
|
||||
|
||||
$relAttributes = $xmlSheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
|
||||
if (isset($relAttributes['id'])) {
|
||||
$unparsedLoadedData['sheets'][$worksheet->getCodeName()]['pageSetupRelId'] = (string) $relAttributes['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return $unparsedLoadedData;
|
||||
}
|
||||
|
||||
private function headerFooter(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
|
||||
{
|
||||
if ($xmlSheet->headerFooter) {
|
||||
$docHeaderFooter = $worksheet->getHeaderFooter();
|
||||
|
||||
if (isset($xmlSheet->headerFooter['differentOddEven']) &&
|
||||
self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])) {
|
||||
$docHeaderFooter->setDifferentOddEven(true);
|
||||
} else {
|
||||
$docHeaderFooter->setDifferentOddEven(false);
|
||||
}
|
||||
if (isset($xmlSheet->headerFooter['differentFirst']) &&
|
||||
self::boolean((string) $xmlSheet->headerFooter['differentFirst'])) {
|
||||
$docHeaderFooter->setDifferentFirst(true);
|
||||
} else {
|
||||
$docHeaderFooter->setDifferentFirst(false);
|
||||
}
|
||||
if (isset($xmlSheet->headerFooter['scaleWithDoc']) &&
|
||||
!self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])) {
|
||||
$docHeaderFooter->setScaleWithDocument(false);
|
||||
} else {
|
||||
$docHeaderFooter->setScaleWithDocument(true);
|
||||
}
|
||||
if (isset($xmlSheet->headerFooter['alignWithMargins']) &&
|
||||
!self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])) {
|
||||
$docHeaderFooter->setAlignWithMargins(false);
|
||||
} else {
|
||||
$docHeaderFooter->setAlignWithMargins(true);
|
||||
}
|
||||
|
||||
$docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
|
||||
$docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
|
||||
$docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
|
||||
$docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
|
||||
$docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
|
||||
$docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
|
||||
}
|
||||
}
|
||||
|
||||
private function pageBreaks(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
|
||||
{
|
||||
if ($xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk) {
|
||||
$this->rowBreaks($xmlSheet, $worksheet);
|
||||
}
|
||||
if ($xmlSheet->colBreaks && $xmlSheet->colBreaks->brk) {
|
||||
$this->columnBreaks($xmlSheet, $worksheet);
|
||||
}
|
||||
}
|
||||
|
||||
private function rowBreaks(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
|
||||
{
|
||||
foreach ($xmlSheet->rowBreaks->brk as $brk) {
|
||||
if ($brk['man']) {
|
||||
$worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function columnBreaks(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
|
||||
{
|
||||
foreach ($xmlSheet->colBreaks->brk as $brk) {
|
||||
if ($brk['man']) {
|
||||
$worksheet->setBreak(
|
||||
Coordinate::stringFromColumnIndex(((int) $brk['id']) + 1) . '1',
|
||||
Worksheet::BREAK_COLUMN
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
|
||||
class Properties
|
||||
{
|
||||
private $securityScanner;
|
||||
|
||||
private $docProps;
|
||||
|
||||
public function __construct(XmlScanner $securityScanner, DocumentProperties $docProps)
|
||||
{
|
||||
$this->securityScanner = $securityScanner;
|
||||
$this->docProps = $docProps;
|
||||
}
|
||||
|
||||
private function extractPropertyData($propertyData)
|
||||
{
|
||||
return simplexml_load_string(
|
||||
$this->securityScanner->scan($propertyData),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
}
|
||||
|
||||
public function readCoreProperties($propertyData)
|
||||
{
|
||||
$xmlCore = $this->extractPropertyData($propertyData);
|
||||
|
||||
if (is_object($xmlCore)) {
|
||||
$xmlCore->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
|
||||
$xmlCore->registerXPathNamespace('dcterms', 'http://purl.org/dc/terms/');
|
||||
$xmlCore->registerXPathNamespace('cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
|
||||
|
||||
$this->docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
|
||||
$this->docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
|
||||
$this->docProps->setCreated(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:created')))); //! respect xsi:type
|
||||
$this->docProps->setModified(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:modified')))); //! respect xsi:type
|
||||
$this->docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
|
||||
$this->docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
|
||||
$this->docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
|
||||
$this->docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
|
||||
$this->docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
|
||||
}
|
||||
}
|
||||
|
||||
public function readExtendedProperties($propertyData)
|
||||
{
|
||||
$xmlCore = $this->extractPropertyData($propertyData);
|
||||
|
||||
if (is_object($xmlCore)) {
|
||||
if (isset($xmlCore->Company)) {
|
||||
$this->docProps->setCompany((string) $xmlCore->Company);
|
||||
}
|
||||
if (isset($xmlCore->Manager)) {
|
||||
$this->docProps->setManager((string) $xmlCore->Manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function readCustomProperties($propertyData)
|
||||
{
|
||||
$xmlCore = $this->extractPropertyData($propertyData);
|
||||
|
||||
if (is_object($xmlCore)) {
|
||||
foreach ($xmlCore as $xmlProperty) {
|
||||
/** @var \SimpleXMLElement $xmlProperty */
|
||||
$cellDataOfficeAttributes = $xmlProperty->attributes();
|
||||
if (isset($cellDataOfficeAttributes['name'])) {
|
||||
$propertyName = (string) $cellDataOfficeAttributes['name'];
|
||||
$cellDataOfficeChildren = $xmlProperty->children('http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
|
||||
|
||||
$attributeType = $cellDataOfficeChildren->getName();
|
||||
$attributeValue = (string) $cellDataOfficeChildren->{$attributeType};
|
||||
$attributeValue = DocumentProperties::convertProperty($attributeValue, $attributeType);
|
||||
$attributeType = DocumentProperties::convertPropertyType($attributeType);
|
||||
$this->docProps->setCustomProperty($propertyName, $attributeValue, $attributeType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function getArrayItem(array $array, $key = 0)
|
||||
{
|
||||
return isset($array[$key]) ? $array[$key] : null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class SheetViewOptions extends BaseParserClass
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $worksheetXml;
|
||||
|
||||
public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml = null)
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
$this->worksheetXml = $worksheetXml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $readDataOnly
|
||||
*/
|
||||
public function load($readDataOnly = false)
|
||||
{
|
||||
if ($this->worksheetXml === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->worksheetXml->sheetPr)) {
|
||||
$this->tabColor($this->worksheetXml->sheetPr);
|
||||
$this->codeName($this->worksheetXml->sheetPr);
|
||||
$this->outlines($this->worksheetXml->sheetPr);
|
||||
$this->pageSetup($this->worksheetXml->sheetPr);
|
||||
}
|
||||
|
||||
if (isset($this->worksheetXml->sheetFormatPr)) {
|
||||
$this->sheetFormat($this->worksheetXml->sheetFormatPr);
|
||||
}
|
||||
|
||||
if (!$readDataOnly && isset($this->worksheetXml->printOptions)) {
|
||||
$this->printOptions($this->worksheetXml->printOptions);
|
||||
}
|
||||
}
|
||||
|
||||
private function tabColor(\SimpleXMLElement $sheetPr)
|
||||
{
|
||||
if (isset($sheetPr->tabColor, $sheetPr->tabColor['rgb'])) {
|
||||
$this->worksheet->getTabColor()->setARGB((string) $sheetPr->tabColor['rgb']);
|
||||
}
|
||||
}
|
||||
|
||||
private function codeName(\SimpleXMLElement $sheetPr)
|
||||
{
|
||||
if (isset($sheetPr['codeName'])) {
|
||||
$this->worksheet->setCodeName((string) $sheetPr['codeName'], false);
|
||||
}
|
||||
}
|
||||
|
||||
private function outlines(\SimpleXMLElement $sheetPr)
|
||||
{
|
||||
if (isset($sheetPr->outlinePr)) {
|
||||
if (isset($sheetPr->outlinePr['summaryRight']) &&
|
||||
!self::boolean((string) $sheetPr->outlinePr['summaryRight'])) {
|
||||
$this->worksheet->setShowSummaryRight(false);
|
||||
} else {
|
||||
$this->worksheet->setShowSummaryRight(true);
|
||||
}
|
||||
|
||||
if (isset($sheetPr->outlinePr['summaryBelow']) &&
|
||||
!self::boolean((string) $sheetPr->outlinePr['summaryBelow'])) {
|
||||
$this->worksheet->setShowSummaryBelow(false);
|
||||
} else {
|
||||
$this->worksheet->setShowSummaryBelow(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function pageSetup(\SimpleXMLElement $sheetPr)
|
||||
{
|
||||
if (isset($sheetPr->pageSetUpPr)) {
|
||||
if (isset($sheetPr->pageSetUpPr['fitToPage']) &&
|
||||
!self::boolean((string) $sheetPr->pageSetUpPr['fitToPage'])) {
|
||||
$this->worksheet->getPageSetup()->setFitToPage(false);
|
||||
} else {
|
||||
$this->worksheet->getPageSetup()->setFitToPage(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function sheetFormat(\SimpleXMLElement $sheetFormatPr)
|
||||
{
|
||||
if (isset($sheetFormatPr['customHeight']) &&
|
||||
self::boolean((string) $sheetFormatPr['customHeight']) &&
|
||||
isset($sheetFormatPr['defaultRowHeight'])) {
|
||||
$this->worksheet->getDefaultRowDimension()
|
||||
->setRowHeight((float) $sheetFormatPr['defaultRowHeight']);
|
||||
}
|
||||
|
||||
if (isset($sheetFormatPr['defaultColWidth'])) {
|
||||
$this->worksheet->getDefaultColumnDimension()
|
||||
->setWidth((float) $sheetFormatPr['defaultColWidth']);
|
||||
}
|
||||
|
||||
if (isset($sheetFormatPr['zeroHeight']) &&
|
||||
((string) $sheetFormatPr['zeroHeight'] === '1')) {
|
||||
$this->worksheet->getDefaultRowDimension()->setZeroHeight(true);
|
||||
}
|
||||
}
|
||||
|
||||
private function printOptions(\SimpleXMLElement $printOptions)
|
||||
{
|
||||
if (self::boolean((string) $printOptions['gridLinesSet'])) {
|
||||
$this->worksheet->setShowGridlines(true);
|
||||
}
|
||||
if (self::boolean((string) $printOptions['gridLines'])) {
|
||||
$this->worksheet->setPrintGridlines(true);
|
||||
}
|
||||
if (self::boolean((string) $printOptions['horizontalCentered'])) {
|
||||
$this->worksheet->getPageSetup()->setHorizontalCentered(true);
|
||||
}
|
||||
if (self::boolean((string) $printOptions['verticalCentered'])) {
|
||||
$this->worksheet->getPageSetup()->setVerticalCentered(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
class SheetViews extends BaseParserClass
|
||||
{
|
||||
private $sheetViewXml;
|
||||
|
||||
private $worksheet;
|
||||
|
||||
public function __construct(\SimpleXMLElement $sheetViewXml, Worksheet $workSheet)
|
||||
{
|
||||
$this->sheetViewXml = $sheetViewXml;
|
||||
$this->worksheet = $workSheet;
|
||||
}
|
||||
|
||||
public function load()
|
||||
{
|
||||
$this->zoomScale();
|
||||
$this->view();
|
||||
$this->gridLines();
|
||||
$this->headers();
|
||||
$this->direction();
|
||||
|
||||
if (isset($this->sheetViewXml->pane)) {
|
||||
$this->pane();
|
||||
}
|
||||
if (isset($this->sheetViewXml->selection, $this->sheetViewXml->selection['sqref'])) {
|
||||
$this->selection();
|
||||
}
|
||||
}
|
||||
|
||||
private function zoomScale()
|
||||
{
|
||||
if (isset($this->sheetViewXml['zoomScale'])) {
|
||||
$zoomScale = (int) ($this->sheetViewXml['zoomScale']);
|
||||
if ($zoomScale <= 0) {
|
||||
// setZoomScale will throw an Exception if the scale is less than or equals 0
|
||||
// that is OK when manually creating documents, but we should be able to read all documents
|
||||
$zoomScale = 100;
|
||||
}
|
||||
|
||||
$this->worksheet->getSheetView()->setZoomScale($zoomScale);
|
||||
}
|
||||
|
||||
if (isset($this->sheetViewXml['zoomScaleNormal'])) {
|
||||
$zoomScaleNormal = (int) ($this->sheetViewXml['zoomScaleNormal']);
|
||||
if ($zoomScaleNormal <= 0) {
|
||||
// setZoomScaleNormal will throw an Exception if the scale is less than or equals 0
|
||||
// that is OK when manually creating documents, but we should be able to read all documents
|
||||
$zoomScaleNormal = 100;
|
||||
}
|
||||
|
||||
$this->worksheet->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
|
||||
}
|
||||
}
|
||||
|
||||
private function view()
|
||||
{
|
||||
if (isset($this->sheetViewXml['view'])) {
|
||||
$this->worksheet->getSheetView()->setView((string) $this->sheetViewXml['view']);
|
||||
}
|
||||
}
|
||||
|
||||
private function gridLines()
|
||||
{
|
||||
if (isset($this->sheetViewXml['showGridLines'])) {
|
||||
$this->worksheet->setShowGridLines(
|
||||
self::boolean((string) $this->sheetViewXml['showGridLines'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function headers()
|
||||
{
|
||||
if (isset($this->sheetViewXml['showRowColHeaders'])) {
|
||||
$this->worksheet->setShowRowColHeaders(
|
||||
self::boolean((string) $this->sheetViewXml['showRowColHeaders'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function direction()
|
||||
{
|
||||
if (isset($this->sheetViewXml['rightToLeft'])) {
|
||||
$this->worksheet->setRightToLeft(
|
||||
self::boolean((string) $this->sheetViewXml['rightToLeft'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function pane()
|
||||
{
|
||||
$xSplit = 0;
|
||||
$ySplit = 0;
|
||||
$topLeftCell = null;
|
||||
|
||||
if (isset($this->sheetViewXml->pane['xSplit'])) {
|
||||
$xSplit = (int) ($this->sheetViewXml->pane['xSplit']);
|
||||
}
|
||||
|
||||
if (isset($this->sheetViewXml->pane['ySplit'])) {
|
||||
$ySplit = (int) ($this->sheetViewXml->pane['ySplit']);
|
||||
}
|
||||
|
||||
if (isset($this->sheetViewXml->pane['topLeftCell'])) {
|
||||
$topLeftCell = (string) $this->sheetViewXml->pane['topLeftCell'];
|
||||
}
|
||||
|
||||
$this->worksheet->freezePane(
|
||||
Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1),
|
||||
$topLeftCell
|
||||
);
|
||||
}
|
||||
|
||||
private function selection()
|
||||
{
|
||||
$sqref = (string) $this->sheetViewXml->selection['sqref'];
|
||||
$sqref = explode(' ', $sqref);
|
||||
$sqref = $sqref[0];
|
||||
|
||||
$this->worksheet->setSelectedCells($sqref);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,265 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Borders;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Color;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Font;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Protection;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||
|
||||
class Styles extends BaseParserClass
|
||||
{
|
||||
/**
|
||||
* Theme instance.
|
||||
*
|
||||
* @var Theme
|
||||
*/
|
||||
private static $theme = null;
|
||||
|
||||
private $styles = [];
|
||||
|
||||
private $cellStyles = [];
|
||||
|
||||
private $styleXml;
|
||||
|
||||
public function __construct(\SimpleXMLElement $styleXml)
|
||||
{
|
||||
$this->styleXml = $styleXml;
|
||||
}
|
||||
|
||||
public function setStyleBaseData(Theme $theme = null, $styles = [], $cellStyles = [])
|
||||
{
|
||||
self::$theme = $theme;
|
||||
$this->styles = $styles;
|
||||
$this->cellStyles = $cellStyles;
|
||||
}
|
||||
|
||||
private static function readFontStyle(Font $fontStyle, \SimpleXMLElement $fontStyleXml)
|
||||
{
|
||||
$fontStyle->setName((string) $fontStyleXml->name['val']);
|
||||
$fontStyle->setSize((float) $fontStyleXml->sz['val']);
|
||||
|
||||
if (isset($fontStyleXml->b)) {
|
||||
$fontStyle->setBold(!isset($fontStyleXml->b['val']) || self::boolean((string) $fontStyleXml->b['val']));
|
||||
}
|
||||
if (isset($fontStyleXml->i)) {
|
||||
$fontStyle->setItalic(!isset($fontStyleXml->i['val']) || self::boolean((string) $fontStyleXml->i['val']));
|
||||
}
|
||||
if (isset($fontStyleXml->strike)) {
|
||||
$fontStyle->setStrikethrough(!isset($fontStyleXml->strike['val']) || self::boolean((string) $fontStyleXml->strike['val']));
|
||||
}
|
||||
$fontStyle->getColor()->setARGB(self::readColor($fontStyleXml->color));
|
||||
|
||||
if (isset($fontStyleXml->u) && !isset($fontStyleXml->u['val'])) {
|
||||
$fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
|
||||
} elseif (isset($fontStyleXml->u, $fontStyleXml->u['val'])) {
|
||||
$fontStyle->setUnderline((string) $fontStyleXml->u['val']);
|
||||
}
|
||||
|
||||
if (isset($fontStyleXml->vertAlign, $fontStyleXml->vertAlign['val'])) {
|
||||
$verticalAlign = strtolower((string) $fontStyleXml->vertAlign['val']);
|
||||
if ($verticalAlign === 'superscript') {
|
||||
$fontStyle->setSuperscript(true);
|
||||
}
|
||||
if ($verticalAlign === 'subscript') {
|
||||
$fontStyle->setSubscript(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function readFillStyle(Fill $fillStyle, \SimpleXMLElement $fillStyleXml)
|
||||
{
|
||||
if ($fillStyleXml->gradientFill) {
|
||||
/** @var \SimpleXMLElement $gradientFill */
|
||||
$gradientFill = $fillStyleXml->gradientFill[0];
|
||||
if (!empty($gradientFill['type'])) {
|
||||
$fillStyle->setFillType((string) $gradientFill['type']);
|
||||
}
|
||||
$fillStyle->setRotation((float) ($gradientFill['degree']));
|
||||
$gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
|
||||
$fillStyle->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
|
||||
$fillStyle->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
|
||||
} elseif ($fillStyleXml->patternFill) {
|
||||
$patternType = (string) $fillStyleXml->patternFill['patternType'] != '' ? (string) $fillStyleXml->patternFill['patternType'] : 'solid';
|
||||
$fillStyle->setFillType($patternType);
|
||||
if ($fillStyleXml->patternFill->fgColor) {
|
||||
$fillStyle->getStartColor()->setARGB(self::readColor($fillStyleXml->patternFill->fgColor, true));
|
||||
} else {
|
||||
$fillStyle->getStartColor()->setARGB('FF000000');
|
||||
}
|
||||
if ($fillStyleXml->patternFill->bgColor) {
|
||||
$fillStyle->getEndColor()->setARGB(self::readColor($fillStyleXml->patternFill->bgColor, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function readBorderStyle(Borders $borderStyle, \SimpleXMLElement $borderStyleXml)
|
||||
{
|
||||
$diagonalUp = self::boolean((string) $borderStyleXml['diagonalUp']);
|
||||
$diagonalDown = self::boolean((string) $borderStyleXml['diagonalDown']);
|
||||
if (!$diagonalUp && !$diagonalDown) {
|
||||
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
|
||||
} elseif ($diagonalUp && !$diagonalDown) {
|
||||
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
|
||||
} elseif (!$diagonalUp && $diagonalDown) {
|
||||
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
|
||||
} else {
|
||||
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
|
||||
}
|
||||
|
||||
self::readBorder($borderStyle->getLeft(), $borderStyleXml->left);
|
||||
self::readBorder($borderStyle->getRight(), $borderStyleXml->right);
|
||||
self::readBorder($borderStyle->getTop(), $borderStyleXml->top);
|
||||
self::readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
|
||||
self::readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
|
||||
}
|
||||
|
||||
private static function readBorder(Border $border, \SimpleXMLElement $borderXml)
|
||||
{
|
||||
if (isset($borderXml['style'])) {
|
||||
$border->setBorderStyle((string) $borderXml['style']);
|
||||
}
|
||||
if (isset($borderXml->color)) {
|
||||
$border->getColor()->setARGB(self::readColor($borderXml->color));
|
||||
}
|
||||
}
|
||||
|
||||
private static function readAlignmentStyle(Alignment $alignment, \SimpleXMLElement $alignmentXml)
|
||||
{
|
||||
$alignment->setHorizontal((string) $alignmentXml->alignment['horizontal']);
|
||||
$alignment->setVertical((string) $alignmentXml->alignment['vertical']);
|
||||
|
||||
$textRotation = 0;
|
||||
if ((int) $alignmentXml->alignment['textRotation'] <= 90) {
|
||||
$textRotation = (int) $alignmentXml->alignment['textRotation'];
|
||||
} elseif ((int) $alignmentXml->alignment['textRotation'] > 90) {
|
||||
$textRotation = 90 - (int) $alignmentXml->alignment['textRotation'];
|
||||
}
|
||||
|
||||
$alignment->setTextRotation((int) $textRotation);
|
||||
$alignment->setWrapText(self::boolean((string) $alignmentXml->alignment['wrapText']));
|
||||
$alignment->setShrinkToFit(self::boolean((string) $alignmentXml->alignment['shrinkToFit']));
|
||||
$alignment->setIndent((int) ((string) $alignmentXml->alignment['indent']) > 0 ? (int) ((string) $alignmentXml->alignment['indent']) : 0);
|
||||
$alignment->setReadOrder((int) ((string) $alignmentXml->alignment['readingOrder']) > 0 ? (int) ((string) $alignmentXml->alignment['readingOrder']) : 0);
|
||||
}
|
||||
|
||||
private function readStyle(Style $docStyle, $style)
|
||||
{
|
||||
$docStyle->getNumberFormat()->setFormatCode($style->numFmt);
|
||||
|
||||
if (isset($style->font)) {
|
||||
self::readFontStyle($docStyle->getFont(), $style->font);
|
||||
}
|
||||
|
||||
if (isset($style->fill)) {
|
||||
self::readFillStyle($docStyle->getFill(), $style->fill);
|
||||
}
|
||||
|
||||
if (isset($style->border)) {
|
||||
self::readBorderStyle($docStyle->getBorders(), $style->border);
|
||||
}
|
||||
|
||||
if (isset($style->alignment)) {
|
||||
self::readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
|
||||
}
|
||||
|
||||
// protection
|
||||
if (isset($style->protection)) {
|
||||
$this->readProtectionLocked($docStyle, $style);
|
||||
$this->readProtectionHidden($docStyle, $style);
|
||||
}
|
||||
|
||||
// top-level style settings
|
||||
if (isset($style->quotePrefix)) {
|
||||
$docStyle->setQuotePrefix(true);
|
||||
}
|
||||
}
|
||||
|
||||
private function readProtectionLocked(Style $docStyle, $style)
|
||||
{
|
||||
if (isset($style->protection['locked'])) {
|
||||
if (self::boolean((string) $style->protection['locked'])) {
|
||||
$docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
|
||||
} else {
|
||||
$docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function readProtectionHidden(Style $docStyle, $style)
|
||||
{
|
||||
if (isset($style->protection['hidden'])) {
|
||||
if (self::boolean((string) $style->protection['hidden'])) {
|
||||
$docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
|
||||
} else {
|
||||
$docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function readColor($color, $background = false)
|
||||
{
|
||||
if (isset($color['rgb'])) {
|
||||
return (string) $color['rgb'];
|
||||
} elseif (isset($color['indexed'])) {
|
||||
return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
|
||||
} elseif (isset($color['theme'])) {
|
||||
if (self::$theme !== null) {
|
||||
$returnColour = self::$theme->getColourByIndex((int) $color['theme']);
|
||||
if (isset($color['tint'])) {
|
||||
$tintAdjust = (float) $color['tint'];
|
||||
$returnColour = Color::changeBrightness($returnColour, $tintAdjust);
|
||||
}
|
||||
|
||||
return 'FF' . $returnColour;
|
||||
}
|
||||
}
|
||||
|
||||
return ($background) ? 'FFFFFFFF' : 'FF000000';
|
||||
}
|
||||
|
||||
public function dxfs($readDataOnly = false)
|
||||
{
|
||||
$dxfs = [];
|
||||
if (!$readDataOnly && $this->styleXml) {
|
||||
// Conditional Styles
|
||||
if ($this->styleXml->dxfs) {
|
||||
foreach ($this->styleXml->dxfs->dxf as $dxf) {
|
||||
$style = new Style(false, true);
|
||||
$this->readStyle($style, $dxf);
|
||||
$dxfs[] = $style;
|
||||
}
|
||||
}
|
||||
// Cell Styles
|
||||
if ($this->styleXml->cellStyles) {
|
||||
foreach ($this->styleXml->cellStyles->cellStyle as $cellStyle) {
|
||||
if ((int) ($cellStyle['builtinId']) == 0) {
|
||||
if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
|
||||
// Set default style
|
||||
$style = new Style();
|
||||
$this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
|
||||
|
||||
// normal style, currently not using it for anything
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $dxfs;
|
||||
}
|
||||
|
||||
public function styles()
|
||||
{
|
||||
return $this->styles;
|
||||
}
|
||||
|
||||
private static function getArrayItem($array, $key = 0)
|
||||
{
|
||||
return isset($array[$key]) ? $array[$key] : null;
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,7 @@ class Xml extends BaseReader
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
parent::__construct();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
}
|
||||
|
||||
|
||||
@ -82,13 +82,13 @@ class ReferenceHelper
|
||||
*/
|
||||
public static function cellSort($a, $b)
|
||||
{
|
||||
$ac = $bc = '';
|
||||
$ar = $br = 0;
|
||||
// TODO Scrutinizer doesn't like sscanf($a, '%[A-Z]%d', $ac, $ar), and we can't use short list() syntax
|
||||
// [$ac, $ar] = sscanf($a, '%[A-Z]%d') while retaining PHP 5.6 support.
|
||||
// Switch when we drop support for 5.6
|
||||
list($ac, $ar) = sscanf($a, '%[A-Z]%d');
|
||||
list($bc, $br) = sscanf($b, '%[A-Z]%d');
|
||||
|
||||
sscanf($a, '%[A-Z]%d', $ac, $ar);
|
||||
sscanf($b, '%[A-Z]%d', $bc, $br);
|
||||
|
||||
if ($ar == $br) {
|
||||
if ($ar === $br) {
|
||||
return strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
|
||||
}
|
||||
|
||||
@ -106,13 +106,13 @@ class ReferenceHelper
|
||||
*/
|
||||
public static function cellReverseSort($a, $b)
|
||||
{
|
||||
$ac = $bc = '';
|
||||
$ar = $br = 0;
|
||||
// TODO Scrutinizer doesn't like sscanf($a, '%[A-Z]%d', $ac, $ar), and we can't use short list() syntax
|
||||
// [$ac, $ar] = sscanf($a, '%[A-Z]%d') while retaining PHP 5.6 support.
|
||||
// Switch when we drop support for 5.6
|
||||
list($ac, $ar) = sscanf($a, '%[A-Z]%d');
|
||||
list($bc, $br) = sscanf($b, '%[A-Z]%d');
|
||||
|
||||
sscanf($a, '%[A-Z]%d', $ac, $ar);
|
||||
sscanf($b, '%[A-Z]%d', $bc, $br);
|
||||
|
||||
if ($ar == $br) {
|
||||
if ($ar === $br) {
|
||||
return 1 - strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
|
||||
}
|
||||
|
||||
@ -625,7 +625,7 @@ class ReferenceHelper
|
||||
* Update references within formulas.
|
||||
*
|
||||
* @param string $pFormula Formula to update
|
||||
* @param int $pBefore Insert before this one
|
||||
* @param string $pBefore Insert before this one
|
||||
* @param int $pNumCols Number of columns to insert
|
||||
* @param int $pNumRows Number of rows to insert
|
||||
* @param string $sheetName Worksheet name/title
|
||||
|
||||
@ -24,6 +24,20 @@ class Settings
|
||||
*/
|
||||
private static $libXmlLoaderOptions = null;
|
||||
|
||||
/**
|
||||
* Allow/disallow libxml_disable_entity_loader() call when not thread safe.
|
||||
* Default behaviour is to do the check, but if you're running PHP versions
|
||||
* 7.2 < 7.2.1
|
||||
* 7.1 < 7.1.13
|
||||
* 7.0 < 7.0.27
|
||||
* 5.6 ANY
|
||||
* then you may need to disable this check to prevent unwanted behaviour in other threads
|
||||
* SECURITY WARNING: Changing this flag is not recommended.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $libXmlDisableEntityLoader = true;
|
||||
|
||||
/**
|
||||
* The cache implementation to be used for cell collection.
|
||||
*
|
||||
@ -101,6 +115,34 @@ class Settings
|
||||
return self::$libXmlLoaderOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable the entity loader for libxml loader.
|
||||
* Allow/disallow libxml_disable_entity_loader() call when not thread safe.
|
||||
* Default behaviour is to do the check, but if you're running PHP versions
|
||||
* 7.2 < 7.2.1
|
||||
* 7.1 < 7.1.13
|
||||
* 7.0 < 7.0.27
|
||||
* 5.6 ANY
|
||||
* then you may need to disable this check to prevent unwanted behaviour in other threads
|
||||
* SECURITY WARNING: Changing this flag to false is not recommended.
|
||||
*
|
||||
* @param bool $state
|
||||
*/
|
||||
public static function setLibXmlDisableEntityLoader($state)
|
||||
{
|
||||
self::$libXmlDisableEntityLoader = (bool) $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the state of the entity loader (disabled/enabled) for libxml loader.
|
||||
*
|
||||
* @return bool $state
|
||||
*/
|
||||
public static function getLibXmlDisableEntityLoader()
|
||||
{
|
||||
return self::$libXmlDisableEntityLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the implementation of cache that should be used for cell collection.
|
||||
*
|
||||
|
||||
@ -192,7 +192,7 @@ class Date
|
||||
$interval = $days . ' days';
|
||||
|
||||
return $baseDate->modify($interval)
|
||||
->setTime($hours, $minutes, $seconds);
|
||||
->setTime((int) $hours, (int) $minutes, (int) $seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,12 +244,12 @@ class Date
|
||||
public static function dateTimeToExcel(DateTimeInterface $dateValue)
|
||||
{
|
||||
return self::formattedPHPToExcel(
|
||||
$dateValue->format('Y'),
|
||||
$dateValue->format('m'),
|
||||
$dateValue->format('d'),
|
||||
$dateValue->format('H'),
|
||||
$dateValue->format('i'),
|
||||
$dateValue->format('s')
|
||||
(int) $dateValue->format('Y'),
|
||||
(int) $dateValue->format('m'),
|
||||
(int) $dateValue->format('d'),
|
||||
(int) $dateValue->format('H'),
|
||||
(int) $dateValue->format('i'),
|
||||
(int) $dateValue->format('s')
|
||||
);
|
||||
}
|
||||
|
||||
@ -325,11 +325,12 @@ class Date
|
||||
*/
|
||||
public static function isDateTime(Cell $pCell)
|
||||
{
|
||||
return self::isDateTimeFormat(
|
||||
$pCell->getWorksheet()->getStyle(
|
||||
$pCell->getCoordinate()
|
||||
)->getNumberFormat()
|
||||
);
|
||||
return is_numeric($pCell->getValue()) &&
|
||||
self::isDateTimeFormat(
|
||||
$pCell->getWorksheet()->getStyle(
|
||||
$pCell->getCoordinate()
|
||||
)->getNumberFormat()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -537,31 +537,37 @@ class OLE
|
||||
/**
|
||||
* Returns a timestamp from an OLE container's date.
|
||||
*
|
||||
* @param int $string A binary string with the encoded date
|
||||
* @param string $oleTimestamp A binary string with the encoded date
|
||||
*
|
||||
* @return string The timestamp corresponding to the string
|
||||
* @throws ReaderException
|
||||
*
|
||||
* @return int The Unix timestamp corresponding to the string
|
||||
*/
|
||||
public static function OLE2LocalDate($string)
|
||||
public static function OLE2LocalDate($oleTimestamp)
|
||||
{
|
||||
if (strlen($string) != 8) {
|
||||
if (strlen($oleTimestamp) != 8) {
|
||||
throw new ReaderException('Expecting 8 byte string');
|
||||
}
|
||||
|
||||
// factor used for separating numbers into 4 bytes parts
|
||||
$factor = pow(2, 32);
|
||||
list(, $high_part) = unpack('V', substr($string, 4, 4));
|
||||
list(, $low_part) = unpack('V', substr($string, 0, 4));
|
||||
// convert to units of 100 ns since 1601:
|
||||
$unpackedTimestamp = unpack('v4', $oleTimestamp);
|
||||
$timestampHigh = (float) $unpackedTimestamp[4] * 65536 + (float) $unpackedTimestamp[3];
|
||||
$timestampLow = (float) $unpackedTimestamp[2] * 65536 + (float) $unpackedTimestamp[1];
|
||||
|
||||
$big_date = ($high_part * $factor) + $low_part;
|
||||
// translate to seconds
|
||||
$big_date /= 10000000;
|
||||
// translate to seconds since 1601:
|
||||
$timestampHigh /= 10000000;
|
||||
$timestampLow /= 10000000;
|
||||
|
||||
// days from 1-1-1601 until the beggining of UNIX era
|
||||
// days from 1601 to 1970:
|
||||
$days = 134774;
|
||||
|
||||
// translate to seconds from beggining of UNIX era
|
||||
$big_date -= $days * 24 * 3600;
|
||||
// translate to seconds since 1970:
|
||||
$unixTimestamp = floor(65536.0 * 65536.0 * $timestampHigh + $timestampLow - $days * 24 * 3600 + 0.5);
|
||||
|
||||
return floor($big_date);
|
||||
if ((int) $unixTimestamp == $unixTimestamp) {
|
||||
return (int) $unixTimestamp;
|
||||
}
|
||||
|
||||
return $unixTimestamp >= 0.0 ? PHP_INT_MAX : PHP_INT_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +113,9 @@ class ExponentialBestFit extends BestFit
|
||||
*/
|
||||
public function __construct($yValues, $xValues = [], $const = true)
|
||||
{
|
||||
if (parent::__construct($yValues, $xValues) !== false) {
|
||||
parent::__construct($yValues, $xValues);
|
||||
|
||||
if (!$this->error) {
|
||||
$this->exponentialRegression($yValues, $xValues, $const);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,9 @@ class LinearBestFit extends BestFit
|
||||
*/
|
||||
public function __construct($yValues, $xValues = [], $const = true)
|
||||
{
|
||||
if (parent::__construct($yValues, $xValues) !== false) {
|
||||
parent::__construct($yValues, $xValues);
|
||||
|
||||
if (!$this->error) {
|
||||
$this->linearRegression($yValues, $xValues, $const);
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +81,9 @@ class LogarithmicBestFit extends BestFit
|
||||
*/
|
||||
public function __construct($yValues, $xValues = [], $const = true)
|
||||
{
|
||||
if (parent::__construct($yValues, $xValues) !== false) {
|
||||
parent::__construct($yValues, $xValues);
|
||||
|
||||
if (!$this->error) {
|
||||
$this->logarithmicRegression($yValues, $xValues, $const);
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +182,9 @@ class PolynomialBestFit extends BestFit
|
||||
*/
|
||||
public function __construct($order, $yValues, $xValues = [], $const = true)
|
||||
{
|
||||
if (parent::__construct($yValues, $xValues) !== false) {
|
||||
parent::__construct($yValues, $xValues);
|
||||
|
||||
if (!$this->error) {
|
||||
if ($order < $this->valueCount) {
|
||||
$this->bestFitType .= '_' . $order;
|
||||
$this->order = $order;
|
||||
|
||||
@ -105,7 +105,9 @@ class PowerBestFit extends BestFit
|
||||
*/
|
||||
public function __construct($yValues, $xValues = [], $const = true)
|
||||
{
|
||||
if (parent::__construct($yValues, $xValues) !== false) {
|
||||
parent::__construct($yValues, $xValues);
|
||||
|
||||
if (!$this->error) {
|
||||
$this->powerRegression($yValues, $xValues, $const);
|
||||
}
|
||||
}
|
||||
|
||||
@ -715,7 +715,7 @@ class Spreadsheet
|
||||
*
|
||||
* @param string $pName Sheet name
|
||||
*
|
||||
* @return Worksheet
|
||||
* @return null|Worksheet
|
||||
*/
|
||||
public function getSheetByName($pName)
|
||||
{
|
||||
|
||||
@ -49,6 +49,8 @@ class NumberFormat extends Supervisor
|
||||
const FORMAT_CURRENCY_USD = '$#,##0_-';
|
||||
const FORMAT_CURRENCY_EUR_SIMPLE = '#,##0.00_-"€"';
|
||||
const FORMAT_CURRENCY_EUR = '#,##0_-"€"';
|
||||
const FORMAT_ACCOUNTING_USD = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
|
||||
const FORMAT_ACCOUNTING_EUR = '_("€"* #,##0.00_);_("€"* \(#,##0.00\);_("€"* "-"??_);_(@_)';
|
||||
|
||||
/**
|
||||
* Excel built-in number formats.
|
||||
@ -308,6 +310,22 @@ class NumberFormat extends Supervisor
|
||||
self::$builtInFormats[69] = 't# ?/?';
|
||||
self::$builtInFormats[70] = 't# ??/??';
|
||||
|
||||
// JPN
|
||||
self::$builtInFormats[28] = '[$-411]ggge"年"m"月"d"日"';
|
||||
self::$builtInFormats[29] = '[$-411]ggge"年"m"月"d"日"';
|
||||
self::$builtInFormats[31] = 'yyyy"年"m"月"d"日"';
|
||||
self::$builtInFormats[32] = 'h"時"mm"分"';
|
||||
self::$builtInFormats[33] = 'h"時"mm"分"ss"秒"';
|
||||
self::$builtInFormats[34] = 'yyyy"年"m"月"';
|
||||
self::$builtInFormats[35] = 'm"月"d"日"';
|
||||
self::$builtInFormats[51] = '[$-411]ggge"年"m"月"d"日"';
|
||||
self::$builtInFormats[52] = 'yyyy"年"m"月"';
|
||||
self::$builtInFormats[53] = 'm"月"d"日"';
|
||||
self::$builtInFormats[54] = '[$-411]ggge"年"m"月"d"日"';
|
||||
self::$builtInFormats[55] = 'yyyy"年"m"月"';
|
||||
self::$builtInFormats[56] = 'm"月"d"日"';
|
||||
self::$builtInFormats[58] = '[$-411]ggge"年"m"月"d"日"';
|
||||
|
||||
// Flip array (for faster lookups)
|
||||
self::$flippedBuiltInFormats = array_flip(self::$builtInFormats);
|
||||
}
|
||||
@ -596,7 +614,7 @@ class NumberFormat extends Supervisor
|
||||
}
|
||||
|
||||
// Convert any other escaped characters to quoted strings, e.g. (\T to "T")
|
||||
$format = preg_replace('/(\\\([^ ]))(?=(?:[^"]|"[^"]*")*$)/u', '"${2}"', $format);
|
||||
$format = preg_replace('/(\\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/u', '"${2}"', $format);
|
||||
|
||||
// Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal)
|
||||
$sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format);
|
||||
|
||||
@ -64,7 +64,7 @@ abstract class Dimension
|
||||
*/
|
||||
public function setVisible($pValue)
|
||||
{
|
||||
$this->visible = $pValue;
|
||||
$this->visible = (bool) $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -119,7 +119,7 @@ abstract class Dimension
|
||||
*/
|
||||
public function setCollapsed($pValue)
|
||||
{
|
||||
$this->collapsed = $pValue;
|
||||
$this->collapsed = (bool) $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -170,6 +170,16 @@ class RowCellIterator extends CellIterator
|
||||
return $this->currentColumnIndex <= $this->endColumnIndex && $this->currentColumnIndex >= $this->startColumnIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current iterator position.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCurrentColumnIndex()
|
||||
{
|
||||
return $this->currentColumnIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary.
|
||||
*
|
||||
|
||||
@ -1096,7 +1096,7 @@ class Worksheet implements IComparable
|
||||
* @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
|
||||
* @return int Highest row number that contains data
|
||||
*/
|
||||
public function getHighestDataRow($column = null)
|
||||
{
|
||||
@ -2064,7 +2064,7 @@ class Worksheet implements IComparable
|
||||
/**
|
||||
* Insert a new column, updating all possible related data.
|
||||
*
|
||||
* @param int $pBefore Insert before this one, eg: 'A'
|
||||
* @param string $pBefore Insert before this one, eg: 'A'
|
||||
* @param int $pNumCols Number of columns to insert
|
||||
*
|
||||
* @throws Exception
|
||||
|
||||
@ -35,27 +35,11 @@ abstract class BaseWriter implements IWriter
|
||||
*/
|
||||
private $diskCachingDirectory = './';
|
||||
|
||||
/**
|
||||
* Write charts in workbook?
|
||||
* If this is true, then the Writer will write definitions for any charts that exist in the PhpSpreadsheet object.
|
||||
* If false (the default) it will ignore any charts defined in the PhpSpreadsheet object.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIncludeCharts()
|
||||
{
|
||||
return $this->includeCharts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set write charts in workbook
|
||||
* Set to true, to advise the Writer to include any charts that exist in the PhpSpreadsheet object.
|
||||
* Set to false (the default) to ignore charts.
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return IWriter
|
||||
*/
|
||||
public function setIncludeCharts($pValue)
|
||||
{
|
||||
$this->includeCharts = (bool) $pValue;
|
||||
@ -63,30 +47,11 @@ abstract class BaseWriter implements IWriter
|
||||
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 PhpSpreadsheet, but slower
|
||||
* when opening the resulting file in MS Excel, because Excel has to recalculate the formulae itself.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
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 bool $pValue Pre-Calculate Formulas?
|
||||
*
|
||||
* @return IWriter
|
||||
*/
|
||||
public function setPreCalculateFormulas($pValue)
|
||||
{
|
||||
$this->preCalculateFormulas = (bool) $pValue;
|
||||
@ -94,26 +59,11 @@ abstract class BaseWriter implements IWriter
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get use disk caching where possible?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseDiskCaching()
|
||||
{
|
||||
return $this->useDiskCaching;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set use disk caching where possible?
|
||||
*
|
||||
* @param bool $pValue
|
||||
* @param string $pDirectory Disk caching directory
|
||||
*
|
||||
* @throws Exception when directory does not exist
|
||||
*
|
||||
* @return IWriter
|
||||
*/
|
||||
public function setUseDiskCaching($pValue, $pDirectory = null)
|
||||
{
|
||||
$this->useDiskCaching = $pValue;
|
||||
@ -129,11 +79,6 @@ abstract class BaseWriter implements IWriter
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get disk caching directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiskCachingDirectory()
|
||||
{
|
||||
return $this->diskCachingDirectory;
|
||||
|
||||
@ -13,6 +13,49 @@ interface IWriter
|
||||
*/
|
||||
public function __construct(Spreadsheet $spreadsheet);
|
||||
|
||||
/**
|
||||
* Write charts in workbook?
|
||||
* If this is true, then the Writer will write definitions for any charts that exist in the PhpSpreadsheet object.
|
||||
* If false (the default) it will ignore any charts defined in the PhpSpreadsheet object.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIncludeCharts();
|
||||
|
||||
/**
|
||||
* Set write charts in workbook
|
||||
* Set to true, to advise the Writer to include any charts that exist in the PhpSpreadsheet object.
|
||||
* Set to false (the default) to ignore charts.
|
||||
*
|
||||
* @param bool $pValue
|
||||
*
|
||||
* @return IWriter
|
||||
*/
|
||||
public function setIncludeCharts($pValue);
|
||||
|
||||
/**
|
||||
* 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 PhpSpreadsheet, but slower
|
||||
* when opening the resulting file in MS Excel, because Excel has to recalculate the formulae itself.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getPreCalculateFormulas();
|
||||
|
||||
/**
|
||||
* 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 bool $pValue Pre-Calculate Formulas?
|
||||
*
|
||||
* @return IWriter
|
||||
*/
|
||||
public function setPreCalculateFormulas($pValue);
|
||||
|
||||
/**
|
||||
* Save PhpSpreadsheet to file.
|
||||
*
|
||||
@ -21,4 +64,30 @@ interface IWriter
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
*/
|
||||
public function save($pFilename);
|
||||
|
||||
/**
|
||||
* Get use disk caching where possible?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseDiskCaching();
|
||||
|
||||
/**
|
||||
* Set use disk caching where possible?
|
||||
*
|
||||
* @param bool $pValue
|
||||
* @param string $pDirectory Disk caching directory
|
||||
*
|
||||
* @throws Exception when directory does not exist
|
||||
*
|
||||
* @return IWriter
|
||||
*/
|
||||
public function setUseDiskCaching($pValue, $pDirectory = null);
|
||||
|
||||
/**
|
||||
* Get disk caching directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDiskCachingDirectory();
|
||||
}
|
||||
|
||||
@ -1009,7 +1009,7 @@ class Parser
|
||||
|
||||
break;
|
||||
case '>':
|
||||
if ($this->lookAhead == '=') { // it's a GE token
|
||||
if ($this->lookAhead === '=') { // it's a GE token
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1018,7 +1018,7 @@ class Parser
|
||||
break;
|
||||
case '<':
|
||||
// it's a LE or a NE token
|
||||
if (($this->lookAhead == '=') or ($this->lookAhead == '>')) {
|
||||
if (($this->lookAhead === '=') or ($this->lookAhead === '>')) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1027,12 +1027,12 @@ class Parser
|
||||
break;
|
||||
default:
|
||||
// if it's a reference A1 or $A$1 or $A1 or A$1
|
||||
if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead != ':') and ($this->lookAhead != '.') and ($this->lookAhead != '!')) {
|
||||
if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead !== ':') and ($this->lookAhead !== '.') and ($this->lookAhead !== '!')) {
|
||||
return $token;
|
||||
} elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead != ':') and ($this->lookAhead != '.')) {
|
||||
} elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead !== ':') and ($this->lookAhead !== '.')) {
|
||||
// If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
|
||||
return $token;
|
||||
} elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead != ':') and ($this->lookAhead != '.')) {
|
||||
} elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead !== ':') and ($this->lookAhead !== '.')) {
|
||||
// If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
|
||||
return $token;
|
||||
} elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead)) {
|
||||
@ -1044,19 +1044,19 @@ class Parser
|
||||
} elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $token) and !preg_match('/\d/', $this->lookAhead)) {
|
||||
// If it's an external range like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
|
||||
return $token;
|
||||
} elseif (is_numeric($token) and (!is_numeric($token . $this->lookAhead) or ($this->lookAhead == '')) and ($this->lookAhead != '!') and ($this->lookAhead != ':')) {
|
||||
} elseif (is_numeric($token) and (!is_numeric($token . $this->lookAhead) or ($this->lookAhead == '')) and ($this->lookAhead !== '!') and ($this->lookAhead !== ':')) {
|
||||
// If it's a number (check that it's not a sheet name or range)
|
||||
return $token;
|
||||
} elseif (preg_match('/"([^"]|""){0,255}"/', $token) and $this->lookAhead != '"' and (substr_count($token, '"') % 2 == 0)) {
|
||||
} elseif (preg_match('/"([^"]|""){0,255}"/', $token) and $this->lookAhead !== '"' and (substr_count($token, '"') % 2 == 0)) {
|
||||
// If it's a string (of maximum 255 characters)
|
||||
return $token;
|
||||
} elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) or $token == '#N/A') {
|
||||
} elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) or $token === '#N/A') {
|
||||
// If it's an error code
|
||||
return $token;
|
||||
} elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) and ($this->lookAhead == '(')) {
|
||||
} elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) and ($this->lookAhead === '(')) {
|
||||
// if it's a function call
|
||||
return $token;
|
||||
} elseif (substr($token, -1) == ')') {
|
||||
} elseif (substr($token, -1) === ')') {
|
||||
// It's an argument of some description (e.g. a named range),
|
||||
// precise nature yet to be determined
|
||||
return $token;
|
||||
@ -1078,8 +1078,7 @@ class Parser
|
||||
{
|
||||
$this->currentCharacter = 0;
|
||||
$this->formula = $formula;
|
||||
$this->lookAhead = isset($formula[1]) ? $formula[1]
|
||||
: '';
|
||||
$this->lookAhead = isset($formula[1]) ? $formula[1] : '';
|
||||
$this->advance();
|
||||
$this->parseTree = $this->condition();
|
||||
|
||||
@ -1248,10 +1247,10 @@ class Parser
|
||||
*/
|
||||
private function fact()
|
||||
{
|
||||
if ($this->currentToken == '(') {
|
||||
if ($this->currentToken === '(') {
|
||||
$this->advance(); // eat the "("
|
||||
$result = $this->parenthesizedExpression();
|
||||
if ($this->currentToken != ')') {
|
||||
if ($this->currentToken !== ')') {
|
||||
throw new WriterException("')' token expected.");
|
||||
}
|
||||
$this->advance(); // eat the ")"
|
||||
@ -1299,7 +1298,7 @@ class Parser
|
||||
return $result;
|
||||
} elseif (is_numeric($this->currentToken)) {
|
||||
// If it's a number or a percent
|
||||
if ($this->lookAhead == '%') {
|
||||
if ($this->lookAhead === '%') {
|
||||
$result = $this->createTree('ptgPercent', $this->currentToken, '');
|
||||
$this->advance(); // Skip the percentage operator once we've pre-built that tree
|
||||
} else {
|
||||
@ -1331,9 +1330,9 @@ class Parser
|
||||
$result = ''; // initialize result
|
||||
$this->advance();
|
||||
$this->advance(); // eat the "("
|
||||
while ($this->currentToken != ')') {
|
||||
while ($this->currentToken !== ')') {
|
||||
if ($num_args > 0) {
|
||||
if ($this->currentToken == ',' || $this->currentToken == ';') {
|
||||
if ($this->currentToken === ',' || $this->currentToken === ';') {
|
||||
$this->advance(); // eat the "," or ";"
|
||||
} else {
|
||||
throw new WriterException("Syntax error: comma expected in function $function, arg #{$num_args}");
|
||||
|
||||
@ -267,9 +267,7 @@ class Workbook extends BIFFwriter
|
||||
|
||||
$this->xfWriters[] = $xfWriter;
|
||||
|
||||
$xfIndex = count($this->xfWriters) - 1;
|
||||
|
||||
return $xfIndex;
|
||||
return count($this->xfWriters) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,7 +317,7 @@ class Workbook extends BIFFwriter
|
||||
if ($colorIndex) {
|
||||
$this->colors[$rgb] = $colorIndex;
|
||||
} else {
|
||||
if (count($this->colors) == 0) {
|
||||
if (count($this->colors) === 0) {
|
||||
$lastColor = 7;
|
||||
} else {
|
||||
$lastColor = end($this->colors);
|
||||
@ -437,7 +435,7 @@ class Workbook extends BIFFwriter
|
||||
|
||||
// Prepare part 3 of the workbook global stream, what goes after the SHEET records
|
||||
$part3 = '';
|
||||
if ($this->countryCode != -1) {
|
||||
if ($this->countryCode !== -1) {
|
||||
$part3 .= $this->writeCountry();
|
||||
}
|
||||
$part3 .= $this->writeRecalcId();
|
||||
@ -918,7 +916,7 @@ class Workbook extends BIFFwriter
|
||||
$record = 0x0022; // Record identifier
|
||||
$length = 0x0002; // Bytes to follow
|
||||
|
||||
$f1904 = (Date::getExcelCalendar() == Date::CALENDAR_MAC_1904)
|
||||
$f1904 = (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904)
|
||||
? 1
|
||||
: 0; // Flag for 1904 date system
|
||||
|
||||
|
||||
@ -328,6 +328,17 @@ class Xlsx extends BaseWriter
|
||||
$zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
|
||||
}
|
||||
|
||||
// Add unparsed drawings
|
||||
if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'])) {
|
||||
foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) {
|
||||
$drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']);
|
||||
if ($drawingFile !== false) {
|
||||
$drawingFile = ltrim($drawingFile, '.');
|
||||
$zip->addFromString('xl' . $drawingFile, $drawingXml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add comment relationship parts
|
||||
if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
|
||||
// VML Comments
|
||||
@ -338,8 +349,8 @@ class Xlsx extends BaseWriter
|
||||
}
|
||||
|
||||
// Add unparsed relationship parts
|
||||
if (isset($unparsedLoadedData['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['vmlDrawings'])) {
|
||||
foreach ($unparsedLoadedData['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['vmlDrawings'] as $vmlDrawing) {
|
||||
if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) {
|
||||
foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) {
|
||||
$zip->addFromString($vmlDrawing['filePath'], $vmlDrawing['content']);
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ class Workbook extends WriterPart
|
||||
{
|
||||
$objWriter->startElement('workbookPr');
|
||||
|
||||
if (Date::getExcelCalendar() == Date::CALENDAR_MAC_1904) {
|
||||
if (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) {
|
||||
$objWriter->writeAttribute('date1904', '1');
|
||||
}
|
||||
|
||||
@ -225,7 +225,7 @@ class Workbook extends WriterPart
|
||||
$objWriter->startElement('sheet');
|
||||
$objWriter->writeAttribute('name', $pSheetname);
|
||||
$objWriter->writeAttribute('sheetId', $pSheetId);
|
||||
if ($sheetState != 'visible' && $sheetState != '') {
|
||||
if ($sheetState !== 'visible' && $sheetState != '') {
|
||||
$objWriter->writeAttribute('state', $sheetState);
|
||||
}
|
||||
$objWriter->writeAttribute('r:id', 'rId' . $pRelId);
|
||||
|
||||
@ -141,7 +141,7 @@ class Worksheet extends WriterPart
|
||||
$objWriter->startElement('sheetPr');
|
||||
if ($pSheet->getParent()->hasMacros()) {
|
||||
//if the workbook have macros, we need to have codeName for the sheet
|
||||
if ($pSheet->hasCodeName() == false) {
|
||||
if (!$pSheet->hasCodeName()) {
|
||||
$pSheet->setCodeName($pSheet->getTitle());
|
||||
}
|
||||
$objWriter->writeAttribute('codeName', $pSheet->getCodeName());
|
||||
@ -322,7 +322,7 @@ class Worksheet extends WriterPart
|
||||
}
|
||||
|
||||
// Set Zero Height row
|
||||
if ((string) $pSheet->getDefaultRowDimension()->getZeroHeight() == '1' ||
|
||||
if ((string) $pSheet->getDefaultRowDimension()->getZeroHeight() === '1' ||
|
||||
strtolower((string) $pSheet->getDefaultRowDimension()->getZeroHeight()) == 'true') {
|
||||
$objWriter->writeAttribute('zeroHeight', '1');
|
||||
}
|
||||
@ -383,7 +383,7 @@ class Worksheet extends WriterPart
|
||||
}
|
||||
|
||||
// Column visibility
|
||||
if ($colDimension->getVisible() == false) {
|
||||
if ($colDimension->getVisible() === false) {
|
||||
$objWriter->writeAttribute('hidden', 'true');
|
||||
}
|
||||
|
||||
@ -398,7 +398,7 @@ class Worksheet extends WriterPart
|
||||
}
|
||||
|
||||
// Collapsed
|
||||
if ($colDimension->getCollapsed() == true) {
|
||||
if ($colDimension->getCollapsed() === true) {
|
||||
$objWriter->writeAttribute('collapsed', 'true');
|
||||
}
|
||||
|
||||
@ -428,7 +428,7 @@ class Worksheet extends WriterPart
|
||||
// sheetProtection
|
||||
$objWriter->startElement('sheetProtection');
|
||||
|
||||
if ($pSheet->getProtection()->getPassword() != '') {
|
||||
if ($pSheet->getProtection()->getPassword() !== '') {
|
||||
$objWriter->writeAttribute('password', $pSheet->getProtection()->getPassword());
|
||||
}
|
||||
|
||||
@ -519,6 +519,9 @@ class Worksheet extends WriterPart
|
||||
// Formula
|
||||
$objWriter->writeElement('formula', $formula);
|
||||
}
|
||||
} elseif ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSBLANKS) {
|
||||
// formula copied from ms xlsx xml source file
|
||||
$objWriter->writeElement('formula', 'LEN(TRIM(' . $cellCoordinate . '))=0');
|
||||
}
|
||||
|
||||
$objWriter->endElement();
|
||||
@ -624,8 +627,9 @@ class Worksheet extends WriterPart
|
||||
$objWriter->writeAttribute('location', str_replace('sheet://', '', $hyperlink->getUrl()));
|
||||
}
|
||||
|
||||
if ($hyperlink->getTooltip() != '') {
|
||||
if ($hyperlink->getTooltip() !== '') {
|
||||
$objWriter->writeAttribute('tooltip', $hyperlink->getTooltip());
|
||||
$objWriter->writeAttribute('display', $hyperlink->getTooltip());
|
||||
}
|
||||
|
||||
$objWriter->endElement();
|
||||
@ -991,12 +995,12 @@ class Worksheet extends WriterPart
|
||||
}
|
||||
|
||||
// Row visibility
|
||||
if ($rowDimension->getVisible() == false) {
|
||||
if (!$rowDimension->getVisible() === true) {
|
||||
$objWriter->writeAttribute('hidden', 'true');
|
||||
}
|
||||
|
||||
// Collapsed
|
||||
if ($rowDimension->getCollapsed() == true) {
|
||||
if ($rowDimension->getCollapsed() === true) {
|
||||
$objWriter->writeAttribute('collapsed', 'true');
|
||||
}
|
||||
|
||||
@ -1101,7 +1105,7 @@ class Worksheet extends WriterPart
|
||||
break;
|
||||
case 'f': // Formula
|
||||
$attributes = $pCell->getFormulaAttributes();
|
||||
if ($attributes['t'] == 'array') {
|
||||
if ($attributes['t'] === 'array') {
|
||||
$objWriter->startElement('f');
|
||||
$objWriter->writeAttribute('t', 'array');
|
||||
$objWriter->writeAttribute('ref', $pCellAddress);
|
||||
@ -1114,7 +1118,7 @@ class Worksheet extends WriterPart
|
||||
}
|
||||
if ($this->getParentWriter()->getOffice2003Compatibility() === false) {
|
||||
if ($this->getParentWriter()->getPreCalculateFormulas()) {
|
||||
if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) != '#') {
|
||||
if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) !== '#') {
|
||||
$objWriter->writeElement('v', StringHelper::formatNumber($calculatedValue));
|
||||
} else {
|
||||
$objWriter->writeElement('v', '0');
|
||||
@ -1135,7 +1139,7 @@ class Worksheet extends WriterPart
|
||||
|
||||
break;
|
||||
case 'e': // Error
|
||||
if (substr($cellValue, 0, 1) == '=') {
|
||||
if (substr($cellValue, 0, 1) === '=') {
|
||||
$objWriter->writeElement('f', substr($cellValue, 1));
|
||||
$objWriter->writeElement('v', substr($cellValue, 1));
|
||||
} else {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user