update inbox list
This commit is contained in:
158
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
vendored
Normal file
158
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
|
||||
abstract class BaseReader implements IReader
|
||||
{
|
||||
/**
|
||||
* Read data only?
|
||||
* Identifies whether the Reader should only read data values for cells, and ignore any formatting information;
|
||||
* or whether it should read both data and formatting.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $readDataOnly = false;
|
||||
|
||||
/**
|
||||
* Read empty cells?
|
||||
* Identifies whether the Reader should read data values for cells all cells, or should ignore cells containing
|
||||
* null value or empty string.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $readEmptyCells = true;
|
||||
|
||||
/**
|
||||
* Read charts that are defined in the workbook?
|
||||
* Identifies whether the Reader should read the definitions for any charts that exist in the workbook;.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $includeCharts = false;
|
||||
|
||||
/**
|
||||
* Restrict which sheets should be loaded?
|
||||
* This property holds an array of worksheet names to be loaded. If null, then all worksheets will be loaded.
|
||||
*
|
||||
* @var array of string
|
||||
*/
|
||||
protected $loadSheetsOnly;
|
||||
|
||||
/**
|
||||
* IReadFilter instance.
|
||||
*
|
||||
* @var IReadFilter
|
||||
*/
|
||||
protected $readFilter;
|
||||
|
||||
protected $fileHandle;
|
||||
|
||||
/**
|
||||
* @var XmlScanner
|
||||
*/
|
||||
protected $securityScanner;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->readFilter = new DefaultReadFilter();
|
||||
}
|
||||
|
||||
public function getReadDataOnly()
|
||||
{
|
||||
return $this->readDataOnly;
|
||||
}
|
||||
|
||||
public function setReadDataOnly($pValue)
|
||||
{
|
||||
$this->readDataOnly = (bool) $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReadEmptyCells()
|
||||
{
|
||||
return $this->readEmptyCells;
|
||||
}
|
||||
|
||||
public function setReadEmptyCells($pValue)
|
||||
{
|
||||
$this->readEmptyCells = (bool) $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIncludeCharts()
|
||||
{
|
||||
return $this->includeCharts;
|
||||
}
|
||||
|
||||
public function setIncludeCharts($pValue)
|
||||
{
|
||||
$this->includeCharts = (bool) $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLoadSheetsOnly()
|
||||
{
|
||||
return $this->loadSheetsOnly;
|
||||
}
|
||||
|
||||
public function setLoadSheetsOnly($value)
|
||||
{
|
||||
if ($value === null) {
|
||||
return $this->setLoadAllSheets();
|
||||
}
|
||||
|
||||
$this->loadSheetsOnly = is_array($value) ? $value : [$value];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLoadAllSheets()
|
||||
{
|
||||
$this->loadSheetsOnly = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReadFilter()
|
||||
{
|
||||
return $this->readFilter;
|
||||
}
|
||||
|
||||
public function setReadFilter(IReadFilter $pValue)
|
||||
{
|
||||
$this->readFilter = $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSecurityScanner()
|
||||
{
|
||||
if (property_exists($this, 'securityScanner')) {
|
||||
return $this->securityScanner;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open file for reading.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*/
|
||||
protected function openFile($pFilename): void
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
// Open file
|
||||
$this->fileHandle = fopen($pFilename, 'rb');
|
||||
if ($this->fileHandle === false) {
|
||||
throw new Exception('Could not open file ' . $pFilename . ' for reading.');
|
||||
}
|
||||
}
|
||||
}
|
||||
533
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
vendored
Normal file
533
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
vendored
Normal file
@@ -0,0 +1,533 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
|
||||
class Csv extends BaseReader
|
||||
{
|
||||
/**
|
||||
* Input encoding.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $inputEncoding = 'UTF-8';
|
||||
|
||||
/**
|
||||
* Delimiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $delimiter;
|
||||
|
||||
/**
|
||||
* Enclosure.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $enclosure = '"';
|
||||
|
||||
/**
|
||||
* Sheet index to read.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $sheetIndex = 0;
|
||||
|
||||
/**
|
||||
* Load rows contiguously.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $contiguous = false;
|
||||
|
||||
/**
|
||||
* The character that can escape the enclosure.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $escapeCharacter = '\\';
|
||||
|
||||
/**
|
||||
* Create a new CSV Reader instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set input encoding.
|
||||
*
|
||||
* @param string $pValue Input encoding, eg: 'UTF-8'
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setInputEncoding($pValue)
|
||||
{
|
||||
$this->inputEncoding = $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input encoding.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInputEncoding()
|
||||
{
|
||||
return $this->inputEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move filepointer past any BOM marker.
|
||||
*/
|
||||
protected function skipBOM(): void
|
||||
{
|
||||
rewind($this->fileHandle);
|
||||
|
||||
switch ($this->inputEncoding) {
|
||||
case 'UTF-8':
|
||||
fgets($this->fileHandle, 4) == "\xEF\xBB\xBF" ?
|
||||
fseek($this->fileHandle, 3) : fseek($this->fileHandle, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify any separator that is explicitly set in the file.
|
||||
*/
|
||||
protected function checkSeparator(): void
|
||||
{
|
||||
$line = fgets($this->fileHandle);
|
||||
if ($line === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((strlen(trim($line, "\r\n")) == 5) && (stripos($line, 'sep=') === 0)) {
|
||||
$this->delimiter = substr($line, 4, 1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->skipBOM();
|
||||
}
|
||||
|
||||
/**
|
||||
* Infer the separator if it isn't explicitly set in the file or specified by the user.
|
||||
*/
|
||||
protected function inferSeparator(): void
|
||||
{
|
||||
if ($this->delimiter !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$potentialDelimiters = [',', ';', "\t", '|', ':', ' ', '~'];
|
||||
$counts = [];
|
||||
foreach ($potentialDelimiters as $delimiter) {
|
||||
$counts[$delimiter] = [];
|
||||
}
|
||||
|
||||
// Count how many times each of the potential delimiters appears in each line
|
||||
$numberLines = 0;
|
||||
while (($line = $this->getNextLine()) !== false && (++$numberLines < 1000)) {
|
||||
$countLine = [];
|
||||
for ($i = strlen($line) - 1; $i >= 0; --$i) {
|
||||
$char = $line[$i];
|
||||
if (isset($counts[$char])) {
|
||||
if (!isset($countLine[$char])) {
|
||||
$countLine[$char] = 0;
|
||||
}
|
||||
++$countLine[$char];
|
||||
}
|
||||
}
|
||||
foreach ($potentialDelimiters as $delimiter) {
|
||||
$counts[$delimiter][] = $countLine[$delimiter]
|
||||
?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If number of lines is 0, nothing to infer : fall back to the default
|
||||
if ($numberLines === 0) {
|
||||
$this->delimiter = reset($potentialDelimiters);
|
||||
$this->skipBOM();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the mean square deviations for each delimiter (ignoring delimiters that haven't been found consistently)
|
||||
$meanSquareDeviations = [];
|
||||
$middleIdx = floor(($numberLines - 1) / 2);
|
||||
|
||||
foreach ($potentialDelimiters as $delimiter) {
|
||||
$series = $counts[$delimiter];
|
||||
sort($series);
|
||||
|
||||
$median = ($numberLines % 2)
|
||||
? $series[$middleIdx]
|
||||
: ($series[$middleIdx] + $series[$middleIdx + 1]) / 2;
|
||||
|
||||
if ($median === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$meanSquareDeviations[$delimiter] = array_reduce(
|
||||
$series,
|
||||
function ($sum, $value) use ($median) {
|
||||
return $sum + ($value - $median) ** 2;
|
||||
}
|
||||
) / count($series);
|
||||
}
|
||||
|
||||
// ... and pick the delimiter with the smallest mean square deviation (in case of ties, the order in potentialDelimiters is respected)
|
||||
$min = INF;
|
||||
foreach ($potentialDelimiters as $delimiter) {
|
||||
if (!isset($meanSquareDeviations[$delimiter])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($meanSquareDeviations[$delimiter] < $min) {
|
||||
$min = $meanSquareDeviations[$delimiter];
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
}
|
||||
|
||||
// If no delimiter could be detected, fall back to the default
|
||||
if ($this->delimiter === null) {
|
||||
$this->delimiter = reset($potentialDelimiters);
|
||||
}
|
||||
|
||||
$this->skipBOM();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next full line from the file.
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
private function getNextLine()
|
||||
{
|
||||
$line = '';
|
||||
$enclosure = '(?<!' . preg_quote($this->escapeCharacter, '/') . ')' . preg_quote($this->enclosure, '/');
|
||||
|
||||
do {
|
||||
// Get the next line in the file
|
||||
$newLine = fgets($this->fileHandle);
|
||||
|
||||
// Return false if there is no next line
|
||||
if ($newLine === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the new line to the line passed in
|
||||
$line = $line . $newLine;
|
||||
|
||||
// Drop everything that is enclosed to avoid counting false positives in enclosures
|
||||
$line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line);
|
||||
|
||||
// See if we have any enclosures left in the line
|
||||
// if we still have an enclosure then we need to read the next line as well
|
||||
} while (preg_match('/(' . $enclosure . ')/', $line) > 0);
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listWorksheetInfo($pFilename)
|
||||
{
|
||||
// Open file
|
||||
$this->openFileOrMemory($pFilename);
|
||||
$fileHandle = $this->fileHandle;
|
||||
|
||||
// Skip BOM, if any
|
||||
$this->skipBOM();
|
||||
$this->checkSeparator();
|
||||
$this->inferSeparator();
|
||||
|
||||
$worksheetInfo = [];
|
||||
$worksheetInfo[0]['worksheetName'] = 'Worksheet';
|
||||
$worksheetInfo[0]['lastColumnLetter'] = 'A';
|
||||
$worksheetInfo[0]['lastColumnIndex'] = 0;
|
||||
$worksheetInfo[0]['totalRows'] = 0;
|
||||
$worksheetInfo[0]['totalColumns'] = 0;
|
||||
|
||||
// Loop through each line of the file in turn
|
||||
while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure, $this->escapeCharacter)) !== false) {
|
||||
++$worksheetInfo[0]['totalRows'];
|
||||
$worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], count($rowData) - 1);
|
||||
}
|
||||
|
||||
$worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
|
||||
$worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
|
||||
|
||||
// Close file
|
||||
fclose($fileHandle);
|
||||
|
||||
return $worksheetInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Spreadsheet from file.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function load($pFilename)
|
||||
{
|
||||
// Create new Spreadsheet
|
||||
$spreadsheet = new Spreadsheet();
|
||||
|
||||
// Load into this instance
|
||||
return $this->loadIntoExisting($pFilename, $spreadsheet);
|
||||
}
|
||||
|
||||
private function openFileOrMemory($pFilename): void
|
||||
{
|
||||
// Open file
|
||||
$fhandle = $this->canRead($pFilename);
|
||||
if (!$fhandle) {
|
||||
throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
|
||||
}
|
||||
$this->openFile($pFilename);
|
||||
if ($this->inputEncoding !== 'UTF-8') {
|
||||
fclose($this->fileHandle);
|
||||
$entireFile = file_get_contents($pFilename);
|
||||
$this->fileHandle = fopen('php://memory', 'r+b');
|
||||
$data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
|
||||
fwrite($this->fileHandle, $data);
|
||||
rewind($this->fileHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
|
||||
{
|
||||
$lineEnding = ini_get('auto_detect_line_endings');
|
||||
ini_set('auto_detect_line_endings', true);
|
||||
|
||||
// Open file
|
||||
$this->openFileOrMemory($pFilename);
|
||||
$fileHandle = $this->fileHandle;
|
||||
|
||||
// Skip BOM, if any
|
||||
$this->skipBOM();
|
||||
$this->checkSeparator();
|
||||
$this->inferSeparator();
|
||||
|
||||
// Create new PhpSpreadsheet object
|
||||
while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
|
||||
$spreadsheet->createSheet();
|
||||
}
|
||||
$sheet = $spreadsheet->setActiveSheetIndex($this->sheetIndex);
|
||||
|
||||
// Set our starting row based on whether we're in contiguous mode or not
|
||||
$currentRow = 1;
|
||||
$outRow = 0;
|
||||
|
||||
// Loop through each line of the file in turn
|
||||
while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure, $this->escapeCharacter)) !== false) {
|
||||
$noOutputYet = true;
|
||||
$columnLetter = 'A';
|
||||
foreach ($rowData as $rowDatum) {
|
||||
if ($rowDatum != '' && $this->readFilter->readCell($columnLetter, $currentRow)) {
|
||||
if ($this->contiguous) {
|
||||
if ($noOutputYet) {
|
||||
$noOutputYet = false;
|
||||
++$outRow;
|
||||
}
|
||||
} else {
|
||||
$outRow = $currentRow;
|
||||
}
|
||||
// Set cell value
|
||||
$sheet->getCell($columnLetter . $outRow)->setValue($rowDatum);
|
||||
}
|
||||
++$columnLetter;
|
||||
}
|
||||
++$currentRow;
|
||||
}
|
||||
|
||||
// Close file
|
||||
fclose($fileHandle);
|
||||
|
||||
ini_set('auto_detect_line_endings', $lineEnding);
|
||||
|
||||
// Return
|
||||
return $spreadsheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get delimiter.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDelimiter()
|
||||
{
|
||||
return $this->delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set delimiter.
|
||||
*
|
||||
* @param string $delimiter Delimiter, eg: ','
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDelimiter($delimiter)
|
||||
{
|
||||
$this->delimiter = $delimiter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enclosure.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEnclosure()
|
||||
{
|
||||
return $this->enclosure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set enclosure.
|
||||
*
|
||||
* @param string $enclosure Enclosure, defaults to "
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setEnclosure($enclosure)
|
||||
{
|
||||
if ($enclosure == '') {
|
||||
$enclosure = '"';
|
||||
}
|
||||
$this->enclosure = $enclosure;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sheet index.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSheetIndex()
|
||||
{
|
||||
return $this->sheetIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sheet index.
|
||||
*
|
||||
* @param int $pValue Sheet index
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSheetIndex($pValue)
|
||||
{
|
||||
$this->sheetIndex = $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Contiguous.
|
||||
*
|
||||
* @param bool $contiguous
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setContiguous($contiguous)
|
||||
{
|
||||
$this->contiguous = (bool) $contiguous;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Contiguous.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getContiguous()
|
||||
{
|
||||
return $this->contiguous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set escape backslashes.
|
||||
*
|
||||
* @param string $escapeCharacter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setEscapeCharacter($escapeCharacter)
|
||||
{
|
||||
$this->escapeCharacter = $escapeCharacter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get escape backslashes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEscapeCharacter()
|
||||
{
|
||||
return $this->escapeCharacter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the current IReader read the file?
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRead($pFilename)
|
||||
{
|
||||
// Check if file exists
|
||||
try {
|
||||
$this->openFile($pFilename);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose($this->fileHandle);
|
||||
|
||||
// Trust file extension if any
|
||||
$extension = strtolower(pathinfo($pFilename, PATHINFO_EXTENSION));
|
||||
if (in_array($extension, ['csv', 'tsv'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attempt to guess mimetype
|
||||
$type = mime_content_type($pFilename);
|
||||
$supportedTypes = [
|
||||
'text/csv',
|
||||
'text/plain',
|
||||
'inode/x-empty',
|
||||
];
|
||||
|
||||
return in_array($type, $supportedTypes, true);
|
||||
}
|
||||
}
|
||||
20
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php
vendored
Normal file
20
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
class DefaultReadFilter implements IReadFilter
|
||||
{
|
||||
/**
|
||||
* Should this cell be read?
|
||||
*
|
||||
* @param string $column Column address (as a string value like "A", or "IV")
|
||||
* @param int $row Row number
|
||||
* @param string $worksheetName Optional worksheet name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function readCell($column, $row, $worksheetName = '')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
9
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Exception.php
vendored
Normal file
9
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Exception.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
||||
|
||||
class Exception extends PhpSpreadsheetException
|
||||
{
|
||||
}
|
||||
882
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
vendored
Normal file
882
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
vendored
Normal file
@@ -0,0 +1,882 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\NamedRange;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Borders;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Font;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use XMLReader;
|
||||
|
||||
class Gnumeric extends BaseReader
|
||||
{
|
||||
/**
|
||||
* Shared Expressions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $expressions = [];
|
||||
|
||||
private $referenceHelper;
|
||||
|
||||
/**
|
||||
* Create a new Gnumeric.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->referenceHelper = ReferenceHelper::getInstance();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the current IReader read the file?
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRead($pFilename)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
// Check if gzlib functions are available
|
||||
if (!function_exists('gzread')) {
|
||||
throw new Exception('gzlib library is not enabled');
|
||||
}
|
||||
|
||||
// Read signature data (first 3 bytes)
|
||||
$fh = fopen($pFilename, 'rb');
|
||||
$data = fread($fh, 2);
|
||||
fclose($fh);
|
||||
|
||||
return $data == chr(0x1F) . chr(0x8B);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listWorksheetNames($pFilename)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
$xml = new XMLReader();
|
||||
$xml->xml($this->securityScanner->scanFile('compress.zlib://' . realpath($pFilename)), null, Settings::getLibXmlLoaderOptions());
|
||||
$xml->setParserProperty(2, true);
|
||||
|
||||
$worksheetNames = [];
|
||||
while ($xml->read()) {
|
||||
if ($xml->name == 'gnm:SheetName' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
$xml->read(); // Move onto the value node
|
||||
$worksheetNames[] = (string) $xml->value;
|
||||
} elseif ($xml->name == 'gnm:Sheets') {
|
||||
// break out of the loop once we've got our sheet names rather than parse the entire file
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $worksheetNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listWorksheetInfo($pFilename)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
$xml = new XMLReader();
|
||||
$xml->xml($this->securityScanner->scanFile('compress.zlib://' . realpath($pFilename)), null, Settings::getLibXmlLoaderOptions());
|
||||
$xml->setParserProperty(2, true);
|
||||
|
||||
$worksheetInfo = [];
|
||||
while ($xml->read()) {
|
||||
if ($xml->name == 'gnm:Sheet' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
$tmpInfo = [
|
||||
'worksheetName' => '',
|
||||
'lastColumnLetter' => 'A',
|
||||
'lastColumnIndex' => 0,
|
||||
'totalRows' => 0,
|
||||
'totalColumns' => 0,
|
||||
];
|
||||
|
||||
while ($xml->read()) {
|
||||
if ($xml->name == 'gnm:Name' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
$xml->read(); // Move onto the value node
|
||||
$tmpInfo['worksheetName'] = (string) $xml->value;
|
||||
} elseif ($xml->name == 'gnm:MaxCol' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
$xml->read(); // Move onto the value node
|
||||
$tmpInfo['lastColumnIndex'] = (int) $xml->value;
|
||||
$tmpInfo['totalColumns'] = (int) $xml->value + 1;
|
||||
} elseif ($xml->name == 'gnm:MaxRow' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
$xml->read(); // Move onto the value node
|
||||
$tmpInfo['totalRows'] = (int) $xml->value + 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
|
||||
$worksheetInfo[] = $tmpInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return $worksheetInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function gzfileGetContents($filename)
|
||||
{
|
||||
$file = @gzopen($filename, 'rb');
|
||||
$data = '';
|
||||
if ($file !== false) {
|
||||
while (!gzeof($file)) {
|
||||
$data .= gzread($file, 1024);
|
||||
}
|
||||
gzclose($file);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Spreadsheet from file.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function load($pFilename)
|
||||
{
|
||||
// Create new Spreadsheet
|
||||
$spreadsheet = new Spreadsheet();
|
||||
|
||||
// Load into this instance
|
||||
return $this->loadIntoExisting($pFilename, $spreadsheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads from file into Spreadsheet instance.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
$gFileData = $this->gzfileGetContents($pFilename);
|
||||
|
||||
$xml = simplexml_load_string($this->securityScanner->scan($gFileData), 'SimpleXMLElement', Settings::getLibXmlLoaderOptions());
|
||||
$namespacesMeta = $xml->getNamespaces(true);
|
||||
|
||||
$gnmXML = $xml->children($namespacesMeta['gnm']);
|
||||
|
||||
$docProps = $spreadsheet->getProperties();
|
||||
// Document Properties are held differently, depending on the version of Gnumeric
|
||||
if (isset($namespacesMeta['office'])) {
|
||||
$officeXML = $xml->children($namespacesMeta['office']);
|
||||
$officeDocXML = $officeXML->{'document-meta'};
|
||||
$officeDocMetaXML = $officeDocXML->meta;
|
||||
|
||||
foreach ($officeDocMetaXML 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(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'subject':
|
||||
$docProps->setSubject(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'creator':
|
||||
$docProps->setCreator(trim($propertyValue));
|
||||
$docProps->setLastModifiedBy(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'date':
|
||||
$creationDate = strtotime(trim($propertyValue));
|
||||
$docProps->setCreated($creationDate);
|
||||
$docProps->setModified($creationDate);
|
||||
|
||||
break;
|
||||
case 'description':
|
||||
$docProps->setDescription(trim($propertyValue));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$officePropertyMeta = [];
|
||||
if (isset($namespacesMeta['meta'])) {
|
||||
$officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
|
||||
}
|
||||
foreach ($officePropertyMeta as $propertyName => $propertyValue) {
|
||||
$attributes = $propertyValue->attributes($namespacesMeta['meta']);
|
||||
$propertyValue = (string) $propertyValue;
|
||||
switch ($propertyName) {
|
||||
case 'keyword':
|
||||
$docProps->setKeywords(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'initial-creator':
|
||||
$docProps->setCreator(trim($propertyValue));
|
||||
$docProps->setLastModifiedBy(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'creation-date':
|
||||
$creationDate = strtotime(trim($propertyValue));
|
||||
$docProps->setCreated($creationDate);
|
||||
$docProps->setModified($creationDate);
|
||||
|
||||
break;
|
||||
case 'user-defined':
|
||||
[, $attrName] = explode(':', $attributes['name']);
|
||||
switch ($attrName) {
|
||||
case 'publisher':
|
||||
$docProps->setCompany(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'category':
|
||||
$docProps->setCategory(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'manager':
|
||||
$docProps->setManager(trim($propertyValue));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (isset($gnmXML->Summary)) {
|
||||
foreach ($gnmXML->Summary->Item as $summaryItem) {
|
||||
$propertyName = $summaryItem->name;
|
||||
$propertyValue = $summaryItem->{'val-string'};
|
||||
switch ($propertyName) {
|
||||
case 'title':
|
||||
$docProps->setTitle(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'comments':
|
||||
$docProps->setDescription(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'keywords':
|
||||
$docProps->setKeywords(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'category':
|
||||
$docProps->setCategory(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'manager':
|
||||
$docProps->setManager(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'author':
|
||||
$docProps->setCreator(trim($propertyValue));
|
||||
$docProps->setLastModifiedBy(trim($propertyValue));
|
||||
|
||||
break;
|
||||
case 'company':
|
||||
$docProps->setCompany(trim($propertyValue));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$worksheetID = 0;
|
||||
foreach ($gnmXML->Sheets->Sheet as $sheet) {
|
||||
$worksheetName = (string) $sheet->Name;
|
||||
if ((isset($this->loadSheetsOnly)) && (!in_array($worksheetName, $this->loadSheetsOnly))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$maxRow = $maxCol = 0;
|
||||
|
||||
// Create new Worksheet
|
||||
$spreadsheet->createSheet();
|
||||
$spreadsheet->setActiveSheetIndex($worksheetID);
|
||||
// Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in formula
|
||||
// cells... during the load, all formulae should be correct, and we're simply bringing the worksheet
|
||||
// name in line with the formula, not the reverse
|
||||
$spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
|
||||
|
||||
if ((!$this->readDataOnly) && (isset($sheet->PrintInformation))) {
|
||||
if (isset($sheet->PrintInformation->Margins)) {
|
||||
foreach ($sheet->PrintInformation->Margins->children('gnm', true) as $key => $margin) {
|
||||
$marginAttributes = $margin->attributes();
|
||||
$marginSize = 72 / 100; // Default
|
||||
switch ($marginAttributes['PrefUnit']) {
|
||||
case 'mm':
|
||||
$marginSize = (int) ($marginAttributes['Points']) / 100;
|
||||
|
||||
break;
|
||||
}
|
||||
switch ($key) {
|
||||
case 'top':
|
||||
$spreadsheet->getActiveSheet()->getPageMargins()->setTop($marginSize);
|
||||
|
||||
break;
|
||||
case 'bottom':
|
||||
$spreadsheet->getActiveSheet()->getPageMargins()->setBottom($marginSize);
|
||||
|
||||
break;
|
||||
case 'left':
|
||||
$spreadsheet->getActiveSheet()->getPageMargins()->setLeft($marginSize);
|
||||
|
||||
break;
|
||||
case 'right':
|
||||
$spreadsheet->getActiveSheet()->getPageMargins()->setRight($marginSize);
|
||||
|
||||
break;
|
||||
case 'header':
|
||||
$spreadsheet->getActiveSheet()->getPageMargins()->setHeader($marginSize);
|
||||
|
||||
break;
|
||||
case 'footer':
|
||||
$spreadsheet->getActiveSheet()->getPageMargins()->setFooter($marginSize);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($sheet->Cells->Cell as $cell) {
|
||||
$cellAttributes = $cell->attributes();
|
||||
$row = (int) $cellAttributes->Row + 1;
|
||||
$column = (int) $cellAttributes->Col;
|
||||
|
||||
if ($row > $maxRow) {
|
||||
$maxRow = $row;
|
||||
}
|
||||
if ($column > $maxCol) {
|
||||
$maxCol = $column;
|
||||
}
|
||||
|
||||
$column = Coordinate::stringFromColumnIndex($column + 1);
|
||||
|
||||
// Read cell?
|
||||
if ($this->getReadFilter() !== null) {
|
||||
if (!$this->getReadFilter()->readCell($column, $row, $worksheetName)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$ValueType = $cellAttributes->ValueType;
|
||||
$ExprID = (string) $cellAttributes->ExprID;
|
||||
$type = DataType::TYPE_FORMULA;
|
||||
if ($ExprID > '') {
|
||||
if (((string) $cell) > '') {
|
||||
$this->expressions[$ExprID] = [
|
||||
'column' => $cellAttributes->Col,
|
||||
'row' => $cellAttributes->Row,
|
||||
'formula' => (string) $cell,
|
||||
];
|
||||
} else {
|
||||
$expression = $this->expressions[$ExprID];
|
||||
|
||||
$cell = $this->referenceHelper->updateFormulaReferences(
|
||||
$expression['formula'],
|
||||
'A1',
|
||||
$cellAttributes->Col - $expression['column'],
|
||||
$cellAttributes->Row - $expression['row'],
|
||||
$worksheetName
|
||||
);
|
||||
}
|
||||
$type = DataType::TYPE_FORMULA;
|
||||
} else {
|
||||
switch ($ValueType) {
|
||||
case '10': // NULL
|
||||
$type = DataType::TYPE_NULL;
|
||||
|
||||
break;
|
||||
case '20': // Boolean
|
||||
$type = DataType::TYPE_BOOL;
|
||||
$cell = $cell == 'TRUE';
|
||||
|
||||
break;
|
||||
case '30': // Integer
|
||||
$cell = (int) $cell;
|
||||
// Excel 2007+ doesn't differentiate between integer and float, so set the value and dropthru to the next (numeric) case
|
||||
// no break
|
||||
case '40': // Float
|
||||
$type = DataType::TYPE_NUMERIC;
|
||||
|
||||
break;
|
||||
case '50': // Error
|
||||
$type = DataType::TYPE_ERROR;
|
||||
|
||||
break;
|
||||
case '60': // String
|
||||
$type = DataType::TYPE_STRING;
|
||||
|
||||
break;
|
||||
case '70': // Cell Range
|
||||
case '80': // Array
|
||||
}
|
||||
}
|
||||
$spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit($cell, $type);
|
||||
}
|
||||
|
||||
if ((!$this->readDataOnly) && (isset($sheet->Objects))) {
|
||||
foreach ($sheet->Objects->children('gnm', true) as $key => $comment) {
|
||||
$commentAttributes = $comment->attributes();
|
||||
// Only comment objects are handled at the moment
|
||||
if ($commentAttributes->Text) {
|
||||
$spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound)->setAuthor((string) $commentAttributes->Author)->setText($this->parseRichText((string) $commentAttributes->Text));
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($sheet->Styles->StyleRegion as $styleRegion) {
|
||||
$styleAttributes = $styleRegion->attributes();
|
||||
if (($styleAttributes['startRow'] <= $maxRow) &&
|
||||
($styleAttributes['startCol'] <= $maxCol)) {
|
||||
$startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1);
|
||||
$startRow = $styleAttributes['startRow'] + 1;
|
||||
|
||||
$endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol'];
|
||||
$endColumn = Coordinate::stringFromColumnIndex($endColumn + 1);
|
||||
$endRow = ($styleAttributes['endRow'] > $maxRow) ? $maxRow : $styleAttributes['endRow'];
|
||||
++$endRow;
|
||||
$cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow;
|
||||
|
||||
$styleAttributes = $styleRegion->Style->attributes();
|
||||
|
||||
// We still set the number format mask for date/time values, even if readDataOnly is true
|
||||
if ((!$this->readDataOnly) ||
|
||||
(Date::isDateTimeFormatCode((string) $styleAttributes['Format']))) {
|
||||
$styleArray = [];
|
||||
$styleArray['numberFormat']['formatCode'] = (string) $styleAttributes['Format'];
|
||||
// If readDataOnly is false, we set all formatting information
|
||||
if (!$this->readDataOnly) {
|
||||
switch ($styleAttributes['HAlign']) {
|
||||
case '1':
|
||||
$styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_GENERAL;
|
||||
|
||||
break;
|
||||
case '2':
|
||||
$styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_LEFT;
|
||||
|
||||
break;
|
||||
case '4':
|
||||
$styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_RIGHT;
|
||||
|
||||
break;
|
||||
case '8':
|
||||
$styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_CENTER;
|
||||
|
||||
break;
|
||||
case '16':
|
||||
case '64':
|
||||
$styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_CENTER_CONTINUOUS;
|
||||
|
||||
break;
|
||||
case '32':
|
||||
$styleArray['alignment']['horizontal'] = Alignment::HORIZONTAL_JUSTIFY;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($styleAttributes['VAlign']) {
|
||||
case '1':
|
||||
$styleArray['alignment']['vertical'] = Alignment::VERTICAL_TOP;
|
||||
|
||||
break;
|
||||
case '2':
|
||||
$styleArray['alignment']['vertical'] = Alignment::VERTICAL_BOTTOM;
|
||||
|
||||
break;
|
||||
case '4':
|
||||
$styleArray['alignment']['vertical'] = Alignment::VERTICAL_CENTER;
|
||||
|
||||
break;
|
||||
case '8':
|
||||
$styleArray['alignment']['vertical'] = Alignment::VERTICAL_JUSTIFY;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1';
|
||||
$styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1';
|
||||
$styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0;
|
||||
|
||||
$RGB = self::parseGnumericColour($styleAttributes['Fore']);
|
||||
$styleArray['font']['color']['rgb'] = $RGB;
|
||||
$RGB = self::parseGnumericColour($styleAttributes['Back']);
|
||||
$shade = $styleAttributes['Shade'];
|
||||
if (($RGB != '000000') || ($shade != '0')) {
|
||||
$styleArray['fill']['color']['rgb'] = $styleArray['fill']['startColor']['rgb'] = $RGB;
|
||||
$RGB2 = self::parseGnumericColour($styleAttributes['PatternColor']);
|
||||
$styleArray['fill']['endColor']['rgb'] = $RGB2;
|
||||
switch ($shade) {
|
||||
case '1':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_SOLID;
|
||||
|
||||
break;
|
||||
case '2':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_GRADIENT_LINEAR;
|
||||
|
||||
break;
|
||||
case '3':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_GRADIENT_PATH;
|
||||
|
||||
break;
|
||||
case '4':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKDOWN;
|
||||
|
||||
break;
|
||||
case '5':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKGRAY;
|
||||
|
||||
break;
|
||||
case '6':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKGRID;
|
||||
|
||||
break;
|
||||
case '7':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKHORIZONTAL;
|
||||
|
||||
break;
|
||||
case '8':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKTRELLIS;
|
||||
|
||||
break;
|
||||
case '9':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKUP;
|
||||
|
||||
break;
|
||||
case '10':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_DARKVERTICAL;
|
||||
|
||||
break;
|
||||
case '11':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_GRAY0625;
|
||||
|
||||
break;
|
||||
case '12':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_GRAY125;
|
||||
|
||||
break;
|
||||
case '13':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTDOWN;
|
||||
|
||||
break;
|
||||
case '14':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTGRAY;
|
||||
|
||||
break;
|
||||
case '15':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTGRID;
|
||||
|
||||
break;
|
||||
case '16':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTHORIZONTAL;
|
||||
|
||||
break;
|
||||
case '17':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTTRELLIS;
|
||||
|
||||
break;
|
||||
case '18':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTUP;
|
||||
|
||||
break;
|
||||
case '19':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_LIGHTVERTICAL;
|
||||
|
||||
break;
|
||||
case '20':
|
||||
$styleArray['fill']['fillType'] = Fill::FILL_PATTERN_MEDIUMGRAY;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$fontAttributes = $styleRegion->Style->Font->attributes();
|
||||
$styleArray['font']['name'] = (string) $styleRegion->Style->Font;
|
||||
$styleArray['font']['size'] = (int) ($fontAttributes['Unit']);
|
||||
$styleArray['font']['bold'] = $fontAttributes['Bold'] == '1';
|
||||
$styleArray['font']['italic'] = $fontAttributes['Italic'] == '1';
|
||||
$styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1';
|
||||
switch ($fontAttributes['Underline']) {
|
||||
case '1':
|
||||
$styleArray['font']['underline'] = Font::UNDERLINE_SINGLE;
|
||||
|
||||
break;
|
||||
case '2':
|
||||
$styleArray['font']['underline'] = Font::UNDERLINE_DOUBLE;
|
||||
|
||||
break;
|
||||
case '3':
|
||||
$styleArray['font']['underline'] = Font::UNDERLINE_SINGLEACCOUNTING;
|
||||
|
||||
break;
|
||||
case '4':
|
||||
$styleArray['font']['underline'] = Font::UNDERLINE_DOUBLEACCOUNTING;
|
||||
|
||||
break;
|
||||
default:
|
||||
$styleArray['font']['underline'] = Font::UNDERLINE_NONE;
|
||||
|
||||
break;
|
||||
}
|
||||
switch ($fontAttributes['Script']) {
|
||||
case '1':
|
||||
$styleArray['font']['superscript'] = true;
|
||||
|
||||
break;
|
||||
case '-1':
|
||||
$styleArray['font']['subscript'] = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($styleRegion->Style->StyleBorder)) {
|
||||
if (isset($styleRegion->Style->StyleBorder->Top)) {
|
||||
$styleArray['borders']['top'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Top->attributes());
|
||||
}
|
||||
if (isset($styleRegion->Style->StyleBorder->Bottom)) {
|
||||
$styleArray['borders']['bottom'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Bottom->attributes());
|
||||
}
|
||||
if (isset($styleRegion->Style->StyleBorder->Left)) {
|
||||
$styleArray['borders']['left'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Left->attributes());
|
||||
}
|
||||
if (isset($styleRegion->Style->StyleBorder->Right)) {
|
||||
$styleArray['borders']['right'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Right->attributes());
|
||||
}
|
||||
if ((isset($styleRegion->Style->StyleBorder->Diagonal)) && (isset($styleRegion->Style->StyleBorder->{'Rev-Diagonal'}))) {
|
||||
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Diagonal->attributes());
|
||||
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH;
|
||||
} elseif (isset($styleRegion->Style->StyleBorder->Diagonal)) {
|
||||
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->Diagonal->attributes());
|
||||
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP;
|
||||
} elseif (isset($styleRegion->Style->StyleBorder->{'Rev-Diagonal'})) {
|
||||
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($styleRegion->Style->StyleBorder->{'Rev-Diagonal'}->attributes());
|
||||
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN;
|
||||
}
|
||||
}
|
||||
if (isset($styleRegion->Style->HyperLink)) {
|
||||
// TO DO
|
||||
$hyperlink = $styleRegion->Style->HyperLink->attributes();
|
||||
}
|
||||
}
|
||||
$spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((!$this->readDataOnly) && (isset($sheet->Cols))) {
|
||||
// Column Widths
|
||||
$columnAttributes = $sheet->Cols->attributes();
|
||||
$defaultWidth = $columnAttributes['DefaultSizePts'] / 5.4;
|
||||
$c = 0;
|
||||
foreach ($sheet->Cols->ColInfo as $columnOverride) {
|
||||
$columnAttributes = $columnOverride->attributes();
|
||||
$column = $columnAttributes['No'];
|
||||
$columnWidth = $columnAttributes['Unit'] / 5.4;
|
||||
$hidden = (isset($columnAttributes['Hidden'])) && ($columnAttributes['Hidden'] == '1');
|
||||
$columnCount = (isset($columnAttributes['Count'])) ? $columnAttributes['Count'] : 1;
|
||||
while ($c < $column) {
|
||||
$spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth);
|
||||
++$c;
|
||||
}
|
||||
while (($c < ($column + $columnCount)) && ($c <= $maxCol)) {
|
||||
$spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($columnWidth);
|
||||
if ($hidden) {
|
||||
$spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setVisible(false);
|
||||
}
|
||||
++$c;
|
||||
}
|
||||
}
|
||||
while ($c <= $maxCol) {
|
||||
$spreadsheet->getActiveSheet()->getColumnDimension(Coordinate::stringFromColumnIndex($c + 1))->setWidth($defaultWidth);
|
||||
++$c;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!$this->readDataOnly) && (isset($sheet->Rows))) {
|
||||
// Row Heights
|
||||
$rowAttributes = $sheet->Rows->attributes();
|
||||
$defaultHeight = $rowAttributes['DefaultSizePts'];
|
||||
$r = 0;
|
||||
|
||||
foreach ($sheet->Rows->RowInfo as $rowOverride) {
|
||||
$rowAttributes = $rowOverride->attributes();
|
||||
$row = $rowAttributes['No'];
|
||||
$rowHeight = $rowAttributes['Unit'];
|
||||
$hidden = (isset($rowAttributes['Hidden'])) && ($rowAttributes['Hidden'] == '1');
|
||||
$rowCount = (isset($rowAttributes['Count'])) ? $rowAttributes['Count'] : 1;
|
||||
while ($r < $row) {
|
||||
++$r;
|
||||
$spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight);
|
||||
}
|
||||
while (($r < ($row + $rowCount)) && ($r < $maxRow)) {
|
||||
++$r;
|
||||
$spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($rowHeight);
|
||||
if ($hidden) {
|
||||
$spreadsheet->getActiveSheet()->getRowDimension($r)->setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
while ($r < $maxRow) {
|
||||
++$r;
|
||||
$spreadsheet->getActiveSheet()->getRowDimension($r)->setRowHeight($defaultHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Merged Cells in this worksheet
|
||||
if (isset($sheet->MergedRegions)) {
|
||||
foreach ($sheet->MergedRegions->Merge as $mergeCells) {
|
||||
if (strpos($mergeCells, ':') !== false) {
|
||||
$spreadsheet->getActiveSheet()->mergeCells($mergeCells);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++$worksheetID;
|
||||
}
|
||||
|
||||
// Loop through definedNames (global named ranges)
|
||||
if (isset($gnmXML->Names)) {
|
||||
foreach ($gnmXML->Names->Name as $namedRange) {
|
||||
$name = (string) $namedRange->name;
|
||||
$range = (string) $namedRange->value;
|
||||
if (stripos($range, '#REF!') !== false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$range = Worksheet::extractSheetTitle($range, true);
|
||||
$range[0] = trim($range[0], "'");
|
||||
if ($worksheet = $spreadsheet->getSheetByName($range[0])) {
|
||||
$extractedRange = str_replace('$', '', $range[1]);
|
||||
$spreadsheet->addNamedRange(new NamedRange($name, $worksheet, $extractedRange));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return
|
||||
return $spreadsheet;
|
||||
}
|
||||
|
||||
private static function parseBorderAttributes($borderAttributes)
|
||||
{
|
||||
$styleArray = [];
|
||||
if (isset($borderAttributes['Color'])) {
|
||||
$styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']);
|
||||
}
|
||||
|
||||
switch ($borderAttributes['Style']) {
|
||||
case '0':
|
||||
$styleArray['borderStyle'] = Border::BORDER_NONE;
|
||||
|
||||
break;
|
||||
case '1':
|
||||
$styleArray['borderStyle'] = Border::BORDER_THIN;
|
||||
|
||||
break;
|
||||
case '2':
|
||||
$styleArray['borderStyle'] = Border::BORDER_MEDIUM;
|
||||
|
||||
break;
|
||||
case '3':
|
||||
$styleArray['borderStyle'] = Border::BORDER_SLANTDASHDOT;
|
||||
|
||||
break;
|
||||
case '4':
|
||||
$styleArray['borderStyle'] = Border::BORDER_DASHED;
|
||||
|
||||
break;
|
||||
case '5':
|
||||
$styleArray['borderStyle'] = Border::BORDER_THICK;
|
||||
|
||||
break;
|
||||
case '6':
|
||||
$styleArray['borderStyle'] = Border::BORDER_DOUBLE;
|
||||
|
||||
break;
|
||||
case '7':
|
||||
$styleArray['borderStyle'] = Border::BORDER_DOTTED;
|
||||
|
||||
break;
|
||||
case '8':
|
||||
$styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHED;
|
||||
|
||||
break;
|
||||
case '9':
|
||||
$styleArray['borderStyle'] = Border::BORDER_DASHDOT;
|
||||
|
||||
break;
|
||||
case '10':
|
||||
$styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOT;
|
||||
|
||||
break;
|
||||
case '11':
|
||||
$styleArray['borderStyle'] = Border::BORDER_DASHDOTDOT;
|
||||
|
||||
break;
|
||||
case '12':
|
||||
$styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOTDOT;
|
||||
|
||||
break;
|
||||
case '13':
|
||||
$styleArray['borderStyle'] = Border::BORDER_MEDIUMDASHDOTDOT;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $styleArray;
|
||||
}
|
||||
|
||||
private function parseRichText($is)
|
||||
{
|
||||
$value = new RichText();
|
||||
$value->createText($is);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private static function parseGnumericColour($gnmColour)
|
||||
{
|
||||
[$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour);
|
||||
$gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2);
|
||||
$gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2);
|
||||
$gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2);
|
||||
|
||||
return $gnmR . $gnmG . $gnmB;
|
||||
}
|
||||
}
|
||||
952
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
vendored
Normal file
952
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
vendored
Normal file
@@ -0,0 +1,952 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use DOMText;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
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 */
|
||||
class Html extends BaseReader
|
||||
{
|
||||
/**
|
||||
* Sample size to read to determine if it's HTML or not.
|
||||
*/
|
||||
const TEST_SAMPLE_SIZE = 2048;
|
||||
|
||||
/**
|
||||
* Input encoding.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $inputEncoding = 'ANSI';
|
||||
|
||||
/**
|
||||
* Sheet index to read.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $sheetIndex = 0;
|
||||
|
||||
/**
|
||||
* Formats.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $formats = [
|
||||
'h1' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 24,
|
||||
],
|
||||
], // Bold, 24pt
|
||||
'h2' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 18,
|
||||
],
|
||||
], // Bold, 18pt
|
||||
'h3' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 13.5,
|
||||
],
|
||||
], // Bold, 13.5pt
|
||||
'h4' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 12,
|
||||
],
|
||||
], // Bold, 12pt
|
||||
'h5' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 10,
|
||||
],
|
||||
], // Bold, 10pt
|
||||
'h6' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
'size' => 7.5,
|
||||
],
|
||||
], // Bold, 7.5pt
|
||||
'a' => [
|
||||
'font' => [
|
||||
'underline' => true,
|
||||
'color' => [
|
||||
'argb' => Color::COLOR_BLUE,
|
||||
],
|
||||
],
|
||||
], // Blue underlined
|
||||
'hr' => [
|
||||
'borders' => [
|
||||
'bottom' => [
|
||||
'borderStyle' => Border::BORDER_THIN,
|
||||
'color' => [
|
||||
Color::COLOR_BLACK,
|
||||
],
|
||||
],
|
||||
],
|
||||
], // Bottom border
|
||||
'strong' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
],
|
||||
], // Bold
|
||||
'b' => [
|
||||
'font' => [
|
||||
'bold' => true,
|
||||
],
|
||||
], // Bold
|
||||
'i' => [
|
||||
'font' => [
|
||||
'italic' => true,
|
||||
],
|
||||
], // Italic
|
||||
'em' => [
|
||||
'font' => [
|
||||
'italic' => true,
|
||||
],
|
||||
], // Italic
|
||||
];
|
||||
|
||||
protected $rowspan = [];
|
||||
|
||||
/**
|
||||
* Create a new HTML Reader instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the current file is an HTML file.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRead($pFilename)
|
||||
{
|
||||
// Check if file exists
|
||||
try {
|
||||
$this->openFile($pFilename);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$beginning = $this->readBeginning();
|
||||
$startWithTag = self::startsWithTag($beginning);
|
||||
$containsTags = self::containsTags($beginning);
|
||||
$endsWithTag = self::endsWithTag($this->readEnding());
|
||||
|
||||
fclose($this->fileHandle);
|
||||
|
||||
return $startWithTag && $containsTags && $endsWithTag;
|
||||
}
|
||||
|
||||
private function readBeginning()
|
||||
{
|
||||
fseek($this->fileHandle, 0);
|
||||
|
||||
return fread($this->fileHandle, self::TEST_SAMPLE_SIZE);
|
||||
}
|
||||
|
||||
private function readEnding()
|
||||
{
|
||||
$meta = stream_get_meta_data($this->fileHandle);
|
||||
$filename = $meta['uri'];
|
||||
|
||||
$size = filesize($filename);
|
||||
if ($size === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$blockSize = self::TEST_SAMPLE_SIZE;
|
||||
if ($size < $blockSize) {
|
||||
$blockSize = $size;
|
||||
}
|
||||
|
||||
fseek($this->fileHandle, $size - $blockSize);
|
||||
|
||||
return fread($this->fileHandle, $blockSize);
|
||||
}
|
||||
|
||||
private static function startsWithTag($data)
|
||||
{
|
||||
return '<' === substr(trim($data), 0, 1);
|
||||
}
|
||||
|
||||
private static function endsWithTag($data)
|
||||
{
|
||||
return '>' === substr(trim($data), -1, 1);
|
||||
}
|
||||
|
||||
private static function containsTags($data)
|
||||
{
|
||||
return strlen($data) !== strlen(strip_tags($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Spreadsheet from file.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function load($pFilename)
|
||||
{
|
||||
// Create new Spreadsheet
|
||||
$spreadsheet = new Spreadsheet();
|
||||
|
||||
// Load into this instance
|
||||
return $this->loadIntoExisting($pFilename, $spreadsheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set input encoding.
|
||||
*
|
||||
* @param string $pValue Input encoding, eg: 'ANSI'
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setInputEncoding($pValue)
|
||||
{
|
||||
$this->inputEncoding = $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input encoding.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInputEncoding()
|
||||
{
|
||||
return $this->inputEncoding;
|
||||
}
|
||||
|
||||
// Data Array used for testing only, should write to Spreadsheet object on completion of tests
|
||||
protected $dataArray = [];
|
||||
|
||||
protected $tableLevel = 0;
|
||||
|
||||
protected $nestedColumn = ['A'];
|
||||
|
||||
protected function setTableStartColumn($column)
|
||||
{
|
||||
if ($this->tableLevel == 0) {
|
||||
$column = 'A';
|
||||
}
|
||||
++$this->tableLevel;
|
||||
$this->nestedColumn[$this->tableLevel] = $column;
|
||||
|
||||
return $this->nestedColumn[$this->tableLevel];
|
||||
}
|
||||
|
||||
protected function getTableStartColumn()
|
||||
{
|
||||
return $this->nestedColumn[$this->tableLevel];
|
||||
}
|
||||
|
||||
protected function releaseTableStartColumn()
|
||||
{
|
||||
--$this->tableLevel;
|
||||
|
||||
return array_pop($this->nestedColumn);
|
||||
}
|
||||
|
||||
protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent): void
|
||||
{
|
||||
if (is_string($cellContent)) {
|
||||
// Simple String content
|
||||
if (trim($cellContent) > '') {
|
||||
// Only actually write it if there's content in the string
|
||||
// Write to worksheet to be done here...
|
||||
// ... we return the cell so we can mess about with styles more easily
|
||||
$sheet->setCellValue($column . $row, $cellContent);
|
||||
$this->dataArray[$row][$column] = $cellContent;
|
||||
}
|
||||
} else {
|
||||
// We have a Rich Text run
|
||||
// TODO
|
||||
$this->dataArray[$row][$column] = 'RICH TEXT: ' . $cellContent;
|
||||
}
|
||||
$cellContent = (string) '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $row
|
||||
* @param string $column
|
||||
* @param string $cellContent
|
||||
*/
|
||||
protected function processDomElement(DOMNode $element, Worksheet $sheet, &$row, &$column, &$cellContent): void
|
||||
{
|
||||
foreach ($element->childNodes as $child) {
|
||||
if ($child instanceof DOMText) {
|
||||
$domText = preg_replace('/\s+/u', ' ', trim($child->nodeValue));
|
||||
if (is_string($cellContent)) {
|
||||
// simply append the text if the cell content is a plain text string
|
||||
$cellContent .= $domText;
|
||||
}
|
||||
// but if we have a rich text run instead, we need to append it correctly
|
||||
// TODO
|
||||
} elseif ($child instanceof DOMElement) {
|
||||
$attributeArray = [];
|
||||
foreach ($child->attributes as $attribute) {
|
||||
$attributeArray[$attribute->name] = $attribute->value;
|
||||
}
|
||||
|
||||
switch ($child->nodeName) {
|
||||
case 'meta':
|
||||
foreach ($attributeArray as $attributeName => $attributeValue) {
|
||||
// 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);
|
||||
|
||||
break;
|
||||
case 'title':
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
$sheet->setTitle($cellContent, true, false);
|
||||
$cellContent = '';
|
||||
|
||||
break;
|
||||
case 'span':
|
||||
case 'div':
|
||||
case 'font':
|
||||
case 'i':
|
||||
case 'em':
|
||||
case 'strong':
|
||||
case 'b':
|
||||
if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') {
|
||||
$sheet->getComment($column . $row)
|
||||
->getText()
|
||||
->createTextRun($child->textContent);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($cellContent > '') {
|
||||
$cellContent .= ' ';
|
||||
}
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
if ($cellContent > '') {
|
||||
$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);
|
||||
++$row;
|
||||
if (isset($this->formats[$child->nodeName])) {
|
||||
$sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
|
||||
} else {
|
||||
$cellContent = '----------';
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
}
|
||||
++$row;
|
||||
// Add a break after a horizontal rule, simply by allowing the code to dropthru
|
||||
// no break
|
||||
case 'br':
|
||||
if ($this->tableLevel > 0) {
|
||||
// If we're inside a table, replace with a \n and set the cell to wrap
|
||||
$cellContent .= "\n";
|
||||
$sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
|
||||
} else {
|
||||
// Otherwise flush our existing content and move the row cursor on
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
++$row;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'a':
|
||||
foreach ($attributeArray as $attributeName => $attributeValue) {
|
||||
switch ($attributeName) {
|
||||
case 'href':
|
||||
$sheet->getCell($column . $row)->getHyperlink()->setUrl($attributeValue);
|
||||
if (isset($this->formats[$child->nodeName])) {
|
||||
$sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'class':
|
||||
if ($attributeValue === 'comment-indicator') {
|
||||
break; // Ignore - it's just a red square.
|
||||
}
|
||||
}
|
||||
}
|
||||
$cellContent .= ' ';
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
|
||||
break;
|
||||
case 'h1':
|
||||
case 'h2':
|
||||
case 'h3':
|
||||
case 'h4':
|
||||
case 'h5':
|
||||
case 'h6':
|
||||
case 'ol':
|
||||
case 'ul':
|
||||
case 'p':
|
||||
if ($this->tableLevel > 0) {
|
||||
// If we're inside a table, replace with a \n
|
||||
$cellContent .= "\n";
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
} else {
|
||||
if ($cellContent > '') {
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
++$row;
|
||||
}
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
|
||||
if (isset($this->formats[$child->nodeName])) {
|
||||
$sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
|
||||
}
|
||||
|
||||
++$row;
|
||||
$column = 'A';
|
||||
}
|
||||
|
||||
break;
|
||||
case 'li':
|
||||
if ($this->tableLevel > 0) {
|
||||
// If we're inside a table, replace with a \n
|
||||
$cellContent .= "\n";
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
} else {
|
||||
if ($cellContent > '') {
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
}
|
||||
++$row;
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
$column = 'A';
|
||||
}
|
||||
|
||||
break;
|
||||
case 'img':
|
||||
$this->insertImage($sheet, $column, $row, $attributeArray);
|
||||
|
||||
break;
|
||||
case 'table':
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
$column = $this->setTableStartColumn($column);
|
||||
if ($this->tableLevel > 1) {
|
||||
--$row;
|
||||
}
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
$column = $this->releaseTableStartColumn();
|
||||
if ($this->tableLevel > 1) {
|
||||
++$column;
|
||||
} else {
|
||||
++$row;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'thead':
|
||||
case 'tbody':
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
|
||||
break;
|
||||
case 'tr':
|
||||
$column = $this->getTableStartColumn();
|
||||
$cellContent = '';
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
|
||||
if (isset($attributeArray['height'])) {
|
||||
$sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
|
||||
}
|
||||
|
||||
++$row;
|
||||
|
||||
break;
|
||||
case 'th':
|
||||
case 'td':
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
|
||||
while (isset($this->rowspan[$column . $row])) {
|
||||
++$column;
|
||||
}
|
||||
|
||||
// apply inline style
|
||||
$this->applyInlineStyle($sheet, $row, $column, $attributeArray);
|
||||
|
||||
$this->flushCell($sheet, $column, $row, $cellContent);
|
||||
|
||||
if (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
|
||||
//create merging rowspan and colspan
|
||||
$columnTo = $column;
|
||||
for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
|
||||
++$columnTo;
|
||||
}
|
||||
$range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
|
||||
foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
|
||||
$this->rowspan[$value] = true;
|
||||
}
|
||||
$sheet->mergeCells($range);
|
||||
$column = $columnTo;
|
||||
} elseif (isset($attributeArray['rowspan'])) {
|
||||
//create merging rowspan
|
||||
$range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
|
||||
foreach (Coordinate::extractAllCellReferencesInRange($range) as $value) {
|
||||
$this->rowspan[$value] = true;
|
||||
}
|
||||
$sheet->mergeCells($range);
|
||||
} elseif (isset($attributeArray['colspan'])) {
|
||||
//create merging colspan
|
||||
$columnTo = $column;
|
||||
for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
|
||||
++$columnTo;
|
||||
}
|
||||
$sheet->mergeCells($column . $row . ':' . $columnTo . $row);
|
||||
$column = $columnTo;
|
||||
} elseif (isset($attributeArray['bgcolor'])) {
|
||||
$sheet->getStyle($column . $row)->applyFromArray(
|
||||
[
|
||||
'fill' => [
|
||||
'fillType' => Fill::FILL_SOLID,
|
||||
'color' => ['rgb' => $attributeArray['bgcolor']],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
case 'body':
|
||||
$row = 1;
|
||||
$column = 'A';
|
||||
$cellContent = '';
|
||||
$this->tableLevel = 0;
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
|
||||
break;
|
||||
default:
|
||||
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
|
||||
{
|
||||
// Validate
|
||||
if (!$this->canRead($pFilename)) {
|
||||
throw new Exception($pFilename . ' is an Invalid HTML file.');
|
||||
}
|
||||
|
||||
// Create a new DOM object
|
||||
$dom = new DOMDocument();
|
||||
// Reload the HTML file into the DOM object
|
||||
$loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scanFile($pFilename), 'HTML-ENTITIES', 'UTF-8'));
|
||||
if ($loaded === false) {
|
||||
throw new Exception('Failed to load ' . $pFilename . ' as a DOM Document');
|
||||
}
|
||||
|
||||
return $this->loadDocument($dom, $spreadsheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spreadsheet from content.
|
||||
*
|
||||
* @param string $content
|
||||
*/
|
||||
public function loadFromString($content, ?Spreadsheet $spreadsheet = null): Spreadsheet
|
||||
{
|
||||
// Create a new DOM object
|
||||
$dom = new DOMDocument();
|
||||
// Reload the HTML file into the DOM object
|
||||
$loaded = $dom->loadHTML(mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8'));
|
||||
if ($loaded === false) {
|
||||
throw new Exception('Failed to load content as a DOM Document');
|
||||
}
|
||||
|
||||
return $this->loadDocument($dom, $spreadsheet ?? new Spreadsheet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from DOMDocument into PhpSpreadsheet instance.
|
||||
*/
|
||||
private function loadDocument(DOMDocument $document, Spreadsheet $spreadsheet): Spreadsheet
|
||||
{
|
||||
while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
|
||||
$spreadsheet->createSheet();
|
||||
}
|
||||
$spreadsheet->setActiveSheetIndex($this->sheetIndex);
|
||||
|
||||
// Discard white space
|
||||
$document->preserveWhiteSpace = false;
|
||||
|
||||
$row = 0;
|
||||
$column = 'A';
|
||||
$content = '';
|
||||
$this->rowspan = [];
|
||||
$this->processDomElement($document, $spreadsheet->getActiveSheet(), $row, $column, $content);
|
||||
|
||||
// Return
|
||||
return $spreadsheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sheet index.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSheetIndex()
|
||||
{
|
||||
return $this->sheetIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sheet index.
|
||||
*
|
||||
* @param int $pValue Sheet index
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSheetIndex($pValue)
|
||||
{
|
||||
$this->sheetIndex = $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply inline css inline style.
|
||||
*
|
||||
* NOTES :
|
||||
* Currently only intended for td & th element,
|
||||
* and only takes 'background-color' and 'color'; property with HEX color
|
||||
*
|
||||
* TODO :
|
||||
* - Implement to other propertie, such as border
|
||||
*
|
||||
* @param Worksheet $sheet
|
||||
* @param int $row
|
||||
* @param string $column
|
||||
* @param array $attributeArray
|
||||
*/
|
||||
private function applyInlineStyle(&$sheet, $row, $column, $attributeArray): void
|
||||
{
|
||||
if (!isset($attributeArray['style'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$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 (!$styleName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($styleName) {
|
||||
case 'background':
|
||||
case 'background-color':
|
||||
$styleColor = $this->getStyleColor($styleValue);
|
||||
|
||||
if (!$styleColor) {
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$cellStyle->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => $styleColor]]]);
|
||||
|
||||
break;
|
||||
case '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 string $column
|
||||
* @param int $row
|
||||
*/
|
||||
private function insertImage(Worksheet $sheet, $column, $row, array $attributes): void
|
||||
{
|
||||
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 string $styleValue
|
||||
* @param string $type
|
||||
*/
|
||||
private function setBorderStyle(Style $cellStyle, $styleValue, $type): void
|
||||
{
|
||||
if (trim($styleValue) === Border::BORDER_NONE) {
|
||||
$borderStyle = Border::BORDER_NONE;
|
||||
$color = null;
|
||||
} else {
|
||||
[, $borderStyle, $color] = explode(' ', $styleValue);
|
||||
}
|
||||
|
||||
$cellStyle->applyFromArray([
|
||||
'borders' => [
|
||||
$type => [
|
||||
'borderStyle' => $this->getBorderStyle($borderStyle),
|
||||
'color' => ['rgb' => $this->getStyleColor($color)],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
17
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php
vendored
Normal file
17
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
interface IReadFilter
|
||||
{
|
||||
/**
|
||||
* Should this cell be read?
|
||||
*
|
||||
* @param string $column Column address (as a string value like "A", or "IV")
|
||||
* @param int $row Row number
|
||||
* @param string $worksheetName Optional worksheet name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function readCell($column, $row, $worksheetName = '');
|
||||
}
|
||||
133
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php
vendored
Normal file
133
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
interface IReader
|
||||
{
|
||||
/**
|
||||
* IReader constructor.
|
||||
*/
|
||||
public function __construct();
|
||||
|
||||
/**
|
||||
* Can the current IReader read the file?
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @return IReader
|
||||
*/
|
||||
public function setReadFilter(IReadFilter $pValue);
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from file.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return \PhpOffice\PhpSpreadsheet\Spreadsheet
|
||||
*/
|
||||
public function load($pFilename);
|
||||
}
|
||||
699
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
vendored
Normal file
699
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
vendored
Normal file
@@ -0,0 +1,699 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use DOMAttr;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||
use XMLReader;
|
||||
use ZipArchive;
|
||||
|
||||
class Ods extends BaseReader
|
||||
{
|
||||
/**
|
||||
* Create a new Ods Reader instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the current IReader read the file?
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRead($pFilename)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
$mimeType = 'UNKNOWN';
|
||||
|
||||
// Load file
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($pFilename) === true) {
|
||||
// check if it is an OOXML archive
|
||||
$stat = $zip->statName('mimetype');
|
||||
if ($stat && ($stat['size'] <= 255)) {
|
||||
$mimeType = $zip->getFromName($stat['name']);
|
||||
} elseif ($zip->statName('META-INF/manifest.xml')) {
|
||||
$xml = simplexml_load_string(
|
||||
$this->securityScanner->scan($zip->getFromName('META-INF/manifest.xml')),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
$namespacesContent = $xml->getNamespaces(true);
|
||||
if (isset($namespacesContent['manifest'])) {
|
||||
$manifest = $xml->children($namespacesContent['manifest']);
|
||||
foreach ($manifest as $manifestDataSet) {
|
||||
$manifestAttributes = $manifestDataSet->attributes($namespacesContent['manifest']);
|
||||
if ($manifestAttributes->{'full-path'} == '/') {
|
||||
$mimeType = (string) $manifestAttributes->{'media-type'};
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
return $mimeType === 'application/vnd.oasis.opendocument.spreadsheet';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function listWorksheetNames($pFilename)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if (!$zip->open($pFilename)) {
|
||||
throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.');
|
||||
}
|
||||
|
||||
$worksheetNames = [];
|
||||
|
||||
$xml = new XMLReader();
|
||||
$xml->xml(
|
||||
$this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#content.xml'),
|
||||
null,
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
$xml->setParserProperty(2, true);
|
||||
|
||||
// Step into the first level of content of the XML
|
||||
$xml->read();
|
||||
while ($xml->read()) {
|
||||
// Quickly jump through to the office:body node
|
||||
while ($xml->name !== 'office:body') {
|
||||
if ($xml->isEmptyElement) {
|
||||
$xml->read();
|
||||
} else {
|
||||
$xml->next();
|
||||
}
|
||||
}
|
||||
// Now read each node until we find our first table:table node
|
||||
while ($xml->read()) {
|
||||
if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
// Loop through each table:table node reading the table:name attribute for each worksheet name
|
||||
do {
|
||||
$worksheetNames[] = $xml->getAttribute('table:name');
|
||||
$xml->next();
|
||||
} while ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $worksheetNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listWorksheetInfo($pFilename)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
$worksheetInfo = [];
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if (!$zip->open($pFilename)) {
|
||||
throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.');
|
||||
}
|
||||
|
||||
$xml = new XMLReader();
|
||||
$xml->xml(
|
||||
$this->securityScanner->scanFile('zip://' . realpath($pFilename) . '#content.xml'),
|
||||
null,
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
$xml->setParserProperty(2, true);
|
||||
|
||||
// Step into the first level of content of the XML
|
||||
$xml->read();
|
||||
while ($xml->read()) {
|
||||
// Quickly jump through to the office:body node
|
||||
while ($xml->name !== 'office:body') {
|
||||
if ($xml->isEmptyElement) {
|
||||
$xml->read();
|
||||
} else {
|
||||
$xml->next();
|
||||
}
|
||||
}
|
||||
// Now read each node until we find our first table:table node
|
||||
while ($xml->read()) {
|
||||
if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
$worksheetNames[] = $xml->getAttribute('table:name');
|
||||
|
||||
$tmpInfo = [
|
||||
'worksheetName' => $xml->getAttribute('table:name'),
|
||||
'lastColumnLetter' => 'A',
|
||||
'lastColumnIndex' => 0,
|
||||
'totalRows' => 0,
|
||||
'totalColumns' => 0,
|
||||
];
|
||||
|
||||
// Loop through each child node of the table:table element reading
|
||||
$currCells = 0;
|
||||
do {
|
||||
$xml->read();
|
||||
if ($xml->name == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
$rowspan = $xml->getAttribute('table:number-rows-repeated');
|
||||
$rowspan = empty($rowspan) ? 1 : $rowspan;
|
||||
$tmpInfo['totalRows'] += $rowspan;
|
||||
$tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
|
||||
$currCells = 0;
|
||||
// Step into the row
|
||||
$xml->read();
|
||||
do {
|
||||
if ($xml->name == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
if (!$xml->isEmptyElement) {
|
||||
++$currCells;
|
||||
$xml->next();
|
||||
} else {
|
||||
$xml->read();
|
||||
}
|
||||
} elseif ($xml->name == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
|
||||
$mergeSize = $xml->getAttribute('table:number-columns-repeated');
|
||||
$currCells += (int) $mergeSize;
|
||||
$xml->read();
|
||||
} else {
|
||||
$xml->read();
|
||||
}
|
||||
} while ($xml->name != 'table:table-row');
|
||||
}
|
||||
} while ($xml->name != 'table:table');
|
||||
|
||||
$tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
|
||||
$tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
|
||||
$tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
|
||||
$worksheetInfo[] = $tmpInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $worksheetInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from file.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function load($pFilename)
|
||||
{
|
||||
// Create new Spreadsheet
|
||||
$spreadsheet = new Spreadsheet();
|
||||
|
||||
// Load into this instance
|
||||
return $this->loadIntoExisting($pFilename, $spreadsheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
$timezoneObj = new DateTimeZone('Europe/London');
|
||||
$GMT = new DateTimeZone('UTC');
|
||||
|
||||
$zip = new ZipArchive();
|
||||
if (!$zip->open($pFilename)) {
|
||||
throw new Exception("Could not open {$pFilename} for reading! Error opening file.");
|
||||
}
|
||||
|
||||
// Meta
|
||||
|
||||
$xml = simplexml_load_string(
|
||||
$this->securityScanner->scan($zip->getFromName('meta.xml')),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
if ($xml === false) {
|
||||
throw new Exception('Unable to read data from {$pFilename}');
|
||||
}
|
||||
|
||||
$namespacesMeta = $xml->getNamespaces(true);
|
||||
|
||||
(new DocumentProperties($spreadsheet))->load($xml, $namespacesMeta);
|
||||
|
||||
// Content
|
||||
|
||||
$dom = new DOMDocument('1.01', 'UTF-8');
|
||||
$dom->loadXML(
|
||||
$this->securityScanner->scan($zip->getFromName('content.xml')),
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
|
||||
$officeNs = $dom->lookupNamespaceUri('office');
|
||||
$tableNs = $dom->lookupNamespaceUri('table');
|
||||
$textNs = $dom->lookupNamespaceUri('text');
|
||||
$xlinkNs = $dom->lookupNamespaceUri('xlink');
|
||||
|
||||
$spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body')
|
||||
->item(0)
|
||||
->getElementsByTagNameNS($officeNs, 'spreadsheet');
|
||||
|
||||
foreach ($spreadsheets as $workbookData) {
|
||||
/** @var DOMElement $workbookData */
|
||||
$tables = $workbookData->getElementsByTagNameNS($tableNs, 'table');
|
||||
|
||||
$worksheetID = 0;
|
||||
foreach ($tables as $worksheetDataSet) {
|
||||
/** @var DOMElement $worksheetDataSet */
|
||||
$worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name');
|
||||
|
||||
// Check loadSheetsOnly
|
||||
if (isset($this->loadSheetsOnly)
|
||||
&& $worksheetName
|
||||
&& !in_array($worksheetName, $this->loadSheetsOnly)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create sheet
|
||||
if ($worksheetID > 0) {
|
||||
$spreadsheet->createSheet(); // First sheet is added by default
|
||||
}
|
||||
$spreadsheet->setActiveSheetIndex($worksheetID);
|
||||
|
||||
if ($worksheetName) {
|
||||
// Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
|
||||
// formula cells... during the load, all formulae should be correct, and we're simply
|
||||
// bringing the worksheet name in line with the formula, not the reverse
|
||||
$spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
|
||||
}
|
||||
|
||||
// Go through every child of table element
|
||||
$rowID = 1;
|
||||
foreach ($worksheetDataSet->childNodes as $childNode) {
|
||||
/** @var DOMElement $childNode */
|
||||
|
||||
// Filter elements which are not under the "table" ns
|
||||
if ($childNode->namespaceURI != $tableNs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = $childNode->nodeName;
|
||||
|
||||
// Remove ns from node name
|
||||
if (strpos($key, ':') !== false) {
|
||||
$keyChunks = explode(':', $key);
|
||||
$key = array_pop($keyChunks);
|
||||
}
|
||||
|
||||
switch ($key) {
|
||||
case 'table-header-rows':
|
||||
/// TODO :: Figure this out. This is only a partial implementation I guess.
|
||||
// ($rowData it's not used at all and I'm not sure that PHPExcel
|
||||
// has an API for this)
|
||||
|
||||
// foreach ($rowData as $keyRowData => $cellData) {
|
||||
// $rowData = $cellData;
|
||||
// break;
|
||||
// }
|
||||
break;
|
||||
case 'table-row':
|
||||
if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) {
|
||||
$rowRepeats = $childNode->getAttributeNS($tableNs, 'number-rows-repeated');
|
||||
} else {
|
||||
$rowRepeats = 1;
|
||||
}
|
||||
|
||||
$columnID = 'A';
|
||||
foreach ($childNode->childNodes as $key => $cellData) {
|
||||
// @var \DOMElement $cellData
|
||||
|
||||
if ($this->getReadFilter() !== null) {
|
||||
if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
|
||||
++$columnID;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize variables
|
||||
$formatting = $hyperlink = null;
|
||||
$hasCalculatedValue = false;
|
||||
$cellDataFormula = '';
|
||||
|
||||
if ($cellData->hasAttributeNS($tableNs, 'formula')) {
|
||||
$cellDataFormula = $cellData->getAttributeNS($tableNs, 'formula');
|
||||
$hasCalculatedValue = true;
|
||||
}
|
||||
|
||||
// Annotations
|
||||
$annotation = $cellData->getElementsByTagNameNS($officeNs, 'annotation');
|
||||
|
||||
if ($annotation->length > 0) {
|
||||
$textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, 'p');
|
||||
|
||||
if ($textNode->length > 0) {
|
||||
$text = $this->scanElementForText($textNode->item(0));
|
||||
|
||||
$spreadsheet->getActiveSheet()
|
||||
->getComment($columnID . $rowID)
|
||||
->setText($this->parseRichText($text));
|
||||
// ->setAuthor( $author )
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
|
||||
/** @var DOMElement[] $paragraphs */
|
||||
$paragraphs = [];
|
||||
|
||||
foreach ($cellData->childNodes as $item) {
|
||||
/** @var DOMElement $item */
|
||||
|
||||
// Filter text:p elements
|
||||
if ($item->nodeName == 'text:p') {
|
||||
$paragraphs[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($paragraphs) > 0) {
|
||||
// Consolidate if there are multiple p records (maybe with spans as well)
|
||||
$dataArray = [];
|
||||
|
||||
// Text can have multiple text:p and within those, multiple text:span.
|
||||
// text:p newlines, but text:span does not.
|
||||
// Also, here we assume there is no text data is span fields are specified, since
|
||||
// we have no way of knowing proper positioning anyway.
|
||||
|
||||
foreach ($paragraphs as $pData) {
|
||||
$dataArray[] = $this->scanElementForText($pData);
|
||||
}
|
||||
$allCellDataText = implode("\n", $dataArray);
|
||||
|
||||
$type = $cellData->getAttributeNS($officeNs, 'value-type');
|
||||
|
||||
switch ($type) {
|
||||
case 'string':
|
||||
$type = DataType::TYPE_STRING;
|
||||
$dataValue = $allCellDataText;
|
||||
|
||||
foreach ($paragraphs as $paragraph) {
|
||||
$link = $paragraph->getElementsByTagNameNS($textNs, 'a');
|
||||
if ($link->length > 0) {
|
||||
$hyperlink = $link->item(0)->getAttributeNS($xlinkNs, 'href');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'boolean':
|
||||
$type = DataType::TYPE_BOOL;
|
||||
$dataValue = ($allCellDataText == 'TRUE') ? true : false;
|
||||
|
||||
break;
|
||||
case 'percentage':
|
||||
$type = DataType::TYPE_NUMERIC;
|
||||
$dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
|
||||
|
||||
if (floor($dataValue) == $dataValue) {
|
||||
$dataValue = (int) $dataValue;
|
||||
}
|
||||
$formatting = NumberFormat::FORMAT_PERCENTAGE_00;
|
||||
|
||||
break;
|
||||
case 'currency':
|
||||
$type = DataType::TYPE_NUMERIC;
|
||||
$dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
|
||||
|
||||
if (floor($dataValue) == $dataValue) {
|
||||
$dataValue = (int) $dataValue;
|
||||
}
|
||||
$formatting = NumberFormat::FORMAT_CURRENCY_USD_SIMPLE;
|
||||
|
||||
break;
|
||||
case 'float':
|
||||
$type = DataType::TYPE_NUMERIC;
|
||||
$dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
|
||||
|
||||
if (floor($dataValue) == $dataValue) {
|
||||
if ($dataValue == (int) $dataValue) {
|
||||
$dataValue = (int) $dataValue;
|
||||
} else {
|
||||
$dataValue = (float) $dataValue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'date':
|
||||
$type = DataType::TYPE_NUMERIC;
|
||||
$value = $cellData->getAttributeNS($officeNs, 'date-value');
|
||||
|
||||
$dateObj = new DateTime($value, $GMT);
|
||||
$dateObj->setTimeZone($timezoneObj);
|
||||
[$year, $month, $day, $hour, $minute, $second] = explode(
|
||||
' ',
|
||||
$dateObj->format('Y m d H i s')
|
||||
);
|
||||
|
||||
$dataValue = Date::formattedPHPToExcel(
|
||||
(int) $year,
|
||||
(int) $month,
|
||||
(int) $day,
|
||||
(int) $hour,
|
||||
(int) $minute,
|
||||
(int) $second
|
||||
);
|
||||
|
||||
if ($dataValue != floor($dataValue)) {
|
||||
$formatting = NumberFormat::FORMAT_DATE_XLSX15
|
||||
. ' '
|
||||
. NumberFormat::FORMAT_DATE_TIME4;
|
||||
} else {
|
||||
$formatting = NumberFormat::FORMAT_DATE_XLSX15;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'time':
|
||||
$type = DataType::TYPE_NUMERIC;
|
||||
|
||||
$timeValue = $cellData->getAttributeNS($officeNs, 'time-value');
|
||||
|
||||
$dataValue = Date::PHPToExcel(
|
||||
strtotime(
|
||||
'01-01-1970 ' . implode(':', sscanf($timeValue, 'PT%dH%dM%dS'))
|
||||
)
|
||||
);
|
||||
$formatting = NumberFormat::FORMAT_DATE_TIME4;
|
||||
|
||||
break;
|
||||
default:
|
||||
$dataValue = null;
|
||||
}
|
||||
} else {
|
||||
$type = DataType::TYPE_NULL;
|
||||
$dataValue = null;
|
||||
}
|
||||
|
||||
if ($hasCalculatedValue) {
|
||||
$type = DataType::TYPE_FORMULA;
|
||||
$cellDataFormula = substr($cellDataFormula, strpos($cellDataFormula, ':=') + 1);
|
||||
$temp = explode('"', $cellDataFormula);
|
||||
$tKey = false;
|
||||
foreach ($temp as &$value) {
|
||||
// Only replace in alternate array entries (i.e. non-quoted blocks)
|
||||
if ($tKey = !$tKey) {
|
||||
// Cell range reference in another sheet
|
||||
$value = preg_replace('/\[([^\.]+)\.([^\.]+):\.([^\.]+)\]/U', '$1!$2:$3', $value);
|
||||
|
||||
// Cell reference in another sheet
|
||||
$value = preg_replace('/\[([^\.]+)\.([^\.]+)\]/U', '$1!$2', $value);
|
||||
|
||||
// Cell range reference
|
||||
$value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/U', '$1:$2', $value);
|
||||
|
||||
// Simple cell reference
|
||||
$value = preg_replace('/\[\.([^\.]+)\]/U', '$1', $value);
|
||||
|
||||
$value = Calculation::translateSeparator(';', ',', $value, $inBraces);
|
||||
}
|
||||
}
|
||||
unset($value);
|
||||
|
||||
// Then rebuild the formula string
|
||||
$cellDataFormula = implode('"', $temp);
|
||||
}
|
||||
|
||||
if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
|
||||
$colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
|
||||
} else {
|
||||
$colRepeats = 1;
|
||||
}
|
||||
|
||||
if ($type !== null) {
|
||||
for ($i = 0; $i < $colRepeats; ++$i) {
|
||||
if ($i > 0) {
|
||||
++$columnID;
|
||||
}
|
||||
|
||||
if ($type !== DataType::TYPE_NULL) {
|
||||
for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
|
||||
$rID = $rowID + $rowAdjust;
|
||||
|
||||
$cell = $spreadsheet->getActiveSheet()
|
||||
->getCell($columnID . $rID);
|
||||
|
||||
// Set value
|
||||
if ($hasCalculatedValue) {
|
||||
$cell->setValueExplicit($cellDataFormula, $type);
|
||||
} else {
|
||||
$cell->setValueExplicit($dataValue, $type);
|
||||
}
|
||||
|
||||
if ($hasCalculatedValue) {
|
||||
$cell->setCalculatedValue($dataValue);
|
||||
}
|
||||
|
||||
// Set other properties
|
||||
if ($formatting !== null) {
|
||||
$spreadsheet->getActiveSheet()
|
||||
->getStyle($columnID . $rID)
|
||||
->getNumberFormat()
|
||||
->setFormatCode($formatting);
|
||||
} else {
|
||||
$spreadsheet->getActiveSheet()
|
||||
->getStyle($columnID . $rID)
|
||||
->getNumberFormat()
|
||||
->setFormatCode(NumberFormat::FORMAT_GENERAL);
|
||||
}
|
||||
|
||||
if ($hyperlink !== null) {
|
||||
$cell->getHyperlink()
|
||||
->setUrl($hyperlink);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merged cells
|
||||
if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned')
|
||||
|| $cellData->hasAttributeNS($tableNs, 'number-rows-spanned')
|
||||
) {
|
||||
if (($type !== DataType::TYPE_NULL) || (!$this->readDataOnly)) {
|
||||
$columnTo = $columnID;
|
||||
|
||||
if ($cellData->hasAttributeNS($tableNs, 'number-columns-spanned')) {
|
||||
$columnIndex = Coordinate::columnIndexFromString($columnID);
|
||||
$columnIndex += (int) $cellData->getAttributeNS($tableNs, 'number-columns-spanned');
|
||||
$columnIndex -= 2;
|
||||
|
||||
$columnTo = Coordinate::stringFromColumnIndex($columnIndex + 1);
|
||||
}
|
||||
|
||||
$rowTo = $rowID;
|
||||
|
||||
if ($cellData->hasAttributeNS($tableNs, 'number-rows-spanned')) {
|
||||
$rowTo = $rowTo + (int) $cellData->getAttributeNS($tableNs, 'number-rows-spanned') - 1;
|
||||
}
|
||||
|
||||
$cellRange = $columnID . $rowID . ':' . $columnTo . $rowTo;
|
||||
$spreadsheet->getActiveSheet()->mergeCells($cellRange);
|
||||
}
|
||||
}
|
||||
|
||||
++$columnID;
|
||||
}
|
||||
$rowID += $rowRepeats;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
++$worksheetID;
|
||||
}
|
||||
}
|
||||
|
||||
// Return
|
||||
return $spreadsheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively scan element.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function scanElementForText(DOMNode $element)
|
||||
{
|
||||
$str = '';
|
||||
foreach ($element->childNodes as $child) {
|
||||
/** @var DOMNode $child */
|
||||
if ($child->nodeType == XML_TEXT_NODE) {
|
||||
$str .= $child->nodeValue;
|
||||
} elseif ($child->nodeType == XML_ELEMENT_NODE && $child->nodeName == 'text:s') {
|
||||
// It's a space
|
||||
|
||||
// Multiple spaces?
|
||||
/** @var DOMAttr $cAttr */
|
||||
$cAttr = $child->attributes->getNamedItem('c');
|
||||
if ($cAttr) {
|
||||
$multiplier = (int) $cAttr->nodeValue;
|
||||
} else {
|
||||
$multiplier = 1;
|
||||
}
|
||||
|
||||
$str .= str_repeat(' ', $multiplier);
|
||||
}
|
||||
|
||||
if ($child->hasChildNodes()) {
|
||||
$str .= $this->scanElementForText($child);
|
||||
}
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $is
|
||||
*
|
||||
* @return RichText
|
||||
*/
|
||||
private function parseRichText($is)
|
||||
{
|
||||
$value = new RichText();
|
||||
$value->createText($is);
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
136
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
vendored
Normal file
136
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Ods;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class Properties
|
||||
{
|
||||
private $spreadsheet;
|
||||
|
||||
public function __construct(Spreadsheet $spreadsheet)
|
||||
{
|
||||
$this->spreadsheet = $spreadsheet;
|
||||
}
|
||||
|
||||
public function load(SimpleXMLElement $xml, $namespacesMeta): void
|
||||
{
|
||||
$docProps = $this->spreadsheet->getProperties();
|
||||
$officeProperty = $xml->children($namespacesMeta['office']);
|
||||
foreach ($officeProperty as $officePropertyData) {
|
||||
// @var \SimpleXMLElement $officePropertyData
|
||||
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): void
|
||||
{
|
||||
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
|
||||
): void {
|
||||
$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): void
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
150
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
vendored
Normal file
150
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Security;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
|
||||
class XmlScanner
|
||||
{
|
||||
/**
|
||||
* String used to identify risky xml elements.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $pattern;
|
||||
|
||||
private $callback;
|
||||
|
||||
private static $libxmlDisableEntityLoaderValue;
|
||||
|
||||
public function __construct($pattern = '<!DOCTYPE')
|
||||
{
|
||||
$this->pattern = $pattern;
|
||||
|
||||
$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)
|
||||
{
|
||||
switch (true) {
|
||||
case $reader instanceof Reader\Html:
|
||||
return new self('<!ENTITY');
|
||||
case $reader instanceof Reader\Xlsx:
|
||||
case $reader instanceof Reader\Xml:
|
||||
case $reader instanceof Reader\Ods:
|
||||
case $reader instanceof Reader\Gnumeric:
|
||||
return new self('<!DOCTYPE');
|
||||
default:
|
||||
return new self('<!DOCTYPE');
|
||||
}
|
||||
}
|
||||
|
||||
public static function threadSafeLibxmlDisableEntityLoaderAvailability()
|
||||
{
|
||||
if (PHP_MAJOR_VERSION == 7) {
|
||||
switch (PHP_MINOR_VERSION) {
|
||||
case 2:
|
||||
return PHP_RELEASE_VERSION >= 1;
|
||||
case 1:
|
||||
return PHP_RELEASE_VERSION >= 13;
|
||||
case 0:
|
||||
return PHP_RELEASE_VERSION >= 27;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function disableEntityLoaderCheck(): void
|
||||
{
|
||||
if (Settings::getLibXmlDisableEntityLoader()) {
|
||||
$libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
|
||||
|
||||
if (self::$libxmlDisableEntityLoaderValue === null) {
|
||||
self::$libxmlDisableEntityLoaderValue = $libxmlDisableEntityLoaderValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function shutdown(): void
|
||||
{
|
||||
if (self::$libxmlDisableEntityLoaderValue !== null) {
|
||||
libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue);
|
||||
self::$libxmlDisableEntityLoaderValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
self::shutdown();
|
||||
}
|
||||
|
||||
public function setAdditionalCallback(callable $callback): void
|
||||
{
|
||||
$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.
|
||||
*
|
||||
* @param mixed $xml
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function scan($xml)
|
||||
{
|
||||
$this->disableEntityLoaderCheck();
|
||||
|
||||
$xml = $this->toUtf8($xml);
|
||||
|
||||
// Don't rely purely on libxml_disable_entity_loader()
|
||||
$pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/';
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan theXML for use of <!ENTITY to prevent XXE/XEE attacks.
|
||||
*
|
||||
* @param string $filestream
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function scanFile($filestream)
|
||||
{
|
||||
return $this->scan(file_get_contents($filestream));
|
||||
}
|
||||
}
|
||||
489
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
vendored
Normal file
489
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
vendored
Normal file
@@ -0,0 +1,489 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
|
||||
class Slk extends BaseReader
|
||||
{
|
||||
/**
|
||||
* Input encoding.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $inputEncoding = 'ANSI';
|
||||
|
||||
/**
|
||||
* Sheet index to read.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $sheetIndex = 0;
|
||||
|
||||
/**
|
||||
* Formats.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $formats = [];
|
||||
|
||||
/**
|
||||
* Format Count.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $format = 0;
|
||||
|
||||
/**
|
||||
* Create a new SYLK Reader instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the current file is a SYLK file.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRead($pFilename)
|
||||
{
|
||||
// Check if file exists
|
||||
try {
|
||||
$this->openFile($pFilename);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read sample data (first 2 KB will do)
|
||||
$data = fread($this->fileHandle, 2048);
|
||||
|
||||
// Count delimiters in file
|
||||
$delimiterCount = substr_count($data, ';');
|
||||
$hasDelimiter = $delimiterCount > 0;
|
||||
|
||||
// Analyze first line looking for ID; signature
|
||||
$lines = explode("\n", $data);
|
||||
$hasId = substr($lines[0], 0, 4) === 'ID;P';
|
||||
|
||||
fclose($this->fileHandle);
|
||||
|
||||
return $hasDelimiter && $hasId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set input encoding.
|
||||
*
|
||||
* @param string $pValue Input encoding, eg: 'ANSI'
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setInputEncoding($pValue)
|
||||
{
|
||||
$this->inputEncoding = $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input encoding.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInputEncoding()
|
||||
{
|
||||
return $this->inputEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listWorksheetInfo($pFilename)
|
||||
{
|
||||
// Open file
|
||||
if (!$this->canRead($pFilename)) {
|
||||
throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
|
||||
}
|
||||
$this->openFile($pFilename);
|
||||
$fileHandle = $this->fileHandle;
|
||||
rewind($fileHandle);
|
||||
|
||||
$worksheetInfo = [];
|
||||
$worksheetInfo[0]['worksheetName'] = 'Worksheet';
|
||||
$worksheetInfo[0]['lastColumnLetter'] = 'A';
|
||||
$worksheetInfo[0]['lastColumnIndex'] = 0;
|
||||
$worksheetInfo[0]['totalRows'] = 0;
|
||||
$worksheetInfo[0]['totalColumns'] = 0;
|
||||
|
||||
// loop through one row (line) at a time in the file
|
||||
$rowIndex = 0;
|
||||
while (($rowData = fgets($fileHandle)) !== false) {
|
||||
$columnIndex = 0;
|
||||
|
||||
// convert SYLK encoded $rowData to UTF-8
|
||||
$rowData = StringHelper::SYLKtoUTF8($rowData);
|
||||
|
||||
// explode each row at semicolons while taking into account that literal semicolon (;)
|
||||
// is escaped like this (;;)
|
||||
$rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));
|
||||
|
||||
$dataType = array_shift($rowData);
|
||||
if ($dataType == 'C') {
|
||||
// Read cell value data
|
||||
foreach ($rowData as $rowDatum) {
|
||||
switch ($rowDatum[0]) {
|
||||
case 'C':
|
||||
case 'X':
|
||||
$columnIndex = substr($rowDatum, 1) - 1;
|
||||
|
||||
break;
|
||||
case 'R':
|
||||
case 'Y':
|
||||
$rowIndex = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$worksheetInfo[0]['totalRows'] = max($worksheetInfo[0]['totalRows'], $rowIndex);
|
||||
$worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], $columnIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
|
||||
$worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
|
||||
|
||||
// Close file
|
||||
fclose($fileHandle);
|
||||
|
||||
return $worksheetInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from file.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function load($pFilename)
|
||||
{
|
||||
// Create new Spreadsheet
|
||||
$spreadsheet = new Spreadsheet();
|
||||
|
||||
// Load into this instance
|
||||
return $this->loadIntoExisting($pFilename, $spreadsheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
|
||||
{
|
||||
// Open file
|
||||
if (!$this->canRead($pFilename)) {
|
||||
throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
|
||||
}
|
||||
$this->openFile($pFilename);
|
||||
$fileHandle = $this->fileHandle;
|
||||
rewind($fileHandle);
|
||||
|
||||
// Create new Worksheets
|
||||
while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
|
||||
$spreadsheet->createSheet();
|
||||
}
|
||||
$spreadsheet->setActiveSheetIndex($this->sheetIndex);
|
||||
|
||||
$fromFormats = ['\-', '\ '];
|
||||
$toFormats = ['-', ' '];
|
||||
|
||||
// Loop through file
|
||||
$column = $row = '';
|
||||
|
||||
// loop through one row (line) at a time in the file
|
||||
while (($rowData = fgets($fileHandle)) !== false) {
|
||||
// convert SYLK encoded $rowData to UTF-8
|
||||
$rowData = StringHelper::SYLKtoUTF8($rowData);
|
||||
|
||||
// explode each row at semicolons while taking into account that literal semicolon (;)
|
||||
// is escaped like this (;;)
|
||||
$rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));
|
||||
|
||||
$dataType = array_shift($rowData);
|
||||
// Read shared styles
|
||||
if ($dataType == 'P') {
|
||||
$formatArray = [];
|
||||
foreach ($rowData as $rowDatum) {
|
||||
switch ($rowDatum[0]) {
|
||||
case 'P':
|
||||
$formatArray['numberFormat']['formatCode'] = str_replace($fromFormats, $toFormats, substr($rowDatum, 1));
|
||||
|
||||
break;
|
||||
case 'E':
|
||||
case 'F':
|
||||
$formatArray['font']['name'] = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
case 'L':
|
||||
$formatArray['font']['size'] = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
case 'S':
|
||||
$styleSettings = substr($rowDatum, 1);
|
||||
$iMax = strlen($styleSettings);
|
||||
for ($i = 0; $i < $iMax; ++$i) {
|
||||
switch ($styleSettings[$i]) {
|
||||
case 'I':
|
||||
$formatArray['font']['italic'] = true;
|
||||
|
||||
break;
|
||||
case 'D':
|
||||
$formatArray['font']['bold'] = true;
|
||||
|
||||
break;
|
||||
case 'T':
|
||||
$formatArray['borders']['top']['borderStyle'] = Border::BORDER_THIN;
|
||||
|
||||
break;
|
||||
case 'B':
|
||||
$formatArray['borders']['bottom']['borderStyle'] = Border::BORDER_THIN;
|
||||
|
||||
break;
|
||||
case 'L':
|
||||
$formatArray['borders']['left']['borderStyle'] = Border::BORDER_THIN;
|
||||
|
||||
break;
|
||||
case 'R':
|
||||
$formatArray['borders']['right']['borderStyle'] = Border::BORDER_THIN;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->formats['P' . $this->format++] = $formatArray;
|
||||
// Read cell value data
|
||||
} elseif ($dataType == 'C') {
|
||||
$hasCalculatedValue = false;
|
||||
$cellData = $cellDataFormula = '';
|
||||
foreach ($rowData as $rowDatum) {
|
||||
switch ($rowDatum[0]) {
|
||||
case 'C':
|
||||
case 'X':
|
||||
$column = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
case 'R':
|
||||
case 'Y':
|
||||
$row = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
case 'K':
|
||||
$cellData = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
case 'E':
|
||||
$cellDataFormula = '=' . substr($rowDatum, 1);
|
||||
// Convert R1C1 style references to A1 style references (but only when not quoted)
|
||||
$temp = explode('"', $cellDataFormula);
|
||||
$key = false;
|
||||
foreach ($temp as &$value) {
|
||||
// Only count/replace in alternate array entries
|
||||
if ($key = !$key) {
|
||||
preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
|
||||
// Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
|
||||
// through the formula from left to right. Reversing means that we work right to left.through
|
||||
// the formula
|
||||
$cellReferences = array_reverse($cellReferences);
|
||||
// Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
|
||||
// then modify the formula to use that new reference
|
||||
foreach ($cellReferences as $cellReference) {
|
||||
$rowReference = $cellReference[2][0];
|
||||
// Empty R reference is the current row
|
||||
if ($rowReference == '') {
|
||||
$rowReference = $row;
|
||||
}
|
||||
// Bracketed R references are relative to the current row
|
||||
if ($rowReference[0] == '[') {
|
||||
$rowReference = $row + trim($rowReference, '[]');
|
||||
}
|
||||
$columnReference = $cellReference[4][0];
|
||||
// Empty C reference is the current column
|
||||
if ($columnReference == '') {
|
||||
$columnReference = $column;
|
||||
}
|
||||
// Bracketed C references are relative to the current column
|
||||
if ($columnReference[0] == '[') {
|
||||
$columnReference = $column + trim($columnReference, '[]');
|
||||
}
|
||||
$A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference;
|
||||
|
||||
$value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($value);
|
||||
// Then rebuild the formula string
|
||||
$cellDataFormula = implode('"', $temp);
|
||||
$hasCalculatedValue = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$columnLetter = Coordinate::stringFromColumnIndex($column);
|
||||
$cellData = Calculation::unwrapResult($cellData);
|
||||
|
||||
// Set cell value
|
||||
$spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData);
|
||||
if ($hasCalculatedValue) {
|
||||
$cellData = Calculation::unwrapResult($cellData);
|
||||
$spreadsheet->getActiveSheet()->getCell($columnLetter . $row)->setCalculatedValue($cellData);
|
||||
}
|
||||
// Read cell formatting
|
||||
} elseif ($dataType == 'F') {
|
||||
$formatStyle = $columnWidth = $styleSettings = '';
|
||||
$styleData = [];
|
||||
foreach ($rowData as $rowDatum) {
|
||||
switch ($rowDatum[0]) {
|
||||
case 'C':
|
||||
case 'X':
|
||||
$column = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
case 'R':
|
||||
case 'Y':
|
||||
$row = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
case 'P':
|
||||
$formatStyle = $rowDatum;
|
||||
|
||||
break;
|
||||
case 'W':
|
||||
[$startCol, $endCol, $columnWidth] = explode(' ', substr($rowDatum, 1));
|
||||
|
||||
break;
|
||||
case 'S':
|
||||
$styleSettings = substr($rowDatum, 1);
|
||||
$iMax = strlen($styleSettings);
|
||||
for ($i = 0; $i < $iMax; ++$i) {
|
||||
switch ($styleSettings[$i]) {
|
||||
case 'I':
|
||||
$styleData['font']['italic'] = true;
|
||||
|
||||
break;
|
||||
case 'D':
|
||||
$styleData['font']['bold'] = true;
|
||||
|
||||
break;
|
||||
case 'T':
|
||||
$styleData['borders']['top']['borderStyle'] = Border::BORDER_THIN;
|
||||
|
||||
break;
|
||||
case 'B':
|
||||
$styleData['borders']['bottom']['borderStyle'] = Border::BORDER_THIN;
|
||||
|
||||
break;
|
||||
case 'L':
|
||||
$styleData['borders']['left']['borderStyle'] = Border::BORDER_THIN;
|
||||
|
||||
break;
|
||||
case 'R':
|
||||
$styleData['borders']['right']['borderStyle'] = Border::BORDER_THIN;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (($formatStyle > '') && ($column > '') && ($row > '')) {
|
||||
$columnLetter = Coordinate::stringFromColumnIndex($column);
|
||||
if (isset($this->formats[$formatStyle])) {
|
||||
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->formats[$formatStyle]);
|
||||
}
|
||||
}
|
||||
if ((!empty($styleData)) && ($column > '') && ($row > '')) {
|
||||
$columnLetter = Coordinate::stringFromColumnIndex($column);
|
||||
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData);
|
||||
}
|
||||
if ($columnWidth > '') {
|
||||
if ($startCol == $endCol) {
|
||||
$startCol = Coordinate::stringFromColumnIndex($startCol);
|
||||
$spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
|
||||
} else {
|
||||
$startCol = Coordinate::stringFromColumnIndex($startCol);
|
||||
$endCol = Coordinate::stringFromColumnIndex($endCol);
|
||||
$spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth($columnWidth);
|
||||
do {
|
||||
$spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth($columnWidth);
|
||||
} while ($startCol != $endCol);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($rowData as $rowDatum) {
|
||||
switch ($rowDatum[0]) {
|
||||
case 'C':
|
||||
case 'X':
|
||||
$column = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
case 'R':
|
||||
case 'Y':
|
||||
$row = substr($rowDatum, 1);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close file
|
||||
fclose($fileHandle);
|
||||
|
||||
// Return
|
||||
return $spreadsheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sheet index.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSheetIndex()
|
||||
{
|
||||
return $this->sheetIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sheet index.
|
||||
*
|
||||
* @param int $pValue Sheet index
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSheetIndex($pValue)
|
||||
{
|
||||
$this->sheetIndex = $pValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
7933
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
vendored
Normal file
7933
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
36
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php
vendored
Normal file
36
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
|
||||
class Color
|
||||
{
|
||||
/**
|
||||
* Read color.
|
||||
*
|
||||
* @param int $color Indexed color
|
||||
* @param array $palette Color palette
|
||||
* @param int $version
|
||||
*
|
||||
* @return array RGB color value, example: ['rgb' => 'FF0000']
|
||||
*/
|
||||
public static function map($color, $palette, $version)
|
||||
{
|
||||
if ($color <= 0x07 || $color >= 0x40) {
|
||||
// special built-in color
|
||||
return Color\BuiltIn::lookup($color);
|
||||
} elseif (isset($palette, $palette[$color - 8])) {
|
||||
// palette color, color index 0x08 maps to pallete index 0
|
||||
return $palette[$color - 8];
|
||||
}
|
||||
|
||||
// default color table
|
||||
if ($version == Xls::XLS_BIFF8) {
|
||||
return Color\BIFF8::lookup($color);
|
||||
}
|
||||
|
||||
// BIFF5
|
||||
return Color\BIFF5::lookup($color);
|
||||
}
|
||||
}
|
||||
81
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
vendored
Normal file
81
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Color;
|
||||
|
||||
class BIFF5
|
||||
{
|
||||
protected static $map = [
|
||||
0x08 => '000000',
|
||||
0x09 => 'FFFFFF',
|
||||
0x0A => 'FF0000',
|
||||
0x0B => '00FF00',
|
||||
0x0C => '0000FF',
|
||||
0x0D => 'FFFF00',
|
||||
0x0E => 'FF00FF',
|
||||
0x0F => '00FFFF',
|
||||
0x10 => '800000',
|
||||
0x11 => '008000',
|
||||
0x12 => '000080',
|
||||
0x13 => '808000',
|
||||
0x14 => '800080',
|
||||
0x15 => '008080',
|
||||
0x16 => 'C0C0C0',
|
||||
0x17 => '808080',
|
||||
0x18 => '8080FF',
|
||||
0x19 => '802060',
|
||||
0x1A => 'FFFFC0',
|
||||
0x1B => 'A0E0F0',
|
||||
0x1C => '600080',
|
||||
0x1D => 'FF8080',
|
||||
0x1E => '0080C0',
|
||||
0x1F => 'C0C0FF',
|
||||
0x20 => '000080',
|
||||
0x21 => 'FF00FF',
|
||||
0x22 => 'FFFF00',
|
||||
0x23 => '00FFFF',
|
||||
0x24 => '800080',
|
||||
0x25 => '800000',
|
||||
0x26 => '008080',
|
||||
0x27 => '0000FF',
|
||||
0x28 => '00CFFF',
|
||||
0x29 => '69FFFF',
|
||||
0x2A => 'E0FFE0',
|
||||
0x2B => 'FFFF80',
|
||||
0x2C => 'A6CAF0',
|
||||
0x2D => 'DD9CB3',
|
||||
0x2E => 'B38FEE',
|
||||
0x2F => 'E3E3E3',
|
||||
0x30 => '2A6FF9',
|
||||
0x31 => '3FB8CD',
|
||||
0x32 => '488436',
|
||||
0x33 => '958C41',
|
||||
0x34 => '8E5E42',
|
||||
0x35 => 'A0627A',
|
||||
0x36 => '624FAC',
|
||||
0x37 => '969696',
|
||||
0x38 => '1D2FBE',
|
||||
0x39 => '286676',
|
||||
0x3A => '004500',
|
||||
0x3B => '453E01',
|
||||
0x3C => '6A2813',
|
||||
0x3D => '85396A',
|
||||
0x3E => '4A3285',
|
||||
0x3F => '424242',
|
||||
];
|
||||
|
||||
/**
|
||||
* Map color array from BIFF5 built-in color index.
|
||||
*
|
||||
* @param int $color
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function lookup($color)
|
||||
{
|
||||
if (isset(self::$map[$color])) {
|
||||
return ['rgb' => self::$map[$color]];
|
||||
}
|
||||
|
||||
return ['rgb' => '000000'];
|
||||
}
|
||||
}
|
||||
81
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
vendored
Normal file
81
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Color;
|
||||
|
||||
class BIFF8
|
||||
{
|
||||
protected static $map = [
|
||||
0x08 => '000000',
|
||||
0x09 => 'FFFFFF',
|
||||
0x0A => 'FF0000',
|
||||
0x0B => '00FF00',
|
||||
0x0C => '0000FF',
|
||||
0x0D => 'FFFF00',
|
||||
0x0E => 'FF00FF',
|
||||
0x0F => '00FFFF',
|
||||
0x10 => '800000',
|
||||
0x11 => '008000',
|
||||
0x12 => '000080',
|
||||
0x13 => '808000',
|
||||
0x14 => '800080',
|
||||
0x15 => '008080',
|
||||
0x16 => 'C0C0C0',
|
||||
0x17 => '808080',
|
||||
0x18 => '9999FF',
|
||||
0x19 => '993366',
|
||||
0x1A => 'FFFFCC',
|
||||
0x1B => 'CCFFFF',
|
||||
0x1C => '660066',
|
||||
0x1D => 'FF8080',
|
||||
0x1E => '0066CC',
|
||||
0x1F => 'CCCCFF',
|
||||
0x20 => '000080',
|
||||
0x21 => 'FF00FF',
|
||||
0x22 => 'FFFF00',
|
||||
0x23 => '00FFFF',
|
||||
0x24 => '800080',
|
||||
0x25 => '800000',
|
||||
0x26 => '008080',
|
||||
0x27 => '0000FF',
|
||||
0x28 => '00CCFF',
|
||||
0x29 => 'CCFFFF',
|
||||
0x2A => 'CCFFCC',
|
||||
0x2B => 'FFFF99',
|
||||
0x2C => '99CCFF',
|
||||
0x2D => 'FF99CC',
|
||||
0x2E => 'CC99FF',
|
||||
0x2F => 'FFCC99',
|
||||
0x30 => '3366FF',
|
||||
0x31 => '33CCCC',
|
||||
0x32 => '99CC00',
|
||||
0x33 => 'FFCC00',
|
||||
0x34 => 'FF9900',
|
||||
0x35 => 'FF6600',
|
||||
0x36 => '666699',
|
||||
0x37 => '969696',
|
||||
0x38 => '003366',
|
||||
0x39 => '339966',
|
||||
0x3A => '003300',
|
||||
0x3B => '333300',
|
||||
0x3C => '993300',
|
||||
0x3D => '993366',
|
||||
0x3E => '333399',
|
||||
0x3F => '333333',
|
||||
];
|
||||
|
||||
/**
|
||||
* Map color array from BIFF8 built-in color index.
|
||||
*
|
||||
* @param int $color
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function lookup($color)
|
||||
{
|
||||
if (isset(self::$map[$color])) {
|
||||
return ['rgb' => self::$map[$color]];
|
||||
}
|
||||
|
||||
return ['rgb' => '000000'];
|
||||
}
|
||||
}
|
||||
35
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
vendored
Normal file
35
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Color;
|
||||
|
||||
class BuiltIn
|
||||
{
|
||||
protected static $map = [
|
||||
0x00 => '000000',
|
||||
0x01 => 'FFFFFF',
|
||||
0x02 => 'FF0000',
|
||||
0x03 => '00FF00',
|
||||
0x04 => '0000FF',
|
||||
0x05 => 'FFFF00',
|
||||
0x06 => 'FF00FF',
|
||||
0x07 => '00FFFF',
|
||||
0x40 => '000000', // system window text color
|
||||
0x41 => 'FFFFFF', // system window background color
|
||||
];
|
||||
|
||||
/**
|
||||
* Map built-in color to RGB value.
|
||||
*
|
||||
* @param int $color Indexed color
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function lookup($color)
|
||||
{
|
||||
if (isset(self::$map[$color])) {
|
||||
return ['rgb' => self::$map[$color]];
|
||||
}
|
||||
|
||||
return ['rgb' => '000000'];
|
||||
}
|
||||
}
|
||||
32
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php
vendored
Normal file
32
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
|
||||
class ErrorCode
|
||||
{
|
||||
protected static $map = [
|
||||
0x00 => '#NULL!',
|
||||
0x07 => '#DIV/0!',
|
||||
0x0F => '#VALUE!',
|
||||
0x17 => '#REF!',
|
||||
0x1D => '#NAME?',
|
||||
0x24 => '#NUM!',
|
||||
0x2A => '#N/A',
|
||||
];
|
||||
|
||||
/**
|
||||
* Map error code, e.g. '#N/A'.
|
||||
*
|
||||
* @param int $code
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public static function lookup($code)
|
||||
{
|
||||
if (isset(self::$map[$code])) {
|
||||
return self::$map[$code];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
677
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php
vendored
Normal file
677
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php
vendored
Normal file
@@ -0,0 +1,677 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE\Blip;
|
||||
|
||||
class Escher
|
||||
{
|
||||
const DGGCONTAINER = 0xF000;
|
||||
const BSTORECONTAINER = 0xF001;
|
||||
const DGCONTAINER = 0xF002;
|
||||
const SPGRCONTAINER = 0xF003;
|
||||
const SPCONTAINER = 0xF004;
|
||||
const DGG = 0xF006;
|
||||
const BSE = 0xF007;
|
||||
const DG = 0xF008;
|
||||
const SPGR = 0xF009;
|
||||
const SP = 0xF00A;
|
||||
const OPT = 0xF00B;
|
||||
const CLIENTTEXTBOX = 0xF00D;
|
||||
const CLIENTANCHOR = 0xF010;
|
||||
const CLIENTDATA = 0xF011;
|
||||
const BLIPJPEG = 0xF01D;
|
||||
const BLIPPNG = 0xF01E;
|
||||
const SPLITMENUCOLORS = 0xF11E;
|
||||
const TERTIARYOPT = 0xF122;
|
||||
|
||||
/**
|
||||
* Escher stream data (binary).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* Size in bytes of the Escher stream data.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $dataSize;
|
||||
|
||||
/**
|
||||
* Current position of stream pointer in Escher stream data.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $pos;
|
||||
|
||||
/**
|
||||
* The object to be returned by the reader. Modified during load.
|
||||
*
|
||||
* @var BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer
|
||||
*/
|
||||
private $object;
|
||||
|
||||
/**
|
||||
* Create a new Escher instance.
|
||||
*
|
||||
* @param mixed $object
|
||||
*/
|
||||
public function __construct($object)
|
||||
{
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Escher stream data. May be a partial Escher stream.
|
||||
*
|
||||
* @param string $data
|
||||
*
|
||||
* @return BSE|BstoreContainer|DgContainer|DggContainer|\PhpOffice\PhpSpreadsheet\Shared\Escher|SpContainer|SpgrContainer
|
||||
*/
|
||||
public function load($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
// total byte size of Excel data (workbook global substream + sheet substreams)
|
||||
$this->dataSize = strlen($this->data);
|
||||
|
||||
$this->pos = 0;
|
||||
|
||||
// Parse Escher stream
|
||||
while ($this->pos < $this->dataSize) {
|
||||
// offset: 2; size: 2: Record Type
|
||||
$fbt = Xls::getUInt2d($this->data, $this->pos + 2);
|
||||
|
||||
switch ($fbt) {
|
||||
case self::DGGCONTAINER:
|
||||
$this->readDggContainer();
|
||||
|
||||
break;
|
||||
case self::DGG:
|
||||
$this->readDgg();
|
||||
|
||||
break;
|
||||
case self::BSTORECONTAINER:
|
||||
$this->readBstoreContainer();
|
||||
|
||||
break;
|
||||
case self::BSE:
|
||||
$this->readBSE();
|
||||
|
||||
break;
|
||||
case self::BLIPJPEG:
|
||||
$this->readBlipJPEG();
|
||||
|
||||
break;
|
||||
case self::BLIPPNG:
|
||||
$this->readBlipPNG();
|
||||
|
||||
break;
|
||||
case self::OPT:
|
||||
$this->readOPT();
|
||||
|
||||
break;
|
||||
case self::TERTIARYOPT:
|
||||
$this->readTertiaryOPT();
|
||||
|
||||
break;
|
||||
case self::SPLITMENUCOLORS:
|
||||
$this->readSplitMenuColors();
|
||||
|
||||
break;
|
||||
case self::DGCONTAINER:
|
||||
$this->readDgContainer();
|
||||
|
||||
break;
|
||||
case self::DG:
|
||||
$this->readDg();
|
||||
|
||||
break;
|
||||
case self::SPGRCONTAINER:
|
||||
$this->readSpgrContainer();
|
||||
|
||||
break;
|
||||
case self::SPCONTAINER:
|
||||
$this->readSpContainer();
|
||||
|
||||
break;
|
||||
case self::SPGR:
|
||||
$this->readSpgr();
|
||||
|
||||
break;
|
||||
case self::SP:
|
||||
$this->readSp();
|
||||
|
||||
break;
|
||||
case self::CLIENTTEXTBOX:
|
||||
$this->readClientTextbox();
|
||||
|
||||
break;
|
||||
case self::CLIENTANCHOR:
|
||||
$this->readClientAnchor();
|
||||
|
||||
break;
|
||||
case self::CLIENTDATA:
|
||||
$this->readClientData();
|
||||
|
||||
break;
|
||||
default:
|
||||
$this->readDefault();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a generic record.
|
||||
*/
|
||||
private function readDefault(): void
|
||||
{
|
||||
// offset 0; size: 2; recVer and recInstance
|
||||
$verInstance = Xls::getUInt2d($this->data, $this->pos);
|
||||
|
||||
// offset: 2; size: 2: Record Type
|
||||
$fbt = Xls::getUInt2d($this->data, $this->pos + 2);
|
||||
|
||||
// bit: 0-3; mask: 0x000F; recVer
|
||||
$recVer = (0x000F & $verInstance) >> 0;
|
||||
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read DggContainer record (Drawing Group Container).
|
||||
*/
|
||||
private function readDggContainer(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
// record is a container, read contents
|
||||
$dggContainer = new DggContainer();
|
||||
$this->object->setDggContainer($dggContainer);
|
||||
$reader = new self($dggContainer);
|
||||
$reader->load($recordData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Dgg record (Drawing Group).
|
||||
*/
|
||||
private function readDgg(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read BstoreContainer record (Blip Store Container).
|
||||
*/
|
||||
private function readBstoreContainer(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
// record is a container, read contents
|
||||
$bstoreContainer = new BstoreContainer();
|
||||
$this->object->setBstoreContainer($bstoreContainer);
|
||||
$reader = new self($bstoreContainer);
|
||||
$reader->load($recordData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read BSE record.
|
||||
*/
|
||||
private function readBSE(): void
|
||||
{
|
||||
// offset: 0; size: 2; recVer and recInstance
|
||||
|
||||
// bit: 4-15; mask: 0xFFF0; recInstance
|
||||
$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
|
||||
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
// add BSE to BstoreContainer
|
||||
$BSE = new BSE();
|
||||
$this->object->addBSE($BSE);
|
||||
|
||||
$BSE->setBLIPType($recInstance);
|
||||
|
||||
// offset: 0; size: 1; btWin32 (MSOBLIPTYPE)
|
||||
$btWin32 = ord($recordData[0]);
|
||||
|
||||
// offset: 1; size: 1; btWin32 (MSOBLIPTYPE)
|
||||
$btMacOS = ord($recordData[1]);
|
||||
|
||||
// offset: 2; size: 16; MD4 digest
|
||||
$rgbUid = substr($recordData, 2, 16);
|
||||
|
||||
// offset: 18; size: 2; tag
|
||||
$tag = Xls::getUInt2d($recordData, 18);
|
||||
|
||||
// offset: 20; size: 4; size of BLIP in bytes
|
||||
$size = Xls::getInt4d($recordData, 20);
|
||||
|
||||
// offset: 24; size: 4; number of references to this BLIP
|
||||
$cRef = Xls::getInt4d($recordData, 24);
|
||||
|
||||
// offset: 28; size: 4; MSOFO file offset
|
||||
$foDelay = Xls::getInt4d($recordData, 28);
|
||||
|
||||
// offset: 32; size: 1; unused1
|
||||
$unused1 = ord($recordData[32]);
|
||||
|
||||
// offset: 33; size: 1; size of nameData in bytes (including null terminator)
|
||||
$cbName = ord($recordData[33]);
|
||||
|
||||
// offset: 34; size: 1; unused2
|
||||
$unused2 = ord($recordData[34]);
|
||||
|
||||
// offset: 35; size: 1; unused3
|
||||
$unused3 = ord($recordData[35]);
|
||||
|
||||
// offset: 36; size: $cbName; nameData
|
||||
$nameData = substr($recordData, 36, $cbName);
|
||||
|
||||
// offset: 36 + $cbName, size: var; the BLIP data
|
||||
$blipData = substr($recordData, 36 + $cbName);
|
||||
|
||||
// record is a container, read contents
|
||||
$reader = new self($BSE);
|
||||
$reader->load($blipData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read BlipJPEG record. Holds raw JPEG image data.
|
||||
*/
|
||||
private function readBlipJPEG(): void
|
||||
{
|
||||
// offset: 0; size: 2; recVer and recInstance
|
||||
|
||||
// bit: 4-15; mask: 0xFFF0; recInstance
|
||||
$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
|
||||
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
$pos = 0;
|
||||
|
||||
// offset: 0; size: 16; rgbUid1 (MD4 digest of)
|
||||
$rgbUid1 = substr($recordData, 0, 16);
|
||||
$pos += 16;
|
||||
|
||||
// offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
|
||||
if (in_array($recInstance, [0x046B, 0x06E3])) {
|
||||
$rgbUid2 = substr($recordData, 16, 16);
|
||||
$pos += 16;
|
||||
}
|
||||
|
||||
// offset: var; size: 1; tag
|
||||
$tag = ord($recordData[$pos]);
|
||||
++$pos;
|
||||
|
||||
// offset: var; size: var; the raw image data
|
||||
$data = substr($recordData, $pos);
|
||||
|
||||
$blip = new Blip();
|
||||
$blip->setData($data);
|
||||
|
||||
$this->object->setBlip($blip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read BlipPNG record. Holds raw PNG image data.
|
||||
*/
|
||||
private function readBlipPNG(): void
|
||||
{
|
||||
// offset: 0; size: 2; recVer and recInstance
|
||||
|
||||
// bit: 4-15; mask: 0xFFF0; recInstance
|
||||
$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
|
||||
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
$pos = 0;
|
||||
|
||||
// offset: 0; size: 16; rgbUid1 (MD4 digest of)
|
||||
$rgbUid1 = substr($recordData, 0, 16);
|
||||
$pos += 16;
|
||||
|
||||
// offset: 16; size: 16; rgbUid2 (MD4 digest), only if $recInstance = 0x46B or 0x6E3
|
||||
if ($recInstance == 0x06E1) {
|
||||
$rgbUid2 = substr($recordData, 16, 16);
|
||||
$pos += 16;
|
||||
}
|
||||
|
||||
// offset: var; size: 1; tag
|
||||
$tag = ord($recordData[$pos]);
|
||||
++$pos;
|
||||
|
||||
// offset: var; size: var; the raw image data
|
||||
$data = substr($recordData, $pos);
|
||||
|
||||
$blip = new Blip();
|
||||
$blip->setData($data);
|
||||
|
||||
$this->object->setBlip($blip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read OPT record. This record may occur within DggContainer record or SpContainer.
|
||||
*/
|
||||
private function readOPT(): void
|
||||
{
|
||||
// offset: 0; size: 2; recVer and recInstance
|
||||
|
||||
// bit: 4-15; mask: 0xFFF0; recInstance
|
||||
$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
|
||||
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
$this->readOfficeArtRGFOPTE($recordData, $recInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read TertiaryOPT record.
|
||||
*/
|
||||
private function readTertiaryOPT(): void
|
||||
{
|
||||
// offset: 0; size: 2; recVer and recInstance
|
||||
|
||||
// bit: 4-15; mask: 0xFFF0; recInstance
|
||||
$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
|
||||
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SplitMenuColors record.
|
||||
*/
|
||||
private function readSplitMenuColors(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read DgContainer record (Drawing Container).
|
||||
*/
|
||||
private function readDgContainer(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
// record is a container, read contents
|
||||
$dgContainer = new DgContainer();
|
||||
$this->object->setDgContainer($dgContainer);
|
||||
$reader = new self($dgContainer);
|
||||
$escher = $reader->load($recordData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Dg record (Drawing).
|
||||
*/
|
||||
private function readDg(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SpgrContainer record (Shape Group Container).
|
||||
*/
|
||||
private function readSpgrContainer(): void
|
||||
{
|
||||
// context is either context DgContainer or SpgrContainer
|
||||
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
// record is a container, read contents
|
||||
$spgrContainer = new SpgrContainer();
|
||||
|
||||
if ($this->object instanceof DgContainer) {
|
||||
// DgContainer
|
||||
$this->object->setSpgrContainer($spgrContainer);
|
||||
} else {
|
||||
// SpgrContainer
|
||||
$this->object->addChild($spgrContainer);
|
||||
}
|
||||
|
||||
$reader = new self($spgrContainer);
|
||||
$escher = $reader->load($recordData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read SpContainer record (Shape Container).
|
||||
*/
|
||||
private function readSpContainer(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// add spContainer to spgrContainer
|
||||
$spContainer = new SpContainer();
|
||||
$this->object->addChild($spContainer);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
// record is a container, read contents
|
||||
$reader = new self($spContainer);
|
||||
$escher = $reader->load($recordData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Spgr record (Shape Group).
|
||||
*/
|
||||
private function readSpgr(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Sp record (Shape).
|
||||
*/
|
||||
private function readSp(): void
|
||||
{
|
||||
// offset: 0; size: 2; recVer and recInstance
|
||||
|
||||
// bit: 4-15; mask: 0xFFF0; recInstance
|
||||
$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
|
||||
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read ClientTextbox record.
|
||||
*/
|
||||
private function readClientTextbox(): void
|
||||
{
|
||||
// offset: 0; size: 2; recVer and recInstance
|
||||
|
||||
// bit: 4-15; mask: 0xFFF0; recInstance
|
||||
$recInstance = (0xFFF0 & Xls::getUInt2d($this->data, $this->pos)) >> 4;
|
||||
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read ClientAnchor record. This record holds information about where the shape is anchored in worksheet.
|
||||
*/
|
||||
private function readClientAnchor(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
|
||||
// offset: 2; size: 2; upper-left corner column index (0-based)
|
||||
$c1 = Xls::getUInt2d($recordData, 2);
|
||||
|
||||
// offset: 4; size: 2; upper-left corner horizontal offset in 1/1024 of column width
|
||||
$startOffsetX = Xls::getUInt2d($recordData, 4);
|
||||
|
||||
// offset: 6; size: 2; upper-left corner row index (0-based)
|
||||
$r1 = Xls::getUInt2d($recordData, 6);
|
||||
|
||||
// offset: 8; size: 2; upper-left corner vertical offset in 1/256 of row height
|
||||
$startOffsetY = Xls::getUInt2d($recordData, 8);
|
||||
|
||||
// offset: 10; size: 2; bottom-right corner column index (0-based)
|
||||
$c2 = Xls::getUInt2d($recordData, 10);
|
||||
|
||||
// offset: 12; size: 2; bottom-right corner horizontal offset in 1/1024 of column width
|
||||
$endOffsetX = Xls::getUInt2d($recordData, 12);
|
||||
|
||||
// offset: 14; size: 2; bottom-right corner row index (0-based)
|
||||
$r2 = Xls::getUInt2d($recordData, 14);
|
||||
|
||||
// offset: 16; size: 2; bottom-right corner vertical offset in 1/256 of row height
|
||||
$endOffsetY = Xls::getUInt2d($recordData, 16);
|
||||
|
||||
// set the start coordinates
|
||||
$this->object->setStartCoordinates(Coordinate::stringFromColumnIndex($c1 + 1) . ($r1 + 1));
|
||||
|
||||
// set the start offsetX
|
||||
$this->object->setStartOffsetX($startOffsetX);
|
||||
|
||||
// set the start offsetY
|
||||
$this->object->setStartOffsetY($startOffsetY);
|
||||
|
||||
// set the end coordinates
|
||||
$this->object->setEndCoordinates(Coordinate::stringFromColumnIndex($c2 + 1) . ($r2 + 1));
|
||||
|
||||
// set the end offsetX
|
||||
$this->object->setEndOffsetX($endOffsetX);
|
||||
|
||||
// set the end offsetY
|
||||
$this->object->setEndOffsetY($endOffsetY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read ClientData record.
|
||||
*/
|
||||
private function readClientData(): void
|
||||
{
|
||||
$length = Xls::getInt4d($this->data, $this->pos + 4);
|
||||
$recordData = substr($this->data, $this->pos + 8, $length);
|
||||
|
||||
// move stream pointer to next record
|
||||
$this->pos += 8 + $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read OfficeArtRGFOPTE table of property-value pairs.
|
||||
*
|
||||
* @param string $data Binary data
|
||||
* @param int $n Number of properties
|
||||
*/
|
||||
private function readOfficeArtRGFOPTE($data, $n): void
|
||||
{
|
||||
$splicedComplexData = substr($data, 6 * $n);
|
||||
|
||||
// loop through property-value pairs
|
||||
for ($i = 0; $i < $n; ++$i) {
|
||||
// read 6 bytes at a time
|
||||
$fopte = substr($data, 6 * $i, 6);
|
||||
|
||||
// offset: 0; size: 2; opid
|
||||
$opid = Xls::getUInt2d($fopte, 0);
|
||||
|
||||
// bit: 0-13; mask: 0x3FFF; opid.opid
|
||||
$opidOpid = (0x3FFF & $opid) >> 0;
|
||||
|
||||
// bit: 14; mask 0x4000; 1 = value in op field is BLIP identifier
|
||||
$opidFBid = (0x4000 & $opid) >> 14;
|
||||
|
||||
// bit: 15; mask 0x8000; 1 = this is a complex property, op field specifies size of complex data
|
||||
$opidFComplex = (0x8000 & $opid) >> 15;
|
||||
|
||||
// offset: 2; size: 4; the value for this property
|
||||
$op = Xls::getInt4d($fopte, 2);
|
||||
|
||||
if ($opidFComplex) {
|
||||
$complexData = substr($splicedComplexData, 0, $op);
|
||||
$splicedComplexData = substr($splicedComplexData, $op);
|
||||
|
||||
// we store string value with complex data
|
||||
$value = $complexData;
|
||||
} else {
|
||||
// we store integer value
|
||||
$value = $op;
|
||||
}
|
||||
|
||||
$this->object->setOPT($opidOpid, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
184
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php
vendored
Normal file
184
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
|
||||
class MD5
|
||||
{
|
||||
// Context
|
||||
private $a;
|
||||
|
||||
private $b;
|
||||
|
||||
private $c;
|
||||
|
||||
private $d;
|
||||
|
||||
/**
|
||||
* MD5 stream constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the MD5 stream context.
|
||||
*/
|
||||
public function reset(): void
|
||||
{
|
||||
$this->a = 0x67452301;
|
||||
$this->b = 0xEFCDAB89;
|
||||
$this->c = 0x98BADCFE;
|
||||
$this->d = 0x10325476;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get MD5 stream context.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContext()
|
||||
{
|
||||
$s = '';
|
||||
foreach (['a', 'b', 'c', 'd'] as $i) {
|
||||
$v = $this->{$i};
|
||||
$s .= chr($v & 0xff);
|
||||
$s .= chr(($v >> 8) & 0xff);
|
||||
$s .= chr(($v >> 16) & 0xff);
|
||||
$s .= chr(($v >> 24) & 0xff);
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add data to context.
|
||||
*
|
||||
* @param string $data Data to add
|
||||
*/
|
||||
public function add($data): void
|
||||
{
|
||||
$words = array_values(unpack('V16', $data));
|
||||
|
||||
$A = $this->a;
|
||||
$B = $this->b;
|
||||
$C = $this->c;
|
||||
$D = $this->d;
|
||||
|
||||
$F = ['self', 'f'];
|
||||
$G = ['self', 'g'];
|
||||
$H = ['self', 'h'];
|
||||
$I = ['self', 'i'];
|
||||
|
||||
// ROUND 1
|
||||
self::step($F, $A, $B, $C, $D, $words[0], 7, 0xd76aa478);
|
||||
self::step($F, $D, $A, $B, $C, $words[1], 12, 0xe8c7b756);
|
||||
self::step($F, $C, $D, $A, $B, $words[2], 17, 0x242070db);
|
||||
self::step($F, $B, $C, $D, $A, $words[3], 22, 0xc1bdceee);
|
||||
self::step($F, $A, $B, $C, $D, $words[4], 7, 0xf57c0faf);
|
||||
self::step($F, $D, $A, $B, $C, $words[5], 12, 0x4787c62a);
|
||||
self::step($F, $C, $D, $A, $B, $words[6], 17, 0xa8304613);
|
||||
self::step($F, $B, $C, $D, $A, $words[7], 22, 0xfd469501);
|
||||
self::step($F, $A, $B, $C, $D, $words[8], 7, 0x698098d8);
|
||||
self::step($F, $D, $A, $B, $C, $words[9], 12, 0x8b44f7af);
|
||||
self::step($F, $C, $D, $A, $B, $words[10], 17, 0xffff5bb1);
|
||||
self::step($F, $B, $C, $D, $A, $words[11], 22, 0x895cd7be);
|
||||
self::step($F, $A, $B, $C, $D, $words[12], 7, 0x6b901122);
|
||||
self::step($F, $D, $A, $B, $C, $words[13], 12, 0xfd987193);
|
||||
self::step($F, $C, $D, $A, $B, $words[14], 17, 0xa679438e);
|
||||
self::step($F, $B, $C, $D, $A, $words[15], 22, 0x49b40821);
|
||||
|
||||
// ROUND 2
|
||||
self::step($G, $A, $B, $C, $D, $words[1], 5, 0xf61e2562);
|
||||
self::step($G, $D, $A, $B, $C, $words[6], 9, 0xc040b340);
|
||||
self::step($G, $C, $D, $A, $B, $words[11], 14, 0x265e5a51);
|
||||
self::step($G, $B, $C, $D, $A, $words[0], 20, 0xe9b6c7aa);
|
||||
self::step($G, $A, $B, $C, $D, $words[5], 5, 0xd62f105d);
|
||||
self::step($G, $D, $A, $B, $C, $words[10], 9, 0x02441453);
|
||||
self::step($G, $C, $D, $A, $B, $words[15], 14, 0xd8a1e681);
|
||||
self::step($G, $B, $C, $D, $A, $words[4], 20, 0xe7d3fbc8);
|
||||
self::step($G, $A, $B, $C, $D, $words[9], 5, 0x21e1cde6);
|
||||
self::step($G, $D, $A, $B, $C, $words[14], 9, 0xc33707d6);
|
||||
self::step($G, $C, $D, $A, $B, $words[3], 14, 0xf4d50d87);
|
||||
self::step($G, $B, $C, $D, $A, $words[8], 20, 0x455a14ed);
|
||||
self::step($G, $A, $B, $C, $D, $words[13], 5, 0xa9e3e905);
|
||||
self::step($G, $D, $A, $B, $C, $words[2], 9, 0xfcefa3f8);
|
||||
self::step($G, $C, $D, $A, $B, $words[7], 14, 0x676f02d9);
|
||||
self::step($G, $B, $C, $D, $A, $words[12], 20, 0x8d2a4c8a);
|
||||
|
||||
// ROUND 3
|
||||
self::step($H, $A, $B, $C, $D, $words[5], 4, 0xfffa3942);
|
||||
self::step($H, $D, $A, $B, $C, $words[8], 11, 0x8771f681);
|
||||
self::step($H, $C, $D, $A, $B, $words[11], 16, 0x6d9d6122);
|
||||
self::step($H, $B, $C, $D, $A, $words[14], 23, 0xfde5380c);
|
||||
self::step($H, $A, $B, $C, $D, $words[1], 4, 0xa4beea44);
|
||||
self::step($H, $D, $A, $B, $C, $words[4], 11, 0x4bdecfa9);
|
||||
self::step($H, $C, $D, $A, $B, $words[7], 16, 0xf6bb4b60);
|
||||
self::step($H, $B, $C, $D, $A, $words[10], 23, 0xbebfbc70);
|
||||
self::step($H, $A, $B, $C, $D, $words[13], 4, 0x289b7ec6);
|
||||
self::step($H, $D, $A, $B, $C, $words[0], 11, 0xeaa127fa);
|
||||
self::step($H, $C, $D, $A, $B, $words[3], 16, 0xd4ef3085);
|
||||
self::step($H, $B, $C, $D, $A, $words[6], 23, 0x04881d05);
|
||||
self::step($H, $A, $B, $C, $D, $words[9], 4, 0xd9d4d039);
|
||||
self::step($H, $D, $A, $B, $C, $words[12], 11, 0xe6db99e5);
|
||||
self::step($H, $C, $D, $A, $B, $words[15], 16, 0x1fa27cf8);
|
||||
self::step($H, $B, $C, $D, $A, $words[2], 23, 0xc4ac5665);
|
||||
|
||||
// ROUND 4
|
||||
self::step($I, $A, $B, $C, $D, $words[0], 6, 0xf4292244);
|
||||
self::step($I, $D, $A, $B, $C, $words[7], 10, 0x432aff97);
|
||||
self::step($I, $C, $D, $A, $B, $words[14], 15, 0xab9423a7);
|
||||
self::step($I, $B, $C, $D, $A, $words[5], 21, 0xfc93a039);
|
||||
self::step($I, $A, $B, $C, $D, $words[12], 6, 0x655b59c3);
|
||||
self::step($I, $D, $A, $B, $C, $words[3], 10, 0x8f0ccc92);
|
||||
self::step($I, $C, $D, $A, $B, $words[10], 15, 0xffeff47d);
|
||||
self::step($I, $B, $C, $D, $A, $words[1], 21, 0x85845dd1);
|
||||
self::step($I, $A, $B, $C, $D, $words[8], 6, 0x6fa87e4f);
|
||||
self::step($I, $D, $A, $B, $C, $words[15], 10, 0xfe2ce6e0);
|
||||
self::step($I, $C, $D, $A, $B, $words[6], 15, 0xa3014314);
|
||||
self::step($I, $B, $C, $D, $A, $words[13], 21, 0x4e0811a1);
|
||||
self::step($I, $A, $B, $C, $D, $words[4], 6, 0xf7537e82);
|
||||
self::step($I, $D, $A, $B, $C, $words[11], 10, 0xbd3af235);
|
||||
self::step($I, $C, $D, $A, $B, $words[2], 15, 0x2ad7d2bb);
|
||||
self::step($I, $B, $C, $D, $A, $words[9], 21, 0xeb86d391);
|
||||
|
||||
$this->a = ($this->a + $A) & 0xffffffff;
|
||||
$this->b = ($this->b + $B) & 0xffffffff;
|
||||
$this->c = ($this->c + $C) & 0xffffffff;
|
||||
$this->d = ($this->d + $D) & 0xffffffff;
|
||||
}
|
||||
|
||||
private static function f($X, $Y, $Z)
|
||||
{
|
||||
return ($X & $Y) | ((~$X) & $Z); // X AND Y OR NOT X AND Z
|
||||
}
|
||||
|
||||
private static function g($X, $Y, $Z)
|
||||
{
|
||||
return ($X & $Z) | ($Y & (~$Z)); // X AND Z OR Y AND NOT Z
|
||||
}
|
||||
|
||||
private static function h($X, $Y, $Z)
|
||||
{
|
||||
return $X ^ $Y ^ $Z; // X XOR Y XOR Z
|
||||
}
|
||||
|
||||
private static function i($X, $Y, $Z)
|
||||
{
|
||||
return $Y ^ ($X | (~$Z)); // Y XOR (X OR NOT Z)
|
||||
}
|
||||
|
||||
private static function step($func, &$A, $B, $C, $D, $M, $s, $t): void
|
||||
{
|
||||
$A = ($A + call_user_func($func, $B, $C, $D) + $M + $t) & 0xffffffff;
|
||||
$A = self::rotate($A, $s);
|
||||
$A = ($B + $A) & 0xffffffff;
|
||||
}
|
||||
|
||||
private static function rotate($decimal, $bits)
|
||||
{
|
||||
$binary = str_pad(decbin($decimal), 32, '0', STR_PAD_LEFT);
|
||||
|
||||
return bindec(substr($binary, $bits) . substr($binary, 0, $bits));
|
||||
}
|
||||
}
|
||||
61
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
vendored
Normal file
61
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
|
||||
class RC4
|
||||
{
|
||||
// Context
|
||||
protected $s = [];
|
||||
|
||||
protected $i = 0;
|
||||
|
||||
protected $j = 0;
|
||||
|
||||
/**
|
||||
* RC4 stream decryption/encryption constrcutor.
|
||||
*
|
||||
* @param string $key Encryption key/passphrase
|
||||
*/
|
||||
public function __construct($key)
|
||||
{
|
||||
$len = strlen($key);
|
||||
|
||||
for ($this->i = 0; $this->i < 256; ++$this->i) {
|
||||
$this->s[$this->i] = $this->i;
|
||||
}
|
||||
|
||||
$this->j = 0;
|
||||
for ($this->i = 0; $this->i < 256; ++$this->i) {
|
||||
$this->j = ($this->j + $this->s[$this->i] + ord($key[$this->i % $len])) % 256;
|
||||
$t = $this->s[$this->i];
|
||||
$this->s[$this->i] = $this->s[$this->j];
|
||||
$this->s[$this->j] = $t;
|
||||
}
|
||||
$this->i = $this->j = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Symmetric decryption/encryption function.
|
||||
*
|
||||
* @param string $data Data to encrypt/decrypt
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function RC4($data)
|
||||
{
|
||||
$len = strlen($data);
|
||||
for ($c = 0; $c < $len; ++$c) {
|
||||
$this->i = ($this->i + 1) % 256;
|
||||
$this->j = ($this->j + $this->s[$this->i]) % 256;
|
||||
$t = $this->s[$this->i];
|
||||
$this->s[$this->i] = $this->s[$this->j];
|
||||
$this->s[$this->j] = $t;
|
||||
|
||||
$t = ($this->s[$this->i] + $this->s[$this->j]) % 256;
|
||||
|
||||
$data[$c] = chr(ord($data[$c]) ^ $this->s[$t]);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
42
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php
vendored
Normal file
42
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Style;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border as StyleBorder;
|
||||
|
||||
class Border
|
||||
{
|
||||
protected static $map = [
|
||||
0x00 => StyleBorder::BORDER_NONE,
|
||||
0x01 => StyleBorder::BORDER_THIN,
|
||||
0x02 => StyleBorder::BORDER_MEDIUM,
|
||||
0x03 => StyleBorder::BORDER_DASHED,
|
||||
0x04 => StyleBorder::BORDER_DOTTED,
|
||||
0x05 => StyleBorder::BORDER_THICK,
|
||||
0x06 => StyleBorder::BORDER_DOUBLE,
|
||||
0x07 => StyleBorder::BORDER_HAIR,
|
||||
0x08 => StyleBorder::BORDER_MEDIUMDASHED,
|
||||
0x09 => StyleBorder::BORDER_DASHDOT,
|
||||
0x0A => StyleBorder::BORDER_MEDIUMDASHDOT,
|
||||
0x0B => StyleBorder::BORDER_DASHDOTDOT,
|
||||
0x0C => StyleBorder::BORDER_MEDIUMDASHDOTDOT,
|
||||
0x0D => StyleBorder::BORDER_SLANTDASHDOT,
|
||||
];
|
||||
|
||||
/**
|
||||
* Map border style
|
||||
* OpenOffice documentation: 2.5.11.
|
||||
*
|
||||
* @param int $index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function lookup($index)
|
||||
{
|
||||
if (isset(self::$map[$index])) {
|
||||
return self::$map[$index];
|
||||
}
|
||||
|
||||
return StyleBorder::BORDER_NONE;
|
||||
}
|
||||
}
|
||||
47
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
vendored
Normal file
47
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls\Style;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||
|
||||
class FillPattern
|
||||
{
|
||||
protected static $map = [
|
||||
0x00 => Fill::FILL_NONE,
|
||||
0x01 => Fill::FILL_SOLID,
|
||||
0x02 => Fill::FILL_PATTERN_MEDIUMGRAY,
|
||||
0x03 => Fill::FILL_PATTERN_DARKGRAY,
|
||||
0x04 => Fill::FILL_PATTERN_LIGHTGRAY,
|
||||
0x05 => Fill::FILL_PATTERN_DARKHORIZONTAL,
|
||||
0x06 => Fill::FILL_PATTERN_DARKVERTICAL,
|
||||
0x07 => Fill::FILL_PATTERN_DARKDOWN,
|
||||
0x08 => Fill::FILL_PATTERN_DARKUP,
|
||||
0x09 => Fill::FILL_PATTERN_DARKGRID,
|
||||
0x0A => Fill::FILL_PATTERN_DARKTRELLIS,
|
||||
0x0B => Fill::FILL_PATTERN_LIGHTHORIZONTAL,
|
||||
0x0C => Fill::FILL_PATTERN_LIGHTVERTICAL,
|
||||
0x0D => Fill::FILL_PATTERN_LIGHTDOWN,
|
||||
0x0E => Fill::FILL_PATTERN_LIGHTUP,
|
||||
0x0F => Fill::FILL_PATTERN_LIGHTGRID,
|
||||
0x10 => Fill::FILL_PATTERN_LIGHTTRELLIS,
|
||||
0x11 => Fill::FILL_PATTERN_GRAY125,
|
||||
0x12 => Fill::FILL_PATTERN_GRAY0625,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get fill pattern from index
|
||||
* OpenOffice documentation: 2.5.12.
|
||||
*
|
||||
* @param int $index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function lookup($index)
|
||||
{
|
||||
if (isset(self::$map[$index])) {
|
||||
return self::$map[$index];
|
||||
}
|
||||
|
||||
return Fill::FILL_NONE;
|
||||
}
|
||||
}
|
||||
2054
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
vendored
Normal file
2054
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
146
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
vendored
Normal file
146
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class AutoFilter
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $worksheetXml;
|
||||
|
||||
public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml)
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
$this->worksheetXml = $worksheetXml;
|
||||
}
|
||||
|
||||
public function load(): void
|
||||
{
|
||||
// Remove all "$" in the auto filter range
|
||||
$autoFilterRange = preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref']);
|
||||
if (strpos($autoFilterRange, ':') !== false) {
|
||||
$this->readAutoFilter($autoFilterRange, $this->worksheetXml);
|
||||
}
|
||||
}
|
||||
|
||||
private function readAutoFilter($autoFilterRange, $xmlSheet): void
|
||||
{
|
||||
$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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
vendored
Normal file
19
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
vendored
Normal file
@@ -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');
|
||||
}
|
||||
}
|
||||
567
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
vendored
Normal file
567
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
vendored
Normal file
@@ -0,0 +1,567 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
|
||||
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
|
||||
use PhpOffice\PhpSpreadsheet\Chart\Layout;
|
||||
use PhpOffice\PhpSpreadsheet\Chart\Legend;
|
||||
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
|
||||
use PhpOffice\PhpSpreadsheet\Chart\Title;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Color;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Font;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class Chart
|
||||
{
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $format
|
||||
*
|
||||
* @return null|bool|float|int|string
|
||||
*/
|
||||
private static function getAttribute(SimpleXMLElement $component, $name, $format)
|
||||
{
|
||||
$attributes = $component->attributes();
|
||||
if (isset($attributes[$name])) {
|
||||
if ($format == 'string') {
|
||||
return (string) $attributes[$name];
|
||||
} elseif ($format == 'integer') {
|
||||
return (int) $attributes[$name];
|
||||
} elseif ($format == 'boolean') {
|
||||
return (bool) ($attributes[$name] === '0' || $attributes[$name] !== 'true') ? false : true;
|
||||
}
|
||||
|
||||
return (float) $attributes[$name];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $chartName
|
||||
*
|
||||
* @return \PhpOffice\PhpSpreadsheet\Chart\Chart
|
||||
*/
|
||||
public static function readChart(SimpleXMLElement $chartElements, $chartName)
|
||||
{
|
||||
$namespacesChartMeta = $chartElements->getNamespaces(true);
|
||||
$chartElementsC = $chartElements->children($namespacesChartMeta['c']);
|
||||
|
||||
$XaxisLabel = $YaxisLabel = $legend = $title = null;
|
||||
$dispBlanksAs = $plotVisOnly = null;
|
||||
|
||||
foreach ($chartElementsC as $chartElementKey => $chartElement) {
|
||||
switch ($chartElementKey) {
|
||||
case 'chart':
|
||||
foreach ($chartElement as $chartDetailsKey => $chartDetails) {
|
||||
$chartDetailsC = $chartDetails->children($namespacesChartMeta['c']);
|
||||
switch ($chartDetailsKey) {
|
||||
case 'plotArea':
|
||||
$plotAreaLayout = $XaxisLable = $YaxisLable = null;
|
||||
$plotSeries = $plotAttributes = [];
|
||||
foreach ($chartDetails as $chartDetailKey => $chartDetail) {
|
||||
switch ($chartDetailKey) {
|
||||
case 'layout':
|
||||
$plotAreaLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
|
||||
|
||||
break;
|
||||
case 'catAx':
|
||||
if (isset($chartDetail->title)) {
|
||||
$XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'dateAx':
|
||||
if (isset($chartDetail->title)) {
|
||||
$XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'valAx':
|
||||
if (isset($chartDetail->title)) {
|
||||
$YaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'barChart':
|
||||
case 'bar3DChart':
|
||||
$barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string');
|
||||
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
|
||||
$plotSer->setPlotDirection($barDirection);
|
||||
$plotSeries[] = $plotSer;
|
||||
$plotAttributes = self::readChartAttributes($chartDetail);
|
||||
|
||||
break;
|
||||
case 'lineChart':
|
||||
case 'line3DChart':
|
||||
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
|
||||
$plotAttributes = self::readChartAttributes($chartDetail);
|
||||
|
||||
break;
|
||||
case 'areaChart':
|
||||
case 'area3DChart':
|
||||
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
|
||||
$plotAttributes = self::readChartAttributes($chartDetail);
|
||||
|
||||
break;
|
||||
case 'doughnutChart':
|
||||
case 'pieChart':
|
||||
case 'pie3DChart':
|
||||
$explosion = isset($chartDetail->ser->explosion);
|
||||
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
|
||||
$plotSer->setPlotStyle($explosion);
|
||||
$plotSeries[] = $plotSer;
|
||||
$plotAttributes = self::readChartAttributes($chartDetail);
|
||||
|
||||
break;
|
||||
case 'scatterChart':
|
||||
$scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string');
|
||||
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
|
||||
$plotSer->setPlotStyle($scatterStyle);
|
||||
$plotSeries[] = $plotSer;
|
||||
$plotAttributes = self::readChartAttributes($chartDetail);
|
||||
|
||||
break;
|
||||
case 'bubbleChart':
|
||||
$bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer');
|
||||
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
|
||||
$plotSer->setPlotStyle($bubbleScale);
|
||||
$plotSeries[] = $plotSer;
|
||||
$plotAttributes = self::readChartAttributes($chartDetail);
|
||||
|
||||
break;
|
||||
case 'radarChart':
|
||||
$radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string');
|
||||
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
|
||||
$plotSer->setPlotStyle($radarStyle);
|
||||
$plotSeries[] = $plotSer;
|
||||
$plotAttributes = self::readChartAttributes($chartDetail);
|
||||
|
||||
break;
|
||||
case 'surfaceChart':
|
||||
case 'surface3DChart':
|
||||
$wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean');
|
||||
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
|
||||
$plotSer->setPlotStyle($wireFrame);
|
||||
$plotSeries[] = $plotSer;
|
||||
$plotAttributes = self::readChartAttributes($chartDetail);
|
||||
|
||||
break;
|
||||
case 'stockChart':
|
||||
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
|
||||
$plotAttributes = self::readChartAttributes($plotAreaLayout);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($plotAreaLayout == null) {
|
||||
$plotAreaLayout = new Layout();
|
||||
}
|
||||
$plotArea = new PlotArea($plotAreaLayout, $plotSeries);
|
||||
self::setChartAttributes($plotAreaLayout, $plotAttributes);
|
||||
|
||||
break;
|
||||
case 'plotVisOnly':
|
||||
$plotVisOnly = self::getAttribute($chartDetails, 'val', 'string');
|
||||
|
||||
break;
|
||||
case 'dispBlanksAs':
|
||||
$dispBlanksAs = self::getAttribute($chartDetails, 'val', 'string');
|
||||
|
||||
break;
|
||||
case 'title':
|
||||
$title = self::chartTitle($chartDetails, $namespacesChartMeta);
|
||||
|
||||
break;
|
||||
case 'legend':
|
||||
$legendPos = 'r';
|
||||
$legendLayout = null;
|
||||
$legendOverlay = false;
|
||||
foreach ($chartDetails as $chartDetailKey => $chartDetail) {
|
||||
switch ($chartDetailKey) {
|
||||
case 'legendPos':
|
||||
$legendPos = self::getAttribute($chartDetail, 'val', 'string');
|
||||
|
||||
break;
|
||||
case 'overlay':
|
||||
$legendOverlay = self::getAttribute($chartDetail, 'val', 'boolean');
|
||||
|
||||
break;
|
||||
case 'layout':
|
||||
$legendLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$legend = new Legend($legendPos, $legendLayout, $legendOverlay);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, $dispBlanksAs, $XaxisLabel, $YaxisLabel);
|
||||
|
||||
return $chart;
|
||||
}
|
||||
|
||||
private static function chartTitle(SimpleXMLElement $titleDetails, array $namespacesChartMeta)
|
||||
{
|
||||
$caption = [];
|
||||
$titleLayout = null;
|
||||
foreach ($titleDetails as $titleDetailKey => $chartDetail) {
|
||||
switch ($titleDetailKey) {
|
||||
case 'tx':
|
||||
$titleDetails = $chartDetail->rich->children($namespacesChartMeta['a']);
|
||||
foreach ($titleDetails as $titleKey => $titleDetail) {
|
||||
switch ($titleKey) {
|
||||
case 'p':
|
||||
$titleDetailPart = $titleDetail->children($namespacesChartMeta['a']);
|
||||
$caption[] = self::parseRichText($titleDetailPart);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'layout':
|
||||
$titleLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Title($caption, $titleLayout);
|
||||
}
|
||||
|
||||
private static function chartLayoutDetails($chartDetail, $namespacesChartMeta)
|
||||
{
|
||||
if (!isset($chartDetail->manualLayout)) {
|
||||
return null;
|
||||
}
|
||||
$details = $chartDetail->manualLayout->children($namespacesChartMeta['c']);
|
||||
if ($details === null) {
|
||||
return null;
|
||||
}
|
||||
$layout = [];
|
||||
foreach ($details as $detailKey => $detail) {
|
||||
$layout[$detailKey] = self::getAttribute($detail, 'val', 'string');
|
||||
}
|
||||
|
||||
return new Layout($layout);
|
||||
}
|
||||
|
||||
private static function chartDataSeries($chartDetail, $namespacesChartMeta, $plotType)
|
||||
{
|
||||
$multiSeriesType = null;
|
||||
$smoothLine = false;
|
||||
$seriesLabel = $seriesCategory = $seriesValues = $plotOrder = [];
|
||||
|
||||
$seriesDetailSet = $chartDetail->children($namespacesChartMeta['c']);
|
||||
foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
|
||||
switch ($seriesDetailKey) {
|
||||
case 'grouping':
|
||||
$multiSeriesType = self::getAttribute($chartDetail->grouping, 'val', 'string');
|
||||
|
||||
break;
|
||||
case 'ser':
|
||||
$marker = null;
|
||||
$seriesIndex = '';
|
||||
foreach ($seriesDetails as $seriesKey => $seriesDetail) {
|
||||
switch ($seriesKey) {
|
||||
case 'idx':
|
||||
$seriesIndex = self::getAttribute($seriesDetail, 'val', 'integer');
|
||||
|
||||
break;
|
||||
case 'order':
|
||||
$seriesOrder = self::getAttribute($seriesDetail, 'val', 'integer');
|
||||
$plotOrder[$seriesIndex] = $seriesOrder;
|
||||
|
||||
break;
|
||||
case 'tx':
|
||||
$seriesLabel[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
|
||||
|
||||
break;
|
||||
case 'marker':
|
||||
$marker = self::getAttribute($seriesDetail->symbol, 'val', 'string');
|
||||
|
||||
break;
|
||||
case 'smooth':
|
||||
$smoothLine = self::getAttribute($seriesDetail, 'val', 'boolean');
|
||||
|
||||
break;
|
||||
case 'cat':
|
||||
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
|
||||
|
||||
break;
|
||||
case 'val':
|
||||
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
|
||||
|
||||
break;
|
||||
case 'xVal':
|
||||
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
|
||||
|
||||
break;
|
||||
case 'yVal':
|
||||
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
|
||||
}
|
||||
|
||||
private static function chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker = null)
|
||||
{
|
||||
if (isset($seriesDetail->strRef)) {
|
||||
$seriesSource = (string) $seriesDetail->strRef->f;
|
||||
$seriesData = self::chartDataSeriesValues($seriesDetail->strRef->strCache->children($namespacesChartMeta['c']), 's');
|
||||
|
||||
return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
|
||||
} elseif (isset($seriesDetail->numRef)) {
|
||||
$seriesSource = (string) $seriesDetail->numRef->f;
|
||||
$seriesData = self::chartDataSeriesValues($seriesDetail->numRef->numCache->children($namespacesChartMeta['c']));
|
||||
|
||||
return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
|
||||
} elseif (isset($seriesDetail->multiLvlStrRef)) {
|
||||
$seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
|
||||
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($namespacesChartMeta['c']), 's');
|
||||
$seriesData['pointCount'] = count($seriesData['dataValues']);
|
||||
|
||||
return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
|
||||
} elseif (isset($seriesDetail->multiLvlNumRef)) {
|
||||
$seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
|
||||
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($namespacesChartMeta['c']), 's');
|
||||
$seriesData['pointCount'] = count($seriesData['dataValues']);
|
||||
|
||||
return new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static function chartDataSeriesValues($seriesValueSet, $dataType = 'n')
|
||||
{
|
||||
$seriesVal = [];
|
||||
$formatCode = '';
|
||||
$pointCount = 0;
|
||||
|
||||
foreach ($seriesValueSet as $seriesValueIdx => $seriesValue) {
|
||||
switch ($seriesValueIdx) {
|
||||
case 'ptCount':
|
||||
$pointCount = self::getAttribute($seriesValue, 'val', 'integer');
|
||||
|
||||
break;
|
||||
case 'formatCode':
|
||||
$formatCode = (string) $seriesValue;
|
||||
|
||||
break;
|
||||
case 'pt':
|
||||
$pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
|
||||
if ($dataType == 's') {
|
||||
$seriesVal[$pointVal] = (string) $seriesValue->v;
|
||||
} elseif ($seriesValue->v === Functions::NA()) {
|
||||
$seriesVal[$pointVal] = null;
|
||||
} else {
|
||||
$seriesVal[$pointVal] = (float) $seriesValue->v;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'formatCode' => $formatCode,
|
||||
'pointCount' => $pointCount,
|
||||
'dataValues' => $seriesVal,
|
||||
];
|
||||
}
|
||||
|
||||
private static function chartDataSeriesValuesMultiLevel($seriesValueSet, $dataType = 'n')
|
||||
{
|
||||
$seriesVal = [];
|
||||
$formatCode = '';
|
||||
$pointCount = 0;
|
||||
|
||||
foreach ($seriesValueSet->lvl as $seriesLevelIdx => $seriesLevel) {
|
||||
foreach ($seriesLevel as $seriesValueIdx => $seriesValue) {
|
||||
switch ($seriesValueIdx) {
|
||||
case 'ptCount':
|
||||
$pointCount = self::getAttribute($seriesValue, 'val', 'integer');
|
||||
|
||||
break;
|
||||
case 'formatCode':
|
||||
$formatCode = (string) $seriesValue;
|
||||
|
||||
break;
|
||||
case 'pt':
|
||||
$pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
|
||||
if ($dataType == 's') {
|
||||
$seriesVal[$pointVal][] = (string) $seriesValue->v;
|
||||
} elseif ($seriesValue->v === Functions::NA()) {
|
||||
$seriesVal[$pointVal] = null;
|
||||
} else {
|
||||
$seriesVal[$pointVal][] = (float) $seriesValue->v;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'formatCode' => $formatCode,
|
||||
'pointCount' => $pointCount,
|
||||
'dataValues' => $seriesVal,
|
||||
];
|
||||
}
|
||||
|
||||
private static function parseRichText(SimpleXMLElement $titleDetailPart)
|
||||
{
|
||||
$value = new RichText();
|
||||
$objText = null;
|
||||
foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
|
||||
if (isset($titleDetailElement->t)) {
|
||||
$objText = $value->createTextRun((string) $titleDetailElement->t);
|
||||
}
|
||||
if (isset($titleDetailElement->rPr)) {
|
||||
if (isset($titleDetailElement->rPr->rFont['val'])) {
|
||||
$objText->getFont()->setName((string) $titleDetailElement->rPr->rFont['val']);
|
||||
}
|
||||
|
||||
$fontSize = (self::getAttribute($titleDetailElement->rPr, 'sz', 'integer'));
|
||||
if ($fontSize !== null) {
|
||||
$objText->getFont()->setSize(floor($fontSize / 100));
|
||||
}
|
||||
|
||||
$fontColor = (self::getAttribute($titleDetailElement->rPr, 'color', 'string'));
|
||||
if ($fontColor !== null) {
|
||||
$objText->getFont()->setColor(new Color(self::readColor($fontColor)));
|
||||
}
|
||||
|
||||
$bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean');
|
||||
if ($bold !== null) {
|
||||
$objText->getFont()->setBold($bold);
|
||||
}
|
||||
|
||||
$italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean');
|
||||
if ($italic !== null) {
|
||||
$objText->getFont()->setItalic($italic);
|
||||
}
|
||||
|
||||
$baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer');
|
||||
if ($baseline !== null) {
|
||||
if ($baseline > 0) {
|
||||
$objText->getFont()->setSuperscript(true);
|
||||
} elseif ($baseline < 0) {
|
||||
$objText->getFont()->setSubscript(true);
|
||||
}
|
||||
}
|
||||
|
||||
$underscore = (self::getAttribute($titleDetailElement->rPr, 'u', 'string'));
|
||||
if ($underscore !== null) {
|
||||
if ($underscore == 'sng') {
|
||||
$objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
|
||||
} elseif ($underscore == 'dbl') {
|
||||
$objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
|
||||
} else {
|
||||
$objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
$strikethrough = (self::getAttribute($titleDetailElement->rPr, 's', 'string'));
|
||||
if ($strikethrough !== null) {
|
||||
if ($strikethrough == 'noStrike') {
|
||||
$objText->getFont()->setStrikethrough(false);
|
||||
} else {
|
||||
$objText->getFont()->setStrikethrough(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private static function readChartAttributes($chartDetail)
|
||||
{
|
||||
$plotAttributes = [];
|
||||
if (isset($chartDetail->dLbls)) {
|
||||
if (isset($chartDetail->dLbls->howLegendKey)) {
|
||||
$plotAttributes['showLegendKey'] = self::getAttribute($chartDetail->dLbls->showLegendKey, 'val', 'string');
|
||||
}
|
||||
if (isset($chartDetail->dLbls->showVal)) {
|
||||
$plotAttributes['showVal'] = self::getAttribute($chartDetail->dLbls->showVal, 'val', 'string');
|
||||
}
|
||||
if (isset($chartDetail->dLbls->showCatName)) {
|
||||
$plotAttributes['showCatName'] = self::getAttribute($chartDetail->dLbls->showCatName, 'val', 'string');
|
||||
}
|
||||
if (isset($chartDetail->dLbls->showSerName)) {
|
||||
$plotAttributes['showSerName'] = self::getAttribute($chartDetail->dLbls->showSerName, 'val', 'string');
|
||||
}
|
||||
if (isset($chartDetail->dLbls->showPercent)) {
|
||||
$plotAttributes['showPercent'] = self::getAttribute($chartDetail->dLbls->showPercent, 'val', 'string');
|
||||
}
|
||||
if (isset($chartDetail->dLbls->showBubbleSize)) {
|
||||
$plotAttributes['showBubbleSize'] = self::getAttribute($chartDetail->dLbls->showBubbleSize, 'val', 'string');
|
||||
}
|
||||
if (isset($chartDetail->dLbls->showLeaderLines)) {
|
||||
$plotAttributes['showLeaderLines'] = self::getAttribute($chartDetail->dLbls->showLeaderLines, 'val', 'string');
|
||||
}
|
||||
}
|
||||
|
||||
return $plotAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $plotAttributes
|
||||
*/
|
||||
private static function setChartAttributes(Layout $plotArea, $plotAttributes): void
|
||||
{
|
||||
foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
|
||||
switch ($plotAttributeKey) {
|
||||
case 'showLegendKey':
|
||||
$plotArea->setShowLegendKey($plotAttributeValue);
|
||||
|
||||
break;
|
||||
case 'showVal':
|
||||
$plotArea->setShowVal($plotAttributeValue);
|
||||
|
||||
break;
|
||||
case 'showCatName':
|
||||
$plotArea->setShowCatName($plotAttributeValue);
|
||||
|
||||
break;
|
||||
case 'showSerName':
|
||||
$plotArea->setShowSerName($plotAttributeValue);
|
||||
|
||||
break;
|
||||
case 'showPercent':
|
||||
$plotArea->setShowPercent($plotAttributeValue);
|
||||
|
||||
break;
|
||||
case 'showBubbleSize':
|
||||
$plotArea->setShowBubbleSize($plotAttributeValue);
|
||||
|
||||
break;
|
||||
case 'showLeaderLines':
|
||||
$plotArea->setShowLeaderLines($plotAttributeValue);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
205
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
vendored
Normal file
205
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
95
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
vendored
Normal file
95
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Style\Conditional;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
|
||||
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(): void
|
||||
{
|
||||
$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_CONTAINSBLANKS
|
||||
|| (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS
|
||||
|| (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): void
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
51
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
vendored
Normal file
51
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class DataValidations
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $worksheetXml;
|
||||
|
||||
public function __construct(Worksheet $workSheet, SimpleXMLElement $worksheetXml)
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
$this->worksheetXml = $worksheetXml;
|
||||
}
|
||||
|
||||
public function load(): void
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
vendored
Normal file
59
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class Hyperlinks
|
||||
{
|
||||
private $worksheet;
|
||||
|
||||
private $hyperlinks = [];
|
||||
|
||||
public function __construct(Worksheet $workSheet)
|
||||
{
|
||||
$this->worksheet = $workSheet;
|
||||
}
|
||||
|
||||
public function readHyperlinks(SimpleXMLElement $relsWorksheet): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
foreach ($worksheetXml->hyperlink as $hyperlink) {
|
||||
$this->setHyperlink($hyperlink, $this->worksheet);
|
||||
}
|
||||
}
|
||||
|
||||
private function setHyperlink(SimpleXMLElement $hyperlink, Worksheet $worksheet): void
|
||||
{
|
||||
// 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']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
151
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
vendored
Normal file
151
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
foreach ($xmlSheet->rowBreaks->brk as $brk) {
|
||||
if ($brk['man']) {
|
||||
$worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function columnBreaks(SimpleXMLElement $xmlSheet, Worksheet $worksheet): void
|
||||
{
|
||||
foreach ($xmlSheet->colBreaks->brk as $brk) {
|
||||
if ($brk['man']) {
|
||||
$worksheet->setBreak(
|
||||
Coordinate::stringFromColumnIndex(((int) $brk['id']) + 1) . '1',
|
||||
Worksheet::BREAK_COLUMN
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
92
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
vendored
Normal file
92
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use SimpleXMLElement;
|
||||
|
||||
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): void
|
||||
{
|
||||
$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): void
|
||||
{
|
||||
$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): void
|
||||
{
|
||||
$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 $array[$key] ?? null;
|
||||
}
|
||||
}
|
||||
125
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
vendored
Normal file
125
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
if (isset($sheetPr->tabColor, $sheetPr->tabColor['rgb'])) {
|
||||
$this->worksheet->getTabColor()->setARGB((string) $sheetPr->tabColor['rgb']);
|
||||
}
|
||||
}
|
||||
|
||||
private function codeName(SimpleXMLElement $sheetPr): void
|
||||
{
|
||||
if (isset($sheetPr['codeName'])) {
|
||||
$this->worksheet->setCodeName((string) $sheetPr['codeName'], false);
|
||||
}
|
||||
}
|
||||
|
||||
private function outlines(SimpleXMLElement $sheetPr): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
138
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
vendored
Normal file
138
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use SimpleXMLElement;
|
||||
|
||||
class SheetViews extends BaseParserClass
|
||||
{
|
||||
private $sheetViewXml;
|
||||
|
||||
private $worksheet;
|
||||
|
||||
public function __construct(SimpleXMLElement $sheetViewXml, Worksheet $workSheet)
|
||||
{
|
||||
$this->sheetViewXml = $sheetViewXml;
|
||||
$this->worksheet = $workSheet;
|
||||
}
|
||||
|
||||
public function load(): void
|
||||
{
|
||||
$this->zoomScale();
|
||||
$this->view();
|
||||
$this->gridLines();
|
||||
$this->headers();
|
||||
$this->direction();
|
||||
$this->showZeros();
|
||||
|
||||
if (isset($this->sheetViewXml->pane)) {
|
||||
$this->pane();
|
||||
}
|
||||
if (isset($this->sheetViewXml->selection, $this->sheetViewXml->selection['sqref'])) {
|
||||
$this->selection();
|
||||
}
|
||||
}
|
||||
|
||||
private function zoomScale(): void
|
||||
{
|
||||
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(): void
|
||||
{
|
||||
if (isset($this->sheetViewXml['view'])) {
|
||||
$this->worksheet->getSheetView()->setView((string) $this->sheetViewXml['view']);
|
||||
}
|
||||
}
|
||||
|
||||
private function gridLines(): void
|
||||
{
|
||||
if (isset($this->sheetViewXml['showGridLines'])) {
|
||||
$this->worksheet->setShowGridLines(
|
||||
self::boolean((string) $this->sheetViewXml['showGridLines'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function headers(): void
|
||||
{
|
||||
if (isset($this->sheetViewXml['showRowColHeaders'])) {
|
||||
$this->worksheet->setShowRowColHeaders(
|
||||
self::boolean((string) $this->sheetViewXml['showRowColHeaders'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function direction(): void
|
||||
{
|
||||
if (isset($this->sheetViewXml['rightToLeft'])) {
|
||||
$this->worksheet->setRightToLeft(
|
||||
self::boolean((string) $this->sheetViewXml['rightToLeft'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function showZeros(): void
|
||||
{
|
||||
if (isset($this->sheetViewXml['showZeros'])) {
|
||||
$this->worksheet->getSheetView()->setShowZeros(
|
||||
self::boolean((string) $this->sheetViewXml['showZeros'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function pane(): void
|
||||
{
|
||||
$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(): void
|
||||
{
|
||||
$sqref = (string) $this->sheetViewXml->selection['sqref'];
|
||||
$sqref = explode(' ', $sqref);
|
||||
$sqref = $sqref[0];
|
||||
|
||||
$this->worksheet->setSelectedCells($sqref);
|
||||
}
|
||||
}
|
||||
282
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
vendored
Normal file
282
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
<?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\NumberFormat;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Protection;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||
use SimpleXMLElement;
|
||||
|
||||
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 = []): void
|
||||
{
|
||||
self::$theme = $theme;
|
||||
$this->styles = $styles;
|
||||
$this->cellStyles = $cellStyles;
|
||||
}
|
||||
|
||||
private static function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
|
||||
{
|
||||
$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 readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
|
||||
{
|
||||
if ($numfmtStyleXml->count() === 0) {
|
||||
return;
|
||||
}
|
||||
$numfmt = $numfmtStyleXml->attributes();
|
||||
if ($numfmt->count() > 0 && isset($numfmt['formatCode'])) {
|
||||
$numfmtStyle->setFormatCode((string) $numfmt['formatCode']);
|
||||
}
|
||||
}
|
||||
|
||||
private static function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
$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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
$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): void
|
||||
{
|
||||
if ($style->numFmt instanceof SimpleXMLElement) {
|
||||
self::readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
|
||||
} else {
|
||||
$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->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): void
|
||||
{
|
||||
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): void
|
||||
{
|
||||
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 $array[$key] ?? null;
|
||||
}
|
||||
}
|
||||
93
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
vendored
Normal file
93
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||
|
||||
class Theme
|
||||
{
|
||||
/**
|
||||
* Theme Name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $themeName;
|
||||
|
||||
/**
|
||||
* Colour Scheme Name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $colourSchemeName;
|
||||
|
||||
/**
|
||||
* Colour Map.
|
||||
*
|
||||
* @var array of string
|
||||
*/
|
||||
private $colourMap;
|
||||
|
||||
/**
|
||||
* Create a new Theme.
|
||||
*
|
||||
* @param mixed $themeName
|
||||
* @param mixed $colourSchemeName
|
||||
* @param mixed $colourMap
|
||||
*/
|
||||
public function __construct($themeName, $colourSchemeName, $colourMap)
|
||||
{
|
||||
// Initialise values
|
||||
$this->themeName = $themeName;
|
||||
$this->colourSchemeName = $colourSchemeName;
|
||||
$this->colourMap = $colourMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Theme Name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getThemeName()
|
||||
{
|
||||
return $this->themeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get colour Scheme Name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getColourSchemeName()
|
||||
{
|
||||
return $this->colourSchemeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get colour Map Value by Position.
|
||||
*
|
||||
* @param mixed $index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getColourByIndex($index)
|
||||
{
|
||||
if (isset($this->colourMap[$index])) {
|
||||
return $this->colourMap[$index];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement PHP __clone to create a deep clone, not just a shallow copy.
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$vars = get_object_vars($this);
|
||||
foreach ($vars as $key => $value) {
|
||||
if ((is_object($value)) && ($key != '_parent')) {
|
||||
$this->$key = clone $value;
|
||||
} else {
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
858
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
vendored
Normal file
858
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
vendored
Normal file
@@ -0,0 +1,858 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\Document\Properties;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||
use PhpOffice\PhpSpreadsheet\Settings;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||
use PhpOffice\PhpSpreadsheet\Style\Font;
|
||||
use SimpleXMLElement;
|
||||
|
||||
/**
|
||||
* Reader for SpreadsheetML, the XML schema for Microsoft Office Excel 2003.
|
||||
*/
|
||||
class Xml extends BaseReader
|
||||
{
|
||||
/**
|
||||
* Formats.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $styles = [];
|
||||
|
||||
/**
|
||||
* Character set used in the file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $charSet = 'UTF-8';
|
||||
|
||||
/**
|
||||
* Create a new Excel2003XML Reader instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the current IReader read the file?
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canRead($pFilename)
|
||||
{
|
||||
// Office xmlns:o="urn:schemas-microsoft-com:office:office"
|
||||
// Excel xmlns:x="urn:schemas-microsoft-com:office:excel"
|
||||
// XML Spreadsheet xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
|
||||
// Spreadsheet component xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
|
||||
// XML schema xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
|
||||
// XML data type xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
|
||||
// MS-persist recordset xmlns:rs="urn:schemas-microsoft-com:rowset"
|
||||
// Rowset xmlns:z="#RowsetSchema"
|
||||
//
|
||||
|
||||
$signature = [
|
||||
'<?xml version="1.0"',
|
||||
'<?mso-application progid="Excel.Sheet"?>',
|
||||
];
|
||||
|
||||
// Open file
|
||||
$this->openFile($pFilename);
|
||||
$fileHandle = $this->fileHandle;
|
||||
|
||||
// Read sample data (first 2 KB will do)
|
||||
$data = fread($fileHandle, 2048);
|
||||
fclose($fileHandle);
|
||||
$data = str_replace("'", '"', $data); // fix headers with single quote
|
||||
|
||||
$valid = true;
|
||||
foreach ($signature as $match) {
|
||||
// every part of the signature must be present
|
||||
if (strpos($data, $match) === false) {
|
||||
$valid = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve charset encoding
|
||||
if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/um', $data, $matches)) {
|
||||
$this->charSet = strtoupper($matches[1]);
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the file is a valid SimpleXML.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return false|SimpleXMLElement
|
||||
*/
|
||||
public function trySimpleXMLLoadString($pFilename)
|
||||
{
|
||||
try {
|
||||
$xml = simplexml_load_string(
|
||||
$this->securityScanner->scan(file_get_contents($pFilename)),
|
||||
'SimpleXMLElement',
|
||||
Settings::getLibXmlLoaderOptions()
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
throw new Exception('Cannot load invalid XML file: ' . $pFilename, 0, $e);
|
||||
}
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listWorksheetNames($pFilename)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
if (!$this->canRead($pFilename)) {
|
||||
throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
|
||||
}
|
||||
|
||||
$worksheetNames = [];
|
||||
|
||||
$xml = $this->trySimpleXMLLoadString($pFilename);
|
||||
|
||||
$namespaces = $xml->getNamespaces(true);
|
||||
|
||||
$xml_ss = $xml->children($namespaces['ss']);
|
||||
foreach ($xml_ss->Worksheet as $worksheet) {
|
||||
$worksheet_ss = $worksheet->attributes($namespaces['ss']);
|
||||
$worksheetNames[] = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet);
|
||||
}
|
||||
|
||||
return $worksheetNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listWorksheetInfo($pFilename)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
|
||||
$worksheetInfo = [];
|
||||
|
||||
$xml = $this->trySimpleXMLLoadString($pFilename);
|
||||
|
||||
$namespaces = $xml->getNamespaces(true);
|
||||
|
||||
$worksheetID = 1;
|
||||
$xml_ss = $xml->children($namespaces['ss']);
|
||||
foreach ($xml_ss->Worksheet as $worksheet) {
|
||||
$worksheet_ss = $worksheet->attributes($namespaces['ss']);
|
||||
|
||||
$tmpInfo = [];
|
||||
$tmpInfo['worksheetName'] = '';
|
||||
$tmpInfo['lastColumnLetter'] = 'A';
|
||||
$tmpInfo['lastColumnIndex'] = 0;
|
||||
$tmpInfo['totalRows'] = 0;
|
||||
$tmpInfo['totalColumns'] = 0;
|
||||
|
||||
if (isset($worksheet_ss['Name'])) {
|
||||
$tmpInfo['worksheetName'] = (string) $worksheet_ss['Name'];
|
||||
} else {
|
||||
$tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}";
|
||||
}
|
||||
|
||||
if (isset($worksheet->Table->Row)) {
|
||||
$rowIndex = 0;
|
||||
|
||||
foreach ($worksheet->Table->Row as $rowData) {
|
||||
$columnIndex = 0;
|
||||
$rowHasData = false;
|
||||
|
||||
foreach ($rowData->Cell as $cell) {
|
||||
if (isset($cell->Data)) {
|
||||
$tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
|
||||
$rowHasData = true;
|
||||
}
|
||||
|
||||
++$columnIndex;
|
||||
}
|
||||
|
||||
++$rowIndex;
|
||||
|
||||
if ($rowHasData) {
|
||||
$tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
|
||||
$tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
|
||||
|
||||
$worksheetInfo[] = $tmpInfo;
|
||||
++$worksheetID;
|
||||
}
|
||||
|
||||
return $worksheetInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Spreadsheet from file.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function load($pFilename)
|
||||
{
|
||||
// Create new Spreadsheet
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$spreadsheet->removeSheetByIndex(0);
|
||||
|
||||
// Load into this instance
|
||||
return $this->loadIntoExisting($pFilename, $spreadsheet);
|
||||
}
|
||||
|
||||
private static function identifyFixedStyleValue($styleList, &$styleAttributeValue)
|
||||
{
|
||||
$styleAttributeValue = strtolower($styleAttributeValue);
|
||||
foreach ($styleList as $style) {
|
||||
if ($styleAttributeValue == strtolower($style)) {
|
||||
$styleAttributeValue = $style;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* pixel units to excel width units(units of 1/256th of a character width).
|
||||
*
|
||||
* @param float $pxs
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected static function pixel2WidthUnits($pxs)
|
||||
{
|
||||
$UNIT_OFFSET_MAP = [0, 36, 73, 109, 146, 182, 219];
|
||||
|
||||
$widthUnits = 256 * ($pxs / 7);
|
||||
$widthUnits += $UNIT_OFFSET_MAP[($pxs % 7)];
|
||||
|
||||
return $widthUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* excel width units(units of 1/256th of a character width) to pixel units.
|
||||
*
|
||||
* @param float $widthUnits
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected static function widthUnits2Pixel($widthUnits)
|
||||
{
|
||||
$pixels = ($widthUnits / 256) * 7;
|
||||
$offsetWidthUnits = $widthUnits % 256;
|
||||
|
||||
return $pixels + round($offsetWidthUnits / (256 / 7));
|
||||
}
|
||||
|
||||
protected static function hex2str($hex)
|
||||
{
|
||||
return chr(hexdec($hex[1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads from file into Spreadsheet instance.
|
||||
*
|
||||
* @param string $pFilename
|
||||
*
|
||||
* @return Spreadsheet
|
||||
*/
|
||||
public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)
|
||||
{
|
||||
File::assertFile($pFilename);
|
||||
if (!$this->canRead($pFilename)) {
|
||||
throw new Exception($pFilename . ' is an Invalid Spreadsheet file.');
|
||||
}
|
||||
|
||||
$xml = $this->trySimpleXMLLoadString($pFilename);
|
||||
|
||||
$namespaces = $xml->getNamespaces(true);
|
||||
|
||||
$docProps = $spreadsheet->getProperties();
|
||||
if (isset($xml->DocumentProperties[0])) {
|
||||
foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
|
||||
switch ($propertyName) {
|
||||
case 'Title':
|
||||
$docProps->setTitle(self::convertStringEncoding($propertyValue, $this->charSet));
|
||||
|
||||
break;
|
||||
case 'Subject':
|
||||
$docProps->setSubject(self::convertStringEncoding($propertyValue, $this->charSet));
|
||||
|
||||
break;
|
||||
case 'Author':
|
||||
$docProps->setCreator(self::convertStringEncoding($propertyValue, $this->charSet));
|
||||
|
||||
break;
|
||||
case 'Created':
|
||||
$creationDate = strtotime($propertyValue);
|
||||
$docProps->setCreated($creationDate);
|
||||
|
||||
break;
|
||||
case 'LastAuthor':
|
||||
$docProps->setLastModifiedBy(self::convertStringEncoding($propertyValue, $this->charSet));
|
||||
|
||||
break;
|
||||
case 'LastSaved':
|
||||
$lastSaveDate = strtotime($propertyValue);
|
||||
$docProps->setModified($lastSaveDate);
|
||||
|
||||
break;
|
||||
case 'Company':
|
||||
$docProps->setCompany(self::convertStringEncoding($propertyValue, $this->charSet));
|
||||
|
||||
break;
|
||||
case 'Category':
|
||||
$docProps->setCategory(self::convertStringEncoding($propertyValue, $this->charSet));
|
||||
|
||||
break;
|
||||
case 'Manager':
|
||||
$docProps->setManager(self::convertStringEncoding($propertyValue, $this->charSet));
|
||||
|
||||
break;
|
||||
case 'Keywords':
|
||||
$docProps->setKeywords(self::convertStringEncoding($propertyValue, $this->charSet));
|
||||
|
||||
break;
|
||||
case 'Description':
|
||||
$docProps->setDescription(self::convertStringEncoding($propertyValue, $this->charSet));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($xml->CustomDocumentProperties)) {
|
||||
foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
|
||||
$propertyAttributes = $propertyValue->attributes($namespaces['dt']);
|
||||
$propertyName = preg_replace_callback('/_x([0-9a-z]{4})_/', ['self', 'hex2str'], $propertyName);
|
||||
$propertyType = Properties::PROPERTY_TYPE_UNKNOWN;
|
||||
switch ((string) $propertyAttributes) {
|
||||
case 'string':
|
||||
$propertyType = Properties::PROPERTY_TYPE_STRING;
|
||||
$propertyValue = trim($propertyValue);
|
||||
|
||||
break;
|
||||
case 'boolean':
|
||||
$propertyType = Properties::PROPERTY_TYPE_BOOLEAN;
|
||||
$propertyValue = (bool) $propertyValue;
|
||||
|
||||
break;
|
||||
case 'integer':
|
||||
$propertyType = Properties::PROPERTY_TYPE_INTEGER;
|
||||
$propertyValue = (int) $propertyValue;
|
||||
|
||||
break;
|
||||
case 'float':
|
||||
$propertyType = Properties::PROPERTY_TYPE_FLOAT;
|
||||
$propertyValue = (float) $propertyValue;
|
||||
|
||||
break;
|
||||
case 'dateTime.tz':
|
||||
$propertyType = Properties::PROPERTY_TYPE_DATE;
|
||||
$propertyValue = strtotime(trim($propertyValue));
|
||||
|
||||
break;
|
||||
}
|
||||
$docProps->setCustomProperty($propertyName, $propertyValue, $propertyType);
|
||||
}
|
||||
}
|
||||
|
||||
$this->parseStyles($xml, $namespaces);
|
||||
|
||||
$worksheetID = 0;
|
||||
$xml_ss = $xml->children($namespaces['ss']);
|
||||
|
||||
foreach ($xml_ss->Worksheet as $worksheet) {
|
||||
$worksheet_ss = $worksheet->attributes($namespaces['ss']);
|
||||
|
||||
if ((isset($this->loadSheetsOnly)) && (isset($worksheet_ss['Name'])) &&
|
||||
(!in_array($worksheet_ss['Name'], $this->loadSheetsOnly))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new Worksheet
|
||||
$spreadsheet->createSheet();
|
||||
$spreadsheet->setActiveSheetIndex($worksheetID);
|
||||
if (isset($worksheet_ss['Name'])) {
|
||||
$worksheetName = self::convertStringEncoding((string) $worksheet_ss['Name'], $this->charSet);
|
||||
// Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
|
||||
// formula cells... during the load, all formulae should be correct, and we're simply bringing
|
||||
// the worksheet name in line with the formula, not the reverse
|
||||
$spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
|
||||
}
|
||||
|
||||
$columnID = 'A';
|
||||
if (isset($worksheet->Table->Column)) {
|
||||
foreach ($worksheet->Table->Column as $columnData) {
|
||||
$columnData_ss = $columnData->attributes($namespaces['ss']);
|
||||
if (isset($columnData_ss['Index'])) {
|
||||
$columnID = Coordinate::stringFromColumnIndex((int) $columnData_ss['Index']);
|
||||
}
|
||||
if (isset($columnData_ss['Width'])) {
|
||||
$columnWidth = $columnData_ss['Width'];
|
||||
$spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
|
||||
}
|
||||
++$columnID;
|
||||
}
|
||||
}
|
||||
|
||||
$rowID = 1;
|
||||
if (isset($worksheet->Table->Row)) {
|
||||
$additionalMergedCells = 0;
|
||||
foreach ($worksheet->Table->Row as $rowData) {
|
||||
$rowHasData = false;
|
||||
$row_ss = $rowData->attributes($namespaces['ss']);
|
||||
if (isset($row_ss['Index'])) {
|
||||
$rowID = (int) $row_ss['Index'];
|
||||
}
|
||||
|
||||
$columnID = 'A';
|
||||
foreach ($rowData->Cell as $cell) {
|
||||
$cell_ss = $cell->attributes($namespaces['ss']);
|
||||
if (isset($cell_ss['Index'])) {
|
||||
$columnID = Coordinate::stringFromColumnIndex((int) $cell_ss['Index']);
|
||||
}
|
||||
$cellRange = $columnID . $rowID;
|
||||
|
||||
if ($this->getReadFilter() !== null) {
|
||||
if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
|
||||
++$columnID;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cell_ss['HRef'])) {
|
||||
$spreadsheet->getActiveSheet()->getCell($cellRange)->getHyperlink()->setUrl($cell_ss['HRef']);
|
||||
}
|
||||
|
||||
if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) {
|
||||
$columnTo = $columnID;
|
||||
if (isset($cell_ss['MergeAcross'])) {
|
||||
$additionalMergedCells += (int) $cell_ss['MergeAcross'];
|
||||
$columnTo = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($columnID) + $cell_ss['MergeAcross']);
|
||||
}
|
||||
$rowTo = $rowID;
|
||||
if (isset($cell_ss['MergeDown'])) {
|
||||
$rowTo = $rowTo + $cell_ss['MergeDown'];
|
||||
}
|
||||
$cellRange .= ':' . $columnTo . $rowTo;
|
||||
$spreadsheet->getActiveSheet()->mergeCells($cellRange);
|
||||
}
|
||||
|
||||
$cellIsSet = $hasCalculatedValue = false;
|
||||
$cellDataFormula = '';
|
||||
if (isset($cell_ss['Formula'])) {
|
||||
$cellDataFormula = $cell_ss['Formula'];
|
||||
$hasCalculatedValue = true;
|
||||
}
|
||||
if (isset($cell->Data)) {
|
||||
$cellValue = $cellData = $cell->Data;
|
||||
$type = DataType::TYPE_NULL;
|
||||
$cellData_ss = $cellData->attributes($namespaces['ss']);
|
||||
if (isset($cellData_ss['Type'])) {
|
||||
$cellDataType = $cellData_ss['Type'];
|
||||
switch ($cellDataType) {
|
||||
/*
|
||||
const TYPE_STRING = 's';
|
||||
const TYPE_FORMULA = 'f';
|
||||
const TYPE_NUMERIC = 'n';
|
||||
const TYPE_BOOL = 'b';
|
||||
const TYPE_NULL = 'null';
|
||||
const TYPE_INLINE = 'inlineStr';
|
||||
const TYPE_ERROR = 'e';
|
||||
*/
|
||||
case 'String':
|
||||
$cellValue = self::convertStringEncoding($cellValue, $this->charSet);
|
||||
$type = DataType::TYPE_STRING;
|
||||
|
||||
break;
|
||||
case 'Number':
|
||||
$type = DataType::TYPE_NUMERIC;
|
||||
$cellValue = (float) $cellValue;
|
||||
if (floor($cellValue) == $cellValue) {
|
||||
$cellValue = (int) $cellValue;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Boolean':
|
||||
$type = DataType::TYPE_BOOL;
|
||||
$cellValue = ($cellValue != 0);
|
||||
|
||||
break;
|
||||
case 'DateTime':
|
||||
$type = DataType::TYPE_NUMERIC;
|
||||
$cellValue = Date::PHPToExcel(strtotime($cellValue));
|
||||
|
||||
break;
|
||||
case 'Error':
|
||||
$type = DataType::TYPE_ERROR;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasCalculatedValue) {
|
||||
$type = DataType::TYPE_FORMULA;
|
||||
$columnNumber = Coordinate::columnIndexFromString($columnID);
|
||||
if (substr($cellDataFormula, 0, 3) == 'of:') {
|
||||
$cellDataFormula = substr($cellDataFormula, 3);
|
||||
$temp = explode('"', $cellDataFormula);
|
||||
$key = false;
|
||||
foreach ($temp as &$value) {
|
||||
// Only replace in alternate array entries (i.e. non-quoted blocks)
|
||||
if ($key = !$key) {
|
||||
$value = str_replace(['[.', '.', ']'], '', $value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Convert R1C1 style references to A1 style references (but only when not quoted)
|
||||
$temp = explode('"', $cellDataFormula);
|
||||
$key = false;
|
||||
foreach ($temp as &$value) {
|
||||
// Only replace in alternate array entries (i.e. non-quoted blocks)
|
||||
if ($key = !$key) {
|
||||
preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
|
||||
// Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
|
||||
// through the formula from left to right. Reversing means that we work right to left.through
|
||||
// the formula
|
||||
$cellReferences = array_reverse($cellReferences);
|
||||
// Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
|
||||
// then modify the formula to use that new reference
|
||||
foreach ($cellReferences as $cellReference) {
|
||||
$rowReference = $cellReference[2][0];
|
||||
// Empty R reference is the current row
|
||||
if ($rowReference == '') {
|
||||
$rowReference = $rowID;
|
||||
}
|
||||
// Bracketed R references are relative to the current row
|
||||
if ($rowReference[0] == '[') {
|
||||
$rowReference = $rowID + trim($rowReference, '[]');
|
||||
}
|
||||
$columnReference = $cellReference[4][0];
|
||||
// Empty C reference is the current column
|
||||
if ($columnReference == '') {
|
||||
$columnReference = $columnNumber;
|
||||
}
|
||||
// Bracketed C references are relative to the current column
|
||||
if ($columnReference[0] == '[') {
|
||||
$columnReference = $columnNumber + trim($columnReference, '[]');
|
||||
}
|
||||
$A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference;
|
||||
$value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($value);
|
||||
// Then rebuild the formula string
|
||||
$cellDataFormula = implode('"', $temp);
|
||||
}
|
||||
|
||||
$spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type);
|
||||
if ($hasCalculatedValue) {
|
||||
$spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue);
|
||||
}
|
||||
$cellIsSet = $rowHasData = true;
|
||||
}
|
||||
|
||||
if (isset($cell->Comment)) {
|
||||
$commentAttributes = $cell->Comment->attributes($namespaces['ss']);
|
||||
$author = 'unknown';
|
||||
if (isset($commentAttributes->Author)) {
|
||||
$author = (string) $commentAttributes->Author;
|
||||
}
|
||||
$node = $cell->Comment->Data->asXML();
|
||||
$annotation = strip_tags($node);
|
||||
$spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setAuthor(self::convertStringEncoding($author, $this->charSet))->setText($this->parseRichText($annotation));
|
||||
}
|
||||
|
||||
if (($cellIsSet) && (isset($cell_ss['StyleID']))) {
|
||||
$style = (string) $cell_ss['StyleID'];
|
||||
if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) {
|
||||
if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) {
|
||||
$spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null);
|
||||
}
|
||||
$spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styles[$style]);
|
||||
}
|
||||
}
|
||||
++$columnID;
|
||||
while ($additionalMergedCells > 0) {
|
||||
++$columnID;
|
||||
--$additionalMergedCells;
|
||||
}
|
||||
}
|
||||
|
||||
if ($rowHasData) {
|
||||
if (isset($row_ss['Height'])) {
|
||||
$rowHeight = $row_ss['Height'];
|
||||
$spreadsheet->getActiveSheet()->getRowDimension($rowID)->setRowHeight($rowHeight);
|
||||
}
|
||||
}
|
||||
|
||||
++$rowID;
|
||||
}
|
||||
}
|
||||
++$worksheetID;
|
||||
}
|
||||
|
||||
// Return
|
||||
return $spreadsheet;
|
||||
}
|
||||
|
||||
protected static function convertStringEncoding($string, $charset)
|
||||
{
|
||||
if ($charset != 'UTF-8') {
|
||||
return StringHelper::convertEncoding($string, 'UTF-8', $charset);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
protected function parseRichText($is)
|
||||
{
|
||||
$value = new RichText();
|
||||
|
||||
$value->createText(self::convertStringEncoding($is, $this->charSet));
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function parseStyles(SimpleXMLElement $xml, array $namespaces): void
|
||||
{
|
||||
if (!isset($xml->Styles)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($xml->Styles[0] as $style) {
|
||||
$style_ss = $style->attributes($namespaces['ss']);
|
||||
$styleID = (string) $style_ss['ID'];
|
||||
$this->styles[$styleID] = (isset($this->styles['Default'])) ? $this->styles['Default'] : [];
|
||||
foreach ($style as $styleType => $styleData) {
|
||||
$styleAttributes = $styleData->attributes($namespaces['ss']);
|
||||
switch ($styleType) {
|
||||
case 'Alignment':
|
||||
$this->parseStyleAlignment($styleID, $styleAttributes);
|
||||
|
||||
break;
|
||||
case 'Borders':
|
||||
$this->parseStyleBorders($styleID, $styleData, $namespaces);
|
||||
|
||||
break;
|
||||
case 'Font':
|
||||
$this->parseStyleFont($styleID, $styleAttributes);
|
||||
|
||||
break;
|
||||
case 'Interior':
|
||||
$this->parseStyleInterior($styleID, $styleAttributes);
|
||||
|
||||
break;
|
||||
case 'NumberFormat':
|
||||
$this->parseStyleNumberFormat($styleID, $styleAttributes);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $styleID
|
||||
*/
|
||||
private function parseStyleAlignment($styleID, SimpleXMLElement $styleAttributes): void
|
||||
{
|
||||
$verticalAlignmentStyles = [
|
||||
Alignment::VERTICAL_BOTTOM,
|
||||
Alignment::VERTICAL_TOP,
|
||||
Alignment::VERTICAL_CENTER,
|
||||
Alignment::VERTICAL_JUSTIFY,
|
||||
];
|
||||
$horizontalAlignmentStyles = [
|
||||
Alignment::HORIZONTAL_GENERAL,
|
||||
Alignment::HORIZONTAL_LEFT,
|
||||
Alignment::HORIZONTAL_RIGHT,
|
||||
Alignment::HORIZONTAL_CENTER,
|
||||
Alignment::HORIZONTAL_CENTER_CONTINUOUS,
|
||||
Alignment::HORIZONTAL_JUSTIFY,
|
||||
];
|
||||
|
||||
foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
|
||||
$styleAttributeValue = (string) $styleAttributeValue;
|
||||
switch ($styleAttributeKey) {
|
||||
case 'Vertical':
|
||||
if (self::identifyFixedStyleValue($verticalAlignmentStyles, $styleAttributeValue)) {
|
||||
$this->styles[$styleID]['alignment']['vertical'] = $styleAttributeValue;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Horizontal':
|
||||
if (self::identifyFixedStyleValue($horizontalAlignmentStyles, $styleAttributeValue)) {
|
||||
$this->styles[$styleID]['alignment']['horizontal'] = $styleAttributeValue;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'WrapText':
|
||||
$this->styles[$styleID]['alignment']['wrapText'] = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $styleID
|
||||
*/
|
||||
private function parseStyleBorders($styleID, SimpleXMLElement $styleData, array $namespaces): void
|
||||
{
|
||||
foreach ($styleData->Border as $borderStyle) {
|
||||
$borderAttributes = $borderStyle->attributes($namespaces['ss']);
|
||||
$thisBorder = [];
|
||||
foreach ($borderAttributes as $borderStyleKey => $borderStyleValue) {
|
||||
switch ($borderStyleKey) {
|
||||
case 'LineStyle':
|
||||
$thisBorder['borderStyle'] = Border::BORDER_MEDIUM;
|
||||
|
||||
break;
|
||||
case 'Weight':
|
||||
break;
|
||||
case 'Position':
|
||||
$borderPosition = strtolower($borderStyleValue);
|
||||
|
||||
break;
|
||||
case 'Color':
|
||||
$borderColour = substr($borderStyleValue, 1);
|
||||
$thisBorder['color']['rgb'] = $borderColour;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!empty($thisBorder)) {
|
||||
if (($borderPosition == 'left') || ($borderPosition == 'right') || ($borderPosition == 'top') || ($borderPosition == 'bottom')) {
|
||||
$this->styles[$styleID]['borders'][$borderPosition] = $thisBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $styleID
|
||||
*/
|
||||
private function parseStyleFont($styleID, SimpleXMLElement $styleAttributes): void
|
||||
{
|
||||
$underlineStyles = [
|
||||
Font::UNDERLINE_NONE,
|
||||
Font::UNDERLINE_DOUBLE,
|
||||
Font::UNDERLINE_DOUBLEACCOUNTING,
|
||||
Font::UNDERLINE_SINGLE,
|
||||
Font::UNDERLINE_SINGLEACCOUNTING,
|
||||
];
|
||||
|
||||
foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
|
||||
$styleAttributeValue = (string) $styleAttributeValue;
|
||||
switch ($styleAttributeKey) {
|
||||
case 'FontName':
|
||||
$this->styles[$styleID]['font']['name'] = $styleAttributeValue;
|
||||
|
||||
break;
|
||||
case 'Size':
|
||||
$this->styles[$styleID]['font']['size'] = $styleAttributeValue;
|
||||
|
||||
break;
|
||||
case 'Color':
|
||||
$this->styles[$styleID]['font']['color']['rgb'] = substr($styleAttributeValue, 1);
|
||||
|
||||
break;
|
||||
case 'Bold':
|
||||
$this->styles[$styleID]['font']['bold'] = true;
|
||||
|
||||
break;
|
||||
case 'Italic':
|
||||
$this->styles[$styleID]['font']['italic'] = true;
|
||||
|
||||
break;
|
||||
case 'Underline':
|
||||
if (self::identifyFixedStyleValue($underlineStyles, $styleAttributeValue)) {
|
||||
$this->styles[$styleID]['font']['underline'] = $styleAttributeValue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $styleID
|
||||
*/
|
||||
private function parseStyleInterior($styleID, SimpleXMLElement $styleAttributes): void
|
||||
{
|
||||
foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
|
||||
switch ($styleAttributeKey) {
|
||||
case 'Color':
|
||||
$this->styles[$styleID]['fill']['color']['rgb'] = substr($styleAttributeValue, 1);
|
||||
|
||||
break;
|
||||
case 'Pattern':
|
||||
$this->styles[$styleID]['fill']['fillType'] = strtolower($styleAttributeValue);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $styleID
|
||||
*/
|
||||
private function parseStyleNumberFormat($styleID, SimpleXMLElement $styleAttributes): void
|
||||
{
|
||||
$fromFormats = ['\-', '\ '];
|
||||
$toFormats = ['-', ' '];
|
||||
|
||||
foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) {
|
||||
$styleAttributeValue = str_replace($fromFormats, $toFormats, $styleAttributeValue);
|
||||
switch ($styleAttributeValue) {
|
||||
case 'Short Date':
|
||||
$styleAttributeValue = 'dd/mm/yyyy';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($styleAttributeValue > '') {
|
||||
$this->styles[$styleID]['numberFormat']['formatCode'] = $styleAttributeValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user