🍲dfcv🏰dd⋉(● ∸ ●)⋊@% PNG %k25u25%fgd5n! PNG %k25u25%fgd5n!phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache3.php 0000644 00000004136 15242753725 0021625 0 ustar 00 cache = [];
return true;
}
/**
* @param string $key
*/
public function delete($key): bool
{
unset($this->cache[$key]);
return true;
}
/**
* @param iterable $keys
*/
public function deleteMultiple($keys): bool
{
foreach ($keys as $key) {
$this->delete($key);
}
return true;
}
/**
* @param string $key
* @param mixed $default
*/
public function get($key, $default = null): mixed
{
if ($this->has($key)) {
return $this->cache[$key];
}
return $default;
}
/**
* @param iterable $keys
* @param mixed $default
*/
public function getMultiple($keys, $default = null): iterable
{
$results = [];
foreach ($keys as $key) {
$results[$key] = $this->get($key, $default);
}
return $results;
}
/**
* @param string $key
*/
public function has($key): bool
{
return array_key_exists($key, $this->cache);
}
/**
* @param string $key
* @param mixed $value
* @param null|DateInterval|int $ttl
*/
public function set($key, $value, $ttl = null): bool
{
$this->cache[$key] = $value;
return true;
}
/**
* @param iterable $values
* @param null|DateInterval|int $ttl
*/
public function setMultiple($values, $ttl = null): bool
{
foreach ($values as $key => $value) {
$this->set($key, $value);
}
return true;
}
}
phpspreadsheet/src/PhpSpreadsheet/Collection/Memory/SimpleCache1.php 0000644 00000004417 15242753725 0021625 0 ustar 00 cache = [];
return true;
}
/**
* @param string $key
*
* @return bool
*/
public function delete($key)
{
unset($this->cache[$key]);
return true;
}
/**
* @param iterable $keys
*
* @return bool
*/
public function deleteMultiple($keys)
{
foreach ($keys as $key) {
$this->delete($key);
}
return true;
}
/**
* @param string $key
* @param mixed $default
*
* @return mixed
*/
public function get($key, $default = null)
{
if ($this->has($key)) {
return $this->cache[$key];
}
return $default;
}
/**
* @param iterable $keys
* @param mixed $default
*
* @return iterable
*/
public function getMultiple($keys, $default = null)
{
$results = [];
foreach ($keys as $key) {
$results[$key] = $this->get($key, $default);
}
return $results;
}
/**
* @param string $key
*
* @return bool
*/
public function has($key)
{
return array_key_exists($key, $this->cache);
}
/**
* @param string $key
* @param mixed $value
* @param null|DateInterval|int $ttl
*
* @return bool
*/
public function set($key, $value, $ttl = null)
{
$this->cache[$key] = $value;
return true;
}
/**
* @param iterable $values
* @param null|DateInterval|int $ttl
*
* @return bool
*/
public function setMultiple($values, $ttl = null)
{
foreach ($values as $key => $value) {
$this->set($key, $value);
}
return true;
}
}
phpspreadsheet/src/PhpSpreadsheet/Collection/CellsFactory.php 0000644 00000000714 15242753725 0020505 0 ustar 00 parent = $parent;
$this->cache = $cache;
$this->cachePrefix = $this->getUniqueID();
}
/**
* Return the parent worksheet for this cell collection.
*
* @return null|Worksheet
*/
public function getParent()
{
return $this->parent;
}
/**
* Whether the collection holds a cell for the given coordinate.
*
* @param string $cellCoordinate Coordinate of the cell to check
*/
public function has($cellCoordinate): bool
{
return ($cellCoordinate === $this->currentCoordinate) || isset($this->index[$cellCoordinate]);
}
/**
* Add or update a cell in the collection.
*
* @param Cell $cell Cell to update
*/
public function update(Cell $cell): Cell
{
return $this->add($cell->getCoordinate(), $cell);
}
/**
* Delete a cell in cache identified by coordinate.
*
* @param string $cellCoordinate Coordinate of the cell to delete
*/
public function delete($cellCoordinate): void
{
if ($cellCoordinate === $this->currentCoordinate && $this->currentCell !== null) {
$this->currentCell->detach();
$this->currentCoordinate = null;
$this->currentCell = null;
$this->currentCellIsDirty = false;
}
unset($this->index[$cellCoordinate]);
// Delete the entry from cache
$this->cache->delete($this->cachePrefix . $cellCoordinate);
}
/**
* Get a list of all cell coordinates currently held in the collection.
*
* @return string[]
*/
public function getCoordinates()
{
return array_keys($this->index);
}
/**
* Get a sorted list of all cell coordinates currently held in the collection by row and column.
*
* @return string[]
*/
public function getSortedCoordinates()
{
asort($this->index);
return array_keys($this->index);
}
/**
* Return the cell coordinate of the currently active cell object.
*
* @return null|string
*/
public function getCurrentCoordinate()
{
return $this->currentCoordinate;
}
/**
* Return the column coordinate of the currently active cell object.
*/
public function getCurrentColumn(): string
{
$column = 0;
$row = '';
sscanf($this->currentCoordinate ?? '', '%[A-Z]%d', $column, $row);
return (string) $column;
}
/**
* Return the row coordinate of the currently active cell object.
*/
public function getCurrentRow(): int
{
$column = 0;
$row = '';
sscanf($this->currentCoordinate ?? '', '%[A-Z]%d', $column, $row);
return (int) $row;
}
/**
* Get highest worksheet column and highest row that have cell records.
*
* @return array Highest column name and highest row number
*/
public function getHighestRowAndColumn()
{
// Lookup highest column and highest row
$maxRow = $maxColumn = 1;
foreach ($this->index as $coordinate) {
$row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1;
$maxRow = ($maxRow > $row) ? $maxRow : $row;
$column = $coordinate % self::MAX_COLUMN_ID;
$maxColumn = ($maxColumn > $column) ? $maxColumn : $column;
}
return [
'row' => $maxRow,
'column' => Coordinate::stringFromColumnIndex($maxColumn),
];
}
/**
* Get highest worksheet column.
*
* @param null|int|string $row Return the highest column for the specified row,
* or the highest column of any row if no row number is passed
*
* @return string Highest column name
*/
public function getHighestColumn($row = null)
{
if ($row === null) {
return $this->getHighestRowAndColumn()['column'];
}
$row = (int) $row;
if ($row <= 0) {
throw new PhpSpreadsheetException('Row number must be a positive integer');
}
$maxColumn = 1;
$toRow = $row * self::MAX_COLUMN_ID;
$fromRow = --$row * self::MAX_COLUMN_ID;
foreach ($this->index as $coordinate) {
if ($coordinate < $fromRow || $coordinate >= $toRow) {
continue;
}
$column = $coordinate % self::MAX_COLUMN_ID;
$maxColumn = $maxColumn > $column ? $maxColumn : $column;
}
return Coordinate::stringFromColumnIndex($maxColumn);
}
/**
* Get highest worksheet row.
*
* @param null|string $column Return the highest row for the specified column,
* or the highest row of any column if no column letter is passed
*
* @return int Highest row number
*/
public function getHighestRow($column = null)
{
if ($column === null) {
return $this->getHighestRowAndColumn()['row'];
}
$maxRow = 1;
$columnIndex = Coordinate::columnIndexFromString($column);
foreach ($this->index as $coordinate) {
if ($coordinate % self::MAX_COLUMN_ID !== $columnIndex) {
continue;
}
$row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1;
$maxRow = ($maxRow > $row) ? $maxRow : $row;
}
return $maxRow;
}
/**
* Generate a unique ID for cache referencing.
*
* @return string Unique Reference
*/
private function getUniqueID()
{
$cacheType = Settings::getCache();
return ($cacheType instanceof Memory\SimpleCache1 || $cacheType instanceof Memory\SimpleCache3)
? random_bytes(7) . ':'
: uniqid('phpspreadsheet.', true) . '.';
}
/**
* Clone the cell collection.
*
* @return self
*/
public function cloneCellCollection(Worksheet $worksheet)
{
$this->storeCurrentCell();
$newCollection = clone $this;
$newCollection->parent = $worksheet;
$newCollection->cachePrefix = $newCollection->getUniqueID();
foreach ($this->index as $key => $value) {
$newCollection->index[$key] = $value;
$stored = $newCollection->cache->set(
$newCollection->cachePrefix . $key,
clone $this->cache->get($this->cachePrefix . $key)
);
if ($stored === false) {
$this->destructIfNeeded($newCollection, 'Failed to copy cells in cache');
}
}
return $newCollection;
}
/**
* Remove a row, deleting all cells in that row.
*
* @param int|string $row Row number to remove
*/
public function removeRow($row): void
{
$this->storeCurrentCell();
$row = (int) $row;
if ($row <= 0) {
throw new PhpSpreadsheetException('Row number must be a positive integer');
}
$toRow = $row * self::MAX_COLUMN_ID;
$fromRow = --$row * self::MAX_COLUMN_ID;
foreach ($this->index as $coordinate) {
if ($coordinate >= $fromRow && $coordinate < $toRow) {
$row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1;
$column = Coordinate::stringFromColumnIndex($coordinate % self::MAX_COLUMN_ID);
$this->delete("{$column}{$row}");
}
}
}
/**
* Remove a column, deleting all cells in that column.
*
* @param string $column Column ID to remove
*/
public function removeColumn($column): void
{
$this->storeCurrentCell();
$columnIndex = Coordinate::columnIndexFromString($column);
foreach ($this->index as $coordinate) {
if ($coordinate % self::MAX_COLUMN_ID === $columnIndex) {
$row = (int) floor($coordinate / self::MAX_COLUMN_ID) + 1;
$column = Coordinate::stringFromColumnIndex($coordinate % self::MAX_COLUMN_ID);
$this->delete("{$column}{$row}");
}
}
}
/**
* Store cell data in cache for the current cell object if it's "dirty",
* and the 'nullify' the current cell object.
*/
private function storeCurrentCell(): void
{
if ($this->currentCellIsDirty && isset($this->currentCoordinate, $this->currentCell)) {
$this->currentCell->/** @scrutinizer ignore-call */ detach();
$stored = $this->cache->set($this->cachePrefix . $this->currentCoordinate, $this->currentCell);
if ($stored === false) {
$this->destructIfNeeded($this, "Failed to store cell {$this->currentCoordinate} in cache");
}
$this->currentCellIsDirty = false;
}
$this->currentCoordinate = null;
$this->currentCell = null;
}
private function destructIfNeeded(self $cells, string $message): void
{
$cells->__destruct();
throw new PhpSpreadsheetException($message);
}
/**
* Add or update a cell identified by its coordinate into the collection.
*
* @param string $cellCoordinate Coordinate of the cell to update
* @param Cell $cell Cell to update
*
* @return Cell
*/
public function add($cellCoordinate, Cell $cell)
{
if ($cellCoordinate !== $this->currentCoordinate) {
$this->storeCurrentCell();
}
$column = 0;
$row = '';
sscanf($cellCoordinate, '%[A-Z]%d', $column, $row);
$this->index[$cellCoordinate] = (--$row * self::MAX_COLUMN_ID) + Coordinate::columnIndexFromString((string) $column);
$this->currentCoordinate = $cellCoordinate;
$this->currentCell = $cell;
$this->currentCellIsDirty = true;
return $cell;
}
/**
* Get cell at a specific coordinate.
*
* @param string $cellCoordinate Coordinate of the cell
*
* @return null|Cell Cell that was found, or null if not found
*/
public function get($cellCoordinate)
{
if ($cellCoordinate === $this->currentCoordinate) {
return $this->currentCell;
}
$this->storeCurrentCell();
// Return null if requested entry doesn't exist in collection
if ($this->has($cellCoordinate) === false) {
return null;
}
// Check if the entry that has been requested actually exists in the cache
$cell = $this->cache->get($this->cachePrefix . $cellCoordinate);
if ($cell === null) {
throw new PhpSpreadsheetException("Cell entry {$cellCoordinate} no longer exists in cache. This probably means that the cache was cleared by someone else.");
}
// Set current entry to the requested entry
$this->currentCoordinate = $cellCoordinate;
$this->currentCell = $cell;
// Re-attach this as the cell's parent
$this->currentCell->attach($this);
// Return requested entry
return $this->currentCell;
}
/**
* Clear the cell collection and disconnect from our parent.
*/
public function unsetWorksheetCells(): void
{
if ($this->currentCell !== null) {
$this->currentCell->detach();
$this->currentCell = null;
$this->currentCoordinate = null;
}
// Flush the cache
$this->__destruct();
$this->index = [];
// detach ourself from the worksheet, so that it can then delete this object successfully
$this->parent = null;
}
/**
* Destroy this cell collection.
*/
public function __destruct()
{
$this->cache->deleteMultiple($this->getAllCacheKeys());
}
/**
* Returns all known cache keys.
*
* @return Generator|string[]
*/
private function getAllCacheKeys()
{
foreach ($this->index as $coordinate => $value) {
yield $this->cachePrefix . $coordinate;
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php 0000644 00000022647 15242753725 0016471 0 ustar 00 getScriptFilename() === 'index';
}
/**
* Return the page title.
*
* @return string
*/
public function getPageTitle()
{
return $this->isIndex() ? 'PHPSpreadsheet' : $this->getScriptFilename();
}
/**
* Return the page heading.
*
* @return string
*/
public function getPageHeading()
{
return $this->isIndex() ? '' : '
' . str_replace('_', ' ', $this->getScriptFilename()) . '
';
}
/**
* Returns an array of all known samples.
*
* @return string[][] [$name => $path]
*/
public function getSamples()
{
// Populate samples
$baseDir = realpath(__DIR__ . '/../../../samples');
if ($baseDir === false) {
// @codeCoverageIgnoreStart
throw new RuntimeException('realpath returned false');
// @codeCoverageIgnoreEnd
}
$directory = new RecursiveDirectoryIterator($baseDir);
$iterator = new RecursiveIteratorIterator($directory);
$regex = new RegexIterator($iterator, '/^.+\.php$/', RecursiveRegexIterator::GET_MATCH);
$files = [];
foreach ($regex as $file) {
$file = str_replace(str_replace('\\', '/', $baseDir) . '/', '', str_replace('\\', '/', $file[0]));
if (is_array($file)) {
// @codeCoverageIgnoreStart
throw new RuntimeException('str_replace returned array');
// @codeCoverageIgnoreEnd
}
$info = pathinfo($file);
$category = str_replace('_', ' ', $info['dirname'] ?? '');
$name = str_replace('_', ' ', (string) preg_replace('/(|\.php)/', '', $info['filename']));
if (!in_array($category, ['.', 'boostrap', 'templates'])) {
if (!isset($files[$category])) {
$files[$category] = [];
}
$files[$category][$name] = $file;
}
}
// Sort everything
ksort($files);
foreach ($files as &$f) {
asort($f);
}
return $files;
}
/**
* Write documents.
*
* @param string $filename
* @param string[] $writers
*/
public function write(Spreadsheet $spreadsheet, $filename, array $writers = ['Xlsx', 'Xls'], bool $withCharts = false, ?callable $writerCallback = null): void
{
// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$spreadsheet->setActiveSheetIndex(0);
// Write documents
foreach ($writers as $writerType) {
$path = $this->getFilename($filename, mb_strtolower($writerType));
$writer = IOFactory::createWriter($spreadsheet, $writerType);
$writer->setIncludeCharts($withCharts);
if ($writerCallback !== null) {
$writerCallback($writer);
}
$callStartTime = microtime(true);
if (PHP_VERSION_ID >= 80400 && $writer instanceof Dompdf) {
@$writer->save($path);
} else {
$writer->save($path);
}
$this->logWrite($writer, $path, /** @scrutinizer ignore-type */ $callStartTime);
if ($this->isCli() === false) {
echo 'Download ' . basename($path) . '
';
}
}
$this->logEndingNotes();
}
protected function isDirOrMkdir(string $folder): bool
{
return \is_dir($folder) || \mkdir($folder);
}
/**
* Returns the temporary directory and make sure it exists.
*
* @return string
*/
public function getTemporaryFolder()
{
$tempFolder = sys_get_temp_dir() . '/phpspreadsheet';
if (!$this->isDirOrMkdir($tempFolder)) {
throw new RuntimeException(sprintf('Directory "%s" was not created', $tempFolder));
}
return $tempFolder;
}
/**
* Returns the filename that should be used for sample output.
*
* @param string $filename
* @param string $extension
*/
public function getFilename($filename, $extension = 'xlsx'): string
{
$originalExtension = pathinfo($filename, PATHINFO_EXTENSION);
return $this->getTemporaryFolder() . '/' . str_replace('.' . /** @scrutinizer ignore-type */ $originalExtension, '.' . $extension, basename($filename));
}
/**
* Return a random temporary file name.
*
* @param string $extension
*
* @return string
*/
public function getTemporaryFilename($extension = 'xlsx')
{
$temporaryFilename = tempnam($this->getTemporaryFolder(), 'phpspreadsheet-');
if ($temporaryFilename === false) {
// @codeCoverageIgnoreStart
throw new RuntimeException('tempnam returned false');
// @codeCoverageIgnoreEnd
}
unlink($temporaryFilename);
return $temporaryFilename . '.' . $extension;
}
public function log(string $message): void
{
$eol = $this->isCli() ? PHP_EOL : '
';
echo($this->isCli() ? date('H:i:s ') : '') . $message . $eol;
}
public function renderChart(Chart $chart, string $fileName): void
{
if ($this->isCli() === true) {
return;
}
Settings::setChartRenderer(\PhpOffice\PhpSpreadsheet\Chart\Renderer\MtJpGraphRenderer::class);
$fileName = $this->getFilename($fileName, 'png');
try {
$chart->render($fileName);
$this->log('Rendered image: ' . $fileName);
$imageData = file_get_contents($fileName);
if ($imageData !== false) {
echo '';
}
} catch (Throwable $e) {
$this->log('Error rendering chart: ' . $e->getMessage() . PHP_EOL);
}
}
public function titles(string $category, string $functionName, ?string $description = null): void
{
$this->log(sprintf('%s Functions:', $category));
$description === null
? $this->log(sprintf('Function: %s()', rtrim($functionName, '()')))
: $this->log(sprintf('Function: %s() - %s.', rtrim($functionName, '()'), rtrim($description, '.')));
}
public function displayGrid(array $matrix): void
{
$renderer = new TextGrid($matrix, $this->isCli());
echo $renderer->render();
}
public function logCalculationResult(
Worksheet $worksheet,
string $functionName,
string $formulaCell,
?string $descriptionCell = null
): void {
if ($descriptionCell !== null) {
$this->log($worksheet->getCell($descriptionCell)->getValue());
}
$this->log($worksheet->getCell($formulaCell)->getValue());
$this->log(sprintf('%s() Result is ', $functionName) . $worksheet->getCell($formulaCell)->getCalculatedValue());
}
/**
* Log ending notes.
*/
public function logEndingNotes(): void
{
// Do not show execution time for index
$this->log('Peak memory usage: ' . (memory_get_peak_usage(true) / 1024 / 1024) . 'MB');
}
/**
* Log a line about the write operation.
*
* @param string $path
* @param float $callStartTime
*/
public function logWrite(IWriter $writer, $path, $callStartTime): void
{
$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
$reflection = new ReflectionClass($writer);
$format = $reflection->getShortName();
$message = ($this->isCli() === true)
? "Write {$format} format to {$path} in " . sprintf('%.4f', $callTime) . ' seconds'
: "Write {$format} format to {$path} in " . sprintf('%.4f', $callTime) . ' seconds';
$this->log($message);
}
/**
* Log a line about the read operation.
*
* @param string $format
* @param string $path
* @param float $callStartTime
*/
public function logRead($format, $path, $callStartTime): void
{
$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
$message = "Read {$format} format from {$path} in " . sprintf('%.4f', $callTime) . ' seconds';
$this->log($message);
}
}
phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php 0000644 00000001636 15242753725 0016155 0 ustar 00 \d*\.?\d+)(?Ppt|px|em)?$/i';
/**
* @var bool
*/
protected $valid;
/**
* @var string
*/
protected $size = '';
/**
* @var string
*/
protected $unit = '';
public function __construct(string $size)
{
$this->valid = (bool) preg_match(self::REGEXP_SIZE_VALIDATION, $size, $matches);
if ($this->valid) {
$this->size = $matches['size'];
$this->unit = $matches['unit'] ?? 'pt';
}
}
public function valid(): bool
{
return $this->valid;
}
public function size(): string
{
return $this->size;
}
public function unit(): string
{
return $this->unit;
}
public function __toString()
{
return $this->size . $this->unit;
}
}
phpspreadsheet/src/PhpSpreadsheet/Helper/Downloader.php 0000644 00000005300 15242753725 0017331 0 ustar 00 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xls' => 'application/vnd.ms-excel',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'csv' => 'text/csv',
'html' => 'text/html',
'pdf' => 'application/pdf',
];
public function __construct(string $folder, string $filename, ?string $filetype = null)
{
if ((is_dir($folder) === false) || (is_readable($folder) === false)) {
throw new Exception("Folder {$folder} is not accessable");
}
$filepath = "{$folder}/{$filename}";
$this->filepath = (string) realpath($filepath);
$this->filename = basename($filepath);
if ((file_exists($this->filepath) === false) || (is_readable($this->filepath) === false)) {
throw new Exception("{$this->filename} not found, or cannot be read");
}
$filetype ??= pathinfo($filename, PATHINFO_EXTENSION);
if (array_key_exists(strtolower($filetype), self::CONTENT_TYPES) === false) {
throw new Exception("Invalid filetype: {$filetype} cannot be downloaded");
}
$this->filetype = strtolower($filetype);
}
public function download(): void
{
$this->headers();
readfile($this->filepath);
}
public function headers(): void
{
ob_clean();
$this->contentType();
$this->contentDisposition();
$this->cacheHeaders();
$this->fileSize();
flush();
}
protected function contentType(): void
{
header('Content-Type: ' . self::CONTENT_TYPES[$this->filetype]);
}
protected function contentDisposition(): void
{
header('Content-Disposition: attachment;filename="' . $this->filename . '"');
}
protected function cacheHeaders(): void
{
header('Cache-Control: max-age=0');
// If you're serving to IE 9, then the following may be needed
header('Cache-Control: max-age=1');
// If you're serving to IE over SSL, then the following may be needed
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
header('Pragma: public'); // HTTP/1.0
}
protected function fileSize(): void
{
header('Content-Length: ' . filesize($this->filepath));
}
}
phpspreadsheet/src/PhpSpreadsheet/Helper/TextGrid.php 0000644 00000010135 15242753725 0016767 0 ustar 00 rows = array_keys($matrix);
$this->columns = array_keys($matrix[$this->rows[0]]);
$matrix = array_values($matrix);
array_walk(
$matrix,
function (&$row): void {
$row = array_values($row);
}
);
$this->matrix = $matrix;
$this->isCli = $isCli;
}
public function render(): string
{
$this->gridDisplay = $this->isCli ? '' : '';
$maxRow = max($this->rows);
$maxRowLength = mb_strlen((string) $maxRow) + 1;
$columnWidths = $this->getColumnWidths();
$this->renderColumnHeader($maxRowLength, $columnWidths);
$this->renderRows($maxRowLength, $columnWidths);
$this->renderFooter($maxRowLength, $columnWidths);
$this->gridDisplay .= $this->isCli ? '' : '';
return $this->gridDisplay;
}
private function renderRows(int $maxRowLength, array $columnWidths): void
{
foreach ($this->matrix as $row => $rowData) {
$this->gridDisplay .= '|' . str_pad((string) $this->rows[$row], $maxRowLength, ' ', STR_PAD_LEFT) . ' ';
$this->renderCells($rowData, $columnWidths);
$this->gridDisplay .= '|' . PHP_EOL;
}
}
private function renderCells(array $rowData, array $columnWidths): void
{
foreach ($rowData as $column => $cell) {
$displayCell = ($this->isCli) ? (string) $cell : htmlentities((string) $cell);
$this->gridDisplay .= '| ';
$this->gridDisplay .= $displayCell . str_repeat(' ', $columnWidths[$column] - mb_strlen($cell ?? '') + 1);
}
}
private function renderColumnHeader(int $maxRowLength, array $columnWidths): void
{
$this->gridDisplay .= str_repeat(' ', $maxRowLength + 2);
foreach ($this->columns as $column => $reference) {
$this->gridDisplay .= '+-' . str_repeat('-', $columnWidths[$column] + 1);
}
$this->gridDisplay .= '+' . PHP_EOL;
$this->gridDisplay .= str_repeat(' ', $maxRowLength + 2);
foreach ($this->columns as $column => $reference) {
$this->gridDisplay .= '| ' . str_pad((string) $reference, $columnWidths[$column] + 1, ' ');
}
$this->gridDisplay .= '|' . PHP_EOL;
$this->renderFooter($maxRowLength, $columnWidths);
}
private function renderFooter(int $maxRowLength, array $columnWidths): void
{
$this->gridDisplay .= '+' . str_repeat('-', $maxRowLength + 1);
foreach ($this->columns as $column => $reference) {
$this->gridDisplay .= '+-';
$this->gridDisplay .= str_pad((string) '', $columnWidths[$column] + 1, '-');
}
$this->gridDisplay .= '+' . PHP_EOL;
}
private function getColumnWidths(): array
{
$columnCount = count($this->matrix, COUNT_RECURSIVE) / count($this->matrix);
$columnWidths = [];
for ($column = 0; $column < $columnCount; ++$column) {
$columnWidths[] = $this->getColumnWidth(array_column($this->matrix, $column));
}
return $columnWidths;
}
private function getColumnWidth(array $columnData): int
{
$columnWidth = 0;
$columnData = array_values($columnData);
foreach ($columnData as $columnValue) {
if (is_string($columnValue)) {
$columnWidth = max($columnWidth, mb_strlen($columnValue));
} elseif (is_bool($columnValue)) {
$columnWidth = max($columnWidth, mb_strlen($columnValue ? 'TRUE' : 'FALSE'));
}
$columnWidth = max($columnWidth, mb_strlen((string) $columnWidth));
}
return $columnWidth;
}
}
phpspreadsheet/src/PhpSpreadsheet/Helper/Dimension.php 0000644 00000006351 15242753725 0017167 0 ustar 00 96.0 / 2.54,
self::UOM_MILLIMETERS => 96.0 / 25.4,
self::UOM_INCHES => 96.0,
self::UOM_PIXELS => 1.0,
self::UOM_POINTS => 96.0 / 72,
self::UOM_PICA => 96.0 * 12 / 72,
];
/**
* Based on a standard column width of 8.54 units in MS Excel.
*/
const RELATIVE_UNITS = [
'em' => 10.0 / 8.54,
'ex' => 10.0 / 8.54,
'ch' => 10.0 / 8.54,
'rem' => 10.0 / 8.54,
'vw' => 8.54,
'vh' => 8.54,
'vmin' => 8.54,
'vmax' => 8.54,
'%' => 8.54 / 100,
];
/**
* @var float|int If this is a width, then size is measured in pixels (if is set)
* or in Excel's default column width units if $unit is null.
* If this is a height, then size is measured in pixels ()
* or in points () if $unit is null.
*/
protected $size;
/**
* @var null|string
*/
protected $unit;
/**
* Phpstan bug has been fixed; this function allows us to
* pass Phpstan whether fixed or not.
*
* @param mixed $value
*/
private static function stanBugFixed($value): array
{
return is_array($value) ? $value : [null, null];
}
public function __construct(string $dimension)
{
[$size, $unit] = self::stanBugFixed(sscanf($dimension, '%[1234567890.]%s'));
$unit = strtolower(trim($unit ?? ''));
$size = (float) $size;
// If a UoM is specified, then convert the size to pixels for internal storage
if (isset(self::ABSOLUTE_UNITS[$unit])) {
$size *= self::ABSOLUTE_UNITS[$unit];
$this->unit = self::UOM_PIXELS;
} elseif (isset(self::RELATIVE_UNITS[$unit])) {
$size *= self::RELATIVE_UNITS[$unit];
$size = round($size, 4);
}
$this->size = $size;
}
public function width(): float
{
return (float) ($this->unit === null)
? $this->size
: round(Drawing::pixelsToCellDimension((int) $this->size, new Font(false)), 4);
}
public function height(): float
{
return (float) ($this->unit === null)
? $this->size
: $this->toUnit(self::UOM_POINTS);
}
public function toUnit(string $unitOfMeasure): float
{
$unitOfMeasure = strtolower($unitOfMeasure);
if (!array_key_exists($unitOfMeasure, self::ABSOLUTE_UNITS)) {
throw new Exception("{$unitOfMeasure} is not a vaid unit of measure");
}
$size = $this->size;
if ($this->unit === null) {
$size = Drawing::cellDimensionToPixels($size, new Font(false));
}
return $size / self::ABSOLUTE_UNITS[$unitOfMeasure];
}
}
phpspreadsheet/src/PhpSpreadsheet/Helper/Html.php 0000644 00000062636 15242753725 0016156 0 ustar 00 'f0f8ff',
'antiquewhite' => 'faebd7',
'antiquewhite1' => 'ffefdb',
'antiquewhite2' => 'eedfcc',
'antiquewhite3' => 'cdc0b0',
'antiquewhite4' => '8b8378',
'aqua' => '00ffff',
'aquamarine1' => '7fffd4',
'aquamarine2' => '76eec6',
'aquamarine4' => '458b74',
'azure1' => 'f0ffff',
'azure2' => 'e0eeee',
'azure3' => 'c1cdcd',
'azure4' => '838b8b',
'beige' => 'f5f5dc',
'bisque1' => 'ffe4c4',
'bisque2' => 'eed5b7',
'bisque3' => 'cdb79e',
'bisque4' => '8b7d6b',
'black' => '000000',
'blanchedalmond' => 'ffebcd',
'blue' => '0000ff',
'blue1' => '0000ff',
'blue2' => '0000ee',
'blue4' => '00008b',
'blueviolet' => '8a2be2',
'brown' => 'a52a2a',
'brown1' => 'ff4040',
'brown2' => 'ee3b3b',
'brown3' => 'cd3333',
'brown4' => '8b2323',
'burlywood' => 'deb887',
'burlywood1' => 'ffd39b',
'burlywood2' => 'eec591',
'burlywood3' => 'cdaa7d',
'burlywood4' => '8b7355',
'cadetblue' => '5f9ea0',
'cadetblue1' => '98f5ff',
'cadetblue2' => '8ee5ee',
'cadetblue3' => '7ac5cd',
'cadetblue4' => '53868b',
'chartreuse1' => '7fff00',
'chartreuse2' => '76ee00',
'chartreuse3' => '66cd00',
'chartreuse4' => '458b00',
'chocolate' => 'd2691e',
'chocolate1' => 'ff7f24',
'chocolate2' => 'ee7621',
'chocolate3' => 'cd661d',
'coral' => 'ff7f50',
'coral1' => 'ff7256',
'coral2' => 'ee6a50',
'coral3' => 'cd5b45',
'coral4' => '8b3e2f',
'cornflowerblue' => '6495ed',
'cornsilk1' => 'fff8dc',
'cornsilk2' => 'eee8cd',
'cornsilk3' => 'cdc8b1',
'cornsilk4' => '8b8878',
'cyan1' => '00ffff',
'cyan2' => '00eeee',
'cyan3' => '00cdcd',
'cyan4' => '008b8b',
'darkgoldenrod' => 'b8860b',
'darkgoldenrod1' => 'ffb90f',
'darkgoldenrod2' => 'eead0e',
'darkgoldenrod3' => 'cd950c',
'darkgoldenrod4' => '8b6508',
'darkgreen' => '006400',
'darkkhaki' => 'bdb76b',
'darkolivegreen' => '556b2f',
'darkolivegreen1' => 'caff70',
'darkolivegreen2' => 'bcee68',
'darkolivegreen3' => 'a2cd5a',
'darkolivegreen4' => '6e8b3d',
'darkorange' => 'ff8c00',
'darkorange1' => 'ff7f00',
'darkorange2' => 'ee7600',
'darkorange3' => 'cd6600',
'darkorange4' => '8b4500',
'darkorchid' => '9932cc',
'darkorchid1' => 'bf3eff',
'darkorchid2' => 'b23aee',
'darkorchid3' => '9a32cd',
'darkorchid4' => '68228b',
'darksalmon' => 'e9967a',
'darkseagreen' => '8fbc8f',
'darkseagreen1' => 'c1ffc1',
'darkseagreen2' => 'b4eeb4',
'darkseagreen3' => '9bcd9b',
'darkseagreen4' => '698b69',
'darkslateblue' => '483d8b',
'darkslategray' => '2f4f4f',
'darkslategray1' => '97ffff',
'darkslategray2' => '8deeee',
'darkslategray3' => '79cdcd',
'darkslategray4' => '528b8b',
'darkturquoise' => '00ced1',
'darkviolet' => '9400d3',
'deeppink1' => 'ff1493',
'deeppink2' => 'ee1289',
'deeppink3' => 'cd1076',
'deeppink4' => '8b0a50',
'deepskyblue1' => '00bfff',
'deepskyblue2' => '00b2ee',
'deepskyblue3' => '009acd',
'deepskyblue4' => '00688b',
'dimgray' => '696969',
'dodgerblue1' => '1e90ff',
'dodgerblue2' => '1c86ee',
'dodgerblue3' => '1874cd',
'dodgerblue4' => '104e8b',
'firebrick' => 'b22222',
'firebrick1' => 'ff3030',
'firebrick2' => 'ee2c2c',
'firebrick3' => 'cd2626',
'firebrick4' => '8b1a1a',
'floralwhite' => 'fffaf0',
'forestgreen' => '228b22',
'fuchsia' => 'ff00ff',
'gainsboro' => 'dcdcdc',
'ghostwhite' => 'f8f8ff',
'gold1' => 'ffd700',
'gold2' => 'eec900',
'gold3' => 'cdad00',
'gold4' => '8b7500',
'goldenrod' => 'daa520',
'goldenrod1' => 'ffc125',
'goldenrod2' => 'eeb422',
'goldenrod3' => 'cd9b1d',
'goldenrod4' => '8b6914',
'gray' => 'bebebe',
'gray1' => '030303',
'gray10' => '1a1a1a',
'gray11' => '1c1c1c',
'gray12' => '1f1f1f',
'gray13' => '212121',
'gray14' => '242424',
'gray15' => '262626',
'gray16' => '292929',
'gray17' => '2b2b2b',
'gray18' => '2e2e2e',
'gray19' => '303030',
'gray2' => '050505',
'gray20' => '333333',
'gray21' => '363636',
'gray22' => '383838',
'gray23' => '3b3b3b',
'gray24' => '3d3d3d',
'gray25' => '404040',
'gray26' => '424242',
'gray27' => '454545',
'gray28' => '474747',
'gray29' => '4a4a4a',
'gray3' => '080808',
'gray30' => '4d4d4d',
'gray31' => '4f4f4f',
'gray32' => '525252',
'gray33' => '545454',
'gray34' => '575757',
'gray35' => '595959',
'gray36' => '5c5c5c',
'gray37' => '5e5e5e',
'gray38' => '616161',
'gray39' => '636363',
'gray4' => '0a0a0a',
'gray40' => '666666',
'gray41' => '696969',
'gray42' => '6b6b6b',
'gray43' => '6e6e6e',
'gray44' => '707070',
'gray45' => '737373',
'gray46' => '757575',
'gray47' => '787878',
'gray48' => '7a7a7a',
'gray49' => '7d7d7d',
'gray5' => '0d0d0d',
'gray50' => '7f7f7f',
'gray51' => '828282',
'gray52' => '858585',
'gray53' => '878787',
'gray54' => '8a8a8a',
'gray55' => '8c8c8c',
'gray56' => '8f8f8f',
'gray57' => '919191',
'gray58' => '949494',
'gray59' => '969696',
'gray6' => '0f0f0f',
'gray60' => '999999',
'gray61' => '9c9c9c',
'gray62' => '9e9e9e',
'gray63' => 'a1a1a1',
'gray64' => 'a3a3a3',
'gray65' => 'a6a6a6',
'gray66' => 'a8a8a8',
'gray67' => 'ababab',
'gray68' => 'adadad',
'gray69' => 'b0b0b0',
'gray7' => '121212',
'gray70' => 'b3b3b3',
'gray71' => 'b5b5b5',
'gray72' => 'b8b8b8',
'gray73' => 'bababa',
'gray74' => 'bdbdbd',
'gray75' => 'bfbfbf',
'gray76' => 'c2c2c2',
'gray77' => 'c4c4c4',
'gray78' => 'c7c7c7',
'gray79' => 'c9c9c9',
'gray8' => '141414',
'gray80' => 'cccccc',
'gray81' => 'cfcfcf',
'gray82' => 'd1d1d1',
'gray83' => 'd4d4d4',
'gray84' => 'd6d6d6',
'gray85' => 'd9d9d9',
'gray86' => 'dbdbdb',
'gray87' => 'dedede',
'gray88' => 'e0e0e0',
'gray89' => 'e3e3e3',
'gray9' => '171717',
'gray90' => 'e5e5e5',
'gray91' => 'e8e8e8',
'gray92' => 'ebebeb',
'gray93' => 'ededed',
'gray94' => 'f0f0f0',
'gray95' => 'f2f2f2',
'gray97' => 'f7f7f7',
'gray98' => 'fafafa',
'gray99' => 'fcfcfc',
'green' => '00ff00',
'green1' => '00ff00',
'green2' => '00ee00',
'green3' => '00cd00',
'green4' => '008b00',
'greenyellow' => 'adff2f',
'honeydew1' => 'f0fff0',
'honeydew2' => 'e0eee0',
'honeydew3' => 'c1cdc1',
'honeydew4' => '838b83',
'hotpink' => 'ff69b4',
'hotpink1' => 'ff6eb4',
'hotpink2' => 'ee6aa7',
'hotpink3' => 'cd6090',
'hotpink4' => '8b3a62',
'indianred' => 'cd5c5c',
'indianred1' => 'ff6a6a',
'indianred2' => 'ee6363',
'indianred3' => 'cd5555',
'indianred4' => '8b3a3a',
'ivory1' => 'fffff0',
'ivory2' => 'eeeee0',
'ivory3' => 'cdcdc1',
'ivory4' => '8b8b83',
'khaki' => 'f0e68c',
'khaki1' => 'fff68f',
'khaki2' => 'eee685',
'khaki3' => 'cdc673',
'khaki4' => '8b864e',
'lavender' => 'e6e6fa',
'lavenderblush1' => 'fff0f5',
'lavenderblush2' => 'eee0e5',
'lavenderblush3' => 'cdc1c5',
'lavenderblush4' => '8b8386',
'lawngreen' => '7cfc00',
'lemonchiffon1' => 'fffacd',
'lemonchiffon2' => 'eee9bf',
'lemonchiffon3' => 'cdc9a5',
'lemonchiffon4' => '8b8970',
'light' => 'eedd82',
'lightblue' => 'add8e6',
'lightblue1' => 'bfefff',
'lightblue2' => 'b2dfee',
'lightblue3' => '9ac0cd',
'lightblue4' => '68838b',
'lightcoral' => 'f08080',
'lightcyan1' => 'e0ffff',
'lightcyan2' => 'd1eeee',
'lightcyan3' => 'b4cdcd',
'lightcyan4' => '7a8b8b',
'lightgoldenrod1' => 'ffec8b',
'lightgoldenrod2' => 'eedc82',
'lightgoldenrod3' => 'cdbe70',
'lightgoldenrod4' => '8b814c',
'lightgoldenrodyellow' => 'fafad2',
'lightgray' => 'd3d3d3',
'lightpink' => 'ffb6c1',
'lightpink1' => 'ffaeb9',
'lightpink2' => 'eea2ad',
'lightpink3' => 'cd8c95',
'lightpink4' => '8b5f65',
'lightsalmon1' => 'ffa07a',
'lightsalmon2' => 'ee9572',
'lightsalmon3' => 'cd8162',
'lightsalmon4' => '8b5742',
'lightseagreen' => '20b2aa',
'lightskyblue' => '87cefa',
'lightskyblue1' => 'b0e2ff',
'lightskyblue2' => 'a4d3ee',
'lightskyblue3' => '8db6cd',
'lightskyblue4' => '607b8b',
'lightslateblue' => '8470ff',
'lightslategray' => '778899',
'lightsteelblue' => 'b0c4de',
'lightsteelblue1' => 'cae1ff',
'lightsteelblue2' => 'bcd2ee',
'lightsteelblue3' => 'a2b5cd',
'lightsteelblue4' => '6e7b8b',
'lightyellow1' => 'ffffe0',
'lightyellow2' => 'eeeed1',
'lightyellow3' => 'cdcdb4',
'lightyellow4' => '8b8b7a',
'lime' => '00ff00',
'limegreen' => '32cd32',
'linen' => 'faf0e6',
'magenta' => 'ff00ff',
'magenta2' => 'ee00ee',
'magenta3' => 'cd00cd',
'magenta4' => '8b008b',
'maroon' => 'b03060',
'maroon1' => 'ff34b3',
'maroon2' => 'ee30a7',
'maroon3' => 'cd2990',
'maroon4' => '8b1c62',
'medium' => '66cdaa',
'mediumaquamarine' => '66cdaa',
'mediumblue' => '0000cd',
'mediumorchid' => 'ba55d3',
'mediumorchid1' => 'e066ff',
'mediumorchid2' => 'd15fee',
'mediumorchid3' => 'b452cd',
'mediumorchid4' => '7a378b',
'mediumpurple' => '9370db',
'mediumpurple1' => 'ab82ff',
'mediumpurple2' => '9f79ee',
'mediumpurple3' => '8968cd',
'mediumpurple4' => '5d478b',
'mediumseagreen' => '3cb371',
'mediumslateblue' => '7b68ee',
'mediumspringgreen' => '00fa9a',
'mediumturquoise' => '48d1cc',
'mediumvioletred' => 'c71585',
'midnightblue' => '191970',
'mintcream' => 'f5fffa',
'mistyrose1' => 'ffe4e1',
'mistyrose2' => 'eed5d2',
'mistyrose3' => 'cdb7b5',
'mistyrose4' => '8b7d7b',
'moccasin' => 'ffe4b5',
'navajowhite1' => 'ffdead',
'navajowhite2' => 'eecfa1',
'navajowhite3' => 'cdb38b',
'navajowhite4' => '8b795e',
'navy' => '000080',
'navyblue' => '000080',
'oldlace' => 'fdf5e6',
'olive' => '808000',
'olivedrab' => '6b8e23',
'olivedrab1' => 'c0ff3e',
'olivedrab2' => 'b3ee3a',
'olivedrab4' => '698b22',
'orange' => 'ffa500',
'orange1' => 'ffa500',
'orange2' => 'ee9a00',
'orange3' => 'cd8500',
'orange4' => '8b5a00',
'orangered1' => 'ff4500',
'orangered2' => 'ee4000',
'orangered3' => 'cd3700',
'orangered4' => '8b2500',
'orchid' => 'da70d6',
'orchid1' => 'ff83fa',
'orchid2' => 'ee7ae9',
'orchid3' => 'cd69c9',
'orchid4' => '8b4789',
'pale' => 'db7093',
'palegoldenrod' => 'eee8aa',
'palegreen' => '98fb98',
'palegreen1' => '9aff9a',
'palegreen2' => '90ee90',
'palegreen3' => '7ccd7c',
'palegreen4' => '548b54',
'paleturquoise' => 'afeeee',
'paleturquoise1' => 'bbffff',
'paleturquoise2' => 'aeeeee',
'paleturquoise3' => '96cdcd',
'paleturquoise4' => '668b8b',
'palevioletred' => 'db7093',
'palevioletred1' => 'ff82ab',
'palevioletred2' => 'ee799f',
'palevioletred3' => 'cd6889',
'palevioletred4' => '8b475d',
'papayawhip' => 'ffefd5',
'peachpuff1' => 'ffdab9',
'peachpuff2' => 'eecbad',
'peachpuff3' => 'cdaf95',
'peachpuff4' => '8b7765',
'pink' => 'ffc0cb',
'pink1' => 'ffb5c5',
'pink2' => 'eea9b8',
'pink3' => 'cd919e',
'pink4' => '8b636c',
'plum' => 'dda0dd',
'plum1' => 'ffbbff',
'plum2' => 'eeaeee',
'plum3' => 'cd96cd',
'plum4' => '8b668b',
'powderblue' => 'b0e0e6',
'purple' => 'a020f0',
'rebeccapurple' => '663399',
'purple1' => '9b30ff',
'purple2' => '912cee',
'purple3' => '7d26cd',
'purple4' => '551a8b',
'red' => 'ff0000',
'red1' => 'ff0000',
'red2' => 'ee0000',
'red3' => 'cd0000',
'red4' => '8b0000',
'rosybrown' => 'bc8f8f',
'rosybrown1' => 'ffc1c1',
'rosybrown2' => 'eeb4b4',
'rosybrown3' => 'cd9b9b',
'rosybrown4' => '8b6969',
'royalblue' => '4169e1',
'royalblue1' => '4876ff',
'royalblue2' => '436eee',
'royalblue3' => '3a5fcd',
'royalblue4' => '27408b',
'saddlebrown' => '8b4513',
'salmon' => 'fa8072',
'salmon1' => 'ff8c69',
'salmon2' => 'ee8262',
'salmon3' => 'cd7054',
'salmon4' => '8b4c39',
'sandybrown' => 'f4a460',
'seagreen1' => '54ff9f',
'seagreen2' => '4eee94',
'seagreen3' => '43cd80',
'seagreen4' => '2e8b57',
'seashell1' => 'fff5ee',
'seashell2' => 'eee5de',
'seashell3' => 'cdc5bf',
'seashell4' => '8b8682',
'sienna' => 'a0522d',
'sienna1' => 'ff8247',
'sienna2' => 'ee7942',
'sienna3' => 'cd6839',
'sienna4' => '8b4726',
'silver' => 'c0c0c0',
'skyblue' => '87ceeb',
'skyblue1' => '87ceff',
'skyblue2' => '7ec0ee',
'skyblue3' => '6ca6cd',
'skyblue4' => '4a708b',
'slateblue' => '6a5acd',
'slateblue1' => '836fff',
'slateblue2' => '7a67ee',
'slateblue3' => '6959cd',
'slateblue4' => '473c8b',
'slategray' => '708090',
'slategray1' => 'c6e2ff',
'slategray2' => 'b9d3ee',
'slategray3' => '9fb6cd',
'slategray4' => '6c7b8b',
'snow1' => 'fffafa',
'snow2' => 'eee9e9',
'snow3' => 'cdc9c9',
'snow4' => '8b8989',
'springgreen1' => '00ff7f',
'springgreen2' => '00ee76',
'springgreen3' => '00cd66',
'springgreen4' => '008b45',
'steelblue' => '4682b4',
'steelblue1' => '63b8ff',
'steelblue2' => '5cacee',
'steelblue3' => '4f94cd',
'steelblue4' => '36648b',
'tan' => 'd2b48c',
'tan1' => 'ffa54f',
'tan2' => 'ee9a49',
'tan3' => 'cd853f',
'tan4' => '8b5a2b',
'teal' => '008080',
'thistle' => 'd8bfd8',
'thistle1' => 'ffe1ff',
'thistle2' => 'eed2ee',
'thistle3' => 'cdb5cd',
'thistle4' => '8b7b8b',
'tomato1' => 'ff6347',
'tomato2' => 'ee5c42',
'tomato3' => 'cd4f39',
'tomato4' => '8b3626',
'turquoise' => '40e0d0',
'turquoise1' => '00f5ff',
'turquoise2' => '00e5ee',
'turquoise3' => '00c5cd',
'turquoise4' => '00868b',
'violet' => 'ee82ee',
'violetred' => 'd02090',
'violetred1' => 'ff3e96',
'violetred2' => 'ee3a8c',
'violetred3' => 'cd3278',
'violetred4' => '8b2252',
'wheat' => 'f5deb3',
'wheat1' => 'ffe7ba',
'wheat2' => 'eed8ae',
'wheat3' => 'cdba96',
'wheat4' => '8b7e66',
'white' => 'ffffff',
'whitesmoke' => 'f5f5f5',
'yellow' => 'ffff00',
'yellow1' => 'ffff00',
'yellow2' => 'eeee00',
'yellow3' => 'cdcd00',
'yellow4' => '8b8b00',
'yellowgreen' => '9acd32',
];
/** @var ?string */
private $face;
/** @var ?string */
private $size;
/** @var ?string */
private $color;
/** @var bool */
private $bold = false;
/** @var bool */
private $italic = false;
/** @var bool */
private $underline = false;
/** @var bool */
private $superscript = false;
/** @var bool */
private $subscript = false;
/** @var bool */
private $strikethrough = false;
private const START_TAG_CALLBACKS = [
'font' => 'startFontTag',
'b' => 'startBoldTag',
'strong' => 'startBoldTag',
'i' => 'startItalicTag',
'em' => 'startItalicTag',
'u' => 'startUnderlineTag',
'ins' => 'startUnderlineTag',
'del' => 'startStrikethruTag',
'sup' => 'startSuperscriptTag',
'sub' => 'startSubscriptTag',
];
private const END_TAG_CALLBACKS = [
'font' => 'endFontTag',
'b' => 'endBoldTag',
'strong' => 'endBoldTag',
'i' => 'endItalicTag',
'em' => 'endItalicTag',
'u' => 'endUnderlineTag',
'ins' => 'endUnderlineTag',
'del' => 'endStrikethruTag',
'sup' => 'endSuperscriptTag',
'sub' => 'endSubscriptTag',
'br' => 'breakTag',
'p' => 'breakTag',
'h1' => 'breakTag',
'h2' => 'breakTag',
'h3' => 'breakTag',
'h4' => 'breakTag',
'h5' => 'breakTag',
'h6' => 'breakTag',
];
/** @var array */
private $stack = [];
/** @var string */
private $stringData = '';
/**
* @var RichText
*/
private $richTextObject;
private function initialise(): void
{
$this->face = $this->size = $this->color = null;
$this->bold = $this->italic = $this->underline = $this->superscript = $this->subscript = $this->strikethrough = false;
$this->stack = [];
$this->stringData = '';
}
/**
* Parse HTML formatting and return the resulting RichText.
*
* @param string $html
*
* @return RichText
*/
public function toRichTextObject($html)
{
$this->initialise();
// Create a new DOM object
$dom = new DOMDocument();
// Load the HTML file into the DOM object
// Note the use of error suppression, because typically this will be an html fragment, so not fully valid markup
$prefix = '';
/** @scrutinizer ignore-unhandled */
@$dom->loadHTML($prefix . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
// Discard excess white space
$dom->preserveWhiteSpace = false;
$this->richTextObject = new RichText();
$this->parseElements($dom);
// Clean any further spurious whitespace
$this->cleanWhitespace();
return $this->richTextObject;
}
private function cleanWhitespace(): void
{
foreach ($this->richTextObject->getRichTextElements() as $key => $element) {
$text = $element->getText();
// Trim any leading spaces on the first run
if ($key == 0) {
$text = ltrim($text);
}
// Trim any spaces immediately after a line break
$text = (string) preg_replace('/\n */mu', "\n", $text);
$element->setText($text);
}
}
private function buildTextRun(): void
{
$text = $this->stringData;
if (trim($text) === '') {
return;
}
$richtextRun = $this->richTextObject->createTextRun($this->stringData);
$font = $richtextRun->getFont();
if ($font !== null) {
if ($this->face) {
$font->setName($this->face);
}
if ($this->size) {
$font->setSize($this->size);
}
if ($this->color) {
$font->setColor(new Color('ff' . $this->color));
}
if ($this->bold) {
$font->setBold(true);
}
if ($this->italic) {
$font->setItalic(true);
}
if ($this->underline) {
$font->setUnderline(Font::UNDERLINE_SINGLE);
}
if ($this->superscript) {
$font->setSuperscript(true);
}
if ($this->subscript) {
$font->setSubscript(true);
}
if ($this->strikethrough) {
$font->setStrikethrough(true);
}
}
$this->stringData = '';
}
private function rgbToColour(string $rgbValue): string
{
preg_match_all('/\d+/', $rgbValue, $values);
foreach ($values[0] as &$value) {
$value = str_pad(dechex($value), 2, '0', STR_PAD_LEFT);
}
return implode('', $values[0]);
}
public static function colourNameLookup(string $colorName): string
{
return self::COLOUR_MAP[$colorName] ?? '';
}
protected function startFontTag(DOMElement $tag): void
{
$attrs = $tag->attributes;
if ($attrs !== null) {
foreach ($attrs as $attribute) {
$attributeName = strtolower($attribute->name);
$attributeValue = $attribute->value;
if ($attributeName == 'color') {
if (preg_match('/rgb\s*\(/', $attributeValue)) {
$this->$attributeName = $this->rgbToColour($attributeValue);
} elseif (strpos(trim($attributeValue), '#') === 0) {
$this->$attributeName = ltrim($attributeValue, '#');
} else {
$this->$attributeName = static::colourNameLookup($attributeValue);
}
} else {
$this->$attributeName = $attributeValue;
}
}
}
}
protected function endFontTag(): void
{
$this->face = $this->size = $this->color = null;
}
protected function startBoldTag(): void
{
$this->bold = true;
}
protected function endBoldTag(): void
{
$this->bold = false;
}
protected function startItalicTag(): void
{
$this->italic = true;
}
protected function endItalicTag(): void
{
$this->italic = false;
}
protected function startUnderlineTag(): void
{
$this->underline = true;
}
protected function endUnderlineTag(): void
{
$this->underline = false;
}
protected function startSubscriptTag(): void
{
$this->subscript = true;
}
protected function endSubscriptTag(): void
{
$this->subscript = false;
}
protected function startSuperscriptTag(): void
{
$this->superscript = true;
}
protected function endSuperscriptTag(): void
{
$this->superscript = false;
}
protected function startStrikethruTag(): void
{
$this->strikethrough = true;
}
protected function endStrikethruTag(): void
{
$this->strikethrough = false;
}
protected function breakTag(): void
{
$this->stringData .= "\n";
}
private function parseTextNode(DOMText $textNode): void
{
$domText = (string) preg_replace(
'/\s+/u',
' ',
str_replace(["\r", "\n"], ' ', $textNode->nodeValue ?? '')
);
$this->stringData .= $domText;
$this->buildTextRun();
}
/**
* @param string $callbackTag
*/
private function handleCallback(DOMElement $element, $callbackTag, array $callbacks): void
{
if (isset($callbacks[$callbackTag])) {
$elementHandler = $callbacks[$callbackTag];
if (method_exists($this, $elementHandler)) {
/** @var callable */
$callable = [$this, $elementHandler];
call_user_func($callable, $element);
}
}
}
private function parseElementNode(DOMElement $element): void
{
$callbackTag = strtolower($element->nodeName);
$this->stack[] = $callbackTag;
$this->handleCallback($element, $callbackTag, self::START_TAG_CALLBACKS);
$this->parseElements($element);
array_pop($this->stack);
$this->handleCallback($element, $callbackTag, self::END_TAG_CALLBACKS);
}
private function parseElements(DOMNode $element): void
{
foreach ($element->childNodes as $child) {
if ($child instanceof DOMText) {
$this->parseTextNode($child);
} elseif ($child instanceof DOMElement) {
$this->parseElementNode($child);
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Helper/Handler.php 0000644 00000002013 15242753725 0016606 0 ustar 00 spreadsheet = $spreadsheet;
}
public function load(SimpleXMLElement $xml, array $namespacesMeta): void
{
$docProps = $this->spreadsheet->getProperties();
$officeProperty = $xml->children($namespacesMeta['office']);
foreach ($officeProperty as $officePropertyData) {
if (isset($namespacesMeta['dc'])) {
/** @scrutinizer ignore-call */
$officePropertiesDC = $officePropertyData->children($namespacesMeta['dc']);
$this->setCoreProperties($docProps, $officePropertiesDC);
}
$officePropertyMeta = null;
if (isset($namespacesMeta['dc'])) {
/** @scrutinizer ignore-call */
$officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
}
$officePropertyMeta = $officePropertyMeta ?? [];
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 'date':
$docProps->setModified($propertyValue);
break;
case 'description':
$docProps->setDescription($propertyValue);
break;
}
}
}
private function setMetaProperties(
array $namespacesMeta,
SimpleXMLElement $propertyValue,
string $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':
$docProps->setCreated($propertyValue);
break;
case 'user-defined':
$this->setUserDefinedProperty($propertyValueAttributes, $propertyValue, $docProps);
break;
}
}
/**
* @param mixed $propertyValueAttributes
* @param mixed $propertyValue
*/
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);
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/FormulaTranslator.php 0000644 00000007120 15242753725 0021424 0 ustar 00 setDomNameSpaces($styleDom);
$this->readPageSettingStyles($styleDom);
$this->readStyleMasterLookup($styleDom);
}
private function setDomNameSpaces(DOMDocument $styleDom): void
{
$this->officeNs = $styleDom->lookupNamespaceUri('office');
$this->stylesNs = $styleDom->lookupNamespaceUri('style');
$this->stylesFo = $styleDom->lookupNamespaceUri('fo');
$this->tableNs = $styleDom->lookupNamespaceUri('table');
}
private function readPageSettingStyles(DOMDocument $styleDom): void
{
$item0 = $styleDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')->item(0);
$styles = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'page-layout');
foreach ($styles as $styleSet) {
$styleName = $styleSet->getAttributeNS($this->stylesNs, 'name');
$pageLayoutProperties = $styleSet->getElementsByTagNameNS($this->stylesNs, 'page-layout-properties')[0];
$styleOrientation = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-orientation');
$styleScale = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'scale-to');
$stylePrintOrder = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-page-order');
$centered = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'table-centering');
$marginLeft = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-left');
$marginRight = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-right');
$marginTop = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-top');
$marginBottom = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-bottom');
$header = $styleSet->getElementsByTagNameNS($this->stylesNs, 'header-style')[0];
$headerProperties = $header->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0];
$marginHeader = isset($headerProperties) ? $headerProperties->getAttributeNS($this->stylesFo, 'min-height') : null;
$footer = $styleSet->getElementsByTagNameNS($this->stylesNs, 'footer-style')[0];
$footerProperties = $footer->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0];
$marginFooter = isset($footerProperties) ? $footerProperties->getAttributeNS($this->stylesFo, 'min-height') : null;
$this->pageLayoutStyles[$styleName] = (object) [
'orientation' => $styleOrientation ?: PageSetup::ORIENTATION_DEFAULT,
'scale' => $styleScale ?: 100,
'printOrder' => $stylePrintOrder,
'horizontalCentered' => $centered === 'horizontal' || $centered === 'both',
'verticalCentered' => $centered === 'vertical' || $centered === 'both',
// margin size is already stored in inches, so no UOM conversion is required
'marginLeft' => (float) ($marginLeft ?? 0.7),
'marginRight' => (float) ($marginRight ?? 0.7),
'marginTop' => (float) ($marginTop ?? 0.3),
'marginBottom' => (float) ($marginBottom ?? 0.3),
'marginHeader' => (float) ($marginHeader ?? 0.45),
'marginFooter' => (float) ($marginFooter ?? 0.45),
];
}
}
private function readStyleMasterLookup(DOMDocument $styleDom): void
{
$item0 = $styleDom->getElementsByTagNameNS($this->officeNs, 'master-styles')->item(0);
$styleMasterLookup = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'master-page');
foreach ($styleMasterLookup as $styleMasterSet) {
$styleMasterName = $styleMasterSet->getAttributeNS($this->stylesNs, 'name');
$pageLayoutName = $styleMasterSet->getAttributeNS($this->stylesNs, 'page-layout-name');
$this->masterPrintStylesCrossReference[$styleMasterName] = $pageLayoutName;
}
}
public function readStyleCrossReferences(DOMDocument $contentDom): void
{
$item0 = $contentDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles')->item(0);
$styleXReferences = ($item0 === null) ? [] : $item0->getElementsByTagNameNS($this->stylesNs, 'style');
foreach ($styleXReferences as $styleXreferenceSet) {
$styleXRefName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'name');
$stylePageLayoutName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'master-page-name');
$styleFamilyName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'family');
if (!empty($styleFamilyName) && $styleFamilyName === 'table') {
$styleVisibility = 'true';
foreach ($styleXreferenceSet->getElementsByTagNameNS($this->stylesNs, 'table-properties') as $tableProperties) {
$styleVisibility = $tableProperties->getAttributeNS($this->tableNs, 'display');
}
$this->tableStylesCrossReference[$styleXRefName] = $styleVisibility;
}
if (!empty($stylePageLayoutName)) {
$this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName;
}
}
}
public function setVisibilityForWorksheet(Worksheet $worksheet, string $styleName): void
{
if (!array_key_exists($styleName, $this->tableStylesCrossReference)) {
return;
}
$worksheet->setSheetState(
$this->tableStylesCrossReference[$styleName] === 'false'
? Worksheet::SHEETSTATE_HIDDEN
: Worksheet::SHEETSTATE_VISIBLE
);
}
public function setPrintSettingsForWorksheet(Worksheet $worksheet, string $styleName): void
{
if (!array_key_exists($styleName, $this->masterStylesCrossReference)) {
return;
}
$masterStyleName = $this->masterStylesCrossReference[$styleName];
if (!array_key_exists($masterStyleName, $this->masterPrintStylesCrossReference)) {
return;
}
$printSettingsIndex = $this->masterPrintStylesCrossReference[$masterStyleName];
if (!array_key_exists($printSettingsIndex, $this->pageLayoutStyles)) {
return;
}
$printSettings = $this->pageLayoutStyles[$printSettingsIndex];
$worksheet->getPageSetup()
->setOrientation($printSettings->orientation ?? PageSetup::ORIENTATION_DEFAULT)
->setPageOrder($printSettings->printOrder === 'ltr' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER)
->setScale((int) trim($printSettings->scale, '%'))
->setHorizontalCentered($printSettings->horizontalCentered)
->setVerticalCentered($printSettings->verticalCentered);
$worksheet->getPageMargins()
->setLeft($printSettings->marginLeft)
->setRight($printSettings->marginRight)
->setTop($printSettings->marginTop)
->setBottom($printSettings->marginBottom)
->setHeader($printSettings->marginHeader)
->setFooter($printSettings->marginFooter);
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/BaseLoader.php 0000644 00000000772 15242753725 0017754 0 ustar 00 spreadsheet = $spreadsheet;
$this->tableNs = $tableNs;
}
abstract public function read(DOMElement $workbookData): void;
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/DefinedNames.php 0000644 00000005353 15242753725 0020275 0 ustar 00 readDefinedRanges($workbookData);
$this->readDefinedExpressions($workbookData);
}
/**
* Read any Named Ranges that are defined in this spreadsheet.
*/
protected function readDefinedRanges(DOMElement $workbookData): void
{
$namedRanges = $workbookData->getElementsByTagNameNS($this->tableNs, 'named-range');
foreach ($namedRanges as $definedNameElement) {
$definedName = $definedNameElement->getAttributeNS($this->tableNs, 'name');
$baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address');
$range = $definedNameElement->getAttributeNS($this->tableNs, 'cell-range-address');
$baseAddress = FormulaTranslator::convertToExcelAddressValue($baseAddress);
$range = FormulaTranslator::convertToExcelAddressValue($range);
$this->addDefinedName($baseAddress, $definedName, $range);
}
}
/**
* Read any Named Formulae that are defined in this spreadsheet.
*/
protected function readDefinedExpressions(DOMElement $workbookData): void
{
$namedExpressions = $workbookData->getElementsByTagNameNS($this->tableNs, 'named-expression');
foreach ($namedExpressions as $definedNameElement) {
$definedName = $definedNameElement->getAttributeNS($this->tableNs, 'name');
$baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address');
$expression = $definedNameElement->getAttributeNS($this->tableNs, 'expression');
$baseAddress = FormulaTranslator::convertToExcelAddressValue($baseAddress);
$expression = substr($expression, strpos($expression, ':=') + 1);
$expression = FormulaTranslator::convertToExcelFormulaValue($expression);
$this->addDefinedName($baseAddress, $definedName, $expression);
}
}
/**
* Assess scope and store the Defined Name.
*/
private function addDefinedName(string $baseAddress, string $definedName, string $value): void
{
[$sheetReference] = Worksheet::extractSheetTitle($baseAddress, true);
$worksheet = $this->spreadsheet->getSheetByName($sheetReference);
// Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet
if ($worksheet !== null) {
$this->spreadsheet->addDefinedName(DefinedName::createInstance((string) $definedName, $worksheet, $value));
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/AutoFilter.php 0000644 00000002474 15242753725 0020032 0 ustar 00 readAutoFilters($workbookData);
}
protected function readAutoFilters(DOMElement $workbookData): void
{
$databases = $workbookData->getElementsByTagNameNS($this->tableNs, 'database-ranges');
foreach ($databases as $autofilters) {
foreach ($autofilters->childNodes as $autofilter) {
$autofilterRange = $this->getAttributeValue($autofilter, 'target-range-address');
if ($autofilterRange !== null) {
$baseAddress = FormulaTranslator::convertToExcelAddressValue($autofilterRange);
$this->spreadsheet->getActiveSheet()->setAutoFilter($baseAddress);
}
}
}
}
protected function getAttributeValue(?DOMNode $node, string $attributeName): ?string
{
if ($node !== null && $node->attributes !== null) {
$attribute = $node->attributes->getNamedItemNS(
$this->tableNs,
$attributeName
);
if ($attribute !== null) {
return $attribute->nodeValue;
}
}
return null;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php 0000644 00000010543 15242753725 0020315 0 ustar 00 securityScanner = $securityScanner;
$this->docProps = $docProps;
}
/**
* @param mixed $obj
*/
private static function nullOrSimple($obj): ?SimpleXMLElement
{
return ($obj instanceof SimpleXMLElement) ? $obj : null;
}
private function extractPropertyData(string $propertyData): ?SimpleXMLElement
{
// okay to omit namespace because everything will be processed by xpath
$obj = simplexml_load_string(
$this->securityScanner->scan($propertyData)
);
return self::nullOrSimple($obj);
}
public function readCoreProperties(string $propertyData): void
{
$xmlCore = $this->extractPropertyData($propertyData);
if (is_object($xmlCore)) {
$xmlCore->registerXPathNamespace('dc', Namespaces::DC_ELEMENTS);
$xmlCore->registerXPathNamespace('dcterms', Namespaces::DC_TERMS);
$xmlCore->registerXPathNamespace('cp', Namespaces::CORE_PROPERTIES2);
$this->docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
$this->docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
$this->docProps->setCreated((string) self::getArrayItem($xmlCore->xpath('dcterms:created'))); //! respect xsi:type
$this->docProps->setModified((string) 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(string $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);
}
if (isset($xmlCore->HyperlinkBase)) {
$this->docProps->setHyperlinkBase((string) $xmlCore->HyperlinkBase);
}
}
}
public function readCustomProperties(string $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);
}
}
}
}
/**
* @param null|array|false $array
* @param mixed $key
*/
private static function getArrayItem($array, $key = 0): ?SimpleXMLElement
{
return is_array($array) ? ($array[$key] ?? null) : null;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php 0000644 00000011331 15242753725 0020243 0 ustar 00 sheetViewXml = $sheetViewXml;
$this->sheetViewAttributes = Xlsx::testSimpleXml($sheetViewXml->attributes());
$this->worksheet = $workSheet;
}
public function load(): void
{
$this->topLeft();
$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->attributes()->sqref)) {
$this->selection();
}
}
private function zoomScale(): void
{
if (isset($this->sheetViewAttributes->zoomScale)) {
$zoomScale = (int) ($this->sheetViewAttributes->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->sheetViewAttributes->zoomScaleNormal)) {
$zoomScaleNormal = (int) ($this->sheetViewAttributes->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->sheetViewAttributes->view)) {
$this->worksheet->getSheetView()->setView((string) $this->sheetViewAttributes->view);
}
}
private function topLeft(): void
{
if (isset($this->sheetViewAttributes->topLeftCell)) {
$this->worksheet->setTopLeftCell($this->sheetViewAttributes->topLeftCell);
}
}
private function gridLines(): void
{
if (isset($this->sheetViewAttributes->showGridLines)) {
$this->worksheet->setShowGridLines(
self::boolean((string) $this->sheetViewAttributes->showGridLines)
);
}
}
private function headers(): void
{
if (isset($this->sheetViewAttributes->showRowColHeaders)) {
$this->worksheet->setShowRowColHeaders(
self::boolean((string) $this->sheetViewAttributes->showRowColHeaders)
);
}
}
private function direction(): void
{
if (isset($this->sheetViewAttributes->rightToLeft)) {
$this->worksheet->setRightToLeft(
self::boolean((string) $this->sheetViewAttributes->rightToLeft)
);
}
}
private function showZeros(): void
{
if (isset($this->sheetViewAttributes->showZeros)) {
$this->worksheet->getSheetView()->setShowZeros(
self::boolean((string) $this->sheetViewAttributes->showZeros)
);
}
}
private function pane(): void
{
$xSplit = 0;
$ySplit = 0;
$topLeftCell = null;
$paneAttributes = $this->sheetViewXml->pane->attributes();
if (isset($paneAttributes->xSplit)) {
$xSplit = (int) ($paneAttributes->xSplit);
}
if (isset($paneAttributes->ySplit)) {
$ySplit = (int) ($paneAttributes->ySplit);
}
if (isset($paneAttributes->topLeftCell)) {
$topLeftCell = (string) $paneAttributes->topLeftCell;
}
$this->worksheet->freezePane(
Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1),
$topLeftCell
);
}
private function selection(): void
{
$attributes = $this->sheetViewXml->selection->attributes();
if ($attributes !== null) {
$sqref = (string) $attributes->sqref;
$sqref = explode(' ', $sqref);
$sqref = $sqref[0];
$this->worksheet->setSelectedCells($sqref);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Namespaces.php 0000644 00000012255 15242753725 0020242 0 ustar 00 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']);
}
}
public function load(?IReadFilter $readFilter = null, bool $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);
}
if ($readFilter !== null && get_class($readFilter) === DefaultReadFilter::class) {
$readFilter = null;
}
// 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, string $columnCoordinate, array $rowsAttributes): bool
{
foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
return true;
}
}
return false;
}
private function readColumnAttributes(SimpleXMLElement $worksheetCols, bool $readDataOnly): array
{
$columnAttributes = [];
foreach ($worksheetCols->col as $columnx) {
/** @scrutinizer ignore-call */
$column = $columnx->attributes();
if ($column !== null) {
$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, bool $readDataOnly): array
{
$columnAttributes = [];
if ($column !== null) {
if (isset($column['style']) && !$readDataOnly) {
$columnAttributes['xfIndex'] = (int) $column['style'];
}
if (isset($column['hidden']) && self::boolean($column['hidden'])) {
$columnAttributes['visible'] = false;
}
if (isset($column['collapsed']) && self::boolean($column['collapsed'])) {
$columnAttributes['collapsed'] = true;
}
if (isset($column['outlineLevel']) && ((int) $column['outlineLevel']) > 0) {
$columnAttributes['outlineLevel'] = (int) $column['outlineLevel'];
}
if (isset($column['width'])) {
$columnAttributes['width'] = (float) $column['width'];
}
}
return $columnAttributes;
}
private function isFilteredRow(IReadFilter $readFilter, int $rowCoordinate, array $columnsAttributes): bool
{
foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
return true;
}
}
return false;
}
private function readRowAttributes(SimpleXMLElement $worksheetRow, bool $readDataOnly): array
{
$rowAttributes = [];
foreach ($worksheetRow as $rowx) {
/** @scrutinizer ignore-call */
$row = $rowx->attributes();
if ($row !== null) {
if (isset($row['ht']) && !$readDataOnly) {
$rowAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
}
if (isset($row['hidden']) && self::boolean($row['hidden'])) {
$rowAttributes[(int) $row['r']]['visible'] = false;
}
if (isset($row['collapsed']) && self::boolean($row['collapsed'])) {
$rowAttributes[(int) $row['r']]['collapsed'] = true;
}
if (isset($row['outlineLevel']) && (int) $row['outlineLevel'] > 0) {
$rowAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel'];
}
if (isset($row['s']) && !$readDataOnly) {
$rowAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s'];
}
}
}
return $rowAttributes;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php 0000644 00000015742 15242753725 0020064 0 ustar 00 worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
}
public function load(array $unparsedLoadedData): array
{
$worksheetXml = $this->worksheetXml;
if ($worksheetXml === null) {
return $unparsedLoadedData;
}
$this->margins($worksheetXml, $this->worksheet);
$unparsedLoadedData = $this->pageSetup($worksheetXml, $this->worksheet, $unparsedLoadedData);
$this->headerFooter($worksheetXml, $this->worksheet);
$this->pageBreaks($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): array
{
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']));
}
if (isset($xmlSheet->pageSetup['pageOrder'])) {
$docPageSetup->setPageOrder((string) $xmlSheet->pageSetup['pageOrder']);
}
$relAttributes = $xmlSheet->pageSetup->attributes(Namespaces::SCHEMA_OFFICE_DOCUMENT);
if (isset($relAttributes['id'])) {
$relid = (string) $relAttributes['id'];
if (substr($relid, -2) !== 'ps') {
$relid .= 'ps';
}
$unparsedLoadedData['sheets'][$worksheet->getCodeName()]['pageSetupRelId'] = $relid;
}
}
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) {
$rowBreakMax = isset($brk['max']) ? ((int) $brk['max']) : -1;
if ($brk['man']) {
$worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW, $rowBreakMax);
}
}
}
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
);
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php 0000644 00000025721 15242753725 0021634 0 ustar 00 worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
$this->dxfs = $dxfs;
}
public function load(): void
{
$selectedCells = $this->worksheet->getSelectedCells();
$this->setConditionalStyles(
$this->worksheet,
$this->readConditionalStyles($this->worksheetXml),
$this->worksheetXml->extLst
);
$this->worksheet->setSelectedCells($selectedCells);
}
public function loadFromExt(StyleReader $styleReader): void
{
$selectedCells = $this->worksheet->getSelectedCells();
$this->ns = $this->worksheetXml->getNamespaces(true);
$this->setConditionalsFromExt(
$this->readConditionalsFromExt($this->worksheetXml->extLst, $styleReader)
);
$this->worksheet->setSelectedCells($selectedCells);
}
private function setConditionalsFromExt(array $conditionals): void
{
foreach ($conditionals as $conditionalRange => $cfRules) {
ksort($cfRules);
// Priority is used as the key for sorting; but may not start at 0,
// so we use array_values to reset the index after sorting.
$this->worksheet->getStyle($conditionalRange)
->setConditionalStyles(array_values($cfRules));
}
}
private function readConditionalsFromExt(SimpleXMLElement $extLst, StyleReader $styleReader): array
{
$conditionals = [];
if (isset($extLst->ext['uri']) && (string) $extLst->ext['uri'] === '{78C0D931-6437-407d-A8EE-F0AAD7539E65}') {
$conditionalFormattingRuleXml = $extLst->ext->children($this->ns['x14']);
if (!$conditionalFormattingRuleXml->conditionalFormattings) {
return [];
}
foreach ($conditionalFormattingRuleXml->children($this->ns['x14']) as $extFormattingXml) {
$extFormattingRangeXml = $extFormattingXml->children($this->ns['xm']);
if (!$extFormattingRangeXml->sqref) {
continue;
}
$sqref = (string) $extFormattingRangeXml->sqref;
$extCfRuleXml = $extFormattingXml->cfRule;
$attributes = $extCfRuleXml->attributes();
if (!$attributes) {
continue;
}
$conditionType = (string) $attributes->type;
if (
!Conditional::isValidConditionType($conditionType) ||
$conditionType === Conditional::CONDITION_DATABAR
) {
continue;
}
$priority = (int) $attributes->priority;
$conditional = $this->readConditionalRuleFromExt($extCfRuleXml, $attributes);
$cfStyle = $this->readStyleFromExt($extCfRuleXml, $styleReader);
$conditional->setStyle($cfStyle);
$conditionals[$sqref][$priority] = $conditional;
}
}
return $conditionals;
}
private function readConditionalRuleFromExt(SimpleXMLElement $cfRuleXml, SimpleXMLElement $attributes): Conditional
{
$conditionType = (string) $attributes->type;
$operatorType = (string) $attributes->operator;
$operands = [];
foreach ($cfRuleXml->children($this->ns['xm']) as $cfRuleOperandsXml) {
$operands[] = (string) $cfRuleOperandsXml;
}
$conditional = new Conditional();
$conditional->setConditionType($conditionType);
$conditional->setOperatorType($operatorType);
if (
$conditionType === Conditional::CONDITION_CONTAINSTEXT ||
$conditionType === Conditional::CONDITION_NOTCONTAINSTEXT ||
$conditionType === Conditional::CONDITION_BEGINSWITH ||
$conditionType === Conditional::CONDITION_ENDSWITH ||
$conditionType === Conditional::CONDITION_TIMEPERIOD
) {
$conditional->setText(array_pop($operands) ?? '');
}
$conditional->setConditions($operands);
return $conditional;
}
private function readStyleFromExt(SimpleXMLElement $extCfRuleXml, StyleReader $styleReader): Style
{
$cfStyle = new Style(false, true);
if ($extCfRuleXml->dxf) {
$styleXML = $extCfRuleXml->dxf->children();
if ($styleXML->borders) {
$styleReader->readBorderStyle($cfStyle->getBorders(), $styleXML->borders);
}
if ($styleXML->fill) {
$styleReader->readFillStyle($cfStyle->getFill(), $styleXML->fill);
}
}
return $cfStyle;
}
private function readConditionalStyles(SimpleXMLElement $xmlSheet): array
{
$conditionals = [];
foreach ($xmlSheet->conditionalFormatting as $conditional) {
foreach ($conditional->cfRule as $cfRule) {
if (Conditional::isValidConditionType((string) $cfRule['type']) && (!isset($cfRule['dxfId']) || isset($this->dxfs[(int) ($cfRule['dxfId'])]))) {
$conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
} elseif ((string) $cfRule['type'] == Conditional::CONDITION_DATABAR) {
$conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
}
}
}
return $conditionals;
}
private function setConditionalStyles(Worksheet $worksheet, array $conditionals, SimpleXMLElement $xmlExtLst): void
{
foreach ($conditionals as $cellRangeReference => $cfRules) {
ksort($cfRules);
$conditionalStyles = $this->readStyleRules($cfRules, $xmlExtLst);
// Extract all cell references in $cellRangeReference
$cellBlocks = explode(' ', str_replace('$', '', strtoupper($cellRangeReference)));
foreach ($cellBlocks as $cellBlock) {
$worksheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
}
}
}
private function readStyleRules(array $cfRules, SimpleXMLElement $extLst): array
{
$conditionalFormattingRuleExtensions = ConditionalFormattingRuleExtension::parseExtLstXml($extLst);
$conditionalStyles = [];
foreach ($cfRules as $cfRule) {
$objConditional = new Conditional();
$objConditional->setConditionType((string) $cfRule['type']);
$objConditional->setOperatorType((string) $cfRule['operator']);
$objConditional->setNoFormatSet(!isset($cfRule['dxfId']));
if ((string) $cfRule['text'] != '') {
$objConditional->setText((string) $cfRule['text']);
} elseif ((string) $cfRule['timePeriod'] != '') {
$objConditional->setText((string) $cfRule['timePeriod']);
}
if (isset($cfRule['stopIfTrue']) && (int) $cfRule['stopIfTrue'] === 1) {
$objConditional->setStopIfTrue(true);
}
if (count($cfRule->formula) >= 1) {
foreach ($cfRule->formula as $formulax) {
$formula = (string) $formulax;
if ($formula === 'TRUE') {
$objConditional->addCondition(true);
} elseif ($formula === 'FALSE') {
$objConditional->addCondition(false);
} else {
$objConditional->addCondition($formula);
}
}
} else {
$objConditional->addCondition('');
}
if (isset($cfRule->dataBar)) {
$objConditional->setDataBar(
$this->readDataBarOfConditionalRule($cfRule, $conditionalFormattingRuleExtensions) // @phpstan-ignore-line
);
} elseif (isset($cfRule['dxfId'])) {
$objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);
}
$conditionalStyles[] = $objConditional;
}
return $conditionalStyles;
}
/**
* @param SimpleXMLElement|stdClass $cfRule
*/
private function readDataBarOfConditionalRule($cfRule, array $conditionalFormattingRuleExtensions): ConditionalDataBar
{
$dataBar = new ConditionalDataBar();
//dataBar attribute
if (isset($cfRule->dataBar['showValue'])) {
$dataBar->setShowValue((bool) $cfRule->dataBar['showValue']);
}
//dataBar children
//conditionalFormatValueObjects
$cfvoXml = $cfRule->dataBar->cfvo;
$cfvoIndex = 0;
foreach ((count($cfvoXml) > 1 ? $cfvoXml : [$cfvoXml]) as $cfvo) {
if ($cfvoIndex === 0) {
$dataBar->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $cfvo['type'], (string) $cfvo['val']));
}
if ($cfvoIndex === 1) {
$dataBar->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $cfvo['type'], (string) $cfvo['val']));
}
++$cfvoIndex;
}
//color
if (isset($cfRule->dataBar->color)) {
$dataBar->setColor((string) $cfRule->dataBar->color['rgb']);
}
//extLst
$this->readDataBarExtLstOfConditionalRule($dataBar, $cfRule, $conditionalFormattingRuleExtensions);
return $dataBar;
}
/**
* @param SimpleXMLElement|stdClass $cfRule
*/
private function readDataBarExtLstOfConditionalRule(ConditionalDataBar $dataBar, $cfRule, array $conditionalFormattingRuleExtensions): void
{
if (isset($cfRule->extLst)) {
$ns = $cfRule->extLst->getNamespaces(true);
foreach ((count($cfRule->extLst) > 0 ? $cfRule->extLst->ext : [$cfRule->extLst->ext]) as $ext) {
$extId = (string) $ext->children($ns['x14'])->id;
if (isset($conditionalFormattingRuleExtensions[$extId]) && (string) $ext['uri'] === '{B025F937-C7B1-47D3-B67F-A62EFF666E3E}') {
$dataBar->setConditionalFormattingRuleExt($conditionalFormattingRuleExtensions[$extId]);
}
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php 0000644 00000037711 15242753725 0017452 0 ustar 00 namespace = $namespace;
}
public function setWorkbookPalette(array $palette): void
{
$this->workbookPalette = $palette;
}
/**
* Cast SimpleXMLElement to bool to overcome Scrutinizer problem.
*
* @param mixed $value
*/
private static function castBool($value): bool
{
return (bool) $value;
}
private function getStyleAttributes(SimpleXMLElement $value): SimpleXMLElement
{
$attr = null;
if (self::castBool($value)) {
$attr = $value->attributes('');
if ($attr === null || count($attr) === 0) {
$attr = $value->attributes($this->namespace);
}
}
return Xlsx::testSimpleXml($attr);
}
public function setStyleXml(SimpleXmlElement $styleXml): void
{
$this->styleXml = $styleXml;
}
public function setTheme(Theme $theme): void
{
$this->theme = $theme;
}
public function setStyleBaseData(?Theme $theme = null, array $styles = [], array $cellStyles = []): void
{
$this->theme = $theme;
$this->styles = $styles;
$this->cellStyles = $cellStyles;
}
public function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
{
if (isset($fontStyleXml->name)) {
$attr = $this->getStyleAttributes($fontStyleXml->name);
if (isset($attr['val'])) {
$fontStyle->setName((string) $attr['val']);
}
}
if (isset($fontStyleXml->sz)) {
$attr = $this->getStyleAttributes($fontStyleXml->sz);
if (isset($attr['val'])) {
$fontStyle->setSize((float) $attr['val']);
}
}
if (isset($fontStyleXml->b)) {
$attr = $this->getStyleAttributes($fontStyleXml->b);
$fontStyle->setBold(!isset($attr['val']) || self::boolean((string) $attr['val']));
}
if (isset($fontStyleXml->i)) {
$attr = $this->getStyleAttributes($fontStyleXml->i);
$fontStyle->setItalic(!isset($attr['val']) || self::boolean((string) $attr['val']));
}
if (isset($fontStyleXml->strike)) {
$attr = $this->getStyleAttributes($fontStyleXml->strike);
$fontStyle->setStrikethrough(!isset($attr['val']) || self::boolean((string) $attr['val']));
}
$fontStyle->getColor()->setARGB($this->readColor($fontStyleXml->color));
if (isset($fontStyleXml->u)) {
$attr = $this->getStyleAttributes($fontStyleXml->u);
if (!isset($attr['val'])) {
$fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
} else {
$fontStyle->setUnderline((string) $attr['val']);
}
}
if (isset($fontStyleXml->vertAlign)) {
$attr = $this->getStyleAttributes($fontStyleXml->vertAlign);
if (isset($attr['val'])) {
$verticalAlign = strtolower((string) $attr['val']);
if ($verticalAlign === 'superscript') {
$fontStyle->setSuperscript(true);
} elseif ($verticalAlign === 'subscript') {
$fontStyle->setSubscript(true);
}
}
}
if (isset($fontStyleXml->scheme)) {
$attr = $this->getStyleAttributes($fontStyleXml->scheme);
$fontStyle->setScheme((string) $attr['val']);
}
}
private function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
{
if ((string) $numfmtStyleXml['formatCode'] !== '') {
$numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmtStyleXml['formatCode']));
return;
}
$numfmt = $this->getStyleAttributes($numfmtStyleXml);
if (isset($numfmt['formatCode'])) {
$numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmt['formatCode']));
}
}
public function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
{
if ($fillStyleXml->gradientFill) {
/** @var SimpleXMLElement $gradientFill */
$gradientFill = $fillStyleXml->gradientFill[0];
$attr = $this->getStyleAttributes($gradientFill);
if (!empty($attr['type'])) {
$fillStyle->setFillType((string) $attr['type']);
}
$fillStyle->setRotation((float) ($attr['degree']));
$gradientFill->registerXPathNamespace('sml', Namespaces::MAIN);
$fillStyle->getStartColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
$fillStyle->getEndColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
} elseif ($fillStyleXml->patternFill) {
$defaultFillStyle = Fill::FILL_NONE;
if ($fillStyleXml->patternFill->fgColor) {
$fillStyle->getStartColor()->setARGB($this->readColor($fillStyleXml->patternFill->fgColor, true));
$defaultFillStyle = Fill::FILL_SOLID;
}
if ($fillStyleXml->patternFill->bgColor) {
$fillStyle->getEndColor()->setARGB($this->readColor($fillStyleXml->patternFill->bgColor, true));
$defaultFillStyle = Fill::FILL_SOLID;
}
$type = '';
if ((string) $fillStyleXml->patternFill['patternType'] !== '') {
$type = (string) $fillStyleXml->patternFill['patternType'];
} else {
$attr = $this->getStyleAttributes($fillStyleXml->patternFill);
$type = (string) $attr['patternType'];
}
$patternType = ($type === '') ? $defaultFillStyle : $type;
$fillStyle->setFillType($patternType);
}
}
public function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
{
$diagonalUp = $this->getAttribute($borderStyleXml, 'diagonalUp');
$diagonalUp = self::boolean($diagonalUp);
$diagonalDown = $this->getAttribute($borderStyleXml, 'diagonalDown');
$diagonalDown = self::boolean($diagonalDown);
if ($diagonalUp === false) {
if ($diagonalDown === false) {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
} else {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
}
} elseif ($diagonalDown === false) {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
} else {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
}
if (isset($borderStyleXml->left)) {
$this->readBorder($borderStyle->getLeft(), $borderStyleXml->left);
}
if (isset($borderStyleXml->right)) {
$this->readBorder($borderStyle->getRight(), $borderStyleXml->right);
}
if (isset($borderStyleXml->top)) {
$this->readBorder($borderStyle->getTop(), $borderStyleXml->top);
}
if (isset($borderStyleXml->bottom)) {
$this->readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
}
if (isset($borderStyleXml->diagonal)) {
$this->readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
}
}
private function getAttribute(SimpleXMLElement $xml, string $attribute): string
{
$style = '';
if ((string) $xml[$attribute] !== '') {
$style = (string) $xml[$attribute];
} else {
$attr = $this->getStyleAttributes($xml);
if (isset($attr[$attribute])) {
$style = (string) $attr[$attribute];
}
}
return $style;
}
private function readBorder(Border $border, SimpleXMLElement $borderXml): void
{
$style = $this->getAttribute($borderXml, 'style');
if ($style !== '') {
$border->setBorderStyle((string) $style);
} else {
$border->setBorderStyle(Border::BORDER_NONE);
}
if (isset($borderXml->color)) {
$border->getColor()->setARGB($this->readColor($borderXml->color));
}
}
public function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
{
$horizontal = (string) $this->getAttribute($alignmentXml, 'horizontal');
if ($horizontal !== '') {
$alignment->setHorizontal($horizontal);
}
$vertical = (string) $this->getAttribute($alignmentXml, 'vertical');
if ($vertical !== '') {
$alignment->setVertical($vertical);
}
$textRotation = (int) $this->getAttribute($alignmentXml, 'textRotation');
if ($textRotation > 90) {
$textRotation = 90 - $textRotation;
}
$alignment->setTextRotation($textRotation);
$wrapText = $this->getAttribute($alignmentXml, 'wrapText');
$alignment->setWrapText(self::boolean((string) $wrapText));
$shrinkToFit = $this->getAttribute($alignmentXml, 'shrinkToFit');
$alignment->setShrinkToFit(self::boolean((string) $shrinkToFit));
$indent = (int) $this->getAttribute($alignmentXml, 'indent');
$alignment->setIndent(max($indent, 0));
$readingOrder = (int) $this->getAttribute($alignmentXml, 'readingOrder');
$alignment->setReadOrder(max($readingOrder, 0));
}
private static function formatGeneral(string $formatString): string
{
if ($formatString === 'GENERAL') {
$formatString = NumberFormat::FORMAT_GENERAL;
}
return $formatString;
}
/**
* Read style.
*
* @param SimpleXMLElement|stdClass $style
*/
public function readStyle(Style $docStyle, $style): void
{
if ($style instanceof SimpleXMLElement) {
$this->readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
} else {
$docStyle->getNumberFormat()->setFormatCode(self::formatGeneral((string) $style->numFmt));
}
if (isset($style->font)) {
$this->readFontStyle($docStyle->getFont(), $style->font);
}
if (isset($style->fill)) {
$this->readFillStyle($docStyle->getFill(), $style->fill);
}
if (isset($style->border)) {
$this->readBorderStyle($docStyle->getBorders(), $style->border);
}
if (isset($style->alignment)) {
$this->readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
}
// protection
if (isset($style->protection)) {
$this->readProtectionLocked($docStyle, $style->protection);
$this->readProtectionHidden($docStyle, $style->protection);
}
// top-level style settings
if (isset($style->quotePrefix)) {
$docStyle->setQuotePrefix((bool) $style->quotePrefix);
}
}
/**
* Read protection locked attribute.
*/
public function readProtectionLocked(Style $docStyle, SimpleXMLElement $style): void
{
$locked = '';
if ((string) $style['locked'] !== '') {
$locked = (string) $style['locked'];
} else {
$attr = $this->getStyleAttributes($style);
if (isset($attr['locked'])) {
$locked = (string) $attr['locked'];
}
}
if ($locked !== '') {
if (self::boolean($locked)) {
$docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
} else {
$docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
}
}
}
/**
* Read protection hidden attribute.
*/
public function readProtectionHidden(Style $docStyle, SimpleXMLElement $style): void
{
$hidden = '';
if ((string) $style['hidden'] !== '') {
$hidden = (string) $style['hidden'];
} else {
$attr = $this->getStyleAttributes($style);
if (isset($attr['hidden'])) {
$hidden = (string) $attr['hidden'];
}
}
if ($hidden !== '') {
if (self::boolean((string) $hidden)) {
$docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
} else {
$docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
}
}
}
public function readColor(SimpleXMLElement $color, bool $background = false): string
{
$attr = $this->getStyleAttributes($color);
if (isset($attr['rgb'])) {
return (string) $attr['rgb'];
}
if (isset($attr['indexed'])) {
$indexedColor = (int) $attr['indexed'];
if ($indexedColor >= count($this->workbookPalette)) {
return Color::indexedColor($indexedColor - 7, $background)->getARGB() ?? '';
}
return Color::indexedColor($indexedColor, $background, $this->workbookPalette)->getARGB() ?? '';
}
if (isset($attr['theme'])) {
if ($this->theme !== null) {
$returnColour = $this->theme->getColourByIndex((int) $attr['theme']);
if (isset($attr['tint'])) {
$tintAdjust = (float) $attr['tint'];
$returnColour = Color::changeBrightness($returnColour ?? '', $tintAdjust);
}
return 'FF' . $returnColour;
}
}
return ($background) ? 'FFFFFFFF' : 'FF000000';
}
public function dxfs(bool $readDataOnly = false): array
{
$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 $cellStylex) {
$cellStyle = Xlsx::getAttributes($cellStylex);
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(): array
{
return $this->styles;
}
/**
* Get array item.
*
* @param mixed $array (usually array, in theory can be false)
*
* @return stdClass
*/
private static function getArrayItem($array, int $key = 0)
{
return is_array($array) ? ($array[$key] ?? null) : null;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php 0000644 00000013205 15242753725 0020607 0 ustar 00 spreadsheet = $spreadsheet;
}
/**
* @param mixed $mainNS
*/
public function viewSettings(SimpleXMLElement $xmlWorkbook, $mainNS, array $mapSheetId, bool $readDataOnly): void
{
if ($this->spreadsheet->getSheetCount() == 0) {
$this->spreadsheet->createSheet();
}
// Default active sheet index to the first loaded worksheet from the file
$this->spreadsheet->setActiveSheetIndex(0);
$workbookView = $xmlWorkbook->children($mainNS)->bookViews->workbookView;
if ($readDataOnly !== true && !empty($workbookView)) {
$workbookViewAttributes = self::testSimpleXml(self::getAttributes($workbookView));
// active sheet index
$activeTab = (int) $workbookViewAttributes->activeTab; // refers to old sheet index
// keep active sheet index if sheet is still loaded, else first sheet is set as the active worksheet
if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) {
$this->spreadsheet->setActiveSheetIndex($mapSheetId[$activeTab]);
}
$this->horizontalScroll($workbookViewAttributes);
$this->verticalScroll($workbookViewAttributes);
$this->sheetTabs($workbookViewAttributes);
$this->minimized($workbookViewAttributes);
$this->autoFilterDateGrouping($workbookViewAttributes);
$this->firstSheet($workbookViewAttributes);
$this->visibility($workbookViewAttributes);
$this->tabRatio($workbookViewAttributes);
}
}
/**
* @param mixed $value
*/
public static function testSimpleXml($value): SimpleXMLElement
{
return ($value instanceof SimpleXMLElement)
? $value
: new SimpleXMLElement('');
}
public static function getAttributes(?SimpleXMLElement $value, string $ns = ''): SimpleXMLElement
{
return self::testSimpleXml($value === null ? $value : $value->attributes($ns));
}
/**
* Convert an 'xsd:boolean' XML value to a PHP boolean value.
* A valid 'xsd:boolean' XML value can be one of the following
* four values: 'true', 'false', '1', '0'. It is case sensitive.
*
* Note that just doing '(bool) $xsdBoolean' is not safe,
* since '(bool) "false"' returns true.
*
* @see https://www.w3.org/TR/xmlschema11-2/#boolean
*
* @param string $xsdBoolean An XML string value of type 'xsd:boolean'
*
* @return bool Boolean value
*/
private function castXsdBooleanToBool(string $xsdBoolean): bool
{
if ($xsdBoolean === 'false') {
return false;
}
return (bool) $xsdBoolean;
}
private function horizontalScroll(SimpleXMLElement $workbookViewAttributes): void
{
if (isset($workbookViewAttributes->showHorizontalScroll)) {
$showHorizontalScroll = (string) $workbookViewAttributes->showHorizontalScroll;
$this->spreadsheet->setShowHorizontalScroll($this->castXsdBooleanToBool($showHorizontalScroll));
}
}
private function verticalScroll(SimpleXMLElement $workbookViewAttributes): void
{
if (isset($workbookViewAttributes->showVerticalScroll)) {
$showVerticalScroll = (string) $workbookViewAttributes->showVerticalScroll;
$this->spreadsheet->setShowVerticalScroll($this->castXsdBooleanToBool($showVerticalScroll));
}
}
private function sheetTabs(SimpleXMLElement $workbookViewAttributes): void
{
if (isset($workbookViewAttributes->showSheetTabs)) {
$showSheetTabs = (string) $workbookViewAttributes->showSheetTabs;
$this->spreadsheet->setShowSheetTabs($this->castXsdBooleanToBool($showSheetTabs));
}
}
private function minimized(SimpleXMLElement $workbookViewAttributes): void
{
if (isset($workbookViewAttributes->minimized)) {
$minimized = (string) $workbookViewAttributes->minimized;
$this->spreadsheet->setMinimized($this->castXsdBooleanToBool($minimized));
}
}
private function autoFilterDateGrouping(SimpleXMLElement $workbookViewAttributes): void
{
if (isset($workbookViewAttributes->autoFilterDateGrouping)) {
$autoFilterDateGrouping = (string) $workbookViewAttributes->autoFilterDateGrouping;
$this->spreadsheet->setAutoFilterDateGrouping($this->castXsdBooleanToBool($autoFilterDateGrouping));
}
}
private function firstSheet(SimpleXMLElement $workbookViewAttributes): void
{
if (isset($workbookViewAttributes->firstSheet)) {
$firstSheet = (string) $workbookViewAttributes->firstSheet;
$this->spreadsheet->setFirstSheetIndex((int) $firstSheet);
}
}
private function visibility(SimpleXMLElement $workbookViewAttributes): void
{
if (isset($workbookViewAttributes->visibility)) {
$visibility = (string) $workbookViewAttributes->visibility;
$this->spreadsheet->setVisibility($visibility);
}
}
private function tabRatio(SimpleXMLElement $workbookViewAttributes): void
{
if (isset($workbookViewAttributes->tabRatio)) {
$tabRatio = (string) $workbookViewAttributes->tabRatio;
$this->spreadsheet->setTabRatio((int) $tabRatio);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php 0000644 00000004331 15242753725 0020307 0 ustar 00 worksheet = $workSheet;
}
public function readHyperlinks(SimpleXMLElement $relsWorksheet): void
{
foreach ($relsWorksheet->children(Namespaces::RELATIONSHIPS)->Relationship as $elementx) {
$element = Xlsx::getAttributes($elementx);
if ($element->Type == Namespaces::HYPERLINK) {
$this->hyperlinks[(string) $element->Id] = (string) $element->Target;
}
}
}
public function setHyperlinks(SimpleXMLElement $worksheetXml): void
{
foreach ($worksheetXml->children(Namespaces::MAIN)->hyperlink as $hyperlink) {
if ($hyperlink !== null) {
$this->setHyperlink($hyperlink, $this->worksheet);
}
}
}
private function setHyperlink(SimpleXMLElement $hyperlink, Worksheet $worksheet): void
{
// Link url
$linkRel = Xlsx::getAttributes($hyperlink, Namespaces::SCHEMA_OFFICE_DOCUMENT);
$attributes = Xlsx::getAttributes($hyperlink);
foreach (Coordinate::extractAllCellReferencesInRange($attributes->ref) as $cellReference) {
$cell = $worksheet->getCell($cellReference);
if (isset($linkRel['id'])) {
$hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']] ?? null;
if (isset($attributes['location'])) {
$hyperlinkUrl .= '#' . (string) $attributes['location'];
}
$cell->getHyperlink()->setUrl($hyperlinkUrl);
} elseif (isset($attributes['location'])) {
$cell->getHyperlink()->setUrl('sheet://' . (string) $attributes['location']);
}
// Tooltip
if (isset($attributes['tooltip'])) {
$cell->getHyperlink()->setTooltip((string) $attributes['tooltip']);
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php 0000644 00000006243 15242753725 0021232 0 ustar 00 worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
}
public function load(): void
{
foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
// Uppercase coordinate
$range = strtoupper((string) $dataValidation['sqref']);
$rangeSet = explode(' ', $range);
foreach ($rangeSet as $range) {
if (preg_match('/^[A-Z]{1,3}\\d{1,7}/', $range, $matches) === 1) {
// Ensure left/top row of range exists, thereby
// adjusting high row/column.
$this->worksheet->getCell($matches[0]);
}
}
}
foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
// Uppercase coordinate
$range = strtoupper((string) $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(filter_var($dataValidation['allowBlank'], FILTER_VALIDATE_BOOLEAN));
// showDropDown is inverted (works as hideDropDown if true)
$docValidation->setShowDropDown(!filter_var($dataValidation['showDropDown'], FILTER_VALIDATE_BOOLEAN));
$docValidation->setShowInputMessage(filter_var($dataValidation['showInputMessage'], FILTER_VALIDATE_BOOLEAN));
$docValidation->setShowErrorMessage(filter_var($dataValidation['showErrorMessage'], FILTER_VALIDATE_BOOLEAN));
$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);
$docValidation->setSqref($range);
}
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php 0000644 00000000655 15242753725 0021201 0 ustar 00 worksheet = $workSheet;
$this->tableXml = $tableXml;
}
/**
* Loads Table into the Worksheet.
*/
public function load(): void
{
// Remove all "$" in the table range
$tableRange = (string) preg_replace('/\$/', '', $this->tableXml['ref'] ?? '');
if (strpos($tableRange, ':') !== false) {
$this->readTable($tableRange, $this->tableXml);
}
}
/**
* Read Table from xml.
*/
private function readTable(string $tableRange, SimpleXMLElement $tableXml): void
{
$table = new Table($tableRange);
$table->setName((string) $tableXml['displayName']);
$table->setShowHeaderRow((string) $tableXml['headerRowCount'] !== '0');
$table->setShowTotalsRow((string) $tableXml['totalsRowCount'] === '1');
$this->readTableAutoFilter($table, $tableXml->autoFilter);
$this->readTableColumns($table, $tableXml->tableColumns);
$this->readTableStyle($table, $tableXml->tableStyleInfo);
(new AutoFilter($table, $tableXml))->load();
$this->worksheet->addTable($table);
}
/**
* Reads TableAutoFilter from xml.
*/
private function readTableAutoFilter(Table $table, SimpleXMLElement $autoFilterXml): void
{
if ($autoFilterXml->filterColumn === null) {
$table->setAllowFilter(false);
return;
}
foreach ($autoFilterXml->filterColumn as $filterColumn) {
$column = $table->getColumnByOffset((int) $filterColumn['colId']);
$column->setShowFilterButton((string) $filterColumn['hiddenButton'] !== '1');
}
}
/**
* Reads TableColumns from xml.
*/
private function readTableColumns(Table $table, SimpleXMLElement $tableColumnsXml): void
{
$offset = 0;
foreach ($tableColumnsXml->tableColumn as $tableColumn) {
$column = $table->getColumnByOffset($offset++);
if ($table->getShowTotalsRow()) {
if ($tableColumn['totalsRowLabel']) {
$column->setTotalsRowLabel((string) $tableColumn['totalsRowLabel']);
}
if ($tableColumn['totalsRowFunction']) {
$column->setTotalsRowFunction((string) $tableColumn['totalsRowFunction']);
}
}
if ($tableColumn->calculatedColumnFormula) {
$column->setColumnFormula((string) $tableColumn->calculatedColumnFormula);
}
}
}
/**
* Reads TableStyle from xml.
*/
private function readTableStyle(Table $table, SimpleXMLElement $tableStyleInfoXml): void
{
$tableStyle = new TableStyle();
$tableStyle->setTheme((string) $tableStyleInfoXml['name']);
$tableStyle->setShowRowStripes((string) $tableStyleInfoXml['showRowStripes'] === '1');
$tableStyle->setShowColumnStripes((string) $tableStyleInfoXml['showColumnStripes'] === '1');
$tableStyle->setShowFirstColumn((string) $tableStyleInfoXml['showFirstColumn'] === '1');
$tableStyle->setShowLastColumn((string) $tableStyleInfoXml['showLastColumn'] === '1');
$table->setStyle($tableStyle);
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php 0000644 00000011337 15242753725 0021442 0 ustar 00 worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
}
public function load(bool $readDataOnly, Styles $styleReader): void
{
if ($this->worksheetXml === null) {
return;
}
if (isset($this->worksheetXml->sheetPr)) {
$sheetPr = $this->worksheetXml->sheetPr;
$this->tabColor($sheetPr, $styleReader);
$this->codeName($sheetPr);
$this->outlines($sheetPr);
$this->pageSetup($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, Styles $styleReader): void
{
if (isset($sheetPr->tabColor)) {
$this->worksheet->getTabColor()->setARGB($styleReader->readColor($sheetPr->tabColor));
}
}
private function codeName(SimpleXMLElement $sheetPrx): void
{
$sheetPr = $sheetPrx->attributes() ?? [];
if (isset($sheetPr['codeName'])) {
$this->worksheet->setCodeName((string) $sheetPr['codeName'], false);
}
}
private function outlines(SimpleXMLElement $sheetPr): void
{
if (isset($sheetPr->outlinePr)) {
$attr = $sheetPr->outlinePr->attributes() ?? [];
if (
isset($attr['summaryRight']) &&
!self::boolean((string) $attr['summaryRight'])
) {
$this->worksheet->setShowSummaryRight(false);
} else {
$this->worksheet->setShowSummaryRight(true);
}
if (
isset($attr['summaryBelow']) &&
!self::boolean((string) $attr['summaryBelow'])
) {
$this->worksheet->setShowSummaryBelow(false);
} else {
$this->worksheet->setShowSummaryBelow(true);
}
}
}
private function pageSetup(SimpleXMLElement $sheetPr): void
{
if (isset($sheetPr->pageSetUpPr)) {
$attr = $sheetPr->pageSetUpPr->attributes() ?? [];
if (
isset($attr['fitToPage']) &&
!self::boolean((string) $attr['fitToPage'])
) {
$this->worksheet->getPageSetup()->setFitToPage(false);
} else {
$this->worksheet->getPageSetup()->setFitToPage(true);
}
}
}
private function sheetFormat(SimpleXMLElement $sheetFormatPrx): void
{
$sheetFormatPr = $sheetFormatPrx->attributes() ?? [];
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 $printOptionsx): void
{
$printOptions = $printOptionsx->attributes() ?? [];
if (isset($printOptions['gridLinesSet']) && self::boolean((string) $printOptions['gridLinesSet'])) {
$this->worksheet->setShowGridlines(true);
}
if (isset($printOptions['gridLines']) && self::boolean((string) $printOptions['gridLines'])) {
$this->worksheet->setPrintGridlines(true);
}
if (isset($printOptions['horizontalCentered']) && self::boolean((string) $printOptions['horizontalCentered'])) {
$this->worksheet->getPageSetup()->setHorizontalCentered(true);
}
if (isset($printOptions['verticalCentered']) && self::boolean((string) $printOptions['verticalCentered'])) {
$this->worksheet->getPageSetup()->setVerticalCentered(true);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Chart.php 0000644 00000230535 15242753725 0017227 0 ustar 00 cNamespace = $cNamespace;
$this->aNamespace = $aNamespace;
}
/**
* @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') {
$value = (string) $attributes[$name];
return $value === 'true' || $value === '1';
}
return (float) $attributes[$name];
}
return null;
}
/**
* @param string $chartName
*
* @return \PhpOffice\PhpSpreadsheet\Chart\Chart
*/
public function readChart(SimpleXMLElement $chartElements, $chartName)
{
$chartElementsC = $chartElements->children($this->cNamespace);
$XaxisLabel = $YaxisLabel = $legend = $title = null;
$dispBlanksAs = $plotVisOnly = null;
$plotArea = null;
$rotX = $rotY = $rAngAx = $perspective = null;
$xAxis = new Axis();
$yAxis = new Axis();
$autoTitleDeleted = null;
$chartNoFill = false;
$chartBorderLines = null;
$chartFillColor = null;
$gradientArray = [];
$gradientLin = null;
$roundedCorners = false;
$gapWidth = null;
$useUpBars = null;
$useDownBars = null;
foreach ($chartElementsC as $chartElementKey => $chartElement) {
switch ($chartElementKey) {
case 'spPr':
$children = $chartElementsC->spPr->children($this->aNamespace);
if (isset($children->noFill)) {
$chartNoFill = true;
}
if (isset($children->solidFill)) {
$chartFillColor = $this->readColor($children->solidFill);
}
if (isset($children->ln)) {
$chartBorderLines = new GridLines();
$this->readLineStyle($chartElementsC, $chartBorderLines);
}
break;
case 'roundedCorners':
/** @var bool */
$roundedCorners = self::getAttribute($chartElementsC->roundedCorners, 'val', 'boolean');
break;
case 'chart':
foreach ($chartElement as $chartDetailsKey => $chartDetails) {
$chartDetails = Xlsx::testSimpleXml($chartDetails);
switch ($chartDetailsKey) {
case 'autoTitleDeleted':
/** @var bool */
$autoTitleDeleted = self::getAttribute($chartElementsC->chart->autoTitleDeleted, 'val', 'boolean');
break;
case 'view3D':
$rotX = self::getAttribute($chartDetails->rotX, 'val', 'integer');
$rotY = self::getAttribute($chartDetails->rotY, 'val', 'integer');
$rAngAx = self::getAttribute($chartDetails->rAngAx, 'val', 'integer');
$perspective = self::getAttribute($chartDetails->perspective, 'val', 'integer');
break;
case 'plotArea':
$plotAreaLayout = $XaxisLabel = $YaxisLabel = null;
$plotSeries = $plotAttributes = [];
$catAxRead = false;
$plotNoFill = false;
foreach ($chartDetails as $chartDetailKey => $chartDetail) {
$chartDetail = Xlsx::testSimpleXml($chartDetail);
switch ($chartDetailKey) {
case 'spPr':
$possibleNoFill = $chartDetails->spPr->children($this->aNamespace);
if (isset($possibleNoFill->noFill)) {
$plotNoFill = true;
}
if (isset($possibleNoFill->gradFill->gsLst)) {
foreach ($possibleNoFill->gradFill->gsLst->gs as $gradient) {
$gradient = Xlsx::testSimpleXml($gradient);
/** @var float */
$pos = self::getAttribute($gradient, 'pos', 'float');
$gradientArray[] = [
$pos / ChartProperties::PERCENTAGE_MULTIPLIER,
new ChartColor($this->readColor($gradient)),
];
}
}
if (isset($possibleNoFill->gradFill->lin)) {
$gradientLin = ChartProperties::XmlToAngle((string) self::getAttribute($possibleNoFill->gradFill->lin, 'ang', 'string'));
}
break;
case 'layout':
$plotAreaLayout = $this->chartLayoutDetails($chartDetail);
break;
case Axis::AXIS_TYPE_CATEGORY:
case Axis::AXIS_TYPE_DATE:
$catAxRead = true;
if (isset($chartDetail->title)) {
$XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
}
$xAxis->setAxisType($chartDetailKey);
$this->readEffects($chartDetail, $xAxis);
$this->readLineStyle($chartDetail, $xAxis);
if (isset($chartDetail->spPr)) {
$sppr = $chartDetail->spPr->children($this->aNamespace);
if (isset($sppr->solidFill)) {
$axisColorArray = $this->readColor($sppr->solidFill);
$xAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
}
if (isset($chartDetail->spPr->ln->noFill)) {
$xAxis->setNoFill(true);
}
}
if (isset($chartDetail->majorGridlines)) {
$majorGridlines = new GridLines();
if (isset($chartDetail->majorGridlines->spPr)) {
$this->readEffects($chartDetail->majorGridlines, $majorGridlines);
$this->readLineStyle($chartDetail->majorGridlines, $majorGridlines);
}
$xAxis->setMajorGridlines($majorGridlines);
}
if (isset($chartDetail->minorGridlines)) {
$minorGridlines = new GridLines();
$minorGridlines->activateObject();
if (isset($chartDetail->minorGridlines->spPr)) {
$this->readEffects($chartDetail->minorGridlines, $minorGridlines);
$this->readLineStyle($chartDetail->minorGridlines, $minorGridlines);
}
$xAxis->setMinorGridlines($minorGridlines);
}
$this->setAxisProperties($chartDetail, $xAxis);
break;
case Axis::AXIS_TYPE_VALUE:
$whichAxis = null;
$axPos = null;
if (isset($chartDetail->axPos)) {
$axPos = self::getAttribute($chartDetail->axPos, 'val', 'string');
}
if ($catAxRead) {
$whichAxis = $yAxis;
$yAxis->setAxisType($chartDetailKey);
} elseif (!empty($axPos)) {
switch ($axPos) {
case 't':
case 'b':
$whichAxis = $xAxis;
$xAxis->setAxisType($chartDetailKey);
break;
case 'r':
case 'l':
$whichAxis = $yAxis;
$yAxis->setAxisType($chartDetailKey);
break;
}
}
if (isset($chartDetail->title)) {
$axisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
switch ($axPos) {
case 't':
case 'b':
$XaxisLabel = $axisLabel;
break;
case 'r':
case 'l':
$YaxisLabel = $axisLabel;
break;
}
}
$this->readEffects($chartDetail, $whichAxis);
$this->readLineStyle($chartDetail, $whichAxis);
if ($whichAxis !== null && isset($chartDetail->spPr)) {
$sppr = $chartDetail->spPr->children($this->aNamespace);
if (isset($sppr->solidFill)) {
$axisColorArray = $this->readColor($sppr->solidFill);
$whichAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
}
if (isset($sppr->ln->noFill)) {
$whichAxis->setNoFill(true);
}
}
if ($whichAxis !== null && isset($chartDetail->majorGridlines)) {
$majorGridlines = new GridLines();
if (isset($chartDetail->majorGridlines->spPr)) {
$this->readEffects($chartDetail->majorGridlines, $majorGridlines);
$this->readLineStyle($chartDetail->majorGridlines, $majorGridlines);
}
$whichAxis->setMajorGridlines($majorGridlines);
}
if ($whichAxis !== null && isset($chartDetail->minorGridlines)) {
$minorGridlines = new GridLines();
$minorGridlines->activateObject();
if (isset($chartDetail->minorGridlines->spPr)) {
$this->readEffects($chartDetail->minorGridlines, $minorGridlines);
$this->readLineStyle($chartDetail->minorGridlines, $minorGridlines);
}
$whichAxis->setMinorGridlines($minorGridlines);
}
$this->setAxisProperties($chartDetail, $whichAxis);
break;
case 'barChart':
case 'bar3DChart':
$barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string');
$plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotDirection("$barDirection");
$plotSeries[] = $plotSer;
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
case 'lineChart':
case 'line3DChart':
$plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
case 'areaChart':
case 'area3DChart':
$plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
case 'doughnutChart':
case 'pieChart':
case 'pie3DChart':
$explosion = self::getAttribute($chartDetail->ser->explosion, 'val', 'string');
$plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle("$explosion");
$plotSeries[] = $plotSer;
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
case 'scatterChart':
/** @var string */
$scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string');
$plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($scatterStyle);
$plotSeries[] = $plotSer;
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
case 'bubbleChart':
$bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer');
$plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle("$bubbleScale");
$plotSeries[] = $plotSer;
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
case 'radarChart':
/** @var string */
$radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string');
$plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($radarStyle);
$plotSeries[] = $plotSer;
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
case 'surfaceChart':
case 'surface3DChart':
$wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean');
$plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle("$wireFrame");
$plotSeries[] = $plotSer;
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
case 'stockChart':
$plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
if (isset($chartDetail->upDownBars->gapWidth)) {
$gapWidth = self::getAttribute($chartDetail->upDownBars->gapWidth, 'val', 'integer');
}
if (isset($chartDetail->upDownBars->upBars)) {
$useUpBars = true;
}
if (isset($chartDetail->upDownBars->downBars)) {
$useDownBars = true;
}
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
}
}
if ($plotAreaLayout == null) {
$plotAreaLayout = new Layout();
}
$plotArea = new PlotArea($plotAreaLayout, $plotSeries);
$this->setChartAttributes($plotAreaLayout, $plotAttributes);
if ($plotNoFill) {
$plotArea->setNoFill(true);
}
if (!empty($gradientArray)) {
$plotArea->setGradientFillProperties($gradientArray, $gradientLin);
}
if (is_int($gapWidth)) {
$plotArea->setGapWidth($gapWidth);
}
if ($useUpBars === true) {
$plotArea->setUseUpBars(true);
}
if ($useDownBars === true) {
$plotArea->setUseDownBars(true);
}
break;
case 'plotVisOnly':
$plotVisOnly = self::getAttribute($chartDetails, 'val', 'string');
break;
case 'dispBlanksAs':
$dispBlanksAs = self::getAttribute($chartDetails, 'val', 'string');
break;
case 'title':
$title = $this->chartTitle($chartDetails);
break;
case 'legend':
$legendPos = 'r';
$legendLayout = null;
$legendOverlay = false;
$legendBorderLines = null;
$legendFillColor = null;
$legendText = null;
$addLegendText = false;
foreach ($chartDetails as $chartDetailKey => $chartDetail) {
$chartDetail = Xlsx::testSimpleXml($chartDetail);
switch ($chartDetailKey) {
case 'legendPos':
$legendPos = self::getAttribute($chartDetail, 'val', 'string');
break;
case 'overlay':
$legendOverlay = self::getAttribute($chartDetail, 'val', 'boolean');
break;
case 'layout':
$legendLayout = $this->chartLayoutDetails($chartDetail);
break;
case 'spPr':
$children = $chartDetails->spPr->children($this->aNamespace);
if (isset($children->solidFill)) {
$legendFillColor = $this->readColor($children->solidFill);
}
if (isset($children->ln)) {
$legendBorderLines = new GridLines();
$this->readLineStyle($chartDetails, $legendBorderLines);
}
break;
case 'txPr':
$children = $chartDetails->txPr->children($this->aNamespace);
$addLegendText = false;
$legendText = new AxisText();
if (isset($children->p->pPr->defRPr->solidFill)) {
$colorArray = $this->readColor($children->p->pPr->defRPr->solidFill);
$legendText->getFillColorObject()->setColorPropertiesArray($colorArray);
$addLegendText = true;
}
if (isset($children->p->pPr->defRPr->effectLst)) {
$this->readEffects($children->p->pPr->defRPr, $legendText, false);
$addLegendText = true;
}
break;
}
}
$legend = new Legend("$legendPos", $legendLayout, (bool) $legendOverlay);
if ($legendFillColor !== null) {
$legend->getFillColor()->setColorPropertiesArray($legendFillColor);
}
if ($legendBorderLines !== null) {
$legend->setBorderLines($legendBorderLines);
}
if ($addLegendText) {
$legend->setLegendText($legendText);
}
break;
}
}
}
}
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, (string) $dispBlanksAs, $XaxisLabel, $YaxisLabel, $xAxis, $yAxis);
if ($chartNoFill) {
$chart->setNoFill(true);
}
if ($chartFillColor !== null) {
$chart->getFillColor()->setColorPropertiesArray($chartFillColor);
}
if ($chartBorderLines !== null) {
$chart->setBorderLines($chartBorderLines);
}
$chart->setRoundedCorners($roundedCorners);
if (is_bool($autoTitleDeleted)) {
$chart->setAutoTitleDeleted($autoTitleDeleted);
}
if (is_int($rotX)) {
$chart->setRotX($rotX);
}
if (is_int($rotY)) {
$chart->setRotY($rotY);
}
if (is_int($rAngAx)) {
$chart->setRAngAx($rAngAx);
}
if (is_int($perspective)) {
$chart->setPerspective($perspective);
}
return $chart;
}
private function chartTitle(SimpleXMLElement $titleDetails): Title
{
$caption = [];
$titleLayout = null;
$titleOverlay = false;
foreach ($titleDetails as $titleDetailKey => $chartDetail) {
$chartDetail = Xlsx::testSimpleXml($chartDetail);
switch ($titleDetailKey) {
case 'tx':
if (isset($chartDetail->rich)) {
$titleDetails = $chartDetail->rich->children($this->aNamespace);
foreach ($titleDetails as $titleKey => $titleDetail) {
$titleDetail = Xlsx::testSimpleXml($titleDetail);
switch ($titleKey) {
case 'p':
$titleDetailPart = $titleDetail->children($this->aNamespace);
$caption[] = $this->parseRichText($titleDetailPart);
}
}
} elseif (isset($chartDetail->strRef->strCache)) {
foreach ($chartDetail->strRef->strCache->pt as $pt) {
if (isset($pt->v)) {
$caption[] = (string) $pt->v;
}
}
}
break;
case 'overlay':
$titleOverlay = self::getAttribute($chartDetail, 'val', 'boolean');
break;
case 'layout':
$titleLayout = $this->chartLayoutDetails($chartDetail);
break;
}
}
return new Title($caption, $titleLayout, (bool) $titleOverlay);
}
private function chartLayoutDetails(SimpleXMLElement $chartDetail): ?Layout
{
if (!isset($chartDetail->manualLayout)) {
return null;
}
$details = $chartDetail->manualLayout->children($this->cNamespace);
if ($details === null) {
return null;
}
$layout = [];
foreach ($details as $detailKey => $detail) {
$detail = Xlsx::testSimpleXml($detail);
$layout[$detailKey] = self::getAttribute($detail, 'val', 'string');
}
return new Layout($layout);
}
private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType): DataSeries
{
$multiSeriesType = null;
$smoothLine = false;
$seriesLabel = $seriesCategory = $seriesValues = $plotOrder = $seriesBubbles = [];
$seriesDetailSet = $chartDetail->children($this->cNamespace);
foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
switch ($seriesDetailKey) {
case 'grouping':
$multiSeriesType = self::getAttribute($chartDetail->grouping, 'val', 'string');
break;
case 'ser':
$marker = null;
$seriesIndex = '';
$fillColor = null;
$pointSize = null;
$noFill = false;
$bubble3D = false;
$dptColors = [];
$markerFillColor = null;
$markerBorderColor = null;
$lineStyle = null;
$labelLayout = null;
$trendLines = [];
foreach ($seriesDetails as $seriesKey => $seriesDetail) {
$seriesDetail = Xlsx::testSimpleXml($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] = $this->chartDataSeriesValueSet($seriesDetail);
break;
case 'spPr':
$children = $seriesDetail->children($this->aNamespace);
if (isset($children->ln)) {
$ln = $children->ln;
if (is_countable($ln->noFill) && count($ln->noFill) === 1) {
$noFill = true;
}
$lineStyle = new GridLines();
$this->readLineStyle($seriesDetails, $lineStyle);
}
if (isset($children->effectLst)) {
if ($lineStyle === null) {
$lineStyle = new GridLines();
}
$this->readEffects($seriesDetails, $lineStyle);
}
if (isset($children->solidFill)) {
$fillColor = new ChartColor($this->readColor($children->solidFill));
}
break;
case 'dPt':
$dptIdx = (int) self::getAttribute($seriesDetail->idx, 'val', 'string');
if (isset($seriesDetail->spPr)) {
$children = $seriesDetail->spPr->children($this->aNamespace);
if (isset($children->solidFill)) {
$arrayColors = $this->readColor($children->solidFill);
$dptColors[$dptIdx] = new ChartColor($arrayColors);
}
}
break;
case 'trendline':
$trendLine = new TrendLine();
$this->readLineStyle($seriesDetail, $trendLine);
/** @var ?string */
$trendLineType = self::getAttribute($seriesDetail->trendlineType, 'val', 'string');
/** @var ?bool */
$dispRSqr = self::getAttribute($seriesDetail->dispRSqr, 'val', 'boolean');
/** @var ?bool */
$dispEq = self::getAttribute($seriesDetail->dispEq, 'val', 'boolean');
/** @var ?int */
$order = self::getAttribute($seriesDetail->order, 'val', 'integer');
/** @var ?int */
$period = self::getAttribute($seriesDetail->period, 'val', 'integer');
/** @var ?float */
$forward = self::getAttribute($seriesDetail->forward, 'val', 'float');
/** @var ?float */
$backward = self::getAttribute($seriesDetail->backward, 'val', 'float');
/** @var ?float */
$intercept = self::getAttribute($seriesDetail->intercept, 'val', 'float');
/** @var ?string */
$name = (string) $seriesDetail->name;
$trendLine->setTrendLineProperties(
$trendLineType,
$order,
$period,
$dispRSqr,
$dispEq,
$backward,
$forward,
$intercept,
$name
);
$trendLines[] = $trendLine;
break;
case 'marker':
$marker = self::getAttribute($seriesDetail->symbol, 'val', 'string');
$pointSize = self::getAttribute($seriesDetail->size, 'val', 'string');
$pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null;
if (isset($seriesDetail->spPr)) {
$children = $seriesDetail->spPr->children($this->aNamespace);
if (isset($children->solidFill)) {
$markerFillColor = $this->readColor($children->solidFill);
}
if (isset($children->ln->solidFill)) {
$markerBorderColor = $this->readColor($children->ln->solidFill);
}
}
break;
case 'smooth':
$smoothLine = self::getAttribute($seriesDetail, 'val', 'boolean');
break;
case 'cat':
$seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
break;
case 'val':
$seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
break;
case 'xVal':
$seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
break;
case 'yVal':
$seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
break;
case 'bubbleSize':
$seriesBubbles[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
break;
case 'bubble3D':
$bubble3D = self::getAttribute($seriesDetail, 'val', 'boolean');
break;
case 'dLbls':
$labelLayout = new Layout($this->readChartAttributes($seriesDetails));
break;
}
}
if ($labelLayout) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setLabelLayout($labelLayout);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setLabelLayout($labelLayout);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setLabelLayout($labelLayout);
}
}
if ($noFill) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setScatterLines(false);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setScatterLines(false);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setScatterLines(false);
}
}
if ($lineStyle !== null) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->copyLineStyles($lineStyle);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->copyLineStyles($lineStyle);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->copyLineStyles($lineStyle);
}
}
if ($bubble3D) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setBubble3D($bubble3D);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setBubble3D($bubble3D);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setBubble3D($bubble3D);
}
}
if (!empty($dptColors)) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setFillColor($dptColors);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setFillColor($dptColors);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setFillColor($dptColors);
}
}
if ($markerFillColor !== null) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor);
}
}
if ($markerBorderColor !== null) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor);
}
}
if ($smoothLine) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setSmoothLine(true);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setSmoothLine(true);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setSmoothLine(true);
}
}
if (!empty($trendLines)) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setTrendLines($trendLines);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setTrendLines($trendLines);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setTrendLines($trendLines);
}
}
}
}
/** @phpstan-ignore-next-line */
$series = new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
$series->setPlotBubbleSizes($seriesBubbles);
return $series;
}
/**
* @return mixed
*/
private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string $marker = null, ?ChartColor $fillColor = null, ?string $pointSize = null)
{
if (isset($seriesDetail->strRef)) {
$seriesSource = (string) $seriesDetail->strRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
if (isset($seriesDetail->strRef->strCache)) {
$seriesData = $this->chartDataSeriesValues($seriesDetail->strRef->strCache->children($this->cNamespace), 's');
$seriesValues
->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']);
}
return $seriesValues;
} elseif (isset($seriesDetail->numRef)) {
$seriesSource = (string) $seriesDetail->numRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
if (isset($seriesDetail->numRef->numCache)) {
$seriesData = $this->chartDataSeriesValues($seriesDetail->numRef->numCache->children($this->cNamespace));
$seriesValues
->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']);
}
return $seriesValues;
} elseif (isset($seriesDetail->multiLvlStrRef)) {
$seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) {
$seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($this->cNamespace), 's');
$seriesValues
->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']);
}
return $seriesValues;
} elseif (isset($seriesDetail->multiLvlNumRef)) {
$seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) {
$seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($this->cNamespace), 's');
$seriesValues
->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']);
}
return $seriesValues;
}
if (isset($seriesDetail->v)) {
return new DataSeriesValues(
DataSeriesValues::DATASERIES_TYPE_STRING,
null,
null,
1,
[(string) $seriesDetail->v]
);
}
return null;
}
private function chartDataSeriesValues(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
{
$seriesVal = [];
$formatCode = '';
$pointCount = 0;
foreach ($seriesValueSet as $seriesValueIdx => $seriesValue) {
$seriesValue = Xlsx::testSimpleXml($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 ((string) $seriesValue->v === ExcelError::NA()) {
$seriesVal[$pointVal] = null;
} else {
$seriesVal[$pointVal] = (float) $seriesValue->v;
}
break;
}
}
return [
'formatCode' => $formatCode,
'pointCount' => $pointCount,
'dataValues' => $seriesVal,
];
}
private function chartDataSeriesValuesMultiLevel(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
{
$seriesVal = [];
$formatCode = '';
$pointCount = 0;
foreach ($seriesValueSet->lvl as $seriesLevelIdx => $seriesLevel) {
foreach ($seriesLevel as $seriesValueIdx => $seriesValue) {
$seriesValue = Xlsx::testSimpleXml($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 ((string) $seriesValue->v === ExcelError::NA()) {
$seriesVal[$pointVal] = null;
} else {
$seriesVal[$pointVal][] = (float) $seriesValue->v;
}
break;
}
}
}
return [
'formatCode' => $formatCode,
'pointCount' => $pointCount,
'dataValues' => $seriesVal,
];
}
private function parseRichText(SimpleXMLElement $titleDetailPart): RichText
{
$value = new RichText();
$defaultFontSize = null;
$defaultBold = null;
$defaultItalic = null;
$defaultUnderscore = null;
$defaultStrikethrough = null;
$defaultBaseline = null;
$defaultFontName = null;
$defaultLatin = null;
$defaultEastAsian = null;
$defaultComplexScript = null;
$defaultFontColor = null;
if (isset($titleDetailPart->pPr->defRPr)) {
/** @var ?int */
$defaultFontSize = self::getAttribute($titleDetailPart->pPr->defRPr, 'sz', 'integer');
/** @var ?bool */
$defaultBold = self::getAttribute($titleDetailPart->pPr->defRPr, 'b', 'boolean');
/** @var ?bool */
$defaultItalic = self::getAttribute($titleDetailPart->pPr->defRPr, 'i', 'boolean');
/** @var ?string */
$defaultUnderscore = self::getAttribute($titleDetailPart->pPr->defRPr, 'u', 'string');
/** @var ?string */
$defaultStrikethrough = self::getAttribute($titleDetailPart->pPr->defRPr, 'strike', 'string');
/** @var ?int */
$defaultBaseline = self::getAttribute($titleDetailPart->pPr->defRPr, 'baseline', 'integer');
if (isset($titleDetailPart->defRPr->rFont['val'])) {
$defaultFontName = (string) $titleDetailPart->defRPr->rFont['val'];
}
if (isset($titleDetailPart->pPr->defRPr->latin)) {
/** @var ?string */
$defaultLatin = self::getAttribute($titleDetailPart->pPr->defRPr->latin, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->ea)) {
/** @var ?string */
$defaultEastAsian = self::getAttribute($titleDetailPart->pPr->defRPr->ea, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->cs)) {
/** @var ?string */
$defaultComplexScript = self::getAttribute($titleDetailPart->pPr->defRPr->cs, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->solidFill)) {
$defaultFontColor = $this->readColor($titleDetailPart->pPr->defRPr->solidFill);
}
}
foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
if (
(string) $titleDetailElementKey !== 'r'
|| !isset($titleDetailElement->t)
) {
continue;
}
$objText = $value->createTextRun((string) $titleDetailElement->t);
if ($objText->getFont() === null) {
// @codeCoverageIgnoreStart
continue;
// @codeCoverageIgnoreEnd
}
$fontSize = null;
$bold = null;
$italic = null;
$underscore = null;
$strikethrough = null;
$baseline = null;
$fontName = null;
$latinName = null;
$eastAsian = null;
$complexScript = null;
$fontColor = null;
$underlineColor = null;
if (isset($titleDetailElement->rPr)) {
// not used now, not sure it ever was, grandfathering
if (isset($titleDetailElement->rPr->rFont['val'])) {
// @codeCoverageIgnoreStart
$fontName = (string) $titleDetailElement->rPr->rFont['val'];
// @codeCoverageIgnoreEnd
}
if (isset($titleDetailElement->rPr->latin)) {
/** @var ?string */
$latinName = self::getAttribute($titleDetailElement->rPr->latin, 'typeface', 'string');
}
if (isset($titleDetailElement->rPr->ea)) {
/** @var ?string */
$eastAsian = self::getAttribute($titleDetailElement->rPr->ea, 'typeface', 'string');
}
if (isset($titleDetailElement->rPr->cs)) {
/** @var ?string */
$complexScript = self::getAttribute($titleDetailElement->rPr->cs, 'typeface', 'string');
}
/** @var ?int */
$fontSize = self::getAttribute($titleDetailElement->rPr, 'sz', 'integer');
// not used now, not sure it ever was, grandfathering
if (isset($titleDetailElement->rPr->solidFill)) {
$fontColor = $this->readColor($titleDetailElement->rPr->solidFill);
}
/** @var ?bool */
$bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean');
/** @var ?bool */
$italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean');
/** @var ?int */
$baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer');
/** @var ?string */
$underscore = self::getAttribute($titleDetailElement->rPr, 'u', 'string');
if (isset($titleDetailElement->rPr->uFill->solidFill)) {
$underlineColor = $this->readColor($titleDetailElement->rPr->uFill->solidFill);
}
/** @var ?string */
$strikethrough = self::getAttribute($titleDetailElement->rPr, 'strike', 'string');
}
$fontFound = false;
$latinName = $latinName ?? $defaultLatin;
if ($latinName !== null) {
$objText->getFont()->setLatin($latinName);
$fontFound = true;
}
$eastAsian = $eastAsian ?? $defaultEastAsian;
if ($eastAsian !== null) {
$objText->getFont()->setEastAsian($eastAsian);
$fontFound = true;
}
$complexScript = $complexScript ?? $defaultComplexScript;
if ($complexScript !== null) {
$objText->getFont()->setComplexScript($complexScript);
$fontFound = true;
}
$fontName = $fontName ?? $defaultFontName;
if ($fontName !== null) {
// @codeCoverageIgnoreStart
$objText->getFont()->setName($fontName);
$fontFound = true;
// @codeCoverageIgnoreEnd
}
$fontSize = $fontSize ?? $defaultFontSize;
if (is_int($fontSize)) {
$objText->getFont()->setSize(floor($fontSize / 100));
$fontFound = true;
} else {
$objText->getFont()->setSize(null, true);
}
$fontColor = $fontColor ?? $defaultFontColor;
if (!empty($fontColor)) {
$objText->getFont()->setChartColor($fontColor);
$fontFound = true;
}
$bold = $bold ?? $defaultBold;
if ($bold !== null) {
$objText->getFont()->setBold($bold);
$fontFound = true;
}
$italic = $italic ?? $defaultItalic;
if ($italic !== null) {
$objText->getFont()->setItalic($italic);
$fontFound = true;
}
$baseline = $baseline ?? $defaultBaseline;
if ($baseline !== null) {
$objText->getFont()->setBaseLine($baseline);
if ($baseline > 0) {
$objText->getFont()->setSuperscript(true);
} elseif ($baseline < 0) {
$objText->getFont()->setSubscript(true);
}
$fontFound = true;
}
$underscore = $underscore ?? $defaultUnderscore;
if ($underscore !== null) {
if ($underscore == 'sng') {
$objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
} elseif ($underscore == 'dbl') {
$objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
} elseif ($underscore !== '') {
$objText->getFont()->setUnderline($underscore);
} else {
$objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
}
$fontFound = true;
if ($underlineColor) {
$objText->getFont()->setUnderlineColor($underlineColor);
}
}
$strikethrough = $strikethrough ?? $defaultStrikethrough;
if ($strikethrough !== null) {
$objText->getFont()->setStrikeType($strikethrough);
if ($strikethrough == 'noStrike') {
$objText->getFont()->setStrikethrough(false);
} else {
$objText->getFont()->setStrikethrough(true);
}
$fontFound = true;
}
if ($fontFound === false) {
$objText->setFont(null);
}
}
return $value;
}
private function parseFont(SimpleXMLElement $titleDetailPart): ?Font
{
if (!isset($titleDetailPart->pPr->defRPr)) {
return null;
}
$fontArray = [];
$fontArray['size'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'sz', 'integer');
$fontArray['bold'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'b', 'boolean');
$fontArray['italic'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'i', 'boolean');
$fontArray['underscore'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'u', 'string');
$fontArray['strikethrough'] = self::getAttribute($titleDetailPart->pPr->defRPr, 'strike', 'string');
if (isset($titleDetailPart->pPr->defRPr->latin)) {
$fontArray['latin'] = self::getAttribute($titleDetailPart->pPr->defRPr->latin, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->ea)) {
$fontArray['eastAsian'] = self::getAttribute($titleDetailPart->pPr->defRPr->ea, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->cs)) {
$fontArray['complexScript'] = self::getAttribute($titleDetailPart->pPr->defRPr->cs, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->solidFill)) {
$fontArray['chartColor'] = new ChartColor($this->readColor($titleDetailPart->pPr->defRPr->solidFill));
}
$font = new Font();
$font->setSize(null, true);
$font->applyFromArray($fontArray);
return $font;
}
/**
* @param ?SimpleXMLElement $chartDetail
*/
private function readChartAttributes($chartDetail): array
{
$plotAttributes = [];
if (isset($chartDetail->dLbls)) {
if (isset($chartDetail->dLbls->dLblPos)) {
$plotAttributes['dLblPos'] = self::getAttribute($chartDetail->dLbls->dLblPos, 'val', 'string');
}
if (isset($chartDetail->dLbls->numFmt)) {
$plotAttributes['numFmtCode'] = self::getAttribute($chartDetail->dLbls->numFmt, 'formatCode', 'string');
$plotAttributes['numFmtLinked'] = self::getAttribute($chartDetail->dLbls->numFmt, 'sourceLinked', 'boolean');
}
if (isset($chartDetail->dLbls->showLegendKey)) {
$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');
}
if (isset($chartDetail->dLbls->spPr)) {
$sppr = $chartDetail->dLbls->spPr->children($this->aNamespace);
if (isset($sppr->solidFill)) {
$plotAttributes['labelFillColor'] = new ChartColor($this->readColor($sppr->solidFill));
}
if (isset($sppr->ln->solidFill)) {
$plotAttributes['labelBorderColor'] = new ChartColor($this->readColor($sppr->ln->solidFill));
}
}
if (isset($chartDetail->dLbls->txPr)) {
$txpr = $chartDetail->dLbls->txPr->children($this->aNamespace);
if (isset($txpr->p)) {
$plotAttributes['labelFont'] = $this->parseFont($txpr->p);
if (isset($txpr->p->pPr->defRPr->effectLst)) {
$labelEffects = new GridLines();
$this->readEffects($txpr->p->pPr->defRPr, $labelEffects, false);
$plotAttributes['labelEffects'] = $labelEffects;
}
}
}
}
return $plotAttributes;
}
/**
* @param mixed $plotAttributes
*/
private 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;
}
}
}
private function readEffects(SimpleXMLElement $chartDetail, ?ChartProperties $chartObject, bool $getSppr = true): void
{
if (!isset($chartObject)) {
return;
}
if ($getSppr) {
if (!isset($chartDetail->spPr)) {
return;
}
$sppr = $chartDetail->spPr->children($this->aNamespace);
} else {
$sppr = $chartDetail;
}
if (isset($sppr->effectLst->glow)) {
$axisGlowSize = (float) self::getAttribute($sppr->effectLst->glow, 'rad', 'integer') / ChartProperties::POINTS_WIDTH_MULTIPLIER;
if ($axisGlowSize != 0.0) {
$colorArray = $this->readColor($sppr->effectLst->glow);
$chartObject->setGlowProperties($axisGlowSize, $colorArray['value'], $colorArray['alpha'], $colorArray['type']);
}
}
if (isset($sppr->effectLst->softEdge)) {
/** @var string */
$softEdgeSize = self::getAttribute($sppr->effectLst->softEdge, 'rad', 'string');
if (is_numeric($softEdgeSize)) {
$chartObject->setSoftEdges((float) ChartProperties::xmlToPoints($softEdgeSize));
}
}
$type = '';
foreach (self::SHADOW_TYPES as $shadowType) {
if (isset($sppr->effectLst->$shadowType)) {
$type = $shadowType;
break;
}
}
if ($type !== '') {
/** @var string */
$blur = self::getAttribute($sppr->effectLst->$type, 'blurRad', 'string');
$blur = is_numeric($blur) ? ChartProperties::xmlToPoints($blur) : null;
/** @var string */
$dist = self::getAttribute($sppr->effectLst->$type, 'dist', 'string');
$dist = is_numeric($dist) ? ChartProperties::xmlToPoints($dist) : null;
/** @var string */
$direction = self::getAttribute($sppr->effectLst->$type, 'dir', 'string');
$direction = is_numeric($direction) ? ChartProperties::xmlToAngle($direction) : null;
$algn = self::getAttribute($sppr->effectLst->$type, 'algn', 'string');
$rot = self::getAttribute($sppr->effectLst->$type, 'rotWithShape', 'string');
$size = [];
foreach (['sx', 'sy'] as $sizeType) {
$sizeValue = self::getAttribute($sppr->effectLst->$type, $sizeType, 'string');
if (is_numeric($sizeValue)) {
$size[$sizeType] = ChartProperties::xmlToTenthOfPercent((string) $sizeValue);
} else {
$size[$sizeType] = null;
}
}
foreach (['kx', 'ky'] as $sizeType) {
$sizeValue = self::getAttribute($sppr->effectLst->$type, $sizeType, 'string');
if (is_numeric($sizeValue)) {
$size[$sizeType] = ChartProperties::xmlToAngle((string) $sizeValue);
} else {
$size[$sizeType] = null;
}
}
$colorArray = $this->readColor($sppr->effectLst->$type);
$chartObject
->setShadowProperty('effect', $type)
->setShadowProperty('blur', $blur)
->setShadowProperty('direction', $direction)
->setShadowProperty('distance', $dist)
->setShadowProperty('algn', $algn)
->setShadowProperty('rotWithShape', $rot)
->setShadowProperty('size', $size)
->setShadowProperty('color', $colorArray);
}
}
private const SHADOW_TYPES = [
'outerShdw',
'innerShdw',
];
private function readColor(SimpleXMLElement $colorXml): array
{
$result = [
'type' => null,
'value' => null,
'alpha' => null,
'brightness' => null,
];
foreach (ChartColor::EXCEL_COLOR_TYPES as $type) {
if (isset($colorXml->$type)) {
$result['type'] = $type;
$result['value'] = self::getAttribute($colorXml->$type, 'val', 'string');
if (isset($colorXml->$type->alpha)) {
/** @var string */
$alpha = self::getAttribute($colorXml->$type->alpha, 'val', 'string');
if (is_numeric($alpha)) {
$result['alpha'] = ChartColor::alphaFromXml($alpha);
}
}
if (isset($colorXml->$type->lumMod)) {
/** @var string */
$brightness = self::getAttribute($colorXml->$type->lumMod, 'val', 'string');
if (is_numeric($brightness)) {
$result['brightness'] = ChartColor::alphaFromXml($brightness);
}
}
break;
}
}
return $result;
}
private function readLineStyle(SimpleXMLElement $chartDetail, ?ChartProperties $chartObject): void
{
if (!isset($chartObject, $chartDetail->spPr)) {
return;
}
$sppr = $chartDetail->spPr->children($this->aNamespace);
if (!isset($sppr->ln)) {
return;
}
$lineWidth = null;
/** @var string */
$lineWidthTemp = self::getAttribute($sppr->ln, 'w', 'string');
if (is_numeric($lineWidthTemp)) {
$lineWidth = ChartProperties::xmlToPoints($lineWidthTemp);
}
/** @var string */
$compoundType = self::getAttribute($sppr->ln, 'cmpd', 'string');
/** @var string */
$dashType = self::getAttribute($sppr->ln->prstDash, 'val', 'string');
/** @var string */
$capType = self::getAttribute($sppr->ln, 'cap', 'string');
if (isset($sppr->ln->miter)) {
$joinType = ChartProperties::LINE_STYLE_JOIN_MITER;
} elseif (isset($sppr->ln->bevel)) {
$joinType = ChartProperties::LINE_STYLE_JOIN_BEVEL;
} else {
$joinType = '';
}
$headArrowSize = '';
$endArrowSize = '';
/** @var string */
$headArrowType = self::getAttribute($sppr->ln->headEnd, 'type', 'string');
/** @var string */
$headArrowWidth = self::getAttribute($sppr->ln->headEnd, 'w', 'string');
/** @var string */
$headArrowLength = self::getAttribute($sppr->ln->headEnd, 'len', 'string');
/** @var string */
$endArrowType = self::getAttribute($sppr->ln->tailEnd, 'type', 'string');
/** @var string */
$endArrowWidth = self::getAttribute($sppr->ln->tailEnd, 'w', 'string');
/** @var string */
$endArrowLength = self::getAttribute($sppr->ln->tailEnd, 'len', 'string');
$chartObject->setLineStyleProperties(
$lineWidth,
$compoundType,
$dashType,
$capType,
$joinType,
$headArrowType,
$headArrowSize,
$endArrowType,
$endArrowSize,
$headArrowWidth,
$headArrowLength,
$endArrowWidth,
$endArrowLength
);
$colorArray = $this->readColor($sppr->ln->solidFill);
$chartObject->getLineColor()->setColorPropertiesArray($colorArray);
}
private function setAxisProperties(SimpleXMLElement $chartDetail, ?Axis $whichAxis): void
{
if (!isset($whichAxis)) {
return;
}
if (isset($chartDetail->delete)) {
$whichAxis->setAxisOption('hidden', (string) self::getAttribute($chartDetail->delete, 'val', 'string'));
}
if (isset($chartDetail->numFmt)) {
$whichAxis->setAxisNumberProperties(
(string) self::getAttribute($chartDetail->numFmt, 'formatCode', 'string'),
null,
(int) self::getAttribute($chartDetail->numFmt, 'sourceLinked', 'int')
);
}
if (isset($chartDetail->crossBetween)) {
$whichAxis->setCrossBetween((string) self::getAttribute($chartDetail->crossBetween, 'val', 'string'));
}
if (isset($chartDetail->majorTickMark)) {
$whichAxis->setAxisOption('major_tick_mark', (string) self::getAttribute($chartDetail->majorTickMark, 'val', 'string'));
}
if (isset($chartDetail->minorTickMark)) {
$whichAxis->setAxisOption('minor_tick_mark', (string) self::getAttribute($chartDetail->minorTickMark, 'val', 'string'));
}
if (isset($chartDetail->tickLblPos)) {
$whichAxis->setAxisOption('axis_labels', (string) self::getAttribute($chartDetail->tickLblPos, 'val', 'string'));
}
if (isset($chartDetail->crosses)) {
$whichAxis->setAxisOption('horizontal_crosses', (string) self::getAttribute($chartDetail->crosses, 'val', 'string'));
}
if (isset($chartDetail->crossesAt)) {
$whichAxis->setAxisOption('horizontal_crosses_value', (string) self::getAttribute($chartDetail->crossesAt, 'val', 'string'));
}
if (isset($chartDetail->scaling->orientation)) {
$whichAxis->setAxisOption('orientation', (string) self::getAttribute($chartDetail->scaling->orientation, 'val', 'string'));
}
if (isset($chartDetail->scaling->max)) {
$whichAxis->setAxisOption('maximum', (string) self::getAttribute($chartDetail->scaling->max, 'val', 'string'));
}
if (isset($chartDetail->scaling->min)) {
$whichAxis->setAxisOption('minimum', (string) self::getAttribute($chartDetail->scaling->min, 'val', 'string'));
}
if (isset($chartDetail->scaling->min)) {
$whichAxis->setAxisOption('minimum', (string) self::getAttribute($chartDetail->scaling->min, 'val', 'string'));
}
if (isset($chartDetail->majorUnit)) {
$whichAxis->setAxisOption('major_unit', (string) self::getAttribute($chartDetail->majorUnit, 'val', 'string'));
}
if (isset($chartDetail->minorUnit)) {
$whichAxis->setAxisOption('minor_unit', (string) self::getAttribute($chartDetail->minorUnit, 'val', 'string'));
}
if (isset($chartDetail->baseTimeUnit)) {
$whichAxis->setAxisOption('baseTimeUnit', (string) self::getAttribute($chartDetail->baseTimeUnit, 'val', 'string'));
}
if (isset($chartDetail->majorTimeUnit)) {
$whichAxis->setAxisOption('majorTimeUnit', (string) self::getAttribute($chartDetail->majorTimeUnit, 'val', 'string'));
}
if (isset($chartDetail->minorTimeUnit)) {
$whichAxis->setAxisOption('minorTimeUnit', (string) self::getAttribute($chartDetail->minorTimeUnit, 'val', 'string'));
}
if (isset($chartDetail->txPr)) {
$children = $chartDetail->txPr->children($this->aNamespace);
$addAxisText = false;
$axisText = new AxisText();
if (isset($children->bodyPr)) {
/** @var string */
$textRotation = self::getAttribute($children->bodyPr, 'rot', 'string');
if (is_numeric($textRotation)) {
$axisText->setRotation((int) ChartProperties::xmlToAngle($textRotation));
$addAxisText = true;
}
}
if (isset($children->p->pPr->defRPr)) {
$font = $this->parseFont($children->p);
if ($font !== null) {
$axisText->setFont($font);
$addAxisText = true;
}
}
if (isset($children->p->pPr->defRPr->effectLst)) {
$this->readEffects($children->p->pPr->defRPr, $axisText, false);
$addAxisText = true;
}
if ($addAxisText) {
$whichAxis->setAxisText($axisText);
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php 0000644 00000002635 15242753725 0017226 0 ustar 00 themeName = $themeName;
$this->colourSchemeName = $colourSchemeName;
$this->colourMap = $colourMap;
}
/**
* Not called by Reader, never accessible any other time.
*
* @return string
*
* @codeCoverageIgnore
*/
public function getThemeName()
{
return $this->themeName;
}
/**
* Not called by Reader, never accessible any other time.
*
* @return string
*
* @codeCoverageIgnore
*/
public function getColourSchemeName()
{
return $this->colourSchemeName;
}
/**
* Get colour Map Value by Position.
*
* @param int $index
*
* @return null|string
*/
public function getColourByIndex($index)
{
return $this->colourMap[$index] ?? null;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SharedFormula.php 0000644 00000000676 15242753725 0020723 0 ustar 00 master = $master;
$this->formula = $formula;
}
public function master(): string
{
return $this->master;
}
public function formula(): string
{
return $this->formula;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php 0000644 00000015201 15242753725 0020233 0 ustar 00 parent = $parent;
$this->worksheetXml = $worksheetXml;
}
public function load(): void
{
// Remove all "$" in the auto filter range
$autoFilterRange = (string) preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref'] ?? '');
if (strpos($autoFilterRange, ':') !== false) {
$this->readAutoFilter($autoFilterRange, $this->worksheetXml);
}
}
private function readAutoFilter(string $autoFilterRange, SimpleXMLElement $xmlSheet): void
{
$autoFilter = $this->parent->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'])) && ((int) $filters['blank'] == 1)) {
// Operator is undefined, but always treated as EQUAL
$column->createRule()->setRule('', '')->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('', (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);
}
$autoFilter->setEvaluated(true);
}
private function readDateRangeAutoFilter(SimpleXMLElement $filters, Column $column): void
{
foreach ($filters->dateGroupItem as $dateGroupItem) {
// Operator is undefined, but always treated as EQUAL
$column->createRule()->setRule(
'',
[
'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 (isset($filterColumn, $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'])) && ((string) $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 (isset($filterColumn, $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(
'',
(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 (isset($filterColumn, $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'])) && ((string) $filterRule['percent'] === '1'))
? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
: Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
),
(string) $filterRule['val'],
(
((isset($filterRule['top'])) && ((string) $filterRule['top'] === '1'))
? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
: Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
)
)->setRuleType(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER);
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php 0000644 00000013142 15242753725 0021102 0 ustar 00 pattern = $pattern;
$this->disableEntityLoaderCheck();
// A fatal error will bypass the destructor, so we register a shutdown here
if (!self::$shutdownRegistered) {
self::$shutdownRegistered = true;
register_shutdown_function([__CLASS__, 'shutdown']);
}
}
public static function getInstance(Reader\IReader $reader): self
{
$pattern = ($reader instanceof Reader\Html) ? '= 1;
case 1:
return PHP_RELEASE_VERSION >= 13;
case 0:
return PHP_RELEASE_VERSION >= 27;
}
return true;
}
return false;
}
/**
* @codeCoverageIgnore
*/
private function disableEntityLoaderCheck(): void
{
if (\PHP_VERSION_ID < 80000) {
$libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
if (self::$libxmlDisableEntityLoaderValue === null) {
self::$libxmlDisableEntityLoaderValue = $libxmlDisableEntityLoaderValue;
}
}
}
/**
* @codeCoverageIgnore
*/
public static function shutdown(): void
{
if (self::$libxmlDisableEntityLoaderValue !== null && \PHP_VERSION_ID < 80000) {
libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue);
self::$libxmlDisableEntityLoaderValue = null;
}
}
public function __destruct()
{
self::shutdown();
}
public function setAdditionalCallback(callable $callback): void
{
$this->callback = $callback;
}
/** @param mixed $arg */
private static function forceString($arg): string
{
return is_string($arg) ? $arg : '';
}
/**
* @param string $xml
*
* @return string
*/
private function toUtf8($xml)
{
$charset = $this->findCharSet($xml);
$foundUtf7 = $charset === 'UTF-7';
if ($charset !== 'UTF-8') {
$testStart = '/^.{0,4}\\s*disableEntityLoaderCheck();
// Don't rely purely on libxml_disable_entity_loader()
$pattern = '/\\0*' . implode('\\0*', /** @scrutinizer ignore-type */ str_split($this->pattern)) . '\\0*/';
$xml = "$xml";
if (preg_match($pattern, $xml)) {
throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
}
$xml = $this->toUtf8($xml);
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) {
$xml = call_user_func($this->callback, $xml);
}
return $xml;
}
/**
* Scan the XML for use of scan(file_get_contents($filestream));
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF5.php 0000644 00000003452 15242753725 0017643 0 ustar 00 '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)
{
return ['rgb' => self::BIFF5_COLOR_MAP[$color] ?? '000000'];
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BIFF8.php 0000644 00000003452 15242753725 0017646 0 ustar 00 '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)
{
return ['rgb' => self::BIFF8_COLOR_MAP[$color] ?? '000000'];
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color/BuiltIn.php 0000644 00000001300 15242753725 0020404 0 ustar 00 '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)
{
return ['rgb' => self::BUILTIN_COLOR_MAP[$color] ?? '000000'];
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/Border.php 0000644 00000002110 15242753725 0020275 0 ustar 00
*/
protected static $borderStyleMap = [
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,
];
public static function lookup(int $index): string
{
if (isset(self::$borderStyleMap[$index])) {
return self::$borderStyleMap[$index];
}
return StyleBorder::BORDER_NONE;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellAlignment.php 0000644 00000002657 15242753725 0021616 0 ustar 00
*/
protected static $horizontalAlignmentMap = [
0 => Alignment::HORIZONTAL_GENERAL,
1 => Alignment::HORIZONTAL_LEFT,
2 => Alignment::HORIZONTAL_CENTER,
3 => Alignment::HORIZONTAL_RIGHT,
4 => Alignment::HORIZONTAL_FILL,
5 => Alignment::HORIZONTAL_JUSTIFY,
6 => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
];
/**
* @var array
*/
protected static $verticalAlignmentMap = [
0 => Alignment::VERTICAL_TOP,
1 => Alignment::VERTICAL_CENTER,
2 => Alignment::VERTICAL_BOTTOM,
3 => Alignment::VERTICAL_JUSTIFY,
];
public static function horizontal(Alignment $alignment, int $horizontal): void
{
if (array_key_exists($horizontal, self::$horizontalAlignmentMap)) {
$alignment->setHorizontal(self::$horizontalAlignmentMap[$horizontal]);
}
}
public static function vertical(Alignment $alignment, int $vertical): void
{
if (array_key_exists($vertical, self::$verticalAlignmentMap)) {
$alignment->setVertical(self::$verticalAlignmentMap[$vertical]);
}
}
public static function wrap(Alignment $alignment, int $wrap): void
{
$alignment->setWrapText((bool) $wrap);
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/FillPattern.php 0000644 00000002633 15242753725 0021316 0 ustar 00
*/
protected static $fillPatternMap = [
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::$fillPatternMap[$index])) {
return self::$fillPatternMap[$index];
}
return Fill::FILL_NONE;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Style/CellFont.php 0000644 00000001645 15242753725 0020602 0 ustar 00 setSuperscript(true);
break;
case 0x0002:
$font->setSubscript(true);
break;
}
}
/**
* @var array
*/
protected static $underlineMap = [
0x01 => Font::UNDERLINE_SINGLE,
0x02 => Font::UNDERLINE_DOUBLE,
0x21 => Font::UNDERLINE_SINGLEACCOUNTING,
0x22 => Font::UNDERLINE_DOUBLEACCOUNTING,
];
public static function underline(Font $font, int $underline): void
{
if (array_key_exists($underline, self::$underlineMap)) {
$font->setUnderline(self::$underlineMap[$underline]);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Escher.php 0000644 00000045726 15242753725 0017215 0 ustar 00 object = $object;
}
private const WHICH_ROUTINE = [
self::DGGCONTAINER => 'readDggContainer',
self::DGG => 'readDgg',
self::BSTORECONTAINER => 'readBstoreContainer',
self::BSE => 'readBSE',
self::BLIPJPEG => 'readBlipJPEG',
self::BLIPPNG => 'readBlipPNG',
self::OPT => 'readOPT',
self::TERTIARYOPT => 'readTertiaryOPT',
self::SPLITMENUCOLORS => 'readSplitMenuColors',
self::DGCONTAINER => 'readDgContainer',
self::DG => 'readDg',
self::SPGRCONTAINER => 'readSpgrContainer',
self::SPCONTAINER => 'readSpContainer',
self::SPGR => 'readSpgr',
self::SP => 'readSp',
self::CLIENTTEXTBOX => 'readClientTextbox',
self::CLIENTANCHOR => 'readClientAnchor',
self::CLIENTDATA => 'readClientData',
];
/**
* 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);
$routine = self::WHICH_ROUTINE[$fbt] ?? 'readDefault';
if (method_exists($this, $routine)) {
$this->$routine();
}
}
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->applyAttribute('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->applyAttribute('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->applyAttribute('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->applyAttribute('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->applyAttribute('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->applyAttribute('setDgContainer', $dgContainer);
$reader = new self($dgContainer);
$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);
} elseif ($this->object instanceof SpgrContainer) {
// SpgrContainer
$this->object->addChild($spgrContainer);
}
$reader = new self($spgrContainer);
$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->applyAttribute('addChild', $spContainer);
// move stream pointer to next record
$this->pos += 8 + $length;
// record is a container, read contents
$reader = new self($spContainer);
$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);
$this->applyAttribute('setStartCoordinates', Coordinate::stringFromColumnIndex($c1 + 1) . ($r1 + 1));
$this->applyAttribute('setStartOffsetX', $startOffsetX);
$this->applyAttribute('setStartOffsetY', $startOffsetY);
$this->applyAttribute('setEndCoordinates', Coordinate::stringFromColumnIndex($c2 + 1) . ($r2 + 1));
$this->applyAttribute('setEndOffsetX', $endOffsetX);
$this->applyAttribute('setEndOffsetY', $endOffsetY);
}
/**
* @param mixed $value
*/
private function applyAttribute(string $name, $value): void
{
if (method_exists($this->object, $name)) {
$this->object->$name($value);
}
}
/**
* 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;
}
if (method_exists($this->object, 'setOPT')) {
$this->object->setOPT($opidOpid, $value);
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ErrorCode.php 0000644 00000001013 15242753725 0017645 0 ustar 00 '#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)
{
return self::ERROR_CODE_MAP[$code] ?? false;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/DataValidationHelper.php 0000644 00000003602 15242753725 0022013 0 ustar 00
*/
private static $types = [
0x00 => DataValidation::TYPE_NONE,
0x01 => DataValidation::TYPE_WHOLE,
0x02 => DataValidation::TYPE_DECIMAL,
0x03 => DataValidation::TYPE_LIST,
0x04 => DataValidation::TYPE_DATE,
0x05 => DataValidation::TYPE_TIME,
0x06 => DataValidation::TYPE_TEXTLENGTH,
0x07 => DataValidation::TYPE_CUSTOM,
];
/**
* @var array
*/
private static $errorStyles = [
0x00 => DataValidation::STYLE_STOP,
0x01 => DataValidation::STYLE_WARNING,
0x02 => DataValidation::STYLE_INFORMATION,
];
/**
* @var array
*/
private static $operators = [
0x00 => DataValidation::OPERATOR_BETWEEN,
0x01 => DataValidation::OPERATOR_NOTBETWEEN,
0x02 => DataValidation::OPERATOR_EQUAL,
0x03 => DataValidation::OPERATOR_NOTEQUAL,
0x04 => DataValidation::OPERATOR_GREATERTHAN,
0x05 => DataValidation::OPERATOR_LESSTHAN,
0x06 => DataValidation::OPERATOR_GREATERTHANOREQUAL,
0x07 => DataValidation::OPERATOR_LESSTHANOREQUAL,
];
public static function type(int $type): ?string
{
if (isset(self::$types[$type])) {
return self::$types[$type];
}
return null;
}
public static function errorStyle(int $errorStyle): ?string
{
if (isset(self::$errorStyles[$errorStyle])) {
return self::$errorStyles[$errorStyle];
}
return null;
}
public static function operator(int $operator): ?string
{
if (isset(self::$operators[$operator])) {
return self::$operators[$operator];
}
return null;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php 0000644 00000002323 15242753725 0022264 0 ustar 00
*/
private static $types = [
0x01 => Conditional::CONDITION_CELLIS,
0x02 => Conditional::CONDITION_EXPRESSION,
];
/**
* @var array
*/
private static $operators = [
0x00 => Conditional::OPERATOR_NONE,
0x01 => Conditional::OPERATOR_BETWEEN,
0x02 => Conditional::OPERATOR_NOTBETWEEN,
0x03 => Conditional::OPERATOR_EQUAL,
0x04 => Conditional::OPERATOR_NOTEQUAL,
0x05 => Conditional::OPERATOR_GREATERTHAN,
0x06 => Conditional::OPERATOR_LESSTHAN,
0x07 => Conditional::OPERATOR_GREATERTHANOREQUAL,
0x08 => Conditional::OPERATOR_LESSTHANOREQUAL,
];
public static function type(int $type): ?string
{
if (isset(self::$types[$type])) {
return self::$types[$type];
}
return null;
}
public static function operator(int $operator): ?string
{
if (isset(self::$operators[$operator])) {
return self::$operators[$operator];
}
return null;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/MD5.php 0000644 00000016665 15242753725 0016371 0 ustar 00 reset();
}
/**
* Reset the MD5 stream context.
*/
public function reset(): void
{
$this->a = 0x67452301;
$this->b = self::signedInt(0xEFCDAB89);
$this->c = self::signedInt(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(string $data): void
{
// @phpstan-ignore-next-line
$words = array_values(unpack('V16', $data));
$A = $this->a;
$B = $this->b;
$C = $this->c;
$D = $this->d;
$F = [self::class, 'f'];
$G = [self::class, 'g'];
$H = [self::class, 'h'];
$I = [self::class, '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) & self::$allOneBits;
$this->b = ($this->b + $B) & self::$allOneBits;
$this->c = ($this->c + $C) & self::$allOneBits;
$this->d = ($this->d + $D) & self::$allOneBits;
}
private static function f(int $X, int $Y, int $Z): int
{
return ($X & $Y) | ((~$X) & $Z); // X AND Y OR NOT X AND Z
}
private static function g(int $X, int $Y, int $Z): int
{
return ($X & $Z) | ($Y & (~$Z)); // X AND Z OR Y AND NOT Z
}
private static function h(int $X, int $Y, int $Z): int
{
return $X ^ $Y ^ $Z; // X XOR Y XOR Z
}
private static function i(int $X, int $Y, int $Z): int
{
return $Y ^ ($X | (~$Z)); // Y XOR (X OR NOT Z)
}
/** @param float|int $t may be float on 32-bit system */
private static function step(callable $func, int &$A, int $B, int $C, int $D, int $M, int $s, $t): void
{
$t = self::signedInt($t);
$A = (int) ($A + call_user_func($func, $B, $C, $D) + $M + $t) & self::$allOneBits;
$A = self::rotate($A, $s);
$A = (int) ($B + $A) & self::$allOneBits;
}
/** @param float|int $result may be float on 32-bit system */
private static function signedInt($result): int
{
return is_int($result) ? $result : (int) (PHP_INT_MIN + $result - 1 - PHP_INT_MAX);
}
private static function rotate(int $decimal, int $bits): int
{
$binary = str_pad(decbin($decimal), 32, '0', STR_PAD_LEFT);
return self::signedInt(bindec(substr($binary, $bits) . substr($binary, 0, $bits)));
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/Color.php 0000644 00000001616 15242753725 0017050 0 ustar 00 '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[$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);
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php 0000644 00000002774 15242753725 0016370 0 ustar 00 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;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Fill.php 0000644 00000005207 15242753725 0017752 0 ustar 00 [
'solid' => FillStyles::FILL_SOLID,
'gray75' => FillStyles::FILL_PATTERN_DARKGRAY,
'gray50' => FillStyles::FILL_PATTERN_MEDIUMGRAY,
'gray25' => FillStyles::FILL_PATTERN_LIGHTGRAY,
'gray125' => FillStyles::FILL_PATTERN_GRAY125,
'gray0625' => FillStyles::FILL_PATTERN_GRAY0625,
'horzstripe' => FillStyles::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe
'vertstripe' => FillStyles::FILL_PATTERN_DARKVERTICAL, // vertical stripe
'reversediagstripe' => FillStyles::FILL_PATTERN_DARKUP, // reverse diagonal stripe
'diagstripe' => FillStyles::FILL_PATTERN_DARKDOWN, // diagonal stripe
'diagcross' => FillStyles::FILL_PATTERN_DARKGRID, // diagoanl crosshatch
'thickdiagcross' => FillStyles::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch
'thinhorzstripe' => FillStyles::FILL_PATTERN_LIGHTHORIZONTAL,
'thinvertstripe' => FillStyles::FILL_PATTERN_LIGHTVERTICAL,
'thinreversediagstripe' => FillStyles::FILL_PATTERN_LIGHTUP,
'thindiagstripe' => FillStyles::FILL_PATTERN_LIGHTDOWN,
'thinhorzcross' => FillStyles::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch
'thindiagcross' => FillStyles::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch
],
];
public function parseStyle(SimpleXMLElement $styleAttributes): array
{
$style = [];
foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValuex) {
$styleAttributeValue = (string) $styleAttributeValuex;
switch ($styleAttributeKey) {
case 'Color':
$style['fill']['endColor']['rgb'] = substr($styleAttributeValue, 1);
$style['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1);
break;
case 'PatternColor':
$style['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1);
break;
case 'Pattern':
$lcStyleAttributeValue = strtolower((string) $styleAttributeValue);
$style['fill']['fillType']
= self::FILL_MAPPINGS['fillType'][$lcStyleAttributeValue] ?? FillStyles::FILL_NONE;
break;
}
}
return $style;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Border.php 0000644 00000007235 15242753725 0020304 0 ustar 00 [
'1continuous' => BorderStyle::BORDER_THIN,
'1dash' => BorderStyle::BORDER_DASHED,
'1dashdot' => BorderStyle::BORDER_DASHDOT,
'1dashdotdot' => BorderStyle::BORDER_DASHDOTDOT,
'1dot' => BorderStyle::BORDER_DOTTED,
'1double' => BorderStyle::BORDER_DOUBLE,
'2continuous' => BorderStyle::BORDER_MEDIUM,
'2dash' => BorderStyle::BORDER_MEDIUMDASHED,
'2dashdot' => BorderStyle::BORDER_MEDIUMDASHDOT,
'2dashdotdot' => BorderStyle::BORDER_MEDIUMDASHDOTDOT,
'2dot' => BorderStyle::BORDER_DOTTED,
'2double' => BorderStyle::BORDER_DOUBLE,
'3continuous' => BorderStyle::BORDER_THICK,
'3dash' => BorderStyle::BORDER_MEDIUMDASHED,
'3dashdot' => BorderStyle::BORDER_MEDIUMDASHDOT,
'3dashdotdot' => BorderStyle::BORDER_MEDIUMDASHDOTDOT,
'3dot' => BorderStyle::BORDER_DOTTED,
'3double' => BorderStyle::BORDER_DOUBLE,
],
];
public function parseStyle(SimpleXMLElement $styleData, array $namespaces): array
{
$style = [];
$diagonalDirection = '';
$borderPosition = '';
foreach ($styleData->Border as $borderStyle) {
$borderAttributes = self::getAttributes($borderStyle, $namespaces['ss']);
$thisBorder = [];
$styleType = (string) $borderAttributes->Weight;
$styleType .= strtolower((string) $borderAttributes->LineStyle);
$thisBorder['borderStyle'] = self::BORDER_MAPPINGS['borderStyle'][$styleType] ?? BorderStyle::BORDER_NONE;
foreach ($borderAttributes as $borderStyleKey => $borderStyleValuex) {
$borderStyleValue = (string) $borderStyleValuex;
switch ($borderStyleKey) {
case 'Position':
[$borderPosition, $diagonalDirection] =
$this->parsePosition($borderStyleValue, $diagonalDirection);
break;
case 'Color':
$borderColour = substr($borderStyleValue, 1);
$thisBorder['color']['rgb'] = $borderColour;
break;
}
}
if ($borderPosition) {
$style['borders'][$borderPosition] = $thisBorder;
} elseif ($diagonalDirection) {
$style['borders']['diagonalDirection'] = $diagonalDirection;
$style['borders']['diagonal'] = $thisBorder;
}
}
return $style;
}
protected function parsePosition(string $borderStyleValue, string $diagonalDirection): array
{
$borderStyleValue = strtolower($borderStyleValue);
if (in_array($borderStyleValue, self::BORDER_POSITIONS)) {
$borderPosition = $borderStyleValue;
} elseif ($borderStyleValue === 'diagonalleft') {
$diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_DOWN;
} elseif ($borderStyleValue === 'diagonalright') {
$diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_UP;
}
return [$borderPosition ?? null, $diagonalDirection];
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Alignment.php 0000644 00000003563 15242753725 0021005 0 ustar 00 $styleAttributeValue) {
$styleAttributeValue = (string) $styleAttributeValue;
switch ($styleAttributeKey) {
case 'Vertical':
if (self::identifyFixedStyleValue(self::VERTICAL_ALIGNMENT_STYLES, $styleAttributeValue)) {
$style['alignment']['vertical'] = $styleAttributeValue;
}
break;
case 'Horizontal':
if (self::identifyFixedStyleValue(self::HORIZONTAL_ALIGNMENT_STYLES, $styleAttributeValue)) {
$style['alignment']['horizontal'] = $styleAttributeValue;
}
break;
case 'WrapText':
$style['alignment']['wrapText'] = true;
break;
case 'Rotate':
$style['alignment']['textRotation'] = $styleAttributeValue;
break;
}
}
return $style;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/Font.php 0000644 00000004532 15242753725 0017772 0 ustar 00 $styleAttributeValue) {
$styleAttributeValue = (string) $styleAttributeValue;
switch ($styleAttributeKey) {
case 'FontName':
$style['font']['name'] = $styleAttributeValue;
break;
case 'Size':
$style['font']['size'] = $styleAttributeValue;
break;
case 'Color':
$style['font']['color']['rgb'] = substr($styleAttributeValue, 1);
break;
case 'Bold':
$style['font']['bold'] = $styleAttributeValue === '1';
break;
case 'Italic':
$style['font']['italic'] = $styleAttributeValue === '1';
break;
case 'Underline':
$style = $this->parseUnderline($style, $styleAttributeValue);
break;
case 'VerticalAlign':
$style = $this->parseVerticalAlign($style, $styleAttributeValue);
break;
}
}
return $style;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/NumberFormat.php 0000644 00000001500 15242753725 0021455 0 ustar 00 $styleAttributeValue) {
$styleAttributeValue = str_replace($fromFormats, $toFormats, $styleAttributeValue);
switch ($styleAttributeValue) {
case 'Short Date':
$styleAttributeValue = 'dd/mm/yyyy';
break;
}
if ($styleAttributeValue > '') {
$style['numberFormat']['formatCode'] = $styleAttributeValue;
}
}
return $style;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php 0000644 00000001525 15242753725 0020756 0 ustar 00 ') : ($simple->attributes($node) ?? new SimpleXMLElement(''));
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php 0000644 00000011556 15242753725 0020124 0 ustar 00 spreadsheet = $spreadsheet;
}
public function readProperties(SimpleXMLElement $xml, array $namespaces): void
{
$this->readStandardProperties($xml);
$this->readCustomProperties($xml, $namespaces);
}
protected function readStandardProperties(SimpleXMLElement $xml): void
{
if (isset($xml->DocumentProperties[0])) {
$docProps = $this->spreadsheet->getProperties();
foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
$propertyValue = (string) $propertyValue;
$this->processStandardProperty($docProps, $propertyName, $propertyValue);
}
}
}
protected function readCustomProperties(SimpleXMLElement $xml, array $namespaces): void
{
if (isset($xml->CustomDocumentProperties) && is_iterable($xml->CustomDocumentProperties[0])) {
$docProps = $this->spreadsheet->getProperties();
foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
$propertyAttributes = self::getAttributes($propertyValue, $namespaces['dt']);
$propertyName = (string) preg_replace_callback('/_x([0-9a-f]{4})_/i', [$this, 'hex2str'], $propertyName);
$this->processCustomProperty($docProps, $propertyName, $propertyValue, $propertyAttributes);
}
}
}
protected function processStandardProperty(
DocumentProperties $docProps,
string $propertyName,
string $stringValue
): void {
switch ($propertyName) {
case 'Title':
$docProps->setTitle($stringValue);
break;
case 'Subject':
$docProps->setSubject($stringValue);
break;
case 'Author':
$docProps->setCreator($stringValue);
break;
case 'Created':
$docProps->setCreated($stringValue);
break;
case 'LastAuthor':
$docProps->setLastModifiedBy($stringValue);
break;
case 'LastSaved':
$docProps->setModified($stringValue);
break;
case 'Company':
$docProps->setCompany($stringValue);
break;
case 'Category':
$docProps->setCategory($stringValue);
break;
case 'Manager':
$docProps->setManager($stringValue);
break;
case 'HyperlinkBase':
$docProps->setHyperlinkBase($stringValue);
break;
case 'Keywords':
$docProps->setKeywords($stringValue);
break;
case 'Description':
$docProps->setDescription($stringValue);
break;
}
}
protected function processCustomProperty(
DocumentProperties $docProps,
string $propertyName,
?SimpleXMLElement $propertyValue,
SimpleXMLElement $propertyAttributes
): void {
switch ((string) $propertyAttributes) {
case 'boolean':
$propertyType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
$propertyValue = (bool) (string) $propertyValue;
break;
case 'integer':
$propertyType = DocumentProperties::PROPERTY_TYPE_INTEGER;
$propertyValue = (int) $propertyValue;
break;
case 'float':
$propertyType = DocumentProperties::PROPERTY_TYPE_FLOAT;
$propertyValue = (float) $propertyValue;
break;
case 'dateTime.tz':
case 'dateTime.iso8601tz':
$propertyType = DocumentProperties::PROPERTY_TYPE_DATE;
$propertyValue = trim((string) $propertyValue);
break;
default:
$propertyType = DocumentProperties::PROPERTY_TYPE_STRING;
$propertyValue = trim((string) $propertyValue);
break;
}
$docProps->setCustomProperty($propertyName, $propertyValue, $propertyType);
}
protected function hex2str(array $hex): string
{
return mb_chr((int) hexdec($hex[1]), 'UTF-8');
}
private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
{
return ($simple === null) ? new SimpleXMLElement('') : ($simple->attributes($node) ?? new SimpleXMLElement(''));
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/PageSettings.php 0000644 00000012235 15242753725 0020360 0 ustar 00 pageSetup($xmlX, $this->getPrintDefaults());
$this->printSettings = $this->printSetup($xmlX, $printSettings);
}
public function loadPageSettings(Spreadsheet $spreadsheet): void
{
$spreadsheet->getActiveSheet()->getPageSetup()
->setPaperSize($this->printSettings->paperSize)
->setOrientation($this->printSettings->orientation)
->setScale($this->printSettings->scale)
->setVerticalCentered($this->printSettings->verticalCentered)
->setHorizontalCentered($this->printSettings->horizontalCentered)
->setPageOrder($this->printSettings->printOrder);
$spreadsheet->getActiveSheet()->getPageMargins()
->setTop($this->printSettings->topMargin)
->setHeader($this->printSettings->headerMargin)
->setLeft($this->printSettings->leftMargin)
->setRight($this->printSettings->rightMargin)
->setBottom($this->printSettings->bottomMargin)
->setFooter($this->printSettings->footerMargin);
}
private function getPrintDefaults(): stdClass
{
return (object) [
'paperSize' => 9,
'orientation' => PageSetup::ORIENTATION_DEFAULT,
'scale' => 100,
'horizontalCentered' => false,
'verticalCentered' => false,
'printOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER,
'topMargin' => 0.75,
'headerMargin' => 0.3,
'leftMargin' => 0.7,
'rightMargin' => 0.7,
'bottomMargin' => 0.75,
'footerMargin' => 0.3,
];
}
private function pageSetup(SimpleXMLElement $xmlX, stdClass $printDefaults): stdClass
{
if (isset($xmlX->WorksheetOptions->PageSetup)) {
foreach ($xmlX->WorksheetOptions->PageSetup as $pageSetupData) {
foreach ($pageSetupData as $pageSetupKey => $pageSetupValue) {
/** @scrutinizer ignore-call */
$pageSetupAttributes = $pageSetupValue->attributes(Namespaces::URN_EXCEL);
if ($pageSetupAttributes !== null) {
switch ($pageSetupKey) {
case 'Layout':
$this->setLayout($printDefaults, $pageSetupAttributes);
break;
case 'Header':
$printDefaults->headerMargin = (float) $pageSetupAttributes->Margin ?: 1.0;
break;
case 'Footer':
$printDefaults->footerMargin = (float) $pageSetupAttributes->Margin ?: 1.0;
break;
case 'PageMargins':
$this->setMargins($printDefaults, $pageSetupAttributes);
break;
}
}
}
}
}
return $printDefaults;
}
private function printSetup(SimpleXMLElement $xmlX, stdClass $printDefaults): stdClass
{
if (isset($xmlX->WorksheetOptions->Print)) {
foreach ($xmlX->WorksheetOptions->Print as $printData) {
foreach ($printData as $printKey => $printValue) {
switch ($printKey) {
case 'LeftToRight':
$printDefaults->printOrder = PageSetup::PAGEORDER_OVER_THEN_DOWN;
break;
case 'PaperSizeIndex':
$printDefaults->paperSize = (int) $printValue ?: 9;
break;
case 'Scale':
$printDefaults->scale = (int) $printValue ?: 100;
break;
}
}
}
}
return $printDefaults;
}
private function setLayout(stdClass $printDefaults, SimpleXMLElement $pageSetupAttributes): void
{
$printDefaults->orientation = (string) strtolower($pageSetupAttributes->Orientation ?? '') ?: PageSetup::ORIENTATION_PORTRAIT;
$printDefaults->horizontalCentered = (bool) $pageSetupAttributes->CenterHorizontal ?: false;
$printDefaults->verticalCentered = (bool) $pageSetupAttributes->CenterVertical ?: false;
}
private function setMargins(stdClass $printDefaults, SimpleXMLElement $pageSetupAttributes): void
{
$printDefaults->leftMargin = (float) $pageSetupAttributes->Left ?: 1.0;
$printDefaults->rightMargin = (float) $pageSetupAttributes->Right ?: 1.0;
$printDefaults->topMargin = (float) $pageSetupAttributes->Top ?: 1.0;
$printDefaults->bottomMargin = (float) $pageSetupAttributes->Bottom ?: 1.0;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/DataValidations.php 0000644 00000017011 15242753725 0021027 0 ustar 00 DataValidation::OPERATOR_BETWEEN,
'equal' => DataValidation::OPERATOR_EQUAL,
'greater' => DataValidation::OPERATOR_GREATERTHAN,
'greaterorequal' => DataValidation::OPERATOR_GREATERTHANOREQUAL,
'less' => DataValidation::OPERATOR_LESSTHAN,
'lessorequal' => DataValidation::OPERATOR_LESSTHANOREQUAL,
'notbetween' => DataValidation::OPERATOR_NOTBETWEEN,
'notequal' => DataValidation::OPERATOR_NOTEQUAL,
];
private const TYPE_MAPPINGS = [
'textlength' => DataValidation::TYPE_TEXTLENGTH,
];
private int $thisRow = 0;
private int $thisColumn = 0;
private function replaceR1C1(array $matches): string
{
return AddressHelper::convertToA1($matches[0], $this->thisRow, $this->thisColumn, false);
}
public function loadDataValidations(SimpleXMLElement $worksheet, Spreadsheet $spreadsheet): void
{
$xmlX = $worksheet->children(Namespaces::URN_EXCEL);
$sheet = $spreadsheet->getActiveSheet();
/** @var callable */
$pregCallback = [$this, 'replaceR1C1'];
foreach ($xmlX->DataValidation as $dataValidation) {
$cells = [];
$validation = new DataValidation();
// set defaults
$validation->setShowDropDown(true);
$validation->setShowInputMessage(true);
$validation->setShowErrorMessage(true);
$validation->setShowDropDown(true);
$this->thisRow = 1;
$this->thisColumn = 1;
foreach ($dataValidation as $tagName => $tagValue) {
$tagValue = (string) $tagValue;
$tagValueLower = strtolower($tagValue);
switch ($tagName) {
case 'Range':
foreach (explode(',', $tagValue) as $range) {
$cell = '';
if (preg_match('/^R(\d+)C(\d+):R(\d+)C(\d+)$/', (string) $range, $selectionMatches) === 1) {
// range
$firstCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
. $selectionMatches[1];
$cell = $firstCell
. ':'
. Coordinate::stringFromColumnIndex((int) $selectionMatches[4])
. $selectionMatches[3];
$this->thisRow = (int) $selectionMatches[1];
$this->thisColumn = (int) $selectionMatches[2];
$sheet->getCell($firstCell);
} elseif (preg_match('/^R(\d+)C(\d+)$/', (string) $range, $selectionMatches) === 1) {
// cell
$cell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
. $selectionMatches[1];
$sheet->getCell($cell);
$this->thisRow = (int) $selectionMatches[1];
$this->thisColumn = (int) $selectionMatches[2];
} elseif (preg_match('/^C(\d+)$/', (string) $range, $selectionMatches) === 1) {
// column
$firstCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[1])
. '1';
$cell = $firstCell
. ':'
. Coordinate::stringFromColumnIndex((int) $selectionMatches[1])
. ((string) AddressRange::MAX_ROW);
$this->thisColumn = (int) $selectionMatches[1];
$sheet->getCell($firstCell);
} elseif (preg_match('/^R(\d+)$/', (string) $range, $selectionMatches)) {
// row
$firstCell = 'A'
. $selectionMatches[1];
$cell = $firstCell
. ':'
. AddressRange::MAX_COLUMN
. $selectionMatches[1];
$this->thisRow = (int) $selectionMatches[1];
$sheet->getCell($firstCell);
}
$validation->setSqref($cell);
$stRange = $sheet->shrinkRangeToFit($cell);
$cells = array_merge($cells, Coordinate::extractAllCellReferencesInRange($stRange));
}
break;
case 'Type':
$validation->setType(self::TYPE_MAPPINGS[$tagValueLower] ?? $tagValueLower);
break;
case 'Qualifier':
$validation->setOperator(self::OPERATOR_MAPPINGS[$tagValueLower] ?? $tagValueLower);
break;
case 'InputTitle':
$validation->setPromptTitle($tagValue);
break;
case 'InputMessage':
$validation->setPrompt($tagValue);
break;
case 'InputHide':
$validation->setShowInputMessage(false);
break;
case 'ErrorStyle':
$validation->setErrorStyle($tagValueLower);
break;
case 'ErrorTitle':
$validation->setErrorTitle($tagValue);
break;
case 'ErrorMessage':
$validation->setError($tagValue);
break;
case 'ErrorHide':
$validation->setShowErrorMessage(false);
break;
case 'ComboHide':
$validation->setShowDropDown(false);
break;
case 'UseBlank':
$validation->setAllowBlank(true);
break;
case 'CellRangeList':
// FIXME missing FIXME
break;
case 'Min':
case 'Value':
$tagValue = (string) preg_replace_callback(AddressHelper::R1C1_COORDINATE_REGEX, $pregCallback, $tagValue);
$validation->setFormula1($tagValue);
break;
case 'Max':
$tagValue = (string) preg_replace_callback(AddressHelper::R1C1_COORDINATE_REGEX, $pregCallback, $tagValue);
$validation->setFormula2($tagValue);
break;
}
}
foreach ($cells as $cell) {
$sheet->getCell($cell)->setDataValidation(clone $validation);
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php 0000644 00000010064 15242753725 0017061 0 ustar 00 Styles) || !is_iterable($xml->Styles[0])) {
return [];
}
$alignmentStyleParser = new Style\Alignment();
$borderStyleParser = new Style\Border();
$fontStyleParser = new Style\Font();
$fillStyleParser = new Style\Fill();
$numberFormatStyleParser = new Style\NumberFormat();
foreach ($xml->Styles[0] as $style) {
$style_ss = self::getAttributes($style, $namespaces['ss']);
$styleID = (string) $style_ss['ID'];
$this->styles[$styleID] = $this->styles['Default'] ?? [];
$alignment = $border = $font = $fill = $numberFormat = $protection = [];
foreach ($style as $styleType => $styleDatax) {
$styleData = self::getSxml($styleDatax);
$styleAttributes = $styleData->attributes($namespaces['ss']);
switch ($styleType) {
case 'Alignment':
if ($styleAttributes) {
$alignment = $alignmentStyleParser->parseStyle($styleAttributes);
}
break;
case 'Borders':
$border = $borderStyleParser->parseStyle($styleData, $namespaces);
break;
case 'Font':
if ($styleAttributes) {
$font = $fontStyleParser->parseStyle($styleAttributes);
}
break;
case 'Interior':
if ($styleAttributes) {
$fill = $fillStyleParser->parseStyle($styleAttributes);
}
break;
case 'NumberFormat':
if ($styleAttributes) {
$numberFormat = $numberFormatStyleParser->parseStyle($styleAttributes);
}
break;
case 'Protection':
$locked = $hidden = null;
$styleAttributesP = $styleData->attributes($namespaces['x']);
if (isset($styleAttributes['Protected'])) {
$locked = ((bool) (string) $styleAttributes['Protected']) ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED;
}
if (isset($styleAttributesP['HideFormula'])) {
$hidden = ((bool) (string) $styleAttributesP['HideFormula']) ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED;
}
if ($locked !== null || $hidden !== null) {
$protection['protection'] = [];
if ($locked !== null) {
$protection['protection']['locked'] = $locked;
}
if ($hidden !== null) {
$protection['protection']['hidden'] = $hidden;
}
}
break;
}
}
$this->styles[$styleID] = array_merge($alignment, $border, $font, $fill, $numberFormat, $protection);
}
return $this->styles;
}
private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
{
return ($simple === null) ? new SimpleXMLElement('') : ($simple->attributes($node) ?? new SimpleXMLElement(''));
}
private static function getSxml(?SimpleXMLElement $simple): SimpleXMLElement
{
return ($simple !== null) ? $simple : new SimpleXMLElement('');
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php 0000644 00000012463 15242753725 0021133 0 ustar 00 spreadsheet = $spreadsheet;
}
private function docPropertiesOld(SimpleXMLElement $gnmXML): void
{
$docProps = $this->spreadsheet->getProperties();
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;
}
}
}
private function docPropertiesDC(SimpleXMLElement $officePropertyDC): void
{
$docProps = $this->spreadsheet->getProperties();
foreach ($officePropertyDC as $propertyName => $propertyValue) {
$propertyValue = trim((string) $propertyValue);
switch ($propertyName) {
case 'title':
$docProps->setTitle($propertyValue);
break;
case 'subject':
$docProps->setSubject($propertyValue);
break;
case 'creator':
$docProps->setCreator($propertyValue);
$docProps->setLastModifiedBy($propertyValue);
break;
case 'date':
$creationDate = $propertyValue;
$docProps->setModified($creationDate);
break;
case 'description':
$docProps->setDescription($propertyValue);
break;
}
}
}
private function docPropertiesMeta(SimpleXMLElement $officePropertyMeta): void
{
$docProps = $this->spreadsheet->getProperties();
foreach ($officePropertyMeta as $propertyName => $propertyValue) {
if ($propertyValue !== null) {
$attributes = $propertyValue->attributes(Gnumeric::NAMESPACE_META);
$propertyValue = trim((string) $propertyValue);
switch ($propertyName) {
case 'keyword':
$docProps->setKeywords($propertyValue);
break;
case 'initial-creator':
$docProps->setCreator($propertyValue);
$docProps->setLastModifiedBy($propertyValue);
break;
case 'creation-date':
$creationDate = $propertyValue;
$docProps->setCreated($creationDate);
break;
case 'user-defined':
if ($attributes) {
[, $attrName] = explode(':', (string) $attributes['name']);
$this->userDefinedProperties($attrName, $propertyValue);
}
break;
}
}
}
}
private function userDefinedProperties(string $attrName, string $propertyValue): void
{
$docProps = $this->spreadsheet->getProperties();
switch ($attrName) {
case 'publisher':
$docProps->setCompany($propertyValue);
break;
case 'category':
$docProps->setCategory($propertyValue);
break;
case 'manager':
$docProps->setManager($propertyValue);
break;
}
}
public function readProperties(SimpleXMLElement $xml, SimpleXMLElement $gnmXML): void
{
$officeXML = $xml->children(Gnumeric::NAMESPACE_OFFICE);
if (!empty($officeXML)) {
$officeDocXML = $officeXML->{'document-meta'};
$officeDocMetaXML = $officeDocXML->meta;
foreach ($officeDocMetaXML as $officePropertyData) {
$officePropertyDC = $officePropertyData->children(Gnumeric::NAMESPACE_DC);
$this->docPropertiesDC($officePropertyDC);
$officePropertyMeta = $officePropertyData->children(Gnumeric::NAMESPACE_META);
$this->docPropertiesMeta($officePropertyMeta);
}
} elseif (isset($gnmXML->Summary)) {
$this->docPropertiesOld($gnmXML);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/PageSetup.php 0000644 00000012177 15242753725 0020676 0 ustar 00 spreadsheet = $spreadsheet;
}
public function printInformation(SimpleXMLElement $sheet): self
{
if (isset($sheet->PrintInformation, $sheet->PrintInformation[0])) {
$printInformation = $sheet->PrintInformation[0];
$setup = $this->spreadsheet->getActiveSheet()->getPageSetup();
$attributes = $printInformation->Scale->attributes();
if (isset($attributes['percentage'])) {
$setup->setScale((int) $attributes['percentage']);
}
$pageOrder = (string) $printInformation->order;
if ($pageOrder === 'r_then_d') {
$setup->setPageOrder(WorksheetPageSetup::PAGEORDER_OVER_THEN_DOWN);
} elseif ($pageOrder === 'd_then_r') {
$setup->setPageOrder(WorksheetPageSetup::PAGEORDER_DOWN_THEN_OVER);
}
$orientation = (string) $printInformation->orientation;
if ($orientation !== '') {
$setup->setOrientation($orientation);
}
$attributes = $printInformation->hcenter->attributes();
if (isset($attributes['value'])) {
$setup->setHorizontalCentered((bool) (string) $attributes['value']);
}
$attributes = $printInformation->vcenter->attributes();
if (isset($attributes['value'])) {
$setup->setVerticalCentered((bool) (string) $attributes['value']);
}
}
return $this;
}
public function sheetMargins(SimpleXMLElement $sheet): self
{
if (isset($sheet->PrintInformation, $sheet->PrintInformation->Margins)) {
$marginSet = [
// Default Settings
'top' => 0.75,
'header' => 0.3,
'left' => 0.7,
'right' => 0.7,
'bottom' => 0.75,
'footer' => 0.3,
];
$marginSet = $this->buildMarginSet($sheet, $marginSet);
$this->adjustMargins($marginSet);
}
return $this;
}
private function buildMarginSet(SimpleXMLElement $sheet, array $marginSet): array
{
foreach ($sheet->PrintInformation->Margins->children(Gnumeric::NAMESPACE_GNM) as $key => $margin) {
$marginAttributes = $margin->attributes();
$marginSize = ($marginAttributes['Points']) ?? 72; // Default is 72pt
// Convert value in points to inches
$marginSize = PageMargins::fromPoints((float) $marginSize);
$marginSet[$key] = $marginSize;
}
return $marginSet;
}
private function adjustMargins(array $marginSet): void
{
foreach ($marginSet as $key => $marginSize) {
// Gnumeric is quirky in the way it displays the header/footer values:
// header is actually the sum of top and header; footer is actually the sum of bottom and footer
// then top is actually the header value, and bottom is actually the footer value
switch ($key) {
case 'left':
case 'right':
$this->sheetMargin($key, $marginSize);
break;
case 'top':
$this->sheetMargin($key, $marginSet['header'] ?? 0);
break;
case 'bottom':
$this->sheetMargin($key, $marginSet['footer'] ?? 0);
break;
case 'header':
$this->sheetMargin($key, ($marginSet['top'] ?? 0) - $marginSize);
break;
case 'footer':
$this->sheetMargin($key, ($marginSet['bottom'] ?? 0) - $marginSize);
break;
}
}
}
private function sheetMargin(string $key, float $marginSize): void
{
switch ($key) {
case 'top':
$this->spreadsheet->getActiveSheet()->getPageMargins()->setTop($marginSize);
break;
case 'bottom':
$this->spreadsheet->getActiveSheet()->getPageMargins()->setBottom($marginSize);
break;
case 'left':
$this->spreadsheet->getActiveSheet()->getPageMargins()->setLeft($marginSize);
break;
case 'right':
$this->spreadsheet->getActiveSheet()->getPageMargins()->setRight($marginSize);
break;
case 'header':
$this->spreadsheet->getActiveSheet()->getPageMargins()->setHeader($marginSize);
break;
case 'footer':
$this->spreadsheet->getActiveSheet()->getPageMargins()->setFooter($marginSize);
break;
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php 0000644 00000027341 15242753725 0020263 0 ustar 00 [
'0' => Border::BORDER_NONE,
'1' => Border::BORDER_THIN,
'2' => Border::BORDER_MEDIUM,
'3' => Border::BORDER_SLANTDASHDOT,
'4' => Border::BORDER_DASHED,
'5' => Border::BORDER_THICK,
'6' => Border::BORDER_DOUBLE,
'7' => Border::BORDER_DOTTED,
'8' => Border::BORDER_MEDIUMDASHED,
'9' => Border::BORDER_DASHDOT,
'10' => Border::BORDER_MEDIUMDASHDOT,
'11' => Border::BORDER_DASHDOTDOT,
'12' => Border::BORDER_MEDIUMDASHDOTDOT,
'13' => Border::BORDER_MEDIUMDASHDOTDOT,
],
'fillType' => [
'1' => Fill::FILL_SOLID,
'2' => Fill::FILL_PATTERN_DARKGRAY,
'3' => Fill::FILL_PATTERN_MEDIUMGRAY,
'4' => Fill::FILL_PATTERN_LIGHTGRAY,
'5' => Fill::FILL_PATTERN_GRAY125,
'6' => Fill::FILL_PATTERN_GRAY0625,
'7' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe
'8' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe
'9' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe
'10' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe
'11' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch
'12' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch
'13' => Fill::FILL_PATTERN_LIGHTHORIZONTAL,
'14' => Fill::FILL_PATTERN_LIGHTVERTICAL,
'15' => Fill::FILL_PATTERN_LIGHTUP,
'16' => Fill::FILL_PATTERN_LIGHTDOWN,
'17' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch
'18' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch
],
'horizontal' => [
'1' => Alignment::HORIZONTAL_GENERAL,
'2' => Alignment::HORIZONTAL_LEFT,
'4' => Alignment::HORIZONTAL_RIGHT,
'8' => Alignment::HORIZONTAL_CENTER,
'16' => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
'32' => Alignment::HORIZONTAL_JUSTIFY,
'64' => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
],
'underline' => [
'1' => Font::UNDERLINE_SINGLE,
'2' => Font::UNDERLINE_DOUBLE,
'3' => Font::UNDERLINE_SINGLEACCOUNTING,
'4' => Font::UNDERLINE_DOUBLEACCOUNTING,
],
'vertical' => [
'1' => Alignment::VERTICAL_TOP,
'2' => Alignment::VERTICAL_BOTTOM,
'4' => Alignment::VERTICAL_CENTER,
'8' => Alignment::VERTICAL_JUSTIFY,
],
];
public function __construct(Spreadsheet $spreadsheet, bool $readDataOnly)
{
$this->spreadsheet = $spreadsheet;
$this->readDataOnly = $readDataOnly;
}
public function read(SimpleXMLElement $sheet, int $maxRow, int $maxCol): void
{
if ($sheet->Styles->StyleRegion !== null) {
$this->readStyles($sheet->Styles->StyleRegion, $maxRow, $maxCol);
}
}
private function readStyles(SimpleXMLElement $styleRegion, int $maxRow, int $maxCol): void
{
foreach ($styleRegion as $style) {
/** @scrutinizer ignore-call */
$styleAttributes = $style->attributes();
if ($styleAttributes !== null && ($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) {
$cellRange = $this->readStyleRange($styleAttributes, $maxCol, $maxRow);
$styleAttributes = $style->Style->attributes();
$styleArray = [];
// We still set the number format mask for date/time values, even if readDataOnly is true
// so that we can identify whether a float is a float or a date value
$formatCode = $styleAttributes ? (string) $styleAttributes['Format'] : null;
if ($formatCode && Date::isDateTimeFormatCode($formatCode)) {
$styleArray['numberFormat']['formatCode'] = $formatCode;
}
if ($this->readDataOnly === false && $styleAttributes !== null) {
// If readDataOnly is false, we set all formatting information
$styleArray['numberFormat']['formatCode'] = $formatCode;
$styleArray = $this->readStyle($styleArray, $styleAttributes, /** @scrutinizer ignore-type */ $style);
}
$this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray);
}
}
}
private function addBorderDiagonal(SimpleXMLElement $srssb, array &$styleArray): void
{
if (isset($srssb->Diagonal, $srssb->{'Rev-Diagonal'})) {
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes());
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH;
} elseif (isset($srssb->Diagonal)) {
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes());
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP;
} elseif (isset($srssb->{'Rev-Diagonal'})) {
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->{'Rev-Diagonal'}->attributes());
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN;
}
}
private function addBorderStyle(SimpleXMLElement $srssb, array &$styleArray, string $direction): void
{
$ucDirection = ucfirst($direction);
if (isset($srssb->$ucDirection)) {
$styleArray['borders'][$direction] = self::parseBorderAttributes($srssb->$ucDirection->attributes());
}
}
private function calcRotation(SimpleXMLElement $styleAttributes): int
{
$rotation = (int) $styleAttributes->Rotation;
if ($rotation >= 270 && $rotation <= 360) {
$rotation -= 360;
}
$rotation = (abs($rotation) > 90) ? 0 : $rotation;
return $rotation;
}
private static function addStyle(array &$styleArray, string $key, string $value): void
{
if (array_key_exists($value, self::$mappings[$key])) {
$styleArray[$key] = self::$mappings[$key][$value];
}
}
private static function addStyle2(array &$styleArray, string $key1, string $key, string $value): void
{
if (array_key_exists($value, self::$mappings[$key])) {
$styleArray[$key1][$key] = self::$mappings[$key][$value];
}
}
private static function parseBorderAttributes(?SimpleXMLElement $borderAttributes): array
{
$styleArray = [];
if ($borderAttributes !== null) {
if (isset($borderAttributes['Color'])) {
$styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']);
}
self::addStyle($styleArray, 'borderStyle', (string) $borderAttributes['Style']);
}
return $styleArray;
}
private static function parseGnumericColour(string $gnmColour): string
{
[$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;
}
private function addColors(array &$styleArray, SimpleXMLElement $styleAttributes): void
{
$RGB = self::parseGnumericColour((string) $styleAttributes['Fore']);
$styleArray['font']['color']['rgb'] = $RGB;
$RGB = self::parseGnumericColour((string) $styleAttributes['Back']);
$shade = (string) $styleAttributes['Shade'];
if (($RGB !== '000000') || ($shade !== '0')) {
$RGB2 = self::parseGnumericColour((string) $styleAttributes['PatternColor']);
if ($shade === '1') {
$styleArray['fill']['startColor']['rgb'] = $RGB;
$styleArray['fill']['endColor']['rgb'] = $RGB2;
} else {
$styleArray['fill']['endColor']['rgb'] = $RGB;
$styleArray['fill']['startColor']['rgb'] = $RGB2;
}
self::addStyle2($styleArray, 'fill', 'fillType', $shade);
}
}
private function readStyleRange(SimpleXMLElement $styleAttributes, int $maxCol, int $maxRow): string
{
$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 = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']);
$cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow;
return $cellRange;
}
private function readStyle(array $styleArray, SimpleXMLElement $styleAttributes, SimpleXMLElement $style): array
{
self::addStyle2($styleArray, 'alignment', 'horizontal', (string) $styleAttributes['HAlign']);
self::addStyle2($styleArray, 'alignment', 'vertical', (string) $styleAttributes['VAlign']);
$styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1';
$styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes);
$styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1';
$styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0;
$this->addColors($styleArray, $styleAttributes);
$fontAttributes = $style->Style->Font->attributes();
if ($fontAttributes !== null) {
$styleArray['font']['name'] = (string) $style->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';
self::addStyle2($styleArray, 'font', 'underline', (string) $fontAttributes['Underline']);
switch ($fontAttributes['Script']) {
case '1':
$styleArray['font']['superscript'] = true;
break;
case '-1':
$styleArray['font']['subscript'] = true;
break;
}
}
if (isset($style->Style->StyleBorder)) {
$srssb = $style->Style->StyleBorder;
$this->addBorderStyle($srssb, $styleArray, 'top');
$this->addBorderStyle($srssb, $styleArray, 'bottom');
$this->addBorderStyle($srssb, $styleArray, 'left');
$this->addBorderStyle($srssb, $styleArray, 'right');
$this->addBorderDiagonal($srssb, $styleArray);
}
// TO DO
/*
if (isset($style->Style->HyperLink)) {
$hyperlink = $style->Style->HyperLink->attributes();
}
*/
return $styleArray;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Csv/Delimiter.php 0000644 00000010775 15242753725 0017703 0 ustar 00 fileHandle = $fileHandle;
$this->escapeCharacter = $escapeCharacter;
$this->enclosure = $enclosure;
$this->countPotentialDelimiters();
}
public function getDefaultDelimiter(): string
{
return self::POTENTIAL_DELIMETERS[0];
}
public function linesCounted(): int
{
return $this->numberLines;
}
protected function countPotentialDelimiters(): void
{
$this->counts = array_fill_keys(self::POTENTIAL_DELIMETERS, []);
$delimiterKeys = array_flip(self::POTENTIAL_DELIMETERS);
// Count how many times each of the potential delimiters appears in each line
$this->numberLines = 0;
while (($line = $this->getNextLine()) !== false && (++$this->numberLines < 1000)) {
$this->countDelimiterValues($line, $delimiterKeys);
}
}
protected function countDelimiterValues(string $line, array $delimiterKeys): void
{
$splitString = str_split($line, 1);
if (is_array($splitString)) {
$distribution = array_count_values($splitString);
$countLine = array_intersect_key($distribution, $delimiterKeys);
foreach (self::POTENTIAL_DELIMETERS as $delimiter) {
$this->counts[$delimiter][] = $countLine[$delimiter] ?? 0;
}
}
}
public function infer(): ?string
{
// Calculate the mean square deviations for each delimiter
// (ignoring delimiters that haven't been found consistently)
$meanSquareDeviations = [];
$middleIdx = floor(($this->numberLines - 1) / 2);
foreach (self::POTENTIAL_DELIMETERS as $delimiter) {
$series = $this->counts[$delimiter];
sort($series);
$median = ($this->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 (self::POTENTIAL_DELIMETERS as $delimiter) {
if (!isset($meanSquareDeviations[$delimiter])) {
continue;
}
if ($meanSquareDeviations[$delimiter] < $min) {
$min = $meanSquareDeviations[$delimiter];
$this->delimiter = $delimiter;
}
}
return $this->delimiter;
}
/**
* Get the next full line from the file.
*
* @return false|string
*/
public function getNextLine()
{
$line = '';
$enclosure = ($this->escapeCharacter === '' ? ''
: ('(?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 = (string) 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 !== '') ? $line : false;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php 0000644 00000011132 15242753725 0016531 0 ustar 00 data.
*
* @var int
*/
private $dataSize;
/**
* Current position in stream.
*
* @var int
*/
private $pos;
/**
* Workbook to be returned by the reader.
*
* @var Spreadsheet
*/
private $spreadsheet;
/**
* Worksheet that is currently being built by the reader.
*
* @var Worksheet
*/
private $phpSheet;
/**
* BIFF version.
*
* @var int
*/
private $version;
/**
* Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95)
* For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE'.
*
* @var string
*/
private $codepage;
/**
* Shared formats.
*
* @var array
*/
private $formats;
/**
* Shared fonts.
*
* @var Font[]
*/
private $objFonts;
/**
* Color palette.
*
* @var array
*/
private $palette;
/**
* Worksheets.
*
* @var array
*/
private $sheets;
/**
* External books.
*
* @var array
*/
private $externalBooks;
/**
* REF structures. Only applies to BIFF8.
*
* @var array
*/
private $ref;
/**
* External names.
*
* @var array
*/
private $externalNames;
/**
* Defined names.
*
* @var array
*/
private $definedname;
/**
* Shared strings. Only applies to BIFF8.
*
* @var array
*/
private $sst;
/**
* Panes are frozen? (in sheet currently being read). See WINDOW2 record.
*
* @var bool
*/
private $frozen;
/**
* Fit printout to number of pages? (in sheet currently being read). See SHEETPR record.
*
* @var bool
*/
private $isFitToPages;
/**
* Objects. One OBJ record contributes with one entry.
*
* @var array
*/
private $objs;
/**
* Text Objects. One TXO record corresponds with one entry.
*
* @var array
*/
private $textObjects;
/**
* Cell Annotations (BIFF8).
*
* @var array
*/
private $cellNotes;
/**
* The combined MSODRAWINGGROUP data.
*
* @var string
*/
private $drawingGroupData;
/**
* The combined MSODRAWING data (per sheet).
*
* @var string
*/
private $drawingData;
/**
* Keep track of XF index.
*
* @var int
*/
private $xfIndex;
/**
* Mapping of XF index (that is a cell XF) to final index in cellXf collection.
*
* @var array
*/
private $mapCellXfIndex;
/**
* Mapping of XF index (that is a style XF) to final index in cellStyleXf collection.
*
* @var array
*/
private $mapCellStyleXfIndex;
/**
* The shared formulas in a sheet. One SHAREDFMLA record contributes with one value.
*
* @var array
*/
private $sharedFormulas;
/**
* The shared formula parts in a sheet. One FORMULA record contributes with one value if it
* refers to a shared formula.
*
* @var array
*/
private $sharedFormulaParts;
/**
* The type of encryption in use.
*
* @var int
*/
private $encryption = 0;
/**
* The position in the stream after which contents are encrypted.
*
* @var int
*/
private $encryptionStartPos = 0;
/**
* The current RC4 decryption object.
*
* @var ?Xls\RC4
*/
private $rc4Key;
/**
* The position in the stream that the RC4 decryption object was left at.
*
* @var int
*/
private $rc4Pos = 0;
/**
* The current MD5 context state.
* It is never set in the program, so code which uses it is suspect.
*
* @var string
*/
private $md5Ctxt; // @phpstan-ignore-line
/**
* @var int
*/
private $textObjRef;
/**
* @var string
*/
private $baseCell;
/**
* Create a new Xls Reader instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Can the current IReader read the file?
*/
public function canRead(string $filename): bool
{
if (File::testFileNoThrow($filename) === false) {
return false;
}
try {
// Use ParseXL for the hard work.
$ole = new OLERead();
// get excel data
$ole->read($filename);
if ($ole->wrkbook === null) {
throw new Exception('The filename ' . $filename . ' is not recognised as a Spreadsheet file');
}
return true;
} catch (PhpSpreadsheetException $e) {
return false;
}
}
public function setCodepage(string $codepage): void
{
if (CodePage::validate($codepage) === false) {
throw new PhpSpreadsheetException('Unknown codepage: ' . $codepage);
}
$this->codepage = $codepage;
}
/**
* Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
*
* @param string $filename
*
* @return array
*/
public function listWorksheetNames($filename)
{
File::assertFile($filename);
$worksheetNames = [];
// Read the OLE file
$this->loadOLE($filename);
// total byte size of Excel data (workbook global substream + sheet substreams)
$this->dataSize = strlen($this->data);
$this->pos = 0;
$this->sheets = [];
// Parse Workbook Global Substream
while ($this->pos < $this->dataSize) {
$code = self::getUInt2d($this->data, $this->pos);
switch ($code) {
case self::XLS_TYPE_BOF:
$this->readBof();
break;
case self::XLS_TYPE_SHEET:
$this->readSheet();
break;
case self::XLS_TYPE_EOF:
$this->readDefault();
break 2;
default:
$this->readDefault();
break;
}
}
foreach ($this->sheets as $sheet) {
if ($sheet['sheetType'] != 0x00) {
// 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
continue;
}
$worksheetNames[] = $sheet['name'];
}
return $worksheetNames;
}
/**
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
*
* @param string $filename
*
* @return array
*/
public function listWorksheetInfo($filename)
{
File::assertFile($filename);
$worksheetInfo = [];
// Read the OLE file
$this->loadOLE($filename);
// total byte size of Excel data (workbook global substream + sheet substreams)
$this->dataSize = strlen($this->data);
// initialize
$this->pos = 0;
$this->sheets = [];
// Parse Workbook Global Substream
while ($this->pos < $this->dataSize) {
$code = self::getUInt2d($this->data, $this->pos);
switch ($code) {
case self::XLS_TYPE_BOF:
$this->readBof();
break;
case self::XLS_TYPE_SHEET:
$this->readSheet();
break;
case self::XLS_TYPE_EOF:
$this->readDefault();
break 2;
default:
$this->readDefault();
break;
}
}
// Parse the individual sheets
foreach ($this->sheets as $sheet) {
if ($sheet['sheetType'] != 0x00) {
// 0x00: Worksheet
// 0x02: Chart
// 0x06: Visual Basic module
continue;
}
$tmpInfo = [];
$tmpInfo['worksheetName'] = $sheet['name'];
$tmpInfo['lastColumnLetter'] = 'A';
$tmpInfo['lastColumnIndex'] = 0;
$tmpInfo['totalRows'] = 0;
$tmpInfo['totalColumns'] = 0;
$this->pos = $sheet['offset'];
while ($this->pos <= $this->dataSize - 4) {
$code = self::getUInt2d($this->data, $this->pos);
switch ($code) {
case self::XLS_TYPE_RK:
case self::XLS_TYPE_LABELSST:
case self::XLS_TYPE_NUMBER:
case self::XLS_TYPE_FORMULA:
case self::XLS_TYPE_BOOLERR:
case self::XLS_TYPE_LABEL:
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
$rowIndex = self::getUInt2d($recordData, 0) + 1;
$columnIndex = self::getUInt2d($recordData, 2);
$tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
$tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
break;
case self::XLS_TYPE_BOF:
$this->readBof();
break;
case self::XLS_TYPE_EOF:
$this->readDefault();
break 2;
default:
$this->readDefault();
break;
}
}
$tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
$tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
$worksheetInfo[] = $tmpInfo;
}
return $worksheetInfo;
}
/**
* Loads PhpSpreadsheet from file.
*/
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
// Read the OLE file
$this->loadOLE($filename);
// Initialisations
$this->spreadsheet = new Spreadsheet();
$this->spreadsheet->removeSheetByIndex(0); // remove 1st sheet
if (!$this->readDataOnly) {
$this->spreadsheet->removeCellStyleXfByIndex(0); // remove the default style
$this->spreadsheet->removeCellXfByIndex(0); // remove the default style
}
// Read the summary information stream (containing meta data)
$this->readSummaryInformation();
// Read the Additional document summary information stream (containing application-specific meta data)
$this->readDocumentSummaryInformation();
// total byte size of Excel data (workbook global substream + sheet substreams)
$this->dataSize = strlen($this->data);
// initialize
$this->pos = 0;
$this->codepage = $this->codepage ?: CodePage::DEFAULT_CODE_PAGE;
$this->formats = [];
$this->objFonts = [];
$this->palette = [];
$this->sheets = [];
$this->externalBooks = [];
$this->ref = [];
$this->definedname = [];
$this->sst = [];
$this->drawingGroupData = '';
$this->xfIndex = 0;
$this->mapCellXfIndex = [];
$this->mapCellStyleXfIndex = [];
// Parse Workbook Global Substream
while ($this->pos < $this->dataSize) {
$code = self::getUInt2d($this->data, $this->pos);
switch ($code) {
case self::XLS_TYPE_BOF:
$this->readBof();
break;
case self::XLS_TYPE_FILEPASS:
$this->readFilepass();
break;
case self::XLS_TYPE_CODEPAGE:
$this->readCodepage();
break;
case self::XLS_TYPE_DATEMODE:
$this->readDateMode();
break;
case self::XLS_TYPE_FONT:
$this->readFont();
break;
case self::XLS_TYPE_FORMAT:
$this->readFormat();
break;
case self::XLS_TYPE_XF:
$this->readXf();
break;
case self::XLS_TYPE_XFEXT:
$this->readXfExt();
break;
case self::XLS_TYPE_STYLE:
$this->readStyle();
break;
case self::XLS_TYPE_PALETTE:
$this->readPalette();
break;
case self::XLS_TYPE_SHEET:
$this->readSheet();
break;
case self::XLS_TYPE_EXTERNALBOOK:
$this->readExternalBook();
break;
case self::XLS_TYPE_EXTERNNAME:
$this->readExternName();
break;
case self::XLS_TYPE_EXTERNSHEET:
$this->readExternSheet();
break;
case self::XLS_TYPE_DEFINEDNAME:
$this->readDefinedName();
break;
case self::XLS_TYPE_MSODRAWINGGROUP:
$this->readMsoDrawingGroup();
break;
case self::XLS_TYPE_SST:
$this->readSst();
break;
case self::XLS_TYPE_EOF:
$this->readDefault();
break 2;
default:
$this->readDefault();
break;
}
}
// Resolve indexed colors for font, fill, and border colors
// Cannot be resolved already in XF record, because PALETTE record comes afterwards
if (!$this->readDataOnly) {
foreach ($this->objFonts as $objFont) {
if (isset($objFont->colorIndex)) {
$color = Xls\Color::map($objFont->colorIndex, $this->palette, $this->version);
$objFont->getColor()->setRGB($color['rgb']);
}
}
foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
// fill start and end color
$fill = $objStyle->getFill();
if (isset($fill->startcolorIndex)) {
$startColor = Xls\Color::map($fill->startcolorIndex, $this->palette, $this->version);
$fill->getStartColor()->setRGB($startColor['rgb']);
}
if (isset($fill->endcolorIndex)) {
$endColor = Xls\Color::map($fill->endcolorIndex, $this->palette, $this->version);
$fill->getEndColor()->setRGB($endColor['rgb']);
}
// border colors
$top = $objStyle->getBorders()->getTop();
$right = $objStyle->getBorders()->getRight();
$bottom = $objStyle->getBorders()->getBottom();
$left = $objStyle->getBorders()->getLeft();
$diagonal = $objStyle->getBorders()->getDiagonal();
if (isset($top->colorIndex)) {
$borderTopColor = Xls\Color::map($top->colorIndex, $this->palette, $this->version);
$top->getColor()->setRGB($borderTopColor['rgb']);
}
if (isset($right->colorIndex)) {
$borderRightColor = Xls\Color::map($right->colorIndex, $this->palette, $this->version);
$right->getColor()->setRGB($borderRightColor['rgb']);
}
if (isset($bottom->colorIndex)) {
$borderBottomColor = Xls\Color::map($bottom->colorIndex, $this->palette, $this->version);
$bottom->getColor()->setRGB($borderBottomColor['rgb']);
}
if (isset($left->colorIndex)) {
$borderLeftColor = Xls\Color::map($left->colorIndex, $this->palette, $this->version);
$left->getColor()->setRGB($borderLeftColor['rgb']);
}
if (isset($diagonal->colorIndex)) {
$borderDiagonalColor = Xls\Color::map($diagonal->colorIndex, $this->palette, $this->version);
$diagonal->getColor()->setRGB($borderDiagonalColor['rgb']);
}
}
}
// treat MSODRAWINGGROUP records, workbook-level Escher
$escherWorkbook = null;
if (!$this->readDataOnly && $this->drawingGroupData) {
$escher = new Escher();
$reader = new Xls\Escher($escher);
$escherWorkbook = $reader->load($this->drawingGroupData);
}
// Parse the individual sheets
foreach ($this->sheets as $sheet) {
if ($sheet['sheetType'] != 0x00) {
// 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
continue;
}
// check if sheet should be skipped
if (isset($this->loadSheetsOnly) && !in_array($sheet['name'], $this->loadSheetsOnly)) {
continue;
}
// add sheet to PhpSpreadsheet object
$this->phpSheet = $this->spreadsheet->createSheet();
// 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
$this->phpSheet->setTitle($sheet['name'], false, false);
$this->phpSheet->setSheetState($sheet['sheetState']);
$this->pos = $sheet['offset'];
// Initialize isFitToPages. May change after reading SHEETPR record.
$this->isFitToPages = false;
// Initialize drawingData
$this->drawingData = '';
// Initialize objs
$this->objs = [];
// Initialize shared formula parts
$this->sharedFormulaParts = [];
// Initialize shared formulas
$this->sharedFormulas = [];
// Initialize text objs
$this->textObjects = [];
// Initialize cell annotations
$this->cellNotes = [];
$this->textObjRef = -1;
while ($this->pos <= $this->dataSize - 4) {
$code = self::getUInt2d($this->data, $this->pos);
switch ($code) {
case self::XLS_TYPE_BOF:
$this->readBof();
break;
case self::XLS_TYPE_PRINTGRIDLINES:
$this->readPrintGridlines();
break;
case self::XLS_TYPE_DEFAULTROWHEIGHT:
$this->readDefaultRowHeight();
break;
case self::XLS_TYPE_SHEETPR:
$this->readSheetPr();
break;
case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
$this->readHorizontalPageBreaks();
break;
case self::XLS_TYPE_VERTICALPAGEBREAKS:
$this->readVerticalPageBreaks();
break;
case self::XLS_TYPE_HEADER:
$this->readHeader();
break;
case self::XLS_TYPE_FOOTER:
$this->readFooter();
break;
case self::XLS_TYPE_HCENTER:
$this->readHcenter();
break;
case self::XLS_TYPE_VCENTER:
$this->readVcenter();
break;
case self::XLS_TYPE_LEFTMARGIN:
$this->readLeftMargin();
break;
case self::XLS_TYPE_RIGHTMARGIN:
$this->readRightMargin();
break;
case self::XLS_TYPE_TOPMARGIN:
$this->readTopMargin();
break;
case self::XLS_TYPE_BOTTOMMARGIN:
$this->readBottomMargin();
break;
case self::XLS_TYPE_PAGESETUP:
$this->readPageSetup();
break;
case self::XLS_TYPE_PROTECT:
$this->readProtect();
break;
case self::XLS_TYPE_SCENPROTECT:
$this->readScenProtect();
break;
case self::XLS_TYPE_OBJECTPROTECT:
$this->readObjectProtect();
break;
case self::XLS_TYPE_PASSWORD:
$this->readPassword();
break;
case self::XLS_TYPE_DEFCOLWIDTH:
$this->readDefColWidth();
break;
case self::XLS_TYPE_COLINFO:
$this->readColInfo();
break;
case self::XLS_TYPE_DIMENSION:
$this->readDefault();
break;
case self::XLS_TYPE_ROW:
$this->readRow();
break;
case self::XLS_TYPE_DBCELL:
$this->readDefault();
break;
case self::XLS_TYPE_RK:
$this->readRk();
break;
case self::XLS_TYPE_LABELSST:
$this->readLabelSst();
break;
case self::XLS_TYPE_MULRK:
$this->readMulRk();
break;
case self::XLS_TYPE_NUMBER:
$this->readNumber();
break;
case self::XLS_TYPE_FORMULA:
$this->readFormula();
break;
case self::XLS_TYPE_SHAREDFMLA:
$this->readSharedFmla();
break;
case self::XLS_TYPE_BOOLERR:
$this->readBoolErr();
break;
case self::XLS_TYPE_MULBLANK:
$this->readMulBlank();
break;
case self::XLS_TYPE_LABEL:
$this->readLabel();
break;
case self::XLS_TYPE_BLANK:
$this->readBlank();
break;
case self::XLS_TYPE_MSODRAWING:
$this->readMsoDrawing();
break;
case self::XLS_TYPE_OBJ:
$this->readObj();
break;
case self::XLS_TYPE_WINDOW2:
$this->readWindow2();
break;
case self::XLS_TYPE_PAGELAYOUTVIEW:
$this->readPageLayoutView();
break;
case self::XLS_TYPE_SCL:
$this->readScl();
break;
case self::XLS_TYPE_PANE:
$this->readPane();
break;
case self::XLS_TYPE_SELECTION:
$this->readSelection();
break;
case self::XLS_TYPE_MERGEDCELLS:
$this->readMergedCells();
break;
case self::XLS_TYPE_HYPERLINK:
$this->readHyperLink();
break;
case self::XLS_TYPE_DATAVALIDATIONS:
$this->readDataValidations();
break;
case self::XLS_TYPE_DATAVALIDATION:
$this->readDataValidation();
break;
case self::XLS_TYPE_CFHEADER:
$cellRangeAddresses = $this->readCFHeader();
break;
case self::XLS_TYPE_CFRULE:
$this->readCFRule($cellRangeAddresses ?? []);
break;
case self::XLS_TYPE_SHEETLAYOUT:
$this->readSheetLayout();
break;
case self::XLS_TYPE_SHEETPROTECTION:
$this->readSheetProtection();
break;
case self::XLS_TYPE_RANGEPROTECTION:
$this->readRangeProtection();
break;
case self::XLS_TYPE_NOTE:
$this->readNote();
break;
case self::XLS_TYPE_TXO:
$this->readTextObject();
break;
case self::XLS_TYPE_CONTINUE:
$this->readContinue();
break;
case self::XLS_TYPE_EOF:
$this->readDefault();
break 2;
default:
$this->readDefault();
break;
}
}
// treat MSODRAWING records, sheet-level Escher
if (!$this->readDataOnly && $this->drawingData) {
$escherWorksheet = new Escher();
$reader = new Xls\Escher($escherWorksheet);
$escherWorksheet = $reader->load($this->drawingData);
// get all spContainers in one long array, so they can be mapped to OBJ records
/** @var SpContainer[] */
$allSpContainers = method_exists($escherWorksheet, 'getDgContainer') ? $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers() : [];
}
// treat OBJ records
foreach ($this->objs as $n => $obj) {
// the first shape container never has a corresponding OBJ record, hence $n + 1
if (isset($allSpContainers[$n + 1])) {
$spContainer = $allSpContainers[$n + 1];
// we skip all spContainers that are a part of a group shape since we cannot yet handle those
if ($spContainer->getNestingLevel() > 1) {
continue;
}
// calculate the width and height of the shape
/** @var int $startRow */
[$startColumn, $startRow] = Coordinate::coordinateFromString($spContainer->getStartCoordinates());
/** @var int $endRow */
[$endColumn, $endRow] = Coordinate::coordinateFromString($spContainer->getEndCoordinates());
$startOffsetX = $spContainer->getStartOffsetX();
$startOffsetY = $spContainer->getStartOffsetY();
$endOffsetX = $spContainer->getEndOffsetX();
$endOffsetY = $spContainer->getEndOffsetY();
$width = SharedXls::getDistanceX($this->phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX);
$height = SharedXls::getDistanceY($this->phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY);
// calculate offsetX and offsetY of the shape
$offsetX = (int) ($startOffsetX * SharedXls::sizeCol($this->phpSheet, $startColumn) / 1024);
$offsetY = (int) ($startOffsetY * SharedXls::sizeRow($this->phpSheet, $startRow) / 256);
switch ($obj['otObjType']) {
case 0x19:
// Note
if (isset($this->cellNotes[$obj['idObjID']])) {
$cellNote = $this->cellNotes[$obj['idObjID']];
if (isset($this->textObjects[$obj['idObjID']])) {
$textObject = $this->textObjects[$obj['idObjID']];
$this->cellNotes[$obj['idObjID']]['objTextData'] = $textObject;
}
}
break;
case 0x08:
// picture
// get index to BSE entry (1-based)
$BSEindex = $spContainer->getOPT(0x0104);
// If there is no BSE Index, we will fail here and other fields are not read.
// Fix by checking here.
// TODO: Why is there no BSE Index? Is this a new Office Version? Password protected field?
// More likely : a uncompatible picture
if (!$BSEindex) {
continue 2;
}
if ($escherWorkbook) {
$BSECollection = method_exists($escherWorkbook, 'getDggContainer') ? $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection() : [];
$BSE = $BSECollection[$BSEindex - 1];
$blipType = $BSE->getBlipType();
// need check because some blip types are not supported by Escher reader such as EMF
if ($blip = $BSE->getBlip()) {
$ih = imagecreatefromstring($blip->getData());
if ($ih !== false) {
$drawing = new MemoryDrawing();
$drawing->setImageResource($ih);
// width, height, offsetX, offsetY
$drawing->setResizeProportional(false);
$drawing->setWidth($width);
$drawing->setHeight($height);
$drawing->setOffsetX($offsetX);
$drawing->setOffsetY($offsetY);
switch ($blipType) {
case BSE::BLIPTYPE_JPEG:
$drawing->setRenderingFunction(MemoryDrawing::RENDERING_JPEG);
$drawing->setMimeType(MemoryDrawing::MIMETYPE_JPEG);
break;
case BSE::BLIPTYPE_PNG:
imagealphablending($ih, false);
imagesavealpha($ih, true);
$drawing->setRenderingFunction(MemoryDrawing::RENDERING_PNG);
$drawing->setMimeType(MemoryDrawing::MIMETYPE_PNG);
break;
}
$drawing->setWorksheet($this->phpSheet);
$drawing->setCoordinates($spContainer->getStartCoordinates());
}
}
}
break;
default:
// other object type
break;
}
}
}
// treat SHAREDFMLA records
if ($this->version == self::XLS_BIFF8) {
foreach ($this->sharedFormulaParts as $cell => $baseCell) {
/** @var int $row */
[$column, $row] = Coordinate::coordinateFromString($cell);
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
$formula = $this->getFormulaFromStructure($this->sharedFormulas[$baseCell], $cell);
$this->phpSheet->getCell($cell)->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
}
}
}
if (!empty($this->cellNotes)) {
foreach ($this->cellNotes as $note => $noteDetails) {
if (!isset($noteDetails['objTextData'])) {
if (isset($this->textObjects[$note])) {
$textObject = $this->textObjects[$note];
$noteDetails['objTextData'] = $textObject;
} else {
$noteDetails['objTextData']['text'] = '';
}
}
$cellAddress = str_replace('$', '', $noteDetails['cellRef']);
$this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails['author'])->setText($this->parseRichText($noteDetails['objTextData']['text']));
}
}
}
// add the named ranges (defined names)
foreach ($this->definedname as $definedName) {
if ($definedName['isBuiltInName']) {
switch ($definedName['name']) {
case pack('C', 0x06):
// print area
// in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2
$ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
$extractedRanges = [];
foreach ($ranges as $range) {
// $range should look like one of these
// Foo!$C$7:$J$66
// Bar!$A$1:$IV$2
$explodes = Worksheet::extractSheetTitle($range, true);
$sheetName = trim($explodes[0], "'");
if (count($explodes) == 2) {
if (strpos($explodes[1], ':') === false) {
$explodes[1] = $explodes[1] . ':' . $explodes[1];
}
$extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66
}
}
if ($docSheet = $this->spreadsheet->getSheetByName($sheetName)) {
$docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2
}
break;
case pack('C', 0x07):
// print titles (repeating rows)
// Assuming BIFF8, there are 3 cases
// 1. repeating rows
// formula looks like this: Sheet!$A$1:$IV$2
// rows 1-2 repeat
// 2. repeating columns
// formula looks like this: Sheet!$A$1:$B$65536
// columns A-B repeat
// 3. both repeating rows and repeating columns
// formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2
$ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma?
foreach ($ranges as $range) {
// $range should look like this one of these
// Sheet!$A$1:$B$65536
// Sheet!$A$1:$IV$2
if (strpos($range, '!') !== false) {
$explodes = Worksheet::extractSheetTitle($range, true);
if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
$extractedRange = $explodes[1];
$extractedRange = str_replace('$', '', $extractedRange);
$coordinateStrings = explode(':', $extractedRange);
if (count($coordinateStrings) == 2) {
[$firstColumn, $firstRow] = Coordinate::coordinateFromString($coordinateStrings[0]);
[$lastColumn, $lastRow] = Coordinate::coordinateFromString($coordinateStrings[1]);
if ($firstColumn == 'A' && $lastColumn == 'IV') {
// then we have repeating rows
$docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
} elseif ($firstRow == 1 && $lastRow == 65536) {
// then we have repeating columns
$docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
}
}
}
}
}
break;
}
} else {
// Extract range
if (strpos($definedName['formula'], '!') !== false) {
$explodes = Worksheet::extractSheetTitle($definedName['formula'], true);
if (
($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0], "'")))
) {
$extractedRange = $explodes[1];
$localOnly = ($definedName['scope'] === 0) ? false : true;
$scope = ($definedName['scope'] === 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName['scope'] - 1]['name']);
$this->spreadsheet->addNamedRange(new NamedRange((string) $definedName['name'], $docSheet, $extractedRange, $localOnly, $scope));
}
}
// Named Value
// TODO Provide support for named values
}
}
$this->data = '';
return $this->spreadsheet;
}
/**
* Read record data from stream, decrypting as required.
*
* @param string $data Data stream to read from
* @param int $pos Position to start reading from
* @param int $len Record data length
*
* @return string Record data
*/
private function readRecordData($data, $pos, $len)
{
$data = substr($data, $pos, $len);
// File not encrypted, or record before encryption start point
if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
return $data;
}
$recordData = '';
if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
$oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
$block = (int) floor($pos / self::REKEY_BLOCK);
$endBlock = (int) floor(($pos + $len) / self::REKEY_BLOCK);
// Spin an RC4 decryptor to the right spot. If we have a decryptor sitting
// at a point earlier in the current block, re-use it as we can save some time.
if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
$this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
$step = $pos % self::REKEY_BLOCK;
} else {
$step = $pos - $this->rc4Pos;
}
$this->rc4Key->RC4(str_repeat("\0", $step));
// Decrypt record data (re-keying at the end of every block)
while ($block != $endBlock) {
$step = self::REKEY_BLOCK - ($pos % self::REKEY_BLOCK);
$recordData .= $this->rc4Key->RC4(substr($data, 0, $step));
$data = substr($data, $step);
$pos += $step;
$len -= $step;
++$block;
$this->rc4Key = $this->makeKey($block, $this->md5Ctxt);
}
$recordData .= $this->rc4Key->RC4(substr($data, 0, $len));
// Keep track of the position of this decryptor.
// We'll try and re-use it later if we can to speed things up
$this->rc4Pos = $pos + $len;
} elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
throw new Exception('XOr encryption not supported');
}
return $recordData;
}
/**
* Use OLE reader to extract the relevant data streams from the OLE file.
*
* @param string $filename
*/
private function loadOLE($filename): void
{
// OLE reader
$ole = new OLERead();
// get excel data,
$ole->read($filename);
// Get workbook data: workbook stream + sheet streams
$this->data = $ole->getStream($ole->wrkbook); // @phpstan-ignore-line
// Get summary information data
$this->summaryInformation = $ole->getStream($ole->summaryInformation);
// Get additional document summary information data
$this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
}
/**
* Read summary information.
*/
private function readSummaryInformation(): void
{
if (!isset($this->summaryInformation)) {
return;
}
// offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
// offset: 2; size: 2;
// offset: 4; size: 2; OS version
// offset: 6; size: 2; OS indicator
// offset: 8; size: 16
// offset: 24; size: 4; section count
$secCount = self::getInt4d($this->summaryInformation, 24);
// offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9
// offset: 44; size: 4
$secOffset = self::getInt4d($this->summaryInformation, 44);
// section header
// offset: $secOffset; size: 4; section length
$secLength = self::getInt4d($this->summaryInformation, $secOffset);
// offset: $secOffset+4; size: 4; property count
$countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
// initialize code page (used to resolve string values)
$codePage = 'CP1252';
// offset: ($secOffset+8); size: var
// loop through property decarations and properties
for ($i = 0; $i < $countProperties; ++$i) {
// offset: ($secOffset+8) + (8 * $i); size: 4; property ID
$id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 * $i));
// Use value of property id as appropriate
// offset: ($secOffset+12) + (8 * $i); size: 4; offset from beginning of section (48)
$offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 * $i));
$type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
// initialize property value
$value = null;
// extract property value based on property type
switch ($type) {
case 0x02: // 2 byte signed integer
$value = self::getUInt2d($this->summaryInformation, $secOffset + 4 + $offset);
break;
case 0x03: // 4 byte signed integer
$value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
break;
case 0x13: // 4 byte unsigned integer
// not needed yet, fix later if necessary
break;
case 0x1E: // null-terminated string prepended by dword string length
$byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
$value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
$value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
$value = rtrim($value);
break;
case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
// PHP-time
$value = OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
break;
case 0x47: // Clipboard format
// not needed yet, fix later if necessary
break;
}
switch ($id) {
case 0x01: // Code Page
$codePage = CodePage::numberToName((int) $value);
break;
case 0x02: // Title
$this->spreadsheet->getProperties()->setTitle("$value");
break;
case 0x03: // Subject
$this->spreadsheet->getProperties()->setSubject("$value");
break;
case 0x04: // Author (Creator)
$this->spreadsheet->getProperties()->setCreator("$value");
break;
case 0x05: // Keywords
$this->spreadsheet->getProperties()->setKeywords("$value");
break;
case 0x06: // Comments (Description)
$this->spreadsheet->getProperties()->setDescription("$value");
break;
case 0x07: // Template
// Not supported by PhpSpreadsheet
break;
case 0x08: // Last Saved By (LastModifiedBy)
$this->spreadsheet->getProperties()->setLastModifiedBy("$value");
break;
case 0x09: // Revision
// Not supported by PhpSpreadsheet
break;
case 0x0A: // Total Editing Time
// Not supported by PhpSpreadsheet
break;
case 0x0B: // Last Printed
// Not supported by PhpSpreadsheet
break;
case 0x0C: // Created Date/Time
$this->spreadsheet->getProperties()->setCreated($value);
break;
case 0x0D: // Modified Date/Time
$this->spreadsheet->getProperties()->setModified($value);
break;
case 0x0E: // Number of Pages
// Not supported by PhpSpreadsheet
break;
case 0x0F: // Number of Words
// Not supported by PhpSpreadsheet
break;
case 0x10: // Number of Characters
// Not supported by PhpSpreadsheet
break;
case 0x11: // Thumbnail
// Not supported by PhpSpreadsheet
break;
case 0x12: // Name of creating application
// Not supported by PhpSpreadsheet
break;
case 0x13: // Security
// Not supported by PhpSpreadsheet
break;
}
}
}
/**
* Read additional document summary information.
*/
private function readDocumentSummaryInformation(): void
{
if (!isset($this->documentSummaryInformation)) {
return;
}
// offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark)
// offset: 2; size: 2;
// offset: 4; size: 2; OS version
// offset: 6; size: 2; OS indicator
// offset: 8; size: 16
// offset: 24; size: 4; section count
$secCount = self::getInt4d($this->documentSummaryInformation, 24);
// offset: 28; size: 16; first section's class id: 02 d5 cd d5 9c 2e 1b 10 93 97 08 00 2b 2c f9 ae
// offset: 44; size: 4; first section offset
$secOffset = self::getInt4d($this->documentSummaryInformation, 44);
// section header
// offset: $secOffset; size: 4; section length
$secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
// offset: $secOffset+4; size: 4; property count
$countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
// initialize code page (used to resolve string values)
$codePage = 'CP1252';
// offset: ($secOffset+8); size: var
// loop through property decarations and properties
for ($i = 0; $i < $countProperties; ++$i) {
// offset: ($secOffset+8) + (8 * $i); size: 4; property ID
$id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 * $i));
// Use value of property id as appropriate
// offset: 60 + 8 * $i; size: 4; offset from beginning of section (48)
$offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 * $i));
$type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
// initialize property value
$value = null;
// extract property value based on property type
switch ($type) {
case 0x02: // 2 byte signed integer
$value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
break;
case 0x03: // 4 byte signed integer
$value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
break;
case 0x0B: // Boolean
$value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
$value = ($value == 0 ? false : true);
break;
case 0x13: // 4 byte unsigned integer
// not needed yet, fix later if necessary
break;
case 0x1E: // null-terminated string prepended by dword string length
$byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
$value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
$value = StringHelper::convertEncoding($value, 'UTF-8', $codePage);
$value = rtrim($value);
break;
case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601)
// PHP-Time
$value = OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
break;
case 0x47: // Clipboard format
// not needed yet, fix later if necessary
break;
}
switch ($id) {
case 0x01: // Code Page
$codePage = CodePage::numberToName((int) $value);
break;
case 0x02: // Category
$this->spreadsheet->getProperties()->setCategory("$value");
break;
case 0x03: // Presentation Target
// Not supported by PhpSpreadsheet
break;
case 0x04: // Bytes
// Not supported by PhpSpreadsheet
break;
case 0x05: // Lines
// Not supported by PhpSpreadsheet
break;
case 0x06: // Paragraphs
// Not supported by PhpSpreadsheet
break;
case 0x07: // Slides
// Not supported by PhpSpreadsheet
break;
case 0x08: // Notes
// Not supported by PhpSpreadsheet
break;
case 0x09: // Hidden Slides
// Not supported by PhpSpreadsheet
break;
case 0x0A: // MM Clips
// Not supported by PhpSpreadsheet
break;
case 0x0B: // Scale Crop
// Not supported by PhpSpreadsheet
break;
case 0x0C: // Heading Pairs
// Not supported by PhpSpreadsheet
break;
case 0x0D: // Titles of Parts
// Not supported by PhpSpreadsheet
break;
case 0x0E: // Manager
$this->spreadsheet->getProperties()->setManager("$value");
break;
case 0x0F: // Company
$this->spreadsheet->getProperties()->setCompany("$value");
break;
case 0x10: // Links up-to-date
// Not supported by PhpSpreadsheet
break;
}
}
}
/**
* Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record.
*/
private function readDefault(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
// move stream pointer to next record
$this->pos += 4 + $length;
}
/**
* The NOTE record specifies a comment associated with a particular cell. In Excel 95 (BIFF7) and earlier versions,
* this record stores a note (cell note). This feature was significantly enhanced in Excel 97.
*/
private function readNote(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->readDataOnly) {
return;
}
$cellAddress = $this->readBIFF8CellAddress(substr($recordData, 0, 4));
if ($this->version == self::XLS_BIFF8) {
$noteObjID = self::getUInt2d($recordData, 6);
$noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
$noteAuthor = $noteAuthor['value'];
$this->cellNotes[$noteObjID] = [
'cellRef' => $cellAddress,
'objectID' => $noteObjID,
'author' => $noteAuthor,
];
} else {
$extension = false;
if ($cellAddress == '$B$65536') {
// If the address row is -1 and the column is 0, (which translates as $B$65536) then this is a continuation
// note from the previous cell annotation. We're not yet handling this, so annotations longer than the
// max 2048 bytes will probably throw a wobbly.
$row = self::getUInt2d($recordData, 0);
$extension = true;
$arrayKeys = array_keys($this->phpSheet->getComments());
$cellAddress = array_pop($arrayKeys);
}
$cellAddress = str_replace('$', '', (string) $cellAddress);
$noteLength = self::getUInt2d($recordData, 4);
$noteText = trim(substr($recordData, 6));
if ($extension) {
// Concatenate this extension with the currently set comment for the cell
$comment = $this->phpSheet->getComment($cellAddress);
$commentText = $comment->getText()->getPlainText();
$comment->setText($this->parseRichText($commentText . $noteText));
} else {
// Set comment for the cell
$this->phpSheet->getComment($cellAddress)->setText($this->parseRichText($noteText));
// ->setAuthor($author)
}
}
}
/**
* The TEXT Object record contains the text associated with a cell annotation.
*/
private function readTextObject(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->readDataOnly) {
return;
}
// recordData consists of an array of subrecords looking like this:
// grbit: 2 bytes; Option Flags
// rot: 2 bytes; rotation
// cchText: 2 bytes; length of the text (in the first continue record)
// cbRuns: 2 bytes; length of the formatting (in the second continue record)
// followed by the continuation records containing the actual text and formatting
$grbitOpts = self::getUInt2d($recordData, 0);
$rot = self::getUInt2d($recordData, 2);
$cchText = self::getUInt2d($recordData, 10);
$cbRuns = self::getUInt2d($recordData, 12);
$text = $this->getSplicedRecordData();
$textByte = $text['spliceOffsets'][1] - $text['spliceOffsets'][0] - 1;
$textStr = substr($text['recordData'], $text['spliceOffsets'][0] + 1, $textByte);
// get 1 byte
$is16Bit = ord($text['recordData'][0]);
// it is possible to use a compressed format,
// which omits the high bytes of all characters, if they are all zero
if (($is16Bit & 0x01) === 0) {
$textStr = StringHelper::ConvertEncoding($textStr, 'UTF-8', 'ISO-8859-1');
} else {
$textStr = $this->decodeCodepage($textStr);
}
$this->textObjects[$this->textObjRef] = [
'text' => $textStr,
'format' => substr($text['recordData'], $text['spliceOffsets'][1], $cbRuns),
'alignment' => $grbitOpts,
'rotation' => $rot,
];
}
/**
* Read BOF.
*/
private function readBof(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = substr($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 2; size: 2; type of the following data
$substreamType = self::getUInt2d($recordData, 2);
switch ($substreamType) {
case self::XLS_WORKBOOKGLOBALS:
$version = self::getUInt2d($recordData, 0);
if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) {
throw new Exception('Cannot read this Excel file. Version is too old.');
}
$this->version = $version;
break;
case self::XLS_WORKSHEET:
// do not use this version information for anything
// it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream
break;
default:
// substream, e.g. chart
// just skip the entire substream
do {
$code = self::getUInt2d($this->data, $this->pos);
$this->readDefault();
} while ($code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
break;
}
}
/**
* FILEPASS.
*
* This record is part of the File Protection Block. It
* contains information about the read/write password of the
* file. All record contents following this record will be
* encrypted.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*
* The decryption functions and objects used from here on in
* are based on the source of Spreadsheet-ParseExcel:
* https://metacpan.org/release/Spreadsheet-ParseExcel
*/
private function readFilepass(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
if ($length != 54) {
throw new Exception('Unexpected file pass record length');
}
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->verifyPassword('VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
throw new Exception('Decryption password incorrect');
}
$this->encryption = self::MS_BIFF_CRYPTO_RC4;
// Decryption required from the record after next onwards
$this->encryptionStartPos = $this->pos + self::getUInt2d($this->data, $this->pos + 2);
}
/**
* Make an RC4 decryptor for the given block.
*
* @param int $block Block for which to create decrypto
* @param string $valContext MD5 context state
*
* @return Xls\RC4
*/
private function makeKey($block, $valContext)
{
$pwarray = str_repeat("\0", 64);
for ($i = 0; $i < 5; ++$i) {
$pwarray[$i] = $valContext[$i];
}
$pwarray[5] = chr($block & 0xff);
$pwarray[6] = chr(($block >> 8) & 0xff);
$pwarray[7] = chr(($block >> 16) & 0xff);
$pwarray[8] = chr(($block >> 24) & 0xff);
$pwarray[9] = "\x80";
$pwarray[56] = "\x48";
$md5 = new Xls\MD5();
$md5->add($pwarray);
$s = $md5->getContext();
return new Xls\RC4($s);
}
/**
* Verify RC4 file password.
*
* @param string $password Password to check
* @param string $docid Document id
* @param string $salt_data Salt data
* @param string $hashedsalt_data Hashed salt data
* @param string $valContext Set to the MD5 context of the value
*
* @return bool Success
*/
private function verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
{
$pwarray = str_repeat("\0", 64);
$iMax = strlen($password);
for ($i = 0; $i < $iMax; ++$i) {
$o = ord(substr($password, $i, 1));
$pwarray[2 * $i] = chr($o & 0xff);
$pwarray[2 * $i + 1] = chr(($o >> 8) & 0xff);
}
$pwarray[2 * $i] = chr(0x80);
$pwarray[56] = chr(($i << 4) & 0xff);
$md5 = new Xls\MD5();
$md5->add($pwarray);
$mdContext1 = $md5->getContext();
$offset = 0;
$keyoffset = 0;
$tocopy = 5;
$md5->reset();
while ($offset != 16) {
if ((64 - $offset) < 5) {
$tocopy = 64 - $offset;
}
for ($i = 0; $i <= $tocopy; ++$i) {
$pwarray[$offset + $i] = $mdContext1[$keyoffset + $i];
}
$offset += $tocopy;
if ($offset == 64) {
$md5->add($pwarray);
$keyoffset = $tocopy;
$tocopy = 5 - $tocopy;
$offset = 0;
continue;
}
$keyoffset = 0;
$tocopy = 5;
for ($i = 0; $i < 16; ++$i) {
$pwarray[$offset + $i] = $docid[$i];
}
$offset += 16;
}
$pwarray[16] = "\x80";
for ($i = 0; $i < 47; ++$i) {
$pwarray[17 + $i] = "\0";
}
$pwarray[56] = "\x80";
$pwarray[57] = "\x0a";
$md5->add($pwarray);
$valContext = $md5->getContext();
$key = $this->makeKey(0, $valContext);
$salt = $key->RC4($salt_data);
$hashedsalt = $key->RC4($hashedsalt_data);
$salt .= "\x80" . str_repeat("\0", 47);
$salt[56] = "\x80";
$md5->reset();
$md5->add($salt);
$mdContext2 = $md5->getContext();
return $mdContext2 == $hashedsalt;
}
/**
* CODEPAGE.
*
* This record stores the text encoding used to write byte
* strings, stored as MS Windows code page identifier.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readCodepage(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; code page identifier
$codepage = self::getUInt2d($recordData, 0);
$this->codepage = CodePage::numberToName($codepage);
}
/**
* DATEMODE.
*
* This record specifies the base date for displaying date
* values. All dates are stored as count of days past this
* base date. In BIFF2-BIFF4 this record is part of the
* Calculation Settings Block. In BIFF5-BIFF8 it is
* stored in the Workbook Globals Substream.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readDateMode(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; 0 = base 1900, 1 = base 1904
Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
if (ord($recordData[0]) == 1) {
Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
}
}
/**
* Read a FONT record.
*/
private function readFont(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
$objFont = new Font();
// offset: 0; size: 2; height of the font (in twips = 1/20 of a point)
$size = self::getUInt2d($recordData, 0);
$objFont->setSize($size / 20);
// offset: 2; size: 2; option flags
// bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8)
// bit: 1; mask 0x0002; italic
$isItalic = (0x0002 & self::getUInt2d($recordData, 2)) >> 1;
if ($isItalic) {
$objFont->setItalic(true);
}
// bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8)
// bit: 3; mask 0x0008; strikethrough
$isStrike = (0x0008 & self::getUInt2d($recordData, 2)) >> 3;
if ($isStrike) {
$objFont->setStrikethrough(true);
}
// offset: 4; size: 2; colour index
$colorIndex = self::getUInt2d($recordData, 4);
$objFont->colorIndex = $colorIndex;
// offset: 6; size: 2; font weight
$weight = self::getUInt2d($recordData, 6);
switch ($weight) {
case 0x02BC:
$objFont->setBold(true);
break;
}
// offset: 8; size: 2; escapement type
$escapement = self::getUInt2d($recordData, 8);
CellFont::escapement($objFont, $escapement);
// offset: 10; size: 1; underline type
$underlineType = ord($recordData[10]);
CellFont::underline($objFont, $underlineType);
// offset: 11; size: 1; font family
// offset: 12; size: 1; character set
// offset: 13; size: 1; not used
// offset: 14; size: var; font name
if ($this->version == self::XLS_BIFF8) {
$string = self::readUnicodeStringShort(substr($recordData, 14));
} else {
$string = $this->readByteStringShort(substr($recordData, 14));
}
$objFont->setName($string['value']);
$this->objFonts[] = $objFont;
}
}
/**
* FORMAT.
*
* This record contains information about a number format.
* All FORMAT records occur together in a sequential list.
*
* In BIFF2-BIFF4 other records referencing a FORMAT record
* contain a zero-based index into this list. From BIFF5 on
* the FORMAT record contains the index itself that will be
* used by other records.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readFormat(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
$indexCode = self::getUInt2d($recordData, 0);
if ($this->version == self::XLS_BIFF8) {
$string = self::readUnicodeStringLong(substr($recordData, 2));
} else {
// BIFF7
$string = $this->readByteStringShort(substr($recordData, 2));
}
$formatString = $string['value'];
// Apache Open Office sets wrong case writing to xls - issue 2239
if ($formatString === 'GENERAL') {
$formatString = NumberFormat::FORMAT_GENERAL;
}
$this->formats[$indexCode] = $formatString;
}
}
/**
* XF - Extended Format.
*
* This record contains formatting information for cells, rows, columns or styles.
* According to https://support.microsoft.com/en-us/help/147732 there are always at least 15 cell style XF
* and 1 cell XF.
* Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF
* and XF record 15 is a cell XF
* We only read the first cell style XF and skip the remaining cell style XF records
* We read all cell XF records.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readXf(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
$objStyle = new Style();
if (!$this->readDataOnly) {
// offset: 0; size: 2; Index to FONT record
if (self::getUInt2d($recordData, 0) < 4) {
$fontIndex = self::getUInt2d($recordData, 0);
} else {
// this has to do with that index 4 is omitted in all BIFF versions for some strange reason
// check the OpenOffice documentation of the FONT record
$fontIndex = self::getUInt2d($recordData, 0) - 1;
}
$objStyle->setFont($this->objFonts[$fontIndex]);
// offset: 2; size: 2; Index to FORMAT record
$numberFormatIndex = self::getUInt2d($recordData, 2);
if (isset($this->formats[$numberFormatIndex])) {
// then we have user-defined format code
$numberFormat = ['formatCode' => $this->formats[$numberFormatIndex]];
} elseif (($code = NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') {
// then we have built-in format code
$numberFormat = ['formatCode' => $code];
} else {
// we set the general format code
$numberFormat = ['formatCode' => NumberFormat::FORMAT_GENERAL];
}
$objStyle->getNumberFormat()->setFormatCode($numberFormat['formatCode']);
// offset: 4; size: 2; XF type, cell protection, and parent style XF
// bit 2-0; mask 0x0007; XF_TYPE_PROT
$xfTypeProt = self::getUInt2d($recordData, 4);
// bit 0; mask 0x01; 1 = cell is locked
$isLocked = (0x01 & $xfTypeProt) >> 0;
$objStyle->getProtection()->setLocked($isLocked ? Protection::PROTECTION_INHERIT : Protection::PROTECTION_UNPROTECTED);
// bit 1; mask 0x02; 1 = Formula is hidden
$isHidden = (0x02 & $xfTypeProt) >> 1;
$objStyle->getProtection()->setHidden($isHidden ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED);
// bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF
$isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
// offset: 6; size: 1; Alignment and text break
// bit 2-0, mask 0x07; horizontal alignment
$horAlign = (0x07 & ord($recordData[6])) >> 0;
Xls\Style\CellAlignment::horizontal($objStyle->getAlignment(), $horAlign);
// bit 3, mask 0x08; wrap text
$wrapText = (0x08 & ord($recordData[6])) >> 3;
Xls\Style\CellAlignment::wrap($objStyle->getAlignment(), $wrapText);
// bit 6-4, mask 0x70; vertical alignment
$vertAlign = (0x70 & ord($recordData[6])) >> 4;
Xls\Style\CellAlignment::vertical($objStyle->getAlignment(), $vertAlign);
if ($this->version == self::XLS_BIFF8) {
// offset: 7; size: 1; XF_ROTATION: Text rotation angle
$angle = ord($recordData[7]);
$rotation = 0;
if ($angle <= 90) {
$rotation = $angle;
} elseif ($angle <= 180) {
$rotation = 90 - $angle;
} elseif ($angle == Alignment::TEXTROTATION_STACK_EXCEL) {
$rotation = Alignment::TEXTROTATION_STACK_PHPSPREADSHEET;
}
$objStyle->getAlignment()->setTextRotation($rotation);
// offset: 8; size: 1; Indentation, shrink to cell size, and text direction
// bit: 3-0; mask: 0x0F; indent level
$indent = (0x0F & ord($recordData[8])) >> 0;
$objStyle->getAlignment()->setIndent($indent);
// bit: 4; mask: 0x10; 1 = shrink content to fit into cell
$shrinkToFit = (0x10 & ord($recordData[8])) >> 4;
switch ($shrinkToFit) {
case 0:
$objStyle->getAlignment()->setShrinkToFit(false);
break;
case 1:
$objStyle->getAlignment()->setShrinkToFit(true);
break;
}
// offset: 9; size: 1; Flags used for attribute groups
// offset: 10; size: 4; Cell border lines and background area
// bit: 3-0; mask: 0x0000000F; left style
if ($bordersLeftStyle = Xls\Style\Border::lookup((0x0000000F & self::getInt4d($recordData, 10)) >> 0)) {
$objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
}
// bit: 7-4; mask: 0x000000F0; right style
if ($bordersRightStyle = Xls\Style\Border::lookup((0x000000F0 & self::getInt4d($recordData, 10)) >> 4)) {
$objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
}
// bit: 11-8; mask: 0x00000F00; top style
if ($bordersTopStyle = Xls\Style\Border::lookup((0x00000F00 & self::getInt4d($recordData, 10)) >> 8)) {
$objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
}
// bit: 15-12; mask: 0x0000F000; bottom style
if ($bordersBottomStyle = Xls\Style\Border::lookup((0x0000F000 & self::getInt4d($recordData, 10)) >> 12)) {
$objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
}
// bit: 22-16; mask: 0x007F0000; left color
$objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & self::getInt4d($recordData, 10)) >> 16;
// bit: 29-23; mask: 0x3F800000; right color
$objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
// bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
$diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
// bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
$diagonalUp = ((int) 0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
if ($diagonalUp === false) {
if ($diagonalDown == false) {
$objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_NONE);
} else {
$objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_DOWN);
}
} elseif ($diagonalDown == false) {
$objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_UP);
} else {
$objStyle->getBorders()->setDiagonalDirection(Borders::DIAGONAL_BOTH);
}
// offset: 14; size: 4;
// bit: 6-0; mask: 0x0000007F; top color
$objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
// bit: 13-7; mask: 0x00003F80; bottom color
$objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
// bit: 20-14; mask: 0x001FC000; diagonal color
$objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
// bit: 24-21; mask: 0x01E00000; diagonal style
if ($bordersDiagonalStyle = Xls\Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) {
$objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
}
// bit: 31-26; mask: 0xFC000000 fill pattern
if ($fillType = Xls\Style\FillPattern::lookup(((int) 0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
$objStyle->getFill()->setFillType($fillType);
}
// offset: 18; size: 2; pattern and background colour
// bit: 6-0; mask: 0x007F; color index for pattern color
$objStyle->getFill()->startcolorIndex = (0x007F & self::getUInt2d($recordData, 18)) >> 0;
// bit: 13-7; mask: 0x3F80; color index for pattern background
$objStyle->getFill()->endcolorIndex = (0x3F80 & self::getUInt2d($recordData, 18)) >> 7;
} else {
// BIFF5
// offset: 7; size: 1; Text orientation and flags
$orientationAndFlags = ord($recordData[7]);
// bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation
$xfOrientation = (0x03 & $orientationAndFlags) >> 0;
switch ($xfOrientation) {
case 0:
$objStyle->getAlignment()->setTextRotation(0);
break;
case 1:
$objStyle->getAlignment()->setTextRotation(Alignment::TEXTROTATION_STACK_PHPSPREADSHEET);
break;
case 2:
$objStyle->getAlignment()->setTextRotation(90);
break;
case 3:
$objStyle->getAlignment()->setTextRotation(-90);
break;
}
// offset: 8; size: 4; cell border lines and background area
$borderAndBackground = self::getInt4d($recordData, 8);
// bit: 6-0; mask: 0x0000007F; color index for pattern color
$objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0;
// bit: 13-7; mask: 0x00003F80; color index for pattern background
$objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7;
// bit: 21-16; mask: 0x003F0000; fill pattern
$objStyle->getFill()->setFillType(Xls\Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
// bit: 24-22; mask: 0x01C00000; bottom line style
$objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
// bit: 31-25; mask: 0xFE000000; bottom line color
$objStyle->getBorders()->getBottom()->colorIndex = ((int) 0xFE000000 & $borderAndBackground) >> 25;
// offset: 12; size: 4; cell border lines
$borderLines = self::getInt4d($recordData, 12);
// bit: 2-0; mask: 0x00000007; top line style
$objStyle->getBorders()->getTop()->setBorderStyle(Xls\Style\Border::lookup((0x00000007 & $borderLines) >> 0));
// bit: 5-3; mask: 0x00000038; left line style
$objStyle->getBorders()->getLeft()->setBorderStyle(Xls\Style\Border::lookup((0x00000038 & $borderLines) >> 3));
// bit: 8-6; mask: 0x000001C0; right line style
$objStyle->getBorders()->getRight()->setBorderStyle(Xls\Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
// bit: 15-9; mask: 0x0000FE00; top line color index
$objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
// bit: 22-16; mask: 0x007F0000; left line color index
$objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
// bit: 29-23; mask: 0x3F800000; right line color index
$objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
}
// add cellStyleXf or cellXf and update mapping
if ($isCellStyleXf) {
// we only read one style XF record which is always the first
if ($this->xfIndex == 0) {
$this->spreadsheet->addCellStyleXf($objStyle);
$this->mapCellStyleXfIndex[$this->xfIndex] = 0;
}
} else {
// we read all cell XF records
$this->spreadsheet->addCellXf($objStyle);
$this->mapCellXfIndex[$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
}
// update XF index for when we read next record
++$this->xfIndex;
}
}
private function readXfExt(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; 0x087D = repeated header
// offset: 2; size: 2
// offset: 4; size: 8; not used
// offset: 12; size: 2; record version
// offset: 14; size: 2; index to XF record which this record modifies
$ixfe = self::getUInt2d($recordData, 14);
// offset: 16; size: 2; not used
// offset: 18; size: 2; number of extension properties that follow
$cexts = self::getUInt2d($recordData, 18);
// start reading the actual extension data
$offset = 20;
while ($offset < $length) {
// extension type
$extType = self::getUInt2d($recordData, $offset);
// extension length
$cb = self::getUInt2d($recordData, $offset + 2);
// extension data
$extData = substr($recordData, $offset + 4, $cb);
switch ($extType) {
case 4: // fill start color
$xclfType = self::getUInt2d($extData, 0); // color type
$xclrValue = substr($extData, 4, 4); // color value (value based on color type)
if ($xclfType == 2) {
$rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
// modify the relevant style property
if (isset($this->mapCellXfIndex[$ixfe])) {
$fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
$fill->getStartColor()->setRGB($rgb);
$fill->startcolorIndex = null; // normal color index does not apply, discard
}
}
break;
case 5: // fill end color
$xclfType = self::getUInt2d($extData, 0); // color type
$xclrValue = substr($extData, 4, 4); // color value (value based on color type)
if ($xclfType == 2) {
$rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
// modify the relevant style property
if (isset($this->mapCellXfIndex[$ixfe])) {
$fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
$fill->getEndColor()->setRGB($rgb);
$fill->endcolorIndex = null; // normal color index does not apply, discard
}
}
break;
case 7: // border color top
$xclfType = self::getUInt2d($extData, 0); // color type
$xclrValue = substr($extData, 4, 4); // color value (value based on color type)
if ($xclfType == 2) {
$rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
// modify the relevant style property
if (isset($this->mapCellXfIndex[$ixfe])) {
$top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
$top->getColor()->setRGB($rgb);
$top->colorIndex = null; // normal color index does not apply, discard
}
}
break;
case 8: // border color bottom
$xclfType = self::getUInt2d($extData, 0); // color type
$xclrValue = substr($extData, 4, 4); // color value (value based on color type)
if ($xclfType == 2) {
$rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
// modify the relevant style property
if (isset($this->mapCellXfIndex[$ixfe])) {
$bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
$bottom->getColor()->setRGB($rgb);
$bottom->colorIndex = null; // normal color index does not apply, discard
}
}
break;
case 9: // border color left
$xclfType = self::getUInt2d($extData, 0); // color type
$xclrValue = substr($extData, 4, 4); // color value (value based on color type)
if ($xclfType == 2) {
$rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
// modify the relevant style property
if (isset($this->mapCellXfIndex[$ixfe])) {
$left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
$left->getColor()->setRGB($rgb);
$left->colorIndex = null; // normal color index does not apply, discard
}
}
break;
case 10: // border color right
$xclfType = self::getUInt2d($extData, 0); // color type
$xclrValue = substr($extData, 4, 4); // color value (value based on color type)
if ($xclfType == 2) {
$rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
// modify the relevant style property
if (isset($this->mapCellXfIndex[$ixfe])) {
$right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
$right->getColor()->setRGB($rgb);
$right->colorIndex = null; // normal color index does not apply, discard
}
}
break;
case 11: // border color diagonal
$xclfType = self::getUInt2d($extData, 0); // color type
$xclrValue = substr($extData, 4, 4); // color value (value based on color type)
if ($xclfType == 2) {
$rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
// modify the relevant style property
if (isset($this->mapCellXfIndex[$ixfe])) {
$diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
$diagonal->getColor()->setRGB($rgb);
$diagonal->colorIndex = null; // normal color index does not apply, discard
}
}
break;
case 13: // font color
$xclfType = self::getUInt2d($extData, 0); // color type
$xclrValue = substr($extData, 4, 4); // color value (value based on color type)
if ($xclfType == 2) {
$rgb = sprintf('%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
// modify the relevant style property
if (isset($this->mapCellXfIndex[$ixfe])) {
$font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
$font->getColor()->setRGB($rgb);
$font->colorIndex = null; // normal color index does not apply, discard
}
}
break;
}
$offset += $cb;
}
}
}
/**
* Read STYLE record.
*/
private function readStyle(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; index to XF record and flag for built-in style
$ixfe = self::getUInt2d($recordData, 0);
// bit: 11-0; mask 0x0FFF; index to XF record
$xfIndex = (0x0FFF & $ixfe) >> 0;
// bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style
$isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
if ($isBuiltIn) {
// offset: 2; size: 1; identifier for built-in style
$builtInId = ord($recordData[2]);
switch ($builtInId) {
case 0x00:
// currently, we are not using this for anything
break;
default:
break;
}
}
// user-defined; not supported by PhpSpreadsheet
}
}
/**
* Read PALETTE record.
*/
private function readPalette(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; number of following colors
$nm = self::getUInt2d($recordData, 0);
// list of RGB colors
for ($i = 0; $i < $nm; ++$i) {
$rgb = substr($recordData, 2 + 4 * $i, 4);
$this->palette[] = self::readRGB($rgb);
}
}
}
/**
* SHEET.
*
* This record is located in the Workbook Globals
* Substream and represents a sheet inside the workbook.
* One SHEET record is written for each sheet. It stores the
* sheet name and a stream offset to the BOF record of the
* respective Sheet Substream within the Workbook Stream.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readSheet(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// offset: 0; size: 4; absolute stream position of the BOF record of the sheet
// NOTE: not encrypted
$rec_offset = self::getInt4d($this->data, $this->pos + 4);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 4; size: 1; sheet state
switch (ord($recordData[4])) {
case 0x00:
$sheetState = Worksheet::SHEETSTATE_VISIBLE;
break;
case 0x01:
$sheetState = Worksheet::SHEETSTATE_HIDDEN;
break;
case 0x02:
$sheetState = Worksheet::SHEETSTATE_VERYHIDDEN;
break;
default:
$sheetState = Worksheet::SHEETSTATE_VISIBLE;
break;
}
// offset: 5; size: 1; sheet type
$sheetType = ord($recordData[5]);
// offset: 6; size: var; sheet name
$rec_name = null;
if ($this->version == self::XLS_BIFF8) {
$string = self::readUnicodeStringShort(substr($recordData, 6));
$rec_name = $string['value'];
} elseif ($this->version == self::XLS_BIFF7) {
$string = $this->readByteStringShort(substr($recordData, 6));
$rec_name = $string['value'];
}
$this->sheets[] = [
'name' => $rec_name,
'offset' => $rec_offset,
'sheetState' => $sheetState,
'sheetType' => $sheetType,
];
}
/**
* Read EXTERNALBOOK record.
*/
private function readExternalBook(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset within record data
$offset = 0;
// there are 4 types of records
if (strlen($recordData) > 4) {
// external reference
// offset: 0; size: 2; number of sheet names ($nm)
$nm = self::getUInt2d($recordData, 0);
$offset += 2;
// offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length)
$encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
$offset += $encodedUrlString['size'];
// offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length)
$externalSheetNames = [];
for ($i = 0; $i < $nm; ++$i) {
$externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
$externalSheetNames[] = $externalSheetNameString['value'];
$offset += $externalSheetNameString['size'];
}
// store the record data
$this->externalBooks[] = [
'type' => 'external',
'encodedUrl' => $encodedUrlString['value'],
'externalSheetNames' => $externalSheetNames,
];
} elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) {
// internal reference
// offset: 0; size: 2; number of sheet in this document
// offset: 2; size: 2; 0x01 0x04
$this->externalBooks[] = [
'type' => 'internal',
];
} elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) {
// add-in function
// offset: 0; size: 2; 0x0001
$this->externalBooks[] = [
'type' => 'addInFunction',
];
} elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) {
// DDE links, OLE links
// offset: 0; size: 2; 0x0000
// offset: 2; size: var; encoded source document name
$this->externalBooks[] = [
'type' => 'DDEorOLE',
];
}
}
/**
* Read EXTERNNAME record.
*/
private function readExternName(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// external sheet references provided for named cells
if ($this->version == self::XLS_BIFF8) {
// offset: 0; size: 2; options
$options = self::getUInt2d($recordData, 0);
// offset: 2; size: 2;
// offset: 4; size: 2; not used
// offset: 6; size: var
$nameString = self::readUnicodeStringShort(substr($recordData, 6));
// offset: var; size: var; formula data
$offset = 6 + $nameString['size'];
$formula = $this->getFormulaFromStructure(substr($recordData, $offset));
$this->externalNames[] = [
'name' => $nameString['value'],
'formula' => $formula,
];
}
}
/**
* Read EXTERNSHEET record.
*/
private function readExternSheet(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// external sheet references provided for named cells
if ($this->version == self::XLS_BIFF8) {
// offset: 0; size: 2; number of following ref structures
$nm = self::getUInt2d($recordData, 0);
for ($i = 0; $i < $nm; ++$i) {
$this->ref[] = [
// offset: 2 + 6 * $i; index to EXTERNALBOOK record
'externalBookIndex' => self::getUInt2d($recordData, 2 + 6 * $i),
// offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record
'firstSheetIndex' => self::getUInt2d($recordData, 4 + 6 * $i),
// offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record
'lastSheetIndex' => self::getUInt2d($recordData, 6 + 6 * $i),
];
}
}
}
/**
* DEFINEDNAME.
*
* This record is part of a Link Table. It contains the name
* and the token array of an internal defined name. Token
* arrays of defined names contain tokens with aberrant
* token classes.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readDefinedName(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->version == self::XLS_BIFF8) {
// retrieves named cells
// offset: 0; size: 2; option flags
$opts = self::getUInt2d($recordData, 0);
// bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name
$isBuiltInName = (0x0020 & $opts) >> 5;
// offset: 2; size: 1; keyboard shortcut
// offset: 3; size: 1; length of the name (character count)
$nlen = ord($recordData[3]);
// offset: 4; size: 2; size of the formula data (it can happen that this is zero)
// note: there can also be additional data, this is not included in $flen
$flen = self::getUInt2d($recordData, 4);
// offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based)
$scope = self::getUInt2d($recordData, 8);
// offset: 14; size: var; Name (Unicode string without length field)
$string = self::readUnicodeString(substr($recordData, 14), $nlen);
// offset: var; size: $flen; formula data
$offset = 14 + $string['size'];
$formulaStructure = pack('v', $flen) . substr($recordData, $offset);
try {
$formula = $this->getFormulaFromStructure($formulaStructure);
} catch (PhpSpreadsheetException $e) {
$formula = '';
}
$this->definedname[] = [
'isBuiltInName' => $isBuiltInName,
'name' => $string['value'],
'formula' => $formula,
'scope' => $scope,
];
}
}
/**
* Read MSODRAWINGGROUP record.
*/
private function readMsoDrawingGroup(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
// get spliced record data
$splicedRecordData = $this->getSplicedRecordData();
$recordData = $splicedRecordData['recordData'];
$this->drawingGroupData .= $recordData;
}
/**
* SST - Shared String Table.
*
* This record contains a list of all strings used anywhere
* in the workbook. Each string occurs only once. The
* workbook uses indexes into the list to reference the
* strings.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readSst(): void
{
// offset within (spliced) record data
$pos = 0;
// Limit global SST position, further control for bad SST Length in BIFF8 data
$limitposSST = 0;
// get spliced record data
$splicedRecordData = $this->getSplicedRecordData();
$recordData = $splicedRecordData['recordData'];
$spliceOffsets = $splicedRecordData['spliceOffsets'];
// offset: 0; size: 4; total number of strings in the workbook
$pos += 4;
// offset: 4; size: 4; number of following strings ($nm)
$nm = self::getInt4d($recordData, 4);
$pos += 4;
// look up limit position
foreach ($spliceOffsets as $spliceOffset) {
// it can happen that the string is empty, therefore we need
// <= and not just <
if ($pos <= $spliceOffset) {
$limitposSST = $spliceOffset;
}
}
// loop through the Unicode strings (16-bit length)
for ($i = 0; $i < $nm && $pos < $limitposSST; ++$i) {
// number of characters in the Unicode string
$numChars = self::getUInt2d($recordData, $pos);
$pos += 2;
// option flags
$optionFlags = ord($recordData[$pos]);
++$pos;
// bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed
$isCompressed = (($optionFlags & 0x01) == 0);
// bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic
$hasAsian = (($optionFlags & 0x04) != 0);
// bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text
$hasRichText = (($optionFlags & 0x08) != 0);
$formattingRuns = 0;
if ($hasRichText) {
// number of Rich-Text formatting runs
$formattingRuns = self::getUInt2d($recordData, $pos);
$pos += 2;
}
$extendedRunLength = 0;
if ($hasAsian) {
// size of Asian phonetic setting
$extendedRunLength = self::getInt4d($recordData, $pos);
$pos += 4;
}
// expected byte length of character array if not split
$len = ($isCompressed) ? $numChars : $numChars * 2;
// look up limit position - Check it again to be sure that no error occurs when parsing SST structure
$limitpos = null;
foreach ($spliceOffsets as $spliceOffset) {
// it can happen that the string is empty, therefore we need
// <= and not just <
if ($pos <= $spliceOffset) {
$limitpos = $spliceOffset;
break;
}
}
if ($pos + $len <= $limitpos) {
// character array is not split between records
$retstr = substr($recordData, $pos, $len);
$pos += $len;
} else {
// character array is split between records
// first part of character array
$retstr = substr($recordData, $pos, $limitpos - $pos);
$bytesRead = $limitpos - $pos;
// remaining characters in Unicode string
$charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
$pos = $limitpos;
// keep reading the characters
while ($charsLeft > 0) {
// look up next limit position, in case the string span more than one continue record
foreach ($spliceOffsets as $spliceOffset) {
if ($pos < $spliceOffset) {
$limitpos = $spliceOffset;
break;
}
}
// repeated option flags
// OpenOffice.org documentation 5.21
$option = ord($recordData[$pos]);
++$pos;
if ($isCompressed && ($option == 0)) {
// 1st fragment compressed
// this fragment compressed
$len = min($charsLeft, $limitpos - $pos);
$retstr .= substr($recordData, $pos, $len);
$charsLeft -= $len;
$isCompressed = true;
} elseif (!$isCompressed && ($option != 0)) {
// 1st fragment uncompressed
// this fragment uncompressed
$len = min($charsLeft * 2, $limitpos - $pos);
$retstr .= substr($recordData, $pos, $len);
$charsLeft -= $len / 2;
$isCompressed = false;
} elseif (!$isCompressed && ($option == 0)) {
// 1st fragment uncompressed
// this fragment compressed
$len = min($charsLeft, $limitpos - $pos);
for ($j = 0; $j < $len; ++$j) {
$retstr .= $recordData[$pos + $j]
. chr(0);
}
$charsLeft -= $len;
$isCompressed = false;
} else {
// 1st fragment compressed
// this fragment uncompressed
$newstr = '';
$jMax = strlen($retstr);
for ($j = 0; $j < $jMax; ++$j) {
$newstr .= $retstr[$j] . chr(0);
}
$retstr = $newstr;
$len = min($charsLeft * 2, $limitpos - $pos);
$retstr .= substr($recordData, $pos, $len);
$charsLeft -= $len / 2;
$isCompressed = false;
}
$pos += $len;
}
}
// convert to UTF-8
$retstr = self::encodeUTF16($retstr, $isCompressed);
// read additional Rich-Text information, if any
$fmtRuns = [];
if ($hasRichText) {
// list of formatting runs
for ($j = 0; $j < $formattingRuns; ++$j) {
// first formatted character; zero-based
$charPos = self::getUInt2d($recordData, $pos + $j * 4);
// index to font record
$fontIndex = self::getUInt2d($recordData, $pos + 2 + $j * 4);
$fmtRuns[] = [
'charPos' => $charPos,
'fontIndex' => $fontIndex,
];
}
$pos += 4 * $formattingRuns;
}
// read additional Asian phonetics information, if any
if ($hasAsian) {
// For Asian phonetic settings, we skip the extended string data
$pos += $extendedRunLength;
}
// store the shared sting
$this->sst[] = [
'value' => $retstr,
'fmtRuns' => $fmtRuns,
];
}
// getSplicedRecordData() takes care of moving current position in data stream
}
/**
* Read PRINTGRIDLINES record.
*/
private function readPrintGridlines(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
// offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines
$printGridlines = (bool) self::getUInt2d($recordData, 0);
$this->phpSheet->setPrintGridlines($printGridlines);
}
}
/**
* Read DEFAULTROWHEIGHT record.
*/
private function readDefaultRowHeight(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; option flags
// offset: 2; size: 2; default height for unused rows, (twips 1/20 point)
$height = self::getUInt2d($recordData, 2);
$this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
}
/**
* Read SHEETPR record.
*/
private function readSheetPr(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2
// bit: 6; mask: 0x0040; 0 = outline buttons above outline group
$isSummaryBelow = (0x0040 & self::getUInt2d($recordData, 0)) >> 6;
$this->phpSheet->setShowSummaryBelow((bool) $isSummaryBelow);
// bit: 7; mask: 0x0080; 0 = outline buttons left of outline group
$isSummaryRight = (0x0080 & self::getUInt2d($recordData, 0)) >> 7;
$this->phpSheet->setShowSummaryRight((bool) $isSummaryRight);
// bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages
// this corresponds to radio button setting in page setup dialog in Excel
$this->isFitToPages = (bool) ((0x0100 & self::getUInt2d($recordData, 0)) >> 8);
}
/**
* Read HORIZONTALPAGEBREAKS record.
*/
private function readHorizontalPageBreaks(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
// offset: 0; size: 2; number of the following row index structures
$nm = self::getUInt2d($recordData, 0);
// offset: 2; size: 6 * $nm; list of $nm row index structures
for ($i = 0; $i < $nm; ++$i) {
$r = self::getUInt2d($recordData, 2 + 6 * $i);
$cf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
$cl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
// not sure why two column indexes are necessary?
$this->phpSheet->setBreak([$cf + 1, $r], Worksheet::BREAK_ROW);
}
}
}
/**
* Read VERTICALPAGEBREAKS record.
*/
private function readVerticalPageBreaks(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
// offset: 0; size: 2; number of the following column index structures
$nm = self::getUInt2d($recordData, 0);
// offset: 2; size: 6 * $nm; list of $nm row index structures
for ($i = 0; $i < $nm; ++$i) {
$c = self::getUInt2d($recordData, 2 + 6 * $i);
$rf = self::getUInt2d($recordData, 2 + 6 * $i + 2);
//$rl = self::getUInt2d($recordData, 2 + 6 * $i + 4);
// not sure why two row indexes are necessary?
$this->phpSheet->setBreak([$c + 1, ($rf > 0) ? $rf : 1], Worksheet::BREAK_COLUMN);
}
}
}
/**
* Read HEADER record.
*/
private function readHeader(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: var
// realized that $recordData can be empty even when record exists
if ($recordData) {
if ($this->version == self::XLS_BIFF8) {
$string = self::readUnicodeStringLong($recordData);
} else {
$string = $this->readByteStringShort($recordData);
}
$this->phpSheet->getHeaderFooter()->setOddHeader($string['value']);
$this->phpSheet->getHeaderFooter()->setEvenHeader($string['value']);
}
}
}
/**
* Read FOOTER record.
*/
private function readFooter(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: var
// realized that $recordData can be empty even when record exists
if ($recordData) {
if ($this->version == self::XLS_BIFF8) {
$string = self::readUnicodeStringLong($recordData);
} else {
$string = $this->readByteStringShort($recordData);
}
$this->phpSheet->getHeaderFooter()->setOddFooter($string['value']);
$this->phpSheet->getHeaderFooter()->setEvenFooter($string['value']);
}
}
}
/**
* Read HCENTER record.
*/
private function readHcenter(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally
$isHorizontalCentered = (bool) self::getUInt2d($recordData, 0);
$this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
}
}
/**
* Read VCENTER record.
*/
private function readVcenter(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered
$isVerticalCentered = (bool) self::getUInt2d($recordData, 0);
$this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
}
}
/**
* Read LEFTMARGIN record.
*/
private function readLeftMargin(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 8
$this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
}
}
/**
* Read RIGHTMARGIN record.
*/
private function readRightMargin(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 8
$this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
}
}
/**
* Read TOPMARGIN record.
*/
private function readTopMargin(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 8
$this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
}
}
/**
* Read BOTTOMMARGIN record.
*/
private function readBottomMargin(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 8
$this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
}
}
/**
* Read PAGESETUP record.
*/
private function readPageSetup(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; paper size
$paperSize = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; scaling factor
$scale = self::getUInt2d($recordData, 2);
// offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed
$fitToWidth = self::getUInt2d($recordData, 6);
// offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed
$fitToHeight = self::getUInt2d($recordData, 8);
// offset: 10; size: 2; option flags
// bit: 0; mask: 0x0001; 0=down then over, 1=over then down
$isOverThenDown = (0x0001 & self::getUInt2d($recordData, 10));
// bit: 1; mask: 0x0002; 0=landscape, 1=portrait
$isPortrait = (0x0002 & self::getUInt2d($recordData, 10)) >> 1;
// bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init
// when this bit is set, do not use flags for those properties
$isNotInit = (0x0004 & self::getUInt2d($recordData, 10)) >> 2;
if (!$isNotInit) {
$this->phpSheet->getPageSetup()->setPaperSize($paperSize);
$this->phpSheet->getPageSetup()->setPageOrder(((bool) $isOverThenDown) ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER);
$this->phpSheet->getPageSetup()->setOrientation(((bool) $isPortrait) ? PageSetup::ORIENTATION_PORTRAIT : PageSetup::ORIENTATION_LANDSCAPE);
$this->phpSheet->getPageSetup()->setScale($scale, false);
$this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages);
$this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false);
$this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false);
}
// offset: 16; size: 8; header margin (IEEE 754 floating-point value)
$marginHeader = self::extractNumber(substr($recordData, 16, 8));
$this->phpSheet->getPageMargins()->setHeader($marginHeader);
// offset: 24; size: 8; footer margin (IEEE 754 floating-point value)
$marginFooter = self::extractNumber(substr($recordData, 24, 8));
$this->phpSheet->getPageMargins()->setFooter($marginFooter);
}
}
/**
* PROTECT - Sheet protection (BIFF2 through BIFF8)
* if this record is omitted, then it also means no sheet protection.
*/
private function readProtect(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->readDataOnly) {
return;
}
// offset: 0; size: 2;
// bit 0, mask 0x01; 1 = sheet is protected
$bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
$this->phpSheet->getProtection()->setSheet((bool) $bool);
}
/**
* SCENPROTECT.
*/
private function readScenProtect(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->readDataOnly) {
return;
}
// offset: 0; size: 2;
// bit: 0, mask 0x01; 1 = scenarios are protected
$bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
$this->phpSheet->getProtection()->setScenarios((bool) $bool);
}
/**
* OBJECTPROTECT.
*/
private function readObjectProtect(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->readDataOnly) {
return;
}
// offset: 0; size: 2;
// bit: 0, mask 0x01; 1 = objects are protected
$bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
$this->phpSheet->getProtection()->setObjects((bool) $bool);
}
/**
* PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8).
*/
private function readPassword(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; 16-bit hash value of password
$password = strtoupper(dechex(self::getUInt2d($recordData, 0))); // the hashed password
$this->phpSheet->getProtection()->setPassword($password, true);
}
}
/**
* Read DEFCOLWIDTH record.
*/
private function readDefColWidth(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; default column width
$width = self::getUInt2d($recordData, 0);
if ($width != 8) {
$this->phpSheet->getDefaultColumnDimension()->setWidth($width);
}
}
/**
* Read COLINFO record.
*/
private function readColInfo(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; index to first column in range
$firstColumnIndex = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; index to last column in range
$lastColumnIndex = self::getUInt2d($recordData, 2);
// offset: 4; size: 2; width of the column in 1/256 of the width of the zero character
$width = self::getUInt2d($recordData, 4);
// offset: 6; size: 2; index to XF record for default column formatting
$xfIndex = self::getUInt2d($recordData, 6);
// offset: 8; size: 2; option flags
// bit: 0; mask: 0x0001; 1= columns are hidden
$isHidden = (0x0001 & self::getUInt2d($recordData, 8)) >> 0;
// bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline)
$level = (0x0700 & self::getUInt2d($recordData, 8)) >> 8;
// bit: 12; mask: 0x1000; 1 = collapsed
$isCollapsed = (bool) ((0x1000 & self::getUInt2d($recordData, 8)) >> 12);
// offset: 10; size: 2; not used
for ($i = $firstColumnIndex + 1; $i <= $lastColumnIndex + 1; ++$i) {
if ($lastColumnIndex == 255 || $lastColumnIndex == 256) {
$this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
break;
}
$this->phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256);
$this->phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden);
$this->phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level);
$this->phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed);
if (isset($this->mapCellXfIndex[$xfIndex])) {
$this->phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
}
}
}
/**
* ROW.
*
* This record contains the properties of a single row in a
* sheet. Rows and cells in a sheet are divided into blocks
* of 32 rows.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readRow(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; index of this row
$r = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; index to column of the first cell which is described by a cell record
// offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1
// offset: 6; size: 2;
// bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point
$height = (0x7FFF & self::getUInt2d($recordData, 6)) >> 0;
// bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height
$useDefaultHeight = (0x8000 & self::getUInt2d($recordData, 6)) >> 15;
if (!$useDefaultHeight) {
$this->phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20);
}
// offset: 8; size: 2; not used
// offset: 10; size: 2; not used in BIFF5-BIFF8
// offset: 12; size: 4; option flags and default row formatting
// bit: 2-0: mask: 0x00000007; outline level of the row
$level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
$this->phpSheet->getRowDimension($r + 1)->setOutlineLevel($level);
// bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed
$isCollapsed = (bool) ((0x00000010 & self::getInt4d($recordData, 12)) >> 4);
$this->phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed);
// bit: 5; mask: 0x00000020; 1 = row is hidden
$isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
$this->phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden);
// bit: 7; mask: 0x00000080; 1 = row has explicit format
$hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
// bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record
$xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
if ($hasExplicitFormat && isset($this->mapCellXfIndex[$xfIndex])) {
$this->phpSheet->getRowDimension($r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
}
}
/**
* Read RK record
* This record represents a cell that contains an RK value
* (encoded integer or floating-point value). If a
* floating-point value cannot be encoded to an RK value,
* a NUMBER record will be written. This record replaces the
* record INTEGER written in BIFF2.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readRk(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; index to row
$row = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; index to column
$column = self::getUInt2d($recordData, 2);
$columnString = Coordinate::stringFromColumnIndex($column + 1);
// Read cell?
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
// offset: 4; size: 2; index to XF record
$xfIndex = self::getUInt2d($recordData, 4);
// offset: 6; size: 4; RK value
$rknum = self::getInt4d($recordData, 6);
$numValue = self::getIEEE754($rknum);
$cell = $this->phpSheet->getCell($columnString . ($row + 1));
if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
// add style information
$cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
// add cell
$cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
}
}
/**
* Read LABELSST record
* This record represents a cell that contains a string. It
* replaces the LABEL record and RSTRING record used in
* BIFF2-BIFF5.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readLabelSst(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; index to row
$row = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; index to column
$column = self::getUInt2d($recordData, 2);
$columnString = Coordinate::stringFromColumnIndex($column + 1);
$emptyCell = true;
// Read cell?
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
// offset: 4; size: 2; index to XF record
$xfIndex = self::getUInt2d($recordData, 4);
// offset: 6; size: 4; index to SST record
$index = self::getInt4d($recordData, 6);
// add cell
if (($fmtRuns = $this->sst[$index]['fmtRuns']) && !$this->readDataOnly) {
// then we should treat as rich text
$richText = new RichText();
$charPos = 0;
$sstCount = count($this->sst[$index]['fmtRuns']);
for ($i = 0; $i <= $sstCount; ++$i) {
if (isset($fmtRuns[$i])) {
$text = StringHelper::substring($this->sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos);
$charPos = $fmtRuns[$i]['charPos'];
} else {
$text = StringHelper::substring($this->sst[$index]['value'], $charPos, StringHelper::countCharacters($this->sst[$index]['value']));
}
if (StringHelper::countCharacters($text) > 0) {
if ($i == 0) { // first text run, no style
$richText->createText($text);
} else {
$textRun = $richText->createTextRun($text);
if (isset($fmtRuns[$i - 1])) {
if ($fmtRuns[$i - 1]['fontIndex'] < 4) {
$fontIndex = $fmtRuns[$i - 1]['fontIndex'];
} else {
// this has to do with that index 4 is omitted in all BIFF versions for some stra nge reason
// check the OpenOffice documentation of the FONT record
$fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1;
}
if (array_key_exists($fontIndex, $this->objFonts) === false) {
$fontIndex = count($this->objFonts) - 1;
}
$textRun->setFont(clone $this->objFonts[$fontIndex]);
}
}
}
}
if ($this->readEmptyCells || trim($richText->getPlainText()) !== '') {
$cell = $this->phpSheet->getCell($columnString . ($row + 1));
$cell->setValueExplicit($richText, DataType::TYPE_STRING);
$emptyCell = false;
}
} else {
if ($this->readEmptyCells || trim($this->sst[$index]['value']) !== '') {
$cell = $this->phpSheet->getCell($columnString . ($row + 1));
$cell->setValueExplicit($this->sst[$index]['value'], DataType::TYPE_STRING);
$emptyCell = false;
}
}
if (!$this->readDataOnly && !$emptyCell && isset($this->mapCellXfIndex[$xfIndex])) {
// add style information
$cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
}
}
/**
* Read MULRK record
* This record represents a cell range containing RK value
* cells. All cells are located in the same row.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readMulRk(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; index to row
$row = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; index to first column
$colFirst = self::getUInt2d($recordData, 2);
// offset: var; size: 2; index to last column
$colLast = self::getUInt2d($recordData, $length - 2);
$columns = $colLast - $colFirst + 1;
// offset within record data
$offset = 4;
for ($i = 1; $i <= $columns; ++$i) {
$columnString = Coordinate::stringFromColumnIndex($colFirst + $i);
// Read cell?
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
// offset: var; size: 2; index to XF record
$xfIndex = self::getUInt2d($recordData, $offset);
// offset: var; size: 4; RK value
$numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
$cell = $this->phpSheet->getCell($columnString . ($row + 1));
if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
// add style
$cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
// add cell value
$cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
}
$offset += 6;
}
}
/**
* Read NUMBER record
* This record represents a cell that contains a
* floating-point value.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readNumber(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; index to row
$row = self::getUInt2d($recordData, 0);
// offset: 2; size 2; index to column
$column = self::getUInt2d($recordData, 2);
$columnString = Coordinate::stringFromColumnIndex($column + 1);
// Read cell?
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
// offset 4; size: 2; index to XF record
$xfIndex = self::getUInt2d($recordData, 4);
$numValue = self::extractNumber(substr($recordData, 6, 8));
$cell = $this->phpSheet->getCell($columnString . ($row + 1));
if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
// add cell style
$cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
// add cell value
$cell->setValueExplicit($numValue, DataType::TYPE_NUMERIC);
}
}
/**
* Read FORMULA record + perhaps a following STRING record if formula result is a string
* This record contains the token array and the result of a
* formula cell.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readFormula(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; row index
$row = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; col index
$column = self::getUInt2d($recordData, 2);
$columnString = Coordinate::stringFromColumnIndex($column + 1);
// offset: 20: size: variable; formula structure
$formulaStructure = substr($recordData, 20);
// offset: 14: size: 2; option flags, recalculate always, recalculate on open etc.
$options = self::getUInt2d($recordData, 14);
// bit: 0; mask: 0x0001; 1 = recalculate always
// bit: 1; mask: 0x0002; 1 = calculate on open
// bit: 2; mask: 0x0008; 1 = part of a shared formula
$isPartOfSharedFormula = (bool) (0x0008 & $options);
// WARNING:
// We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true
// the formula data may be ordinary formula data, therefore we need to check
// explicitly for the tExp token (0x01)
$isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure[2]) == 0x01;
if ($isPartOfSharedFormula) {
// part of shared formula which means there will be a formula with a tExp token and nothing else
// get the base cell, grab tExp token
$baseRow = self::getUInt2d($formulaStructure, 3);
$baseCol = self::getUInt2d($formulaStructure, 5);
$this->baseCell = Coordinate::stringFromColumnIndex($baseCol + 1) . ($baseRow + 1);
}
// Read cell?
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
if ($isPartOfSharedFormula) {
// formula is added to this cell after the sheet has been read
$this->sharedFormulaParts[$columnString . ($row + 1)] = $this->baseCell;
}
// offset: 16: size: 4; not used
// offset: 4; size: 2; XF index
$xfIndex = self::getUInt2d($recordData, 4);
// offset: 6; size: 8; result of the formula
if ((ord($recordData[6]) == 0) && (ord($recordData[12]) == 255) && (ord($recordData[13]) == 255)) {
// String formula. Result follows in appended STRING record
$dataType = DataType::TYPE_STRING;
// read possible SHAREDFMLA record
$code = self::getUInt2d($this->data, $this->pos);
if ($code == self::XLS_TYPE_SHAREDFMLA) {
$this->readSharedFmla();
}
// read STRING record
$value = $this->readString();
} elseif (
(ord($recordData[6]) == 1)
&& (ord($recordData[12]) == 255)
&& (ord($recordData[13]) == 255)
) {
// Boolean formula. Result is in +2; 0=false, 1=true
$dataType = DataType::TYPE_BOOL;
$value = (bool) ord($recordData[8]);
} elseif (
(ord($recordData[6]) == 2)
&& (ord($recordData[12]) == 255)
&& (ord($recordData[13]) == 255)
) {
// Error formula. Error code is in +2
$dataType = DataType::TYPE_ERROR;
$value = Xls\ErrorCode::lookup(ord($recordData[8]));
} elseif (
(ord($recordData[6]) == 3)
&& (ord($recordData[12]) == 255)
&& (ord($recordData[13]) == 255)
) {
// Formula result is a null string
$dataType = DataType::TYPE_NULL;
$value = '';
} else {
// forumla result is a number, first 14 bytes like _NUMBER record
$dataType = DataType::TYPE_NUMERIC;
$value = self::extractNumber(substr($recordData, 6, 8));
}
$cell = $this->phpSheet->getCell($columnString . ($row + 1));
if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
// add cell style
$cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
// store the formula
if (!$isPartOfSharedFormula) {
// not part of shared formula
// add cell value. If we can read formula, populate with formula, otherwise just used cached value
try {
if ($this->version != self::XLS_BIFF8) {
throw new Exception('Not BIFF8. Can only read BIFF8 formulas');
}
$formula = $this->getFormulaFromStructure($formulaStructure); // get formula in human language
$cell->setValueExplicit('=' . $formula, DataType::TYPE_FORMULA);
} catch (PhpSpreadsheetException $e) {
$cell->setValueExplicit($value, $dataType);
}
} else {
if ($this->version == self::XLS_BIFF8) {
// do nothing at this point, formula id added later in the code
} else {
$cell->setValueExplicit($value, $dataType);
}
}
// store the cached calculated value
$cell->setCalculatedValue($value);
}
}
/**
* Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader,
* which usually contains relative references.
* These will be used to construct the formula in each shared formula part after the sheet is read.
*/
private function readSharedFmla(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything
$cellRange = substr($recordData, 0, 6);
$cellRange = $this->readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax
// offset: 6, size: 1; not used
// offset: 7, size: 1; number of existing FORMULA records for this shared formula
$no = ord($recordData[7]);
// offset: 8, size: var; Binary token array of the shared formula
$formula = substr($recordData, 8);
// at this point we only store the shared formula for later use
$this->sharedFormulas[$this->baseCell] = $formula;
}
/**
* Read a STRING record from current stream position and advance the stream pointer to next record
* This record is used for storing result from FORMULA record when it is a string, and
* it occurs directly after the FORMULA record.
*
* @return string The string contents as UTF-8
*/
private function readString()
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->version == self::XLS_BIFF8) {
$string = self::readUnicodeStringLong($recordData);
$value = $string['value'];
} else {
$string = $this->readByteStringLong($recordData);
$value = $string['value'];
}
return $value;
}
/**
* Read BOOLERR record
* This record represents a Boolean value or error value
* cell.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readBoolErr(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; row index
$row = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; column index
$column = self::getUInt2d($recordData, 2);
$columnString = Coordinate::stringFromColumnIndex($column + 1);
// Read cell?
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
// offset: 4; size: 2; index to XF record
$xfIndex = self::getUInt2d($recordData, 4);
// offset: 6; size: 1; the boolean value or error value
$boolErr = ord($recordData[6]);
// offset: 7; size: 1; 0=boolean; 1=error
$isError = ord($recordData[7]);
$cell = $this->phpSheet->getCell($columnString . ($row + 1));
switch ($isError) {
case 0: // boolean
$value = (bool) $boolErr;
// add cell value
$cell->setValueExplicit($value, DataType::TYPE_BOOL);
break;
case 1: // error type
$value = Xls\ErrorCode::lookup($boolErr);
// add cell value
$cell->setValueExplicit($value, DataType::TYPE_ERROR);
break;
}
if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
// add cell style
$cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
}
}
/**
* Read MULBLANK record
* This record represents a cell range of empty cells. All
* cells are located in the same row.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readMulBlank(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; index to row
$row = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; index to first column
$fc = self::getUInt2d($recordData, 2);
// offset: 4; size: 2 x nc; list of indexes to XF records
// add style information
if (!$this->readDataOnly && $this->readEmptyCells) {
for ($i = 0; $i < $length / 2 - 3; ++$i) {
$columnString = Coordinate::stringFromColumnIndex($fc + $i + 1);
// Read cell?
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
$xfIndex = self::getUInt2d($recordData, 4 + 2 * $i);
if (isset($this->mapCellXfIndex[$xfIndex])) {
$this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
}
}
}
// offset: 6; size 2; index to last column (not needed)
}
/**
* Read LABEL record
* This record represents a cell that contains a string. In
* BIFF8 it is usually replaced by the LABELSST record.
* Excel still uses this record, if it copies unformatted
* text cells to the clipboard.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readLabel(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; index to row
$row = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; index to column
$column = self::getUInt2d($recordData, 2);
$columnString = Coordinate::stringFromColumnIndex($column + 1);
// Read cell?
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
// offset: 4; size: 2; XF index
$xfIndex = self::getUInt2d($recordData, 4);
// add cell value
// todo: what if string is very long? continue record
if ($this->version == self::XLS_BIFF8) {
$string = self::readUnicodeStringLong(substr($recordData, 6));
$value = $string['value'];
} else {
$string = $this->readByteStringLong(substr($recordData, 6));
$value = $string['value'];
}
if ($this->readEmptyCells || trim($value) !== '') {
$cell = $this->phpSheet->getCell($columnString . ($row + 1));
$cell->setValueExplicit($value, DataType::TYPE_STRING);
if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
// add cell style
$cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
}
}
}
/**
* Read BLANK record.
*/
private function readBlank(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; row index
$row = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; col index
$col = self::getUInt2d($recordData, 2);
$columnString = Coordinate::stringFromColumnIndex($col + 1);
// Read cell?
if (($this->getReadFilter() !== null) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->phpSheet->getTitle())) {
// offset: 4; size: 2; XF index
$xfIndex = self::getUInt2d($recordData, 4);
// add style information
if (!$this->readDataOnly && $this->readEmptyCells && isset($this->mapCellXfIndex[$xfIndex])) {
$this->phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
}
}
}
/**
* Read MSODRAWING record.
*/
private function readMsoDrawing(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
// get spliced record data
$splicedRecordData = $this->getSplicedRecordData();
$recordData = $splicedRecordData['recordData'];
$this->drawingData .= $recordData;
}
/**
* Read OBJ record.
*/
private function readObj(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
return;
}
// recordData consists of an array of subrecords looking like this:
// ft: 2 bytes; ftCmo type (0x15)
// cb: 2 bytes; size in bytes of ftCmo data
// ot: 2 bytes; Object Type
// id: 2 bytes; Object id number
// grbit: 2 bytes; Option Flags
// data: var; subrecord data
// for now, we are just interested in the second subrecord containing the object type
$ftCmoType = self::getUInt2d($recordData, 0);
$cbCmoSize = self::getUInt2d($recordData, 2);
$otObjType = self::getUInt2d($recordData, 4);
$idObjID = self::getUInt2d($recordData, 6);
$grbitOpts = self::getUInt2d($recordData, 6);
$this->objs[] = [
'ftCmoType' => $ftCmoType,
'cbCmoSize' => $cbCmoSize,
'otObjType' => $otObjType,
'idObjID' => $idObjID,
'grbitOpts' => $grbitOpts,
];
$this->textObjRef = $idObjID;
}
/**
* Read WINDOW2 record.
*/
private function readWindow2(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; option flags
$options = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; index to first visible row
$firstVisibleRow = self::getUInt2d($recordData, 2);
// offset: 4; size: 2; index to first visible colum
$firstVisibleColumn = self::getUInt2d($recordData, 4);
$zoomscaleInPageBreakPreview = 0;
$zoomscaleInNormalView = 0;
if ($this->version === self::XLS_BIFF8) {
// offset: 8; size: 2; not used
// offset: 10; size: 2; cached magnification factor in page break preview (in percent); 0 = Default (60%)
// offset: 12; size: 2; cached magnification factor in normal view (in percent); 0 = Default (100%)
// offset: 14; size: 4; not used
if (!isset($recordData[10])) {
$zoomscaleInPageBreakPreview = 0;
} else {
$zoomscaleInPageBreakPreview = self::getUInt2d($recordData, 10);
}
if ($zoomscaleInPageBreakPreview === 0) {
$zoomscaleInPageBreakPreview = 60;
}
if (!isset($recordData[12])) {
$zoomscaleInNormalView = 0;
} else {
$zoomscaleInNormalView = self::getUInt2d($recordData, 12);
}
if ($zoomscaleInNormalView === 0) {
$zoomscaleInNormalView = 100;
}
}
// bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines
$showGridlines = (bool) ((0x0002 & $options) >> 1);
$this->phpSheet->setShowGridlines($showGridlines);
// bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers
$showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
$this->phpSheet->setShowRowColHeaders($showRowColHeaders);
// bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen
$this->frozen = (bool) ((0x0008 & $options) >> 3);
// bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left
$this->phpSheet->setRightToLeft((bool) ((0x0040 & $options) >> 6));
// bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active
$isActive = (bool) ((0x0400 & $options) >> 10);
if ($isActive) {
$this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
}
// bit: 11; mask: 0x0800; 0 = normal view, 1 = page break view
$isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
//FIXME: set $firstVisibleRow and $firstVisibleColumn
if ($this->phpSheet->getSheetView()->getView() !== SheetView::SHEETVIEW_PAGE_LAYOUT) {
//NOTE: this setting is inferior to page layout view(Excel2007-)
$view = $isPageBreakPreview ? SheetView::SHEETVIEW_PAGE_BREAK_PREVIEW : SheetView::SHEETVIEW_NORMAL;
$this->phpSheet->getSheetView()->setView($view);
if ($this->version === self::XLS_BIFF8) {
$zoomScale = $isPageBreakPreview ? $zoomscaleInPageBreakPreview : $zoomscaleInNormalView;
$this->phpSheet->getSheetView()->setZoomScale($zoomScale);
$this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
}
}
}
/**
* Read PLV Record(Created by Excel2007 or upper).
*/
private function readPageLayoutView(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; rt
//->ignore
$rt = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; grbitfr
//->ignore
$grbitFrt = self::getUInt2d($recordData, 2);
// offset: 4; size: 8; reserved
//->ignore
// offset: 12; size 2; zoom scale
$wScalePLV = self::getUInt2d($recordData, 12);
// offset: 14; size 2; grbit
$grbit = self::getUInt2d($recordData, 14);
// decomprise grbit
$fPageLayoutView = $grbit & 0x01;
$fRulerVisible = ($grbit >> 1) & 0x01; //no support
$fWhitespaceHidden = ($grbit >> 3) & 0x01; //no support
if ($fPageLayoutView === 1) {
$this->phpSheet->getSheetView()->setView(SheetView::SHEETVIEW_PAGE_LAYOUT);
$this->phpSheet->getSheetView()->setZoomScale($wScalePLV); //set by Excel2007 only if SHEETVIEW_PAGE_LAYOUT
}
//otherwise, we cannot know whether SHEETVIEW_PAGE_LAYOUT or SHEETVIEW_PAGE_BREAK_PREVIEW.
}
/**
* Read SCL record.
*/
private function readScl(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// offset: 0; size: 2; numerator of the view magnification
$numerator = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; numerator of the view magnification
$denumerator = self::getUInt2d($recordData, 2);
// set the zoom scale (in percent)
$this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
}
/**
* Read PANE record.
*/
private function readPane(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 2; position of vertical split
$px = self::getUInt2d($recordData, 0);
// offset: 2; size: 2; position of horizontal split
$py = self::getUInt2d($recordData, 2);
// offset: 4; size: 2; top most visible row in the bottom pane
$rwTop = self::getUInt2d($recordData, 4);
// offset: 6; size: 2; first visible left column in the right pane
$colLeft = self::getUInt2d($recordData, 6);
if ($this->frozen) {
// frozen panes
$cell = Coordinate::stringFromColumnIndex($px + 1) . ($py + 1);
$topLeftCell = Coordinate::stringFromColumnIndex($colLeft + 1) . ($rwTop + 1);
$this->phpSheet->freezePane($cell, $topLeftCell);
}
// unfrozen panes; split windows; not supported by PhpSpreadsheet core
}
}
/**
* Read SELECTION record. There is one such record for each pane in the sheet.
*/
private function readSelection(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 1; pane identifier
$paneId = ord($recordData[0]);
// offset: 1; size: 2; index to row of the active cell
$r = self::getUInt2d($recordData, 1);
// offset: 3; size: 2; index to column of the active cell
$c = self::getUInt2d($recordData, 3);
// offset: 5; size: 2; index into the following cell range list to the
// entry that contains the active cell
$index = self::getUInt2d($recordData, 5);
// offset: 7; size: var; cell range address list containing all selected cell ranges
$data = substr($recordData, 7);
$cellRangeAddressList = $this->readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax
$selectedCells = $cellRangeAddressList['cellRangeAddresses'][0];
// first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
$selectedCells = (string) preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
}
// first row '1' + last row '65536' indicates that full column is selected
if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
$selectedCells = (string) preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
}
// first column 'A' + last column 'IV' indicates that full row is selected
if (preg_match('/^(A\d+\:)IV(\d+)$/', $selectedCells)) {
$selectedCells = (string) preg_replace('/^(A\d+\:)IV(\d+)$/', '${1}XFD${2}', $selectedCells);
}
$this->phpSheet->setSelectedCells($selectedCells);
}
}
private function includeCellRangeFiltered(string $cellRangeAddress): bool
{
$includeCellRange = true;
if ($this->getReadFilter() !== null) {
$includeCellRange = false;
$rangeBoundaries = Coordinate::getRangeBoundaries($cellRangeAddress);
++$rangeBoundaries[1][0];
for ($row = $rangeBoundaries[0][1]; $row <= $rangeBoundaries[1][1]; ++$row) {
for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) {
if ($this->getReadFilter()->readCell($column, $row, $this->phpSheet->getTitle())) {
$includeCellRange = true;
break 2;
}
}
}
}
return $includeCellRange;
}
/**
* MERGEDCELLS.
*
* This record contains the addresses of merged cell ranges
* in the current sheet.
*
* -- "OpenOffice.org's Documentation of the Microsoft
* Excel File Format"
*/
private function readMergedCells(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
$cellRangeAddressList = $this->readBIFF8CellRangeAddressList($recordData);
foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) {
if (
(strpos($cellRangeAddress, ':') !== false) &&
($this->includeCellRangeFiltered($cellRangeAddress))
) {
$this->phpSheet->mergeCells($cellRangeAddress, Worksheet::MERGE_CELL_CONTENT_HIDE);
}
}
}
}
/**
* Read HYPERLINK record.
*/
private function readHyperLink(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer forward to next record
$this->pos += 4 + $length;
if (!$this->readDataOnly) {
// offset: 0; size: 8; cell range address of all cells containing this hyperlink
try {
$cellRange = $this->readBIFF8CellRangeAddressFixed($recordData);
} catch (PhpSpreadsheetException $e) {
return;
}
// offset: 8, size: 16; GUID of StdLink
// offset: 24, size: 4; unknown value
// offset: 28, size: 4; option flags
// bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL
$isFileLinkOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 0;
// bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL
$isAbsPathOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 1;
// bit: 2 (and 4); mask: 0x00000014; 0 = no description
$hasDesc = (0x00000014 & self::getUInt2d($recordData, 28)) >> 2;
// bit: 3; mask: 0x00000008; 0 = no text, 1 = has text
$hasText = (0x00000008 & self::getUInt2d($recordData, 28)) >> 3;
// bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame
$hasFrame = (0x00000080 & self::getUInt2d($recordData, 28)) >> 7;
// bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name)
$isUNC = (0x00000100 & self::getUInt2d($recordData, 28)) >> 8;
// offset within record data
$offset = 32;
if ($hasDesc) {
// offset: 32; size: var; character count of description text
$dl = self::getInt4d($recordData, 32);
// offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated
$desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false);
$offset += 4 + 2 * $dl;
}
if ($hasFrame) {
$fl = self::getInt4d($recordData, $offset);
$offset += 4 + 2 * $fl;
}
// detect type of hyperlink (there are 4 types)
$hyperlinkType = null;
if ($isUNC) {
$hyperlinkType = 'UNC';
} elseif (!$isFileLinkOrUrl) {
$hyperlinkType = 'workbook';
} elseif (ord($recordData[$offset]) == 0x03) {
$hyperlinkType = 'local';
} elseif (ord($recordData[$offset]) == 0xE0) {
$hyperlinkType = 'URL';
}
switch ($hyperlinkType) {
case 'URL':
// section 5.58.2: Hyperlink containing a URL
// e.g. http://example.org/index.php
// offset: var; size: 16; GUID of URL Moniker
$offset += 16;
// offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word
$us = self::getInt4d($recordData, $offset);
$offset += 4;
// offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated
$url = self::encodeUTF16(substr($recordData, $offset, $us - 2), false);
$nullOffset = strpos($url, chr(0x00));
if ($nullOffset) {
$url = substr($url, 0, $nullOffset);
}
$url .= $hasText ? '#' : '';
$offset += $us;
break;
case 'local':
// section 5.58.3: Hyperlink to local file
// examples:
// mydoc.txt
// ../../somedoc.xls#Sheet!A1
// offset: var; size: 16; GUI of File Moniker
$offset += 16;
// offset: var; size: 2; directory up-level count.
$upLevelCount = self::getUInt2d($recordData, $offset);
$offset += 2;
// offset: var; size: 4; character count of the shortened file path and name, including trailing zero word
$sl = self::getInt4d($recordData, $offset);
$offset += 4;
// offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string)
$shortenedFilePath = substr($recordData, $offset, $sl);
$shortenedFilePath = self::encodeUTF16($shortenedFilePath, true);
$shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero
$offset += $sl;
// offset: var; size: 24; unknown sequence
$offset += 24;
// extended file path
// offset: var; size: 4; size of the following file link field including string lenth mark
$sz = self::getInt4d($recordData, $offset);
$offset += 4;
// only present if $sz > 0
if ($sz > 0) {
// offset: var; size: 4; size of the character array of the extended file path and name
$xl = self::getInt4d($recordData, $offset);
$offset += 4;
// offset: var; size 2; unknown
$offset += 2;
// offset: var; size $xl; character array of the extended file path and name.
$extendedFilePath = substr($recordData, $offset, $xl);
$extendedFilePath = self::encodeUTF16($extendedFilePath, false);
$offset += $xl;
}
// construct the path
$url = str_repeat('..\\', $upLevelCount);
$url .= ($sz > 0) ? $extendedFilePath : $shortenedFilePath; // use extended path if available
$url .= $hasText ? '#' : '';
break;
case 'UNC':
// section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path
// todo: implement
return;
case 'workbook':
// section 5.58.5: Hyperlink to the Current Workbook
// e.g. Sheet2!B1:C2, stored in text mark field
$url = 'sheet://';
break;
default:
return;
}
if ($hasText) {
// offset: var; size: 4; character count of text mark including trailing zero word
$tl = self::getInt4d($recordData, $offset);
$offset += 4;
// offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated
$text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false);
$url .= $text;
}
// apply the hyperlink to all the relevant cells
foreach (Coordinate::extractAllCellReferencesInRange($cellRange) as $coordinate) {
$this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url);
}
}
}
/**
* Read DATAVALIDATIONS record.
*/
private function readDataValidations(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer forward to next record
$this->pos += 4 + $length;
}
/**
* Read DATAVALIDATION record.
*/
private function readDataValidation(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer forward to next record
$this->pos += 4 + $length;
if ($this->readDataOnly) {
return;
}
// offset: 0; size: 4; Options
$options = self::getInt4d($recordData, 0);
// bit: 0-3; mask: 0x0000000F; type
$type = (0x0000000F & $options) >> 0;
$type = Xls\DataValidationHelper::type($type);
// bit: 4-6; mask: 0x00000070; error type
$errorStyle = (0x00000070 & $options) >> 4;
$errorStyle = Xls\DataValidationHelper::errorStyle($errorStyle);
// bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
// I have only seen cases where this is 1
$explicitFormula = (0x00000080 & $options) >> 7;
// bit: 8; mask: 0x00000100; 1= empty cells allowed
$allowBlank = (0x00000100 & $options) >> 8;
// bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
$suppressDropDown = (0x00000200 & $options) >> 9;
// bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
$showInputMessage = (0x00040000 & $options) >> 18;
// bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
$showErrorMessage = (0x00080000 & $options) >> 19;
// bit: 20-23; mask: 0x00F00000; condition operator
$operator = (0x00F00000 & $options) >> 20;
$operator = Xls\DataValidationHelper::operator($operator);
if ($type === null || $errorStyle === null || $operator === null) {
return;
}
// offset: 4; size: var; title of the prompt box
$offset = 4;
$string = self::readUnicodeStringLong(substr($recordData, $offset));
$promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
$offset += $string['size'];
// offset: var; size: var; title of the error box
$string = self::readUnicodeStringLong(substr($recordData, $offset));
$errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
$offset += $string['size'];
// offset: var; size: var; text of the prompt box
$string = self::readUnicodeStringLong(substr($recordData, $offset));
$prompt = $string['value'] !== chr(0) ? $string['value'] : '';
$offset += $string['size'];
// offset: var; size: var; text of the error box
$string = self::readUnicodeStringLong(substr($recordData, $offset));
$error = $string['value'] !== chr(0) ? $string['value'] : '';
$offset += $string['size'];
// offset: var; size: 2; size of the formula data for the first condition
$sz1 = self::getUInt2d($recordData, $offset);
$offset += 2;
// offset: var; size: 2; not used
$offset += 2;
// offset: var; size: $sz1; formula data for first condition (without size field)
$formula1 = substr($recordData, $offset, $sz1);
$formula1 = pack('v', $sz1) . $formula1; // prepend the length
try {
$formula1 = $this->getFormulaFromStructure($formula1);
// in list type validity, null characters are used as item separators
if ($type == DataValidation::TYPE_LIST) {
$formula1 = str_replace(chr(0), ',', $formula1);
}
} catch (PhpSpreadsheetException $e) {
return;
}
$offset += $sz1;
// offset: var; size: 2; size of the formula data for the first condition
$sz2 = self::getUInt2d($recordData, $offset);
$offset += 2;
// offset: var; size: 2; not used
$offset += 2;
// offset: var; size: $sz2; formula data for second condition (without size field)
$formula2 = substr($recordData, $offset, $sz2);
$formula2 = pack('v', $sz2) . $formula2; // prepend the length
try {
$formula2 = $this->getFormulaFromStructure($formula2);
} catch (PhpSpreadsheetException $e) {
return;
}
$offset += $sz2;
// offset: var; size: var; cell range address list with
$cellRangeAddressList = $this->readBIFF8CellRangeAddressList(substr($recordData, $offset));
$cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
foreach ($cellRangeAddresses as $cellRange) {
$stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $coordinate) {
$objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
$objValidation->setType($type);
$objValidation->setErrorStyle($errorStyle);
$objValidation->setAllowBlank((bool) $allowBlank);
$objValidation->setShowInputMessage((bool) $showInputMessage);
$objValidation->setShowErrorMessage((bool) $showErrorMessage);
$objValidation->setShowDropDown(!$suppressDropDown);
$objValidation->setOperator($operator);
$objValidation->setErrorTitle($errorTitle);
$objValidation->setError($error);
$objValidation->setPromptTitle($promptTitle);
$objValidation->setPrompt($prompt);
$objValidation->setFormula1($formula1);
$objValidation->setFormula2($formula2);
}
}
}
/**
* Read SHEETLAYOUT record. Stores sheet tab color information.
*/
private function readSheetLayout(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// local pointer in record data
$offset = 0;
if (!$this->readDataOnly) {
// offset: 0; size: 2; repeated record identifier 0x0862
// offset: 2; size: 10; not used
// offset: 12; size: 4; size of record data
// Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?)
$sz = self::getInt4d($recordData, 12);
switch ($sz) {
case 0x14:
// offset: 16; size: 2; color index for sheet tab
$colorIndex = self::getUInt2d($recordData, 16);
$color = Xls\Color::map($colorIndex, $this->palette, $this->version);
$this->phpSheet->getTabColor()->setRGB($color['rgb']);
break;
case 0x28:
// TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007
return;
}
}
}
/**
* Read SHEETPROTECTION record (FEATHEADR).
*/
private function readSheetProtection(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
if ($this->readDataOnly) {
return;
}
// offset: 0; size: 2; repeated record header
// offset: 2; size: 2; FRT cell reference flag (=0 currently)
// offset: 4; size: 8; Currently not used and set to 0
// offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag)
$isf = self::getUInt2d($recordData, 12);
if ($isf != 2) {
return;
}
// offset: 14; size: 1; =1 since this is a feat header
// offset: 15; size: 4; size of rgbHdrSData
// rgbHdrSData, assume "Enhanced Protection"
// offset: 19; size: 2; option flags
$options = self::getUInt2d($recordData, 19);
// bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects
// Note - do not negate $bool
$bool = (0x0001 & $options) >> 0;
$this->phpSheet->getProtection()->setObjects((bool) $bool);
// bit: 1; mask 0x0002; edit scenarios
// Note - do not negate $bool
$bool = (0x0002 & $options) >> 1;
$this->phpSheet->getProtection()->setScenarios((bool) $bool);
// bit: 2; mask 0x0004; format cells
$bool = (0x0004 & $options) >> 2;
$this->phpSheet->getProtection()->setFormatCells(!$bool);
// bit: 3; mask 0x0008; format columns
$bool = (0x0008 & $options) >> 3;
$this->phpSheet->getProtection()->setFormatColumns(!$bool);
// bit: 4; mask 0x0010; format rows
$bool = (0x0010 & $options) >> 4;
$this->phpSheet->getProtection()->setFormatRows(!$bool);
// bit: 5; mask 0x0020; insert columns
$bool = (0x0020 & $options) >> 5;
$this->phpSheet->getProtection()->setInsertColumns(!$bool);
// bit: 6; mask 0x0040; insert rows
$bool = (0x0040 & $options) >> 6;
$this->phpSheet->getProtection()->setInsertRows(!$bool);
// bit: 7; mask 0x0080; insert hyperlinks
$bool = (0x0080 & $options) >> 7;
$this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
// bit: 8; mask 0x0100; delete columns
$bool = (0x0100 & $options) >> 8;
$this->phpSheet->getProtection()->setDeleteColumns(!$bool);
// bit: 9; mask 0x0200; delete rows
$bool = (0x0200 & $options) >> 9;
$this->phpSheet->getProtection()->setDeleteRows(!$bool);
// bit: 10; mask 0x0400; select locked cells
// Note that this is opposite of most of above.
$bool = (0x0400 & $options) >> 10;
$this->phpSheet->getProtection()->setSelectLockedCells((bool) $bool);
// bit: 11; mask 0x0800; sort cell range
$bool = (0x0800 & $options) >> 11;
$this->phpSheet->getProtection()->setSort(!$bool);
// bit: 12; mask 0x1000; auto filter
$bool = (0x1000 & $options) >> 12;
$this->phpSheet->getProtection()->setAutoFilter(!$bool);
// bit: 13; mask 0x2000; pivot tables
$bool = (0x2000 & $options) >> 13;
$this->phpSheet->getProtection()->setPivotTables(!$bool);
// bit: 14; mask 0x4000; select unlocked cells
// Note that this is opposite of most of above.
$bool = (0x4000 & $options) >> 14;
$this->phpSheet->getProtection()->setSelectUnlockedCells((bool) $bool);
// offset: 21; size: 2; not used
}
/**
* Read RANGEPROTECTION record
* Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification,
* where it is referred to as FEAT record.
*/
private function readRangeProtection(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer to next record
$this->pos += 4 + $length;
// local pointer in record data
$offset = 0;
if (!$this->readDataOnly) {
$offset += 12;
// offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag
$isf = self::getUInt2d($recordData, 12);
if ($isf != 2) {
// we only read FEAT records of type 2
return;
}
$offset += 2;
$offset += 5;
// offset: 19; size: 2; count of ref ranges this feature is on
$cref = self::getUInt2d($recordData, 19);
$offset += 2;
$offset += 6;
// offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record)
$cellRanges = [];
for ($i = 0; $i < $cref; ++$i) {
try {
$cellRange = $this->readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8));
} catch (PhpSpreadsheetException $e) {
return;
}
$cellRanges[] = $cellRange;
$offset += 8;
}
// offset: var; size: var; variable length of feature specific data
$rgbFeat = substr($recordData, $offset);
$offset += 4;
// offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit)
$wPassword = self::getInt4d($recordData, $offset);
$offset += 4;
// Apply range protection to sheet
if ($cellRanges) {
$this->phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true);
}
}
}
/**
* Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record
* When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented.
* In this case, we must treat the CONTINUE record as a MSODRAWING record.
*/
private function readContinue(): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// check if we are reading drawing data
// this is in case a free CONTINUE record occurs in other circumstances we are unaware of
if ($this->drawingData == '') {
// move stream pointer to next record
$this->pos += 4 + $length;
return;
}
// check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data
if ($length < 4) {
// move stream pointer to next record
$this->pos += 4 + $length;
return;
}
// dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record
// look inside CONTINUE record to see if it looks like a part of an Escher stream
// we know that Escher stream may be split at least at
// 0xF003 MsofbtSpgrContainer
// 0xF004 MsofbtSpContainer
// 0xF00D MsofbtClientTextbox
$validSplitPoints = [0xF003, 0xF004, 0xF00D]; // add identifiers if we find more
$splitPoint = self::getUInt2d($recordData, 2);
if (in_array($splitPoint, $validSplitPoints)) {
// get spliced record data (and move pointer to next record)
$splicedRecordData = $this->getSplicedRecordData();
$this->drawingData .= $splicedRecordData['recordData'];
return;
}
// move stream pointer to next record
$this->pos += 4 + $length;
}
/**
* Reads a record from current position in data stream and continues reading data as long as CONTINUE
* records are found. Splices the record data pieces and returns the combined string as if record data
* is in one piece.
* Moves to next current position in data stream to start of next record different from a CONtINUE record.
*
* @return array
*/
private function getSplicedRecordData()
{
$data = '';
$spliceOffsets = [];
$i = 0;
$spliceOffsets[0] = 0;
do {
++$i;
// offset: 0; size: 2; identifier
$identifier = self::getUInt2d($this->data, $this->pos);
// offset: 2; size: 2; length
$length = self::getUInt2d($this->data, $this->pos + 2);
$data .= $this->readRecordData($this->data, $this->pos + 4, $length);
$spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length;
$this->pos += 4 + $length;
$nextIdentifier = self::getUInt2d($this->data, $this->pos);
} while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
return [
'recordData' => $data,
'spliceOffsets' => $spliceOffsets,
];
}
/**
* Convert formula structure into human readable Excel formula like 'A3+A5*5'.
*
* @param string $formulaStructure The complete binary data for the formula
* @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
*
* @return string Human readable formula
*/
private function getFormulaFromStructure($formulaStructure, $baseCell = 'A1')
{
// offset: 0; size: 2; size of the following formula data
$sz = self::getUInt2d($formulaStructure, 0);
// offset: 2; size: sz
$formulaData = substr($formulaStructure, 2, $sz);
// offset: 2 + sz; size: variable (optional)
if (strlen($formulaStructure) > 2 + $sz) {
$additionalData = substr($formulaStructure, 2 + $sz);
} else {
$additionalData = '';
}
return $this->getFormulaFromData($formulaData, $additionalData, $baseCell);
}
/**
* Take formula data and additional data for formula and return human readable formula.
*
* @param string $formulaData The binary data for the formula itself
* @param string $additionalData Additional binary data going with the formula
* @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
*
* @return string Human readable formula
*/
private function getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1')
{
// start parsing the formula data
$tokens = [];
while (strlen($formulaData) > 0 && $token = $this->getNextToken($formulaData, $baseCell)) {
$tokens[] = $token;
$formulaData = substr($formulaData, $token['size']);
}
$formulaString = $this->createFormulaFromTokens($tokens, $additionalData);
return $formulaString;
}
/**
* Take array of tokens together with additional data for formula and return human readable formula.
*
* @param array $tokens
* @param string $additionalData Additional binary data going with the formula
*
* @return string Human readable formula
*/
private function createFormulaFromTokens($tokens, $additionalData)
{
// empty formula?
if (empty($tokens)) {
return '';
}
$formulaStrings = [];
foreach ($tokens as $token) {
// initialize spaces
$space0 = $space0 ?? ''; // spaces before next token, not tParen
$space1 = $space1 ?? ''; // carriage returns before next token, not tParen
$space2 = $space2 ?? ''; // spaces before opening parenthesis
$space3 = $space3 ?? ''; // carriage returns before opening parenthesis
$space4 = $space4 ?? ''; // spaces before closing parenthesis
$space5 = $space5 ?? ''; // carriage returns before closing parenthesis
switch ($token['name']) {
case 'tAdd': // addition
case 'tConcat': // addition
case 'tDiv': // division
case 'tEQ': // equality
case 'tGE': // greater than or equal
case 'tGT': // greater than
case 'tIsect': // intersection
case 'tLE': // less than or equal
case 'tList': // less than or equal
case 'tLT': // less than
case 'tMul': // multiplication
case 'tNE': // multiplication
case 'tPower': // power
case 'tRange': // range
case 'tSub': // subtraction
$op2 = array_pop($formulaStrings);
$op1 = array_pop($formulaStrings);
$formulaStrings[] = "$op1$space1$space0{$token['data']}$op2";
unset($space0, $space1);
break;
case 'tUplus': // unary plus
case 'tUminus': // unary minus
$op = array_pop($formulaStrings);
$formulaStrings[] = "$space1$space0{$token['data']}$op";
unset($space0, $space1);
break;
case 'tPercent': // percent sign
$op = array_pop($formulaStrings);
$formulaStrings[] = "$op$space1$space0{$token['data']}";
unset($space0, $space1);
break;
case 'tAttrVolatile': // indicates volatile function
case 'tAttrIf':
case 'tAttrSkip':
case 'tAttrChoose':
// token is only important for Excel formula evaluator
// do nothing
break;
case 'tAttrSpace': // space / carriage return
// space will be used when next token arrives, do not alter formulaString stack
switch ($token['data']['spacetype']) {
case 'type0':
$space0 = str_repeat(' ', $token['data']['spacecount']);
break;
case 'type1':
$space1 = str_repeat("\n", $token['data']['spacecount']);
break;
case 'type2':
$space2 = str_repeat(' ', $token['data']['spacecount']);
break;
case 'type3':
$space3 = str_repeat("\n", $token['data']['spacecount']);
break;
case 'type4':
$space4 = str_repeat(' ', $token['data']['spacecount']);
break;
case 'type5':
$space5 = str_repeat("\n", $token['data']['spacecount']);
break;
}
break;
case 'tAttrSum': // SUM function with one parameter
$op = array_pop($formulaStrings);
$formulaStrings[] = "{$space1}{$space0}SUM($op)";
unset($space0, $space1);
break;
case 'tFunc': // function with fixed number of arguments
case 'tFuncV': // function with variable number of arguments
if ($token['data']['function'] != '') {
// normal function
$ops = []; // array of operators
for ($i = 0; $i < $token['data']['args']; ++$i) {
$ops[] = array_pop($formulaStrings);
}
$ops = array_reverse($ops);
$formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ')';
unset($space0, $space1);
} else {
// add-in function
$ops = []; // array of operators
for ($i = 0; $i < $token['data']['args'] - 1; ++$i) {
$ops[] = array_pop($formulaStrings);
}
$ops = array_reverse($ops);
$function = array_pop($formulaStrings);
$formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ')';
unset($space0, $space1);
}
break;
case 'tParen': // parenthesis
$expression = array_pop($formulaStrings);
$formulaStrings[] = "$space3$space2($expression$space5$space4)";
unset($space2, $space3, $space4, $space5);
break;
case 'tArray': // array constant
$constantArray = self::readBIFF8ConstantArray($additionalData);
$formulaStrings[] = $space1 . $space0 . $constantArray['value'];
$additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data
unset($space0, $space1);
break;
case 'tMemArea':
// bite off chunk of additional data
$cellRangeAddressList = $this->readBIFF8CellRangeAddressList($additionalData);
$additionalData = substr($additionalData, $cellRangeAddressList['size']);
$formulaStrings[] = "$space1$space0{$token['data']}";
unset($space0, $space1);
break;
case 'tArea': // cell range address
case 'tBool': // boolean
case 'tErr': // error code
case 'tInt': // integer
case 'tMemErr':
case 'tMemFunc':
case 'tMissArg':
case 'tName':
case 'tNameX':
case 'tNum': // number
case 'tRef': // single cell reference
case 'tRef3d': // 3d cell reference
case 'tArea3d': // 3d cell range reference
case 'tRefN':
case 'tAreaN':
case 'tStr': // string
$formulaStrings[] = "$space1$space0{$token['data']}";
unset($space0, $space1);
break;
}
}
$formulaString = $formulaStrings[0];
return $formulaString;
}
/**
* Fetch next token from binary formula data.
*
* @param string $formulaData Formula data
* @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
*
* @return array
*/
private function getNextToken($formulaData, $baseCell = 'A1')
{
// offset: 0; size: 1; token id
$id = ord($formulaData[0]); // token id
$name = false; // initialize token name
switch ($id) {
case 0x03:
$name = 'tAdd';
$size = 1;
$data = '+';
break;
case 0x04:
$name = 'tSub';
$size = 1;
$data = '-';
break;
case 0x05:
$name = 'tMul';
$size = 1;
$data = '*';
break;
case 0x06:
$name = 'tDiv';
$size = 1;
$data = '/';
break;
case 0x07:
$name = 'tPower';
$size = 1;
$data = '^';
break;
case 0x08:
$name = 'tConcat';
$size = 1;
$data = '&';
break;
case 0x09:
$name = 'tLT';
$size = 1;
$data = '<';
break;
case 0x0A:
$name = 'tLE';
$size = 1;
$data = '<=';
break;
case 0x0B:
$name = 'tEQ';
$size = 1;
$data = '=';
break;
case 0x0C:
$name = 'tGE';
$size = 1;
$data = '>=';
break;
case 0x0D:
$name = 'tGT';
$size = 1;
$data = '>';
break;
case 0x0E:
$name = 'tNE';
$size = 1;
$data = '<>';
break;
case 0x0F:
$name = 'tIsect';
$size = 1;
$data = ' ';
break;
case 0x10:
$name = 'tList';
$size = 1;
$data = ',';
break;
case 0x11:
$name = 'tRange';
$size = 1;
$data = ':';
break;
case 0x12:
$name = 'tUplus';
$size = 1;
$data = '+';
break;
case 0x13:
$name = 'tUminus';
$size = 1;
$data = '-';
break;
case 0x14:
$name = 'tPercent';
$size = 1;
$data = '%';
break;
case 0x15: // parenthesis
$name = 'tParen';
$size = 1;
$data = null;
break;
case 0x16: // missing argument
$name = 'tMissArg';
$size = 1;
$data = '';
break;
case 0x17: // string
$name = 'tStr';
// offset: 1; size: var; Unicode string, 8-bit string length
$string = self::readUnicodeStringShort(substr($formulaData, 1));
$size = 1 + $string['size'];
$data = self::UTF8toExcelDoubleQuoted($string['value']);
break;
case 0x19: // Special attribute
// offset: 1; size: 1; attribute type flags:
switch (ord($formulaData[1])) {
case 0x01:
$name = 'tAttrVolatile';
$size = 4;
$data = null;
break;
case 0x02:
$name = 'tAttrIf';
$size = 4;
$data = null;
break;
case 0x04:
$name = 'tAttrChoose';
// offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1)
$nc = self::getUInt2d($formulaData, 2);
// offset: 4; size: 2 * $nc
// offset: 4 + 2 * $nc; size: 2
$size = 2 * $nc + 6;
$data = null;
break;
case 0x08:
$name = 'tAttrSkip';
$size = 4;
$data = null;
break;
case 0x10:
$name = 'tAttrSum';
$size = 4;
$data = null;
break;
case 0x40:
case 0x41:
$name = 'tAttrSpace';
$size = 4;
// offset: 2; size: 2; space type and position
switch (ord($formulaData[2])) {
case 0x00:
$spacetype = 'type0';
break;
case 0x01:
$spacetype = 'type1';
break;
case 0x02:
$spacetype = 'type2';
break;
case 0x03:
$spacetype = 'type3';
break;
case 0x04:
$spacetype = 'type4';
break;
case 0x05:
$spacetype = 'type5';
break;
default:
throw new Exception('Unrecognized space type in tAttrSpace token');
}
// offset: 3; size: 1; number of inserted spaces/carriage returns
$spacecount = ord($formulaData[3]);
$data = ['spacetype' => $spacetype, 'spacecount' => $spacecount];
break;
default:
throw new Exception('Unrecognized attribute flag in tAttr token');
}
break;
case 0x1C: // error code
// offset: 1; size: 1; error code
$name = 'tErr';
$size = 2;
$data = Xls\ErrorCode::lookup(ord($formulaData[1]));
break;
case 0x1D: // boolean
// offset: 1; size: 1; 0 = false, 1 = true;
$name = 'tBool';
$size = 2;
$data = ord($formulaData[1]) ? 'TRUE' : 'FALSE';
break;
case 0x1E: // integer
// offset: 1; size: 2; unsigned 16-bit integer
$name = 'tInt';
$size = 3;
$data = self::getUInt2d($formulaData, 1);
break;
case 0x1F: // number
// offset: 1; size: 8;
$name = 'tNum';
$size = 9;
$data = self::extractNumber(substr($formulaData, 1));
$data = str_replace(',', '.', (string) $data); // in case non-English locale
break;
case 0x20: // array constant
case 0x40:
case 0x60:
// offset: 1; size: 7; not used
$name = 'tArray';
$size = 8;
$data = null;
break;
case 0x21: // function with fixed number of arguments
case 0x41:
case 0x61:
$name = 'tFunc';
$size = 3;
// offset: 1; size: 2; index to built-in sheet function
switch (self::getUInt2d($formulaData, 1)) {
case 2:
$function = 'ISNA';
$args = 1;
break;
case 3:
$function = 'ISERROR';
$args = 1;
break;
case 10:
$function = 'NA';
$args = 0;
break;
case 15:
$function = 'SIN';
$args = 1;
break;
case 16:
$function = 'COS';
$args = 1;
break;
case 17:
$function = 'TAN';
$args = 1;
break;
case 18:
$function = 'ATAN';
$args = 1;
break;
case 19:
$function = 'PI';
$args = 0;
break;
case 20:
$function = 'SQRT';
$args = 1;
break;
case 21:
$function = 'EXP';
$args = 1;
break;
case 22:
$function = 'LN';
$args = 1;
break;
case 23:
$function = 'LOG10';
$args = 1;
break;
case 24:
$function = 'ABS';
$args = 1;
break;
case 25:
$function = 'INT';
$args = 1;
break;
case 26:
$function = 'SIGN';
$args = 1;
break;
case 27:
$function = 'ROUND';
$args = 2;
break;
case 30:
$function = 'REPT';
$args = 2;
break;
case 31:
$function = 'MID';
$args = 3;
break;
case 32:
$function = 'LEN';
$args = 1;
break;
case 33:
$function = 'VALUE';
$args = 1;
break;
case 34:
$function = 'TRUE';
$args = 0;
break;
case 35:
$function = 'FALSE';
$args = 0;
break;
case 38:
$function = 'NOT';
$args = 1;
break;
case 39:
$function = 'MOD';
$args = 2;
break;
case 40:
$function = 'DCOUNT';
$args = 3;
break;
case 41:
$function = 'DSUM';
$args = 3;
break;
case 42:
$function = 'DAVERAGE';
$args = 3;
break;
case 43:
$function = 'DMIN';
$args = 3;
break;
case 44:
$function = 'DMAX';
$args = 3;
break;
case 45:
$function = 'DSTDEV';
$args = 3;
break;
case 48:
$function = 'TEXT';
$args = 2;
break;
case 61:
$function = 'MIRR';
$args = 3;
break;
case 63:
$function = 'RAND';
$args = 0;
break;
case 65:
$function = 'DATE';
$args = 3;
break;
case 66:
$function = 'TIME';
$args = 3;
break;
case 67:
$function = 'DAY';
$args = 1;
break;
case 68:
$function = 'MONTH';
$args = 1;
break;
case 69:
$function = 'YEAR';
$args = 1;
break;
case 71:
$function = 'HOUR';
$args = 1;
break;
case 72:
$function = 'MINUTE';
$args = 1;
break;
case 73:
$function = 'SECOND';
$args = 1;
break;
case 74:
$function = 'NOW';
$args = 0;
break;
case 75:
$function = 'AREAS';
$args = 1;
break;
case 76:
$function = 'ROWS';
$args = 1;
break;
case 77:
$function = 'COLUMNS';
$args = 1;
break;
case 83:
$function = 'TRANSPOSE';
$args = 1;
break;
case 86:
$function = 'TYPE';
$args = 1;
break;
case 97:
$function = 'ATAN2';
$args = 2;
break;
case 98:
$function = 'ASIN';
$args = 1;
break;
case 99:
$function = 'ACOS';
$args = 1;
break;
case 105:
$function = 'ISREF';
$args = 1;
break;
case 111:
$function = 'CHAR';
$args = 1;
break;
case 112:
$function = 'LOWER';
$args = 1;
break;
case 113:
$function = 'UPPER';
$args = 1;
break;
case 114:
$function = 'PROPER';
$args = 1;
break;
case 117:
$function = 'EXACT';
$args = 2;
break;
case 118:
$function = 'TRIM';
$args = 1;
break;
case 119:
$function = 'REPLACE';
$args = 4;
break;
case 121:
$function = 'CODE';
$args = 1;
break;
case 126:
$function = 'ISERR';
$args = 1;
break;
case 127:
$function = 'ISTEXT';
$args = 1;
break;
case 128:
$function = 'ISNUMBER';
$args = 1;
break;
case 129:
$function = 'ISBLANK';
$args = 1;
break;
case 130:
$function = 'T';
$args = 1;
break;
case 131:
$function = 'N';
$args = 1;
break;
case 140:
$function = 'DATEVALUE';
$args = 1;
break;
case 141:
$function = 'TIMEVALUE';
$args = 1;
break;
case 142:
$function = 'SLN';
$args = 3;
break;
case 143:
$function = 'SYD';
$args = 4;
break;
case 162:
$function = 'CLEAN';
$args = 1;
break;
case 163:
$function = 'MDETERM';
$args = 1;
break;
case 164:
$function = 'MINVERSE';
$args = 1;
break;
case 165:
$function = 'MMULT';
$args = 2;
break;
case 184:
$function = 'FACT';
$args = 1;
break;
case 189:
$function = 'DPRODUCT';
$args = 3;
break;
case 190:
$function = 'ISNONTEXT';
$args = 1;
break;
case 195:
$function = 'DSTDEVP';
$args = 3;
break;
case 196:
$function = 'DVARP';
$args = 3;
break;
case 198:
$function = 'ISLOGICAL';
$args = 1;
break;
case 199:
$function = 'DCOUNTA';
$args = 3;
break;
case 207:
$function = 'REPLACEB';
$args = 4;
break;
case 210:
$function = 'MIDB';
$args = 3;
break;
case 211:
$function = 'LENB';
$args = 1;
break;
case 212:
$function = 'ROUNDUP';
$args = 2;
break;
case 213:
$function = 'ROUNDDOWN';
$args = 2;
break;
case 214:
$function = 'ASC';
$args = 1;
break;
case 215:
$function = 'DBCS';
$args = 1;
break;
case 221:
$function = 'TODAY';
$args = 0;
break;
case 229:
$function = 'SINH';
$args = 1;
break;
case 230:
$function = 'COSH';
$args = 1;
break;
case 231:
$function = 'TANH';
$args = 1;
break;
case 232:
$function = 'ASINH';
$args = 1;
break;
case 233:
$function = 'ACOSH';
$args = 1;
break;
case 234:
$function = 'ATANH';
$args = 1;
break;
case 235:
$function = 'DGET';
$args = 3;
break;
case 244:
$function = 'INFO';
$args = 1;
break;
case 252:
$function = 'FREQUENCY';
$args = 2;
break;
case 261:
$function = 'ERROR.TYPE';
$args = 1;
break;
case 271:
$function = 'GAMMALN';
$args = 1;
break;
case 273:
$function = 'BINOMDIST';
$args = 4;
break;
case 274:
$function = 'CHIDIST';
$args = 2;
break;
case 275:
$function = 'CHIINV';
$args = 2;
break;
case 276:
$function = 'COMBIN';
$args = 2;
break;
case 277:
$function = 'CONFIDENCE';
$args = 3;
break;
case 278:
$function = 'CRITBINOM';
$args = 3;
break;
case 279:
$function = 'EVEN';
$args = 1;
break;
case 280:
$function = 'EXPONDIST';
$args = 3;
break;
case 281:
$function = 'FDIST';
$args = 3;
break;
case 282:
$function = 'FINV';
$args = 3;
break;
case 283:
$function = 'FISHER';
$args = 1;
break;
case 284:
$function = 'FISHERINV';
$args = 1;
break;
case 285:
$function = 'FLOOR';
$args = 2;
break;
case 286:
$function = 'GAMMADIST';
$args = 4;
break;
case 287:
$function = 'GAMMAINV';
$args = 3;
break;
case 288:
$function = 'CEILING';
$args = 2;
break;
case 289:
$function = 'HYPGEOMDIST';
$args = 4;
break;
case 290:
$function = 'LOGNORMDIST';
$args = 3;
break;
case 291:
$function = 'LOGINV';
$args = 3;
break;
case 292:
$function = 'NEGBINOMDIST';
$args = 3;
break;
case 293:
$function = 'NORMDIST';
$args = 4;
break;
case 294:
$function = 'NORMSDIST';
$args = 1;
break;
case 295:
$function = 'NORMINV';
$args = 3;
break;
case 296:
$function = 'NORMSINV';
$args = 1;
break;
case 297:
$function = 'STANDARDIZE';
$args = 3;
break;
case 298:
$function = 'ODD';
$args = 1;
break;
case 299:
$function = 'PERMUT';
$args = 2;
break;
case 300:
$function = 'POISSON';
$args = 3;
break;
case 301:
$function = 'TDIST';
$args = 3;
break;
case 302:
$function = 'WEIBULL';
$args = 4;
break;
case 303:
$function = 'SUMXMY2';
$args = 2;
break;
case 304:
$function = 'SUMX2MY2';
$args = 2;
break;
case 305:
$function = 'SUMX2PY2';
$args = 2;
break;
case 306:
$function = 'CHITEST';
$args = 2;
break;
case 307:
$function = 'CORREL';
$args = 2;
break;
case 308:
$function = 'COVAR';
$args = 2;
break;
case 309:
$function = 'FORECAST';
$args = 3;
break;
case 310:
$function = 'FTEST';
$args = 2;
break;
case 311:
$function = 'INTERCEPT';
$args = 2;
break;
case 312:
$function = 'PEARSON';
$args = 2;
break;
case 313:
$function = 'RSQ';
$args = 2;
break;
case 314:
$function = 'STEYX';
$args = 2;
break;
case 315:
$function = 'SLOPE';
$args = 2;
break;
case 316:
$function = 'TTEST';
$args = 4;
break;
case 325:
$function = 'LARGE';
$args = 2;
break;
case 326:
$function = 'SMALL';
$args = 2;
break;
case 327:
$function = 'QUARTILE';
$args = 2;
break;
case 328:
$function = 'PERCENTILE';
$args = 2;
break;
case 331:
$function = 'TRIMMEAN';
$args = 2;
break;
case 332:
$function = 'TINV';
$args = 2;
break;
case 337:
$function = 'POWER';
$args = 2;
break;
case 342:
$function = 'RADIANS';
$args = 1;
break;
case 343:
$function = 'DEGREES';
$args = 1;
break;
case 346:
$function = 'COUNTIF';
$args = 2;
break;
case 347:
$function = 'COUNTBLANK';
$args = 1;
break;
case 350:
$function = 'ISPMT';
$args = 4;
break;
case 351:
$function = 'DATEDIF';
$args = 3;
break;
case 352:
$function = 'DATESTRING';
$args = 1;
break;
case 353:
$function = 'NUMBERSTRING';
$args = 2;
break;
case 360:
$function = 'PHONETIC';
$args = 1;
break;
case 368:
$function = 'BAHTTEXT';
$args = 1;
break;
default:
throw new Exception('Unrecognized function in formula');
}
$data = ['function' => $function, 'args' => $args];
break;
case 0x22: // function with variable number of arguments
case 0x42:
case 0x62:
$name = 'tFuncV';
$size = 4;
// offset: 1; size: 1; number of arguments
$args = ord($formulaData[1]);
// offset: 2: size: 2; index to built-in sheet function
$index = self::getUInt2d($formulaData, 2);
switch ($index) {
case 0:
$function = 'COUNT';
break;
case 1:
$function = 'IF';
break;
case 4:
$function = 'SUM';
break;
case 5:
$function = 'AVERAGE';
break;
case 6:
$function = 'MIN';
break;
case 7:
$function = 'MAX';
break;
case 8:
$function = 'ROW';
break;
case 9:
$function = 'COLUMN';
break;
case 11:
$function = 'NPV';
break;
case 12:
$function = 'STDEV';
break;
case 13:
$function = 'DOLLAR';
break;
case 14:
$function = 'FIXED';
break;
case 28:
$function = 'LOOKUP';
break;
case 29:
$function = 'INDEX';
break;
case 36:
$function = 'AND';
break;
case 37:
$function = 'OR';
break;
case 46:
$function = 'VAR';
break;
case 49:
$function = 'LINEST';
break;
case 50:
$function = 'TREND';
break;
case 51:
$function = 'LOGEST';
break;
case 52:
$function = 'GROWTH';
break;
case 56:
$function = 'PV';
break;
case 57:
$function = 'FV';
break;
case 58:
$function = 'NPER';
break;
case 59:
$function = 'PMT';
break;
case 60:
$function = 'RATE';
break;
case 62:
$function = 'IRR';
break;
case 64:
$function = 'MATCH';
break;
case 70:
$function = 'WEEKDAY';
break;
case 78:
$function = 'OFFSET';
break;
case 82:
$function = 'SEARCH';
break;
case 100:
$function = 'CHOOSE';
break;
case 101:
$function = 'HLOOKUP';
break;
case 102:
$function = 'VLOOKUP';
break;
case 109:
$function = 'LOG';
break;
case 115:
$function = 'LEFT';
break;
case 116:
$function = 'RIGHT';
break;
case 120:
$function = 'SUBSTITUTE';
break;
case 124:
$function = 'FIND';
break;
case 125:
$function = 'CELL';
break;
case 144:
$function = 'DDB';
break;
case 148:
$function = 'INDIRECT';
break;
case 167:
$function = 'IPMT';
break;
case 168:
$function = 'PPMT';
break;
case 169:
$function = 'COUNTA';
break;
case 183:
$function = 'PRODUCT';
break;
case 193:
$function = 'STDEVP';
break;
case 194:
$function = 'VARP';
break;
case 197:
$function = 'TRUNC';
break;
case 204:
$function = 'USDOLLAR';
break;
case 205:
$function = 'FINDB';
break;
case 206:
$function = 'SEARCHB';
break;
case 208:
$function = 'LEFTB';
break;
case 209:
$function = 'RIGHTB';
break;
case 216:
$function = 'RANK';
break;
case 219:
$function = 'ADDRESS';
break;
case 220:
$function = 'DAYS360';
break;
case 222:
$function = 'VDB';
break;
case 227:
$function = 'MEDIAN';
break;
case 228:
$function = 'SUMPRODUCT';
break;
case 247:
$function = 'DB';
break;
case 255:
$function = '';
break;
case 269:
$function = 'AVEDEV';
break;
case 270:
$function = 'BETADIST';
break;
case 272:
$function = 'BETAINV';
break;
case 317:
$function = 'PROB';
break;
case 318:
$function = 'DEVSQ';
break;
case 319:
$function = 'GEOMEAN';
break;
case 320:
$function = 'HARMEAN';
break;
case 321:
$function = 'SUMSQ';
break;
case 322:
$function = 'KURT';
break;
case 323:
$function = 'SKEW';
break;
case 324:
$function = 'ZTEST';
break;
case 329:
$function = 'PERCENTRANK';
break;
case 330:
$function = 'MODE';
break;
case 336:
$function = 'CONCATENATE';
break;
case 344:
$function = 'SUBTOTAL';
break;
case 345:
$function = 'SUMIF';
break;
case 354:
$function = 'ROMAN';
break;
case 358:
$function = 'GETPIVOTDATA';
break;
case 359:
$function = 'HYPERLINK';
break;
case 361:
$function = 'AVERAGEA';
break;
case 362:
$function = 'MAXA';
break;
case 363:
$function = 'MINA';
break;
case 364:
$function = 'STDEVPA';
break;
case 365:
$function = 'VARPA';
break;
case 366:
$function = 'STDEVA';
break;
case 367:
$function = 'VARA';
break;
default:
throw new Exception('Unrecognized function in formula');
}
$data = ['function' => $function, 'args' => $args];
break;
case 0x23: // index to defined name
case 0x43:
case 0x63:
$name = 'tName';
$size = 5;
// offset: 1; size: 2; one-based index to definedname record
$definedNameIndex = self::getUInt2d($formulaData, 1) - 1;
// offset: 2; size: 2; not used
$data = $this->definedname[$definedNameIndex]['name'] ?? '';
break;
case 0x24: // single cell reference e.g. A5
case 0x44:
case 0x64:
$name = 'tRef';
$size = 5;
$data = $this->readBIFF8CellAddress(substr($formulaData, 1, 4));
break;
case 0x25: // cell range reference to cells in the same sheet (2d)
case 0x45:
case 0x65:
$name = 'tArea';
$size = 9;
$data = $this->readBIFF8CellRangeAddress(substr($formulaData, 1, 8));
break;
case 0x26: // Constant reference sub-expression
case 0x46:
case 0x66:
$name = 'tMemArea';
// offset: 1; size: 4; not used
// offset: 5; size: 2; size of the following subexpression
$subSize = self::getUInt2d($formulaData, 5);
$size = 7 + $subSize;
$data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
break;
case 0x27: // Deleted constant reference sub-expression
case 0x47:
case 0x67:
$name = 'tMemErr';
// offset: 1; size: 4; not used
// offset: 5; size: 2; size of the following subexpression
$subSize = self::getUInt2d($formulaData, 5);
$size = 7 + $subSize;
$data = $this->getFormulaFromData(substr($formulaData, 7, $subSize));
break;
case 0x29: // Variable reference sub-expression
case 0x49:
case 0x69:
$name = 'tMemFunc';
// offset: 1; size: 2; size of the following sub-expression
$subSize = self::getUInt2d($formulaData, 1);
$size = 3 + $subSize;
$data = $this->getFormulaFromData(substr($formulaData, 3, $subSize));
break;
case 0x2C: // Relative 2d cell reference reference, used in shared formulas and some other places
case 0x4C:
case 0x6C:
$name = 'tRefN';
$size = 5;
$data = $this->readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell);
break;
case 0x2D: // Relative 2d range reference
case 0x4D:
case 0x6D:
$name = 'tAreaN';
$size = 9;
$data = $this->readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell);
break;
case 0x39: // External name
case 0x59:
case 0x79:
$name = 'tNameX';
$size = 7;
// offset: 1; size: 2; index to REF entry in EXTERNSHEET record
// offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record
$index = self::getUInt2d($formulaData, 3);
// assume index is to EXTERNNAME record
$data = $this->externalNames[$index - 1]['name'] ?? '';
// offset: 5; size: 2; not used
break;
case 0x3A: // 3d reference to cell
case 0x5A:
case 0x7A:
$name = 'tRef3d';
$size = 7;
try {
// offset: 1; size: 2; index to REF entry
$sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1));
// offset: 3; size: 4; cell address
$cellAddress = $this->readBIFF8CellAddress(substr($formulaData, 3, 4));
$data = "$sheetRange!$cellAddress";
} catch (PhpSpreadsheetException $e) {
// deleted sheet reference
$data = '#REF!';
}
break;
case 0x3B: // 3d reference to cell range
case 0x5B:
case 0x7B:
$name = 'tArea3d';
$size = 11;
try {
// offset: 1; size: 2; index to REF entry
$sheetRange = $this->readSheetRangeByRefIndex(self::getUInt2d($formulaData, 1));
// offset: 3; size: 8; cell address
$cellRangeAddress = $this->readBIFF8CellRangeAddress(substr($formulaData, 3, 8));
$data = "$sheetRange!$cellRangeAddress";
} catch (PhpSpreadsheetException $e) {
// deleted sheet reference
$data = '#REF!';
}
break;
// Unknown cases // don't know how to deal with
default:
throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula');
}
return [
'id' => $id,
'name' => $name,
'size' => $size,
'data' => $data,
];
}
/**
* Reads a cell address in BIFF8 e.g. 'A2' or '$A$2'
* section 3.3.4.
*
* @param string $cellAddressStructure
*
* @return string
*/
private function readBIFF8CellAddress($cellAddressStructure)
{
// offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
$row = self::getUInt2d($cellAddressStructure, 0) + 1;
// offset: 2; size: 2; index to column or column offset + relative flags
// bit: 7-0; mask 0x00FF; column index
$column = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($cellAddressStructure, 2)) + 1);
// bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
$column = '$' . $column;
}
// bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
$row = '$' . $row;
}
return $column . $row;
}
/**
* Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column
* to indicate offsets from a base cell
* section 3.3.4.
*
* @param string $cellAddressStructure
* @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas
*
* @return string
*/
private function readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1')
{
[$baseCol, $baseRow] = Coordinate::coordinateFromString($baseCell);
$baseCol = Coordinate::columnIndexFromString($baseCol) - 1;
$baseRow = (int) $baseRow;
// offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767))
$rowIndex = self::getUInt2d($cellAddressStructure, 0);
$row = self::getUInt2d($cellAddressStructure, 0) + 1;
// bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
// offset: 2; size: 2; index to column or column offset + relative flags
// bit: 7-0; mask 0x00FF; column index
$colIndex = 0x00FF & self::getUInt2d($cellAddressStructure, 2);
$column = Coordinate::stringFromColumnIndex($colIndex + 1);
$column = '$' . $column;
} else {
// offset: 2; size: 2; index to column or column offset + relative flags
// bit: 7-0; mask 0x00FF; column index
$relativeColIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
$colIndex = $baseCol + $relativeColIndex;
$colIndex = ($colIndex < 256) ? $colIndex : $colIndex - 256;
$colIndex = ($colIndex >= 0) ? $colIndex : $colIndex + 256;
$column = Coordinate::stringFromColumnIndex($colIndex + 1);
}
// bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
$row = '$' . $row;
} else {
$rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
$row = $baseRow + $rowIndex;
}
return $column . $row;
}
/**
* Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1'
* always fixed range
* section 2.5.14.
*
* @param string $subData
*
* @return string
*/
private function readBIFF5CellRangeAddressFixed($subData)
{
// offset: 0; size: 2; index to first row
$fr = self::getUInt2d($subData, 0) + 1;
// offset: 2; size: 2; index to last row
$lr = self::getUInt2d($subData, 2) + 1;
// offset: 4; size: 1; index to first column
$fc = ord($subData[4]);
// offset: 5; size: 1; index to last column
$lc = ord($subData[5]);
// check values
if ($fr > $lr || $fc > $lc) {
throw new Exception('Not a cell range address');
}
// column index to letter
$fc = Coordinate::stringFromColumnIndex($fc + 1);
$lc = Coordinate::stringFromColumnIndex($lc + 1);
if ($fr == $lr && $fc == $lc) {
return "$fc$fr";
}
return "$fc$fr:$lc$lr";
}
/**
* Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1'
* always fixed range
* section 2.5.14.
*
* @param string $subData
*
* @return string
*/
private function readBIFF8CellRangeAddressFixed($subData)
{
// offset: 0; size: 2; index to first row
$fr = self::getUInt2d($subData, 0) + 1;
// offset: 2; size: 2; index to last row
$lr = self::getUInt2d($subData, 2) + 1;
// offset: 4; size: 2; index to first column
$fc = self::getUInt2d($subData, 4);
// offset: 6; size: 2; index to last column
$lc = self::getUInt2d($subData, 6);
// check values
if ($fr > $lr || $fc > $lc) {
throw new Exception('Not a cell range address');
}
// column index to letter
$fc = Coordinate::stringFromColumnIndex($fc + 1);
$lc = Coordinate::stringFromColumnIndex($lc + 1);
if ($fr == $lr && $fc == $lc) {
return "$fc$fr";
}
return "$fc$fr:$lc$lr";
}
/**
* Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6'
* there are flags indicating whether column/row index is relative
* section 3.3.4.
*
* @param string $subData
*
* @return string
*/
private function readBIFF8CellRangeAddress($subData)
{
// todo: if cell range is just a single cell, should this funciton
// not just return e.g. 'A1' and not 'A1:A1' ?
// offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767))
$fr = self::getUInt2d($subData, 0) + 1;
// offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767))
$lr = self::getUInt2d($subData, 2) + 1;
// offset: 4; size: 2; index to first column or column offset + relative flags
// bit: 7-0; mask 0x00FF; column index
$fc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 4)) + 1);
// bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
if (!(0x4000 & self::getUInt2d($subData, 4))) {
$fc = '$' . $fc;
}
// bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
if (!(0x8000 & self::getUInt2d($subData, 4))) {
$fr = '$' . $fr;
}
// offset: 6; size: 2; index to last column or column offset + relative flags
// bit: 7-0; mask 0x00FF; column index
$lc = Coordinate::stringFromColumnIndex((0x00FF & self::getUInt2d($subData, 6)) + 1);
// bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
if (!(0x4000 & self::getUInt2d($subData, 6))) {
$lc = '$' . $lc;
}
// bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
if (!(0x8000 & self::getUInt2d($subData, 6))) {
$lr = '$' . $lr;
}
return "$fc$fr:$lc$lr";
}
/**
* Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column
* to indicate offsets from a base cell
* section 3.3.4.
*
* @param string $subData
* @param string $baseCell Base cell
*
* @return string Cell range address
*/
private function readBIFF8CellRangeAddressB($subData, $baseCell = 'A1')
{
[$baseCol, $baseRow] = Coordinate::indexesFromString($baseCell);
$baseCol = $baseCol - 1;
// TODO: if cell range is just a single cell, should this funciton
// not just return e.g. 'A1' and not 'A1:A1' ?
// offset: 0; size: 2; first row
$frIndex = self::getUInt2d($subData, 0); // adjust below
// offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767)
$lrIndex = self::getUInt2d($subData, 2); // adjust below
// bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
if (!(0x4000 & self::getUInt2d($subData, 4))) {
// absolute column index
// offset: 4; size: 2; first column with relative/absolute flags
// bit: 7-0; mask 0x00FF; column index
$fcIndex = 0x00FF & self::getUInt2d($subData, 4);
$fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
$fc = '$' . $fc;
} else {
// column offset
// offset: 4; size: 2; first column with relative/absolute flags
// bit: 7-0; mask 0x00FF; column index
$relativeFcIndex = 0x00FF & self::getInt2d($subData, 4);
$fcIndex = $baseCol + $relativeFcIndex;
$fcIndex = ($fcIndex < 256) ? $fcIndex : $fcIndex - 256;
$fcIndex = ($fcIndex >= 0) ? $fcIndex : $fcIndex + 256;
$fc = Coordinate::stringFromColumnIndex($fcIndex + 1);
}
// bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
if (!(0x8000 & self::getUInt2d($subData, 4))) {
// absolute row index
$fr = $frIndex + 1;
$fr = '$' . $fr;
} else {
// row offset
$frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
$fr = $baseRow + $frIndex;
}
// bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index)
if (!(0x4000 & self::getUInt2d($subData, 6))) {
// absolute column index
// offset: 6; size: 2; last column with relative/absolute flags
// bit: 7-0; mask 0x00FF; column index
$lcIndex = 0x00FF & self::getUInt2d($subData, 6);
$lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
$lc = '$' . $lc;
} else {
// column offset
// offset: 4; size: 2; first column with relative/absolute flags
// bit: 7-0; mask 0x00FF; column index
$relativeLcIndex = 0x00FF & self::getInt2d($subData, 4);
$lcIndex = $baseCol + $relativeLcIndex;
$lcIndex = ($lcIndex < 256) ? $lcIndex : $lcIndex - 256;
$lcIndex = ($lcIndex >= 0) ? $lcIndex : $lcIndex + 256;
$lc = Coordinate::stringFromColumnIndex($lcIndex + 1);
}
// bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index)
if (!(0x8000 & self::getUInt2d($subData, 6))) {
// absolute row index
$lr = $lrIndex + 1;
$lr = '$' . $lr;
} else {
// row offset
$lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
$lr = $baseRow + $lrIndex;
}
return "$fc$fr:$lc$lr";
}
/**
* Read BIFF8 cell range address list
* section 2.5.15.
*
* @param string $subData
*
* @return array
*/
private function readBIFF8CellRangeAddressList($subData)
{
$cellRangeAddresses = [];
// offset: 0; size: 2; number of the following cell range addresses
$nm = self::getUInt2d($subData, 0);
$offset = 2;
// offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses
for ($i = 0; $i < $nm; ++$i) {
$cellRangeAddresses[] = $this->readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8));
$offset += 8;
}
return [
'size' => 2 + 8 * $nm,
'cellRangeAddresses' => $cellRangeAddresses,
];
}
/**
* Read BIFF5 cell range address list
* section 2.5.15.
*
* @param string $subData
*
* @return array
*/
private function readBIFF5CellRangeAddressList($subData)
{
$cellRangeAddresses = [];
// offset: 0; size: 2; number of the following cell range addresses
$nm = self::getUInt2d($subData, 0);
$offset = 2;
// offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses
for ($i = 0; $i < $nm; ++$i) {
$cellRangeAddresses[] = $this->readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6));
$offset += 6;
}
return [
'size' => 2 + 6 * $nm,
'cellRangeAddresses' => $cellRangeAddresses,
];
}
/**
* Get a sheet range like Sheet1:Sheet3 from REF index
* Note: If there is only one sheet in the range, one gets e.g Sheet1
* It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets,
* in which case an Exception is thrown.
*
* @param int $index
*
* @return false|string
*/
private function readSheetRangeByRefIndex($index)
{
if (isset($this->ref[$index])) {
$type = $this->externalBooks[$this->ref[$index]['externalBookIndex']]['type'];
switch ($type) {
case 'internal':
// check if we have a deleted 3d reference
if ($this->ref[$index]['firstSheetIndex'] == 0xFFFF || $this->ref[$index]['lastSheetIndex'] == 0xFFFF) {
throw new Exception('Deleted sheet reference');
}
// we have normal sheet range (collapsed or uncollapsed)
$firstSheetName = $this->sheets[$this->ref[$index]['firstSheetIndex']]['name'];
$lastSheetName = $this->sheets[$this->ref[$index]['lastSheetIndex']]['name'];
if ($firstSheetName == $lastSheetName) {
// collapsed sheet range
$sheetRange = $firstSheetName;
} else {
$sheetRange = "$firstSheetName:$lastSheetName";
}
// escape the single-quotes
$sheetRange = str_replace("'", "''", $sheetRange);
// if there are special characters, we need to enclose the range in single-quotes
// todo: check if we have identified the whole set of special characters
// it seems that the following characters are not accepted for sheet names
// and we may assume that they are not present: []*/:\?
if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/u", $sheetRange)) {
$sheetRange = "'$sheetRange'";
}
return $sheetRange;
default:
// TODO: external sheet support
throw new Exception('Xls reader only supports internal sheets in formulas');
}
}
return false;
}
/**
* read BIFF8 constant value array from array data
* returns e.g. ['value' => '{1,2;3,4}', 'size' => 40]
* section 2.5.8.
*
* @param string $arrayData
*
* @return array
*/
private static function readBIFF8ConstantArray($arrayData)
{
// offset: 0; size: 1; number of columns decreased by 1
$nc = ord($arrayData[0]);
// offset: 1; size: 2; number of rows decreased by 1
$nr = self::getUInt2d($arrayData, 1);
$size = 3; // initialize
$arrayData = substr($arrayData, 3);
// offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values
$matrixChunks = [];
for ($r = 1; $r <= $nr + 1; ++$r) {
$items = [];
for ($c = 1; $c <= $nc + 1; ++$c) {
$constant = self::readBIFF8Constant($arrayData);
$items[] = $constant['value'];
$arrayData = substr($arrayData, $constant['size']);
$size += $constant['size'];
}
$matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"'
}
$matrix = '{' . implode(';', $matrixChunks) . '}';
return [
'value' => $matrix,
'size' => $size,
];
}
/**
* read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value'
* section 2.5.7
* returns e.g. ['value' => '5', 'size' => 9].
*
* @param string $valueData
*
* @return array
*/
private static function readBIFF8Constant($valueData)
{
// offset: 0; size: 1; identifier for type of constant
$identifier = ord($valueData[0]);
switch ($identifier) {
case 0x00: // empty constant (what is this?)
$value = '';
$size = 9;
break;
case 0x01: // number
// offset: 1; size: 8; IEEE 754 floating-point value
$value = self::extractNumber(substr($valueData, 1, 8));
$size = 9;
break;
case 0x02: // string value
// offset: 1; size: var; Unicode string, 16-bit string length
$string = self::readUnicodeStringLong(substr($valueData, 1));
$value = '"' . $string['value'] . '"';
$size = 1 + $string['size'];
break;
case 0x04: // boolean
// offset: 1; size: 1; 0 = FALSE, 1 = TRUE
if (ord($valueData[1])) {
$value = 'TRUE';
} else {
$value = 'FALSE';
}
$size = 9;
break;
case 0x10: // error code
// offset: 1; size: 1; error code
$value = Xls\ErrorCode::lookup(ord($valueData[1]));
$size = 9;
break;
default:
throw new PhpSpreadsheetException('Unsupported BIFF8 constant');
}
return [
'value' => $value,
'size' => $size,
];
}
/**
* Extract RGB color
* OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4.
*
* @param string $rgb Encoded RGB value (4 bytes)
*
* @return array
*/
private static function readRGB($rgb)
{
// offset: 0; size 1; Red component
$r = ord($rgb[0]);
// offset: 1; size: 1; Green component
$g = ord($rgb[1]);
// offset: 2; size: 1; Blue component
$b = ord($rgb[2]);
// HEX notation, e.g. 'FF00FC'
$rgb = sprintf('%02X%02X%02X', $r, $g, $b);
return ['rgb' => $rgb];
}
/**
* Read byte string (8-bit string length)
* OpenOffice documentation: 2.5.2.
*
* @param string $subData
*
* @return array
*/
private function readByteStringShort($subData)
{
// offset: 0; size: 1; length of the string (character count)
$ln = ord($subData[0]);
// offset: 1: size: var; character array (8-bit characters)
$value = $this->decodeCodepage(substr($subData, 1, $ln));
return [
'value' => $value,
'size' => 1 + $ln, // size in bytes of data structure
];
}
/**
* Read byte string (16-bit string length)
* OpenOffice documentation: 2.5.2.
*
* @param string $subData
*
* @return array
*/
private function readByteStringLong($subData)
{
// offset: 0; size: 2; length of the string (character count)
$ln = self::getUInt2d($subData, 0);
// offset: 2: size: var; character array (8-bit characters)
$value = $this->decodeCodepage(substr($subData, 2));
//return $string;
return [
'value' => $value,
'size' => 2 + $ln, // size in bytes of data structure
];
}
/**
* Extracts an Excel Unicode short string (8-bit string length)
* OpenOffice documentation: 2.5.3
* function will automatically find out where the Unicode string ends.
*
* @param string $subData
*
* @return array
*/
private static function readUnicodeStringShort($subData)
{
$value = '';
// offset: 0: size: 1; length of the string (character count)
$characterCount = ord($subData[0]);
$string = self::readUnicodeString(substr($subData, 1), $characterCount);
// add 1 for the string length
++$string['size'];
return $string;
}
/**
* Extracts an Excel Unicode long string (16-bit string length)
* OpenOffice documentation: 2.5.3
* this function is under construction, needs to support rich text, and Asian phonetic settings.
*
* @param string $subData
*
* @return array
*/
private static function readUnicodeStringLong($subData)
{
$value = '';
// offset: 0: size: 2; length of the string (character count)
$characterCount = self::getUInt2d($subData, 0);
$string = self::readUnicodeString(substr($subData, 2), $characterCount);
// add 2 for the string length
$string['size'] += 2;
return $string;
}
/**
* Read Unicode string with no string length field, but with known character count
* this function is under construction, needs to support rich text, and Asian phonetic settings
* OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3.
*
* @param string $subData
* @param int $characterCount
*
* @return array
*/
private static function readUnicodeString($subData, $characterCount)
{
$value = '';
// offset: 0: size: 1; option flags
// bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit)
$isCompressed = !((0x01 & ord($subData[0])) >> 0);
// bit: 2; mask: 0x04; Asian phonetic settings
$hasAsian = (0x04) & ord($subData[0]) >> 2;
// bit: 3; mask: 0x08; Rich-Text settings
$hasRichText = (0x08) & ord($subData[0]) >> 3;
// offset: 1: size: var; character array
// this offset assumes richtext and Asian phonetic settings are off which is generally wrong
// needs to be fixed
$value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
return [
'value' => $value,
'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags
];
}
/**
* Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas.
* Example: hello"world --> "hello""world".
*
* @param string $value UTF-8 encoded string
*
* @return string
*/
private static function UTF8toExcelDoubleQuoted($value)
{
return '"' . str_replace('"', '""', $value) . '"';
}
/**
* Reads first 8 bytes of a string and return IEEE 754 float.
*
* @param string $data Binary string that is at least 8 bytes long
*
* @return float
*/
private static function extractNumber($data)
{
$rknumhigh = self::getInt4d($data, 4);
$rknumlow = self::getInt4d($data, 0);
$sign = ($rknumhigh & (int) 0x80000000) >> 31;
$exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
$mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
$mantissalow1 = ($rknumlow & (int) 0x80000000) >> 31;
$mantissalow2 = ($rknumlow & 0x7fffffff);
$value = $mantissa / 2 ** (20 - $exp);
if ($mantissalow1 != 0) {
$value += 1 / 2 ** (21 - $exp);
}
$value += $mantissalow2 / 2 ** (52 - $exp);
if ($sign) {
$value *= -1;
}
return $value;
}
/**
* @param int $rknum
*
* @return float
*/
private static function getIEEE754($rknum)
{
if (($rknum & 0x02) != 0) {
$value = $rknum >> 2;
} else {
// changes by mmp, info on IEEE754 encoding from
// research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
// The RK format calls for using only the most significant 30 bits
// of the 64 bit floating point value. The other 34 bits are assumed
// to be 0 so we use the upper 30 bits of $rknum as follows...
$sign = ($rknum & (int) 0x80000000) >> 31;
$exp = ($rknum & 0x7ff00000) >> 20;
$mantissa = (0x100000 | ($rknum & 0x000ffffc));
$value = $mantissa / 2 ** (20 - ($exp - 1023));
if ($sign) {
$value = -1 * $value;
}
//end of changes by mmp
}
if (($rknum & 0x01) != 0) {
$value /= 100;
}
return $value;
}
/**
* Get UTF-8 string from (compressed or uncompressed) UTF-16 string.
*
* @param string $string
* @param bool $compressed
*
* @return string
*/
private static function encodeUTF16($string, $compressed = false)
{
if ($compressed) {
$string = self::uncompressByteString($string);
}
return StringHelper::convertEncoding($string, 'UTF-8', 'UTF-16LE');
}
/**
* Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8.
*
* @param string $string
*
* @return string
*/
private static function uncompressByteString($string)
{
$uncompressedString = '';
$strLen = strlen($string);
for ($i = 0; $i < $strLen; ++$i) {
$uncompressedString .= $string[$i] . "\0";
}
return $uncompressedString;
}
/**
* Convert string to UTF-8. Only used for BIFF5.
*
* @param string $string
*
* @return string
*/
private function decodeCodepage($string)
{
return StringHelper::convertEncoding($string, 'UTF-8', $this->codepage);
}
/**
* Read 16-bit unsigned integer.
*
* @param string $data
* @param int $pos
*
* @return int
*/
public static function getUInt2d($data, $pos)
{
return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
}
/**
* Read 16-bit signed integer.
*
* @param string $data
* @param int $pos
*
* @return int
*/
public static function getInt2d($data, $pos)
{
return unpack('s', $data[$pos] . $data[$pos + 1])[1]; // @phpstan-ignore-line
}
/**
* Read 32-bit signed integer.
*
* @param string $data
* @param int $pos
*
* @return int
*/
public static function getInt4d($data, $pos)
{
// FIX: represent numbers correctly on 64-bit system
// http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
// Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
$_or_24 = ord($data[$pos + 3]);
if ($_or_24 >= 128) {
// negative number
$_ord_24 = -abs((256 - $_or_24) << 24);
} else {
$_ord_24 = ($_or_24 & 127) << 24;
}
return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
}
private function parseRichText(string $is): RichText
{
$value = new RichText();
$value->createText($is);
return $value;
}
/**
* Phpstan 1.4.4 complains that this property is never read.
* So, we might be able to get rid of it altogether.
* For now, however, this function makes it readable,
* which satisfies Phpstan.
*
* @codeCoverageIgnore
*/
public function getMapCellStyleXfIndex(): array
{
return $this->mapCellStyleXfIndex;
}
private function readCFHeader(): array
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer forward to next record
$this->pos += 4 + $length;
if ($this->readDataOnly) {
return [];
}
// offset: 0; size: 2; Rule Count
// $ruleCount = self::getUInt2d($recordData, 0);
// offset: var; size: var; cell range address list with
$cellRangeAddressList = ($this->version == self::XLS_BIFF8)
? $this->readBIFF8CellRangeAddressList(substr($recordData, 12))
: $this->readBIFF5CellRangeAddressList(substr($recordData, 12));
$cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
return $cellRangeAddresses;
}
private function readCFRule(array $cellRangeAddresses): void
{
$length = self::getUInt2d($this->data, $this->pos + 2);
$recordData = $this->readRecordData($this->data, $this->pos + 4, $length);
// move stream pointer forward to next record
$this->pos += 4 + $length;
if ($this->readDataOnly) {
return;
}
// offset: 0; size: 2; Options
$cfRule = self::getUInt2d($recordData, 0);
// bit: 8-15; mask: 0x00FF; type
$type = (0x00FF & $cfRule) >> 0;
$type = ConditionalFormatting::type($type);
// bit: 0-7; mask: 0xFF00; type
$operator = (0xFF00 & $cfRule) >> 8;
$operator = ConditionalFormatting::operator($operator);
if ($type === null || $operator === null) {
return;
}
// offset: 2; size: 2; Size1
$size1 = self::getUInt2d($recordData, 2);
// offset: 4; size: 2; Size2
$size2 = self::getUInt2d($recordData, 4);
// offset: 6; size: 4; Options
$options = self::getInt4d($recordData, 6);
$style = new Style(false, true); // non-supervisor, conditional
$this->getCFStyleOptions($options, $style);
$hasFontRecord = (bool) ((0x04000000 & $options) >> 26);
$hasAlignmentRecord = (bool) ((0x08000000 & $options) >> 27);
$hasBorderRecord = (bool) ((0x10000000 & $options) >> 28);
$hasFillRecord = (bool) ((0x20000000 & $options) >> 29);
$hasProtectionRecord = (bool) ((0x40000000 & $options) >> 30);
$offset = 12;
if ($hasFontRecord === true) {
$fontStyle = substr($recordData, $offset, 118);
$this->getCFFontStyle($fontStyle, $style);
$offset += 118;
}
if ($hasAlignmentRecord === true) {
$alignmentStyle = substr($recordData, $offset, 8);
$this->getCFAlignmentStyle($alignmentStyle, $style);
$offset += 8;
}
if ($hasBorderRecord === true) {
$borderStyle = substr($recordData, $offset, 8);
$this->getCFBorderStyle($borderStyle, $style);
$offset += 8;
}
if ($hasFillRecord === true) {
$fillStyle = substr($recordData, $offset, 4);
$this->getCFFillStyle($fillStyle, $style);
$offset += 4;
}
if ($hasProtectionRecord === true) {
$protectionStyle = substr($recordData, $offset, 4);
$this->getCFProtectionStyle($protectionStyle, $style);
$offset += 2;
}
$formula1 = $formula2 = null;
if ($size1 > 0) {
$formula1 = $this->readCFFormula($recordData, $offset, $size1);
if ($formula1 === null) {
return;
}
$offset += $size1;
}
if ($size2 > 0) {
$formula2 = $this->readCFFormula($recordData, $offset, $size2);
if ($formula2 === null) {
return;
}
$offset += $size2;
}
$this->setCFRules($cellRangeAddresses, $type, $operator, $formula1, $formula2, $style);
}
private function getCFStyleOptions(int $options, Style $style): void
{
}
private function getCFFontStyle(string $options, Style $style): void
{
$fontSize = self::getInt4d($options, 64);
if ($fontSize !== -1) {
$style->getFont()->setSize($fontSize / 20); // Convert twips to points
}
$bold = self::getUInt2d($options, 72) === 700; // 400 = normal, 700 = bold
$style->getFont()->setBold($bold);
$color = self::getInt4d($options, 80);
if ($color !== -1) {
$style->getFont()->getColor()->setRGB(Xls\Color::map($color, $this->palette, $this->version)['rgb']);
}
}
private function getCFAlignmentStyle(string $options, Style $style): void
{
}
private function getCFBorderStyle(string $options, Style $style): void
{
}
private function getCFFillStyle(string $options, Style $style): void
{
$fillPattern = self::getUInt2d($options, 0);
// bit: 10-15; mask: 0xFC00; type
$fillPattern = (0xFC00 & $fillPattern) >> 10;
$fillPattern = FillPattern::lookup($fillPattern);
$fillPattern = $fillPattern === Fill::FILL_NONE ? Fill::FILL_SOLID : $fillPattern;
if ($fillPattern !== Fill::FILL_NONE) {
$style->getFill()->setFillType($fillPattern);
$fillColors = self::getUInt2d($options, 2);
// bit: 0-6; mask: 0x007F; type
$color1 = (0x007F & $fillColors) >> 0;
$style->getFill()->getStartColor()->setRGB(Xls\Color::map($color1, $this->palette, $this->version)['rgb']);
// bit: 7-13; mask: 0x3F80; type
$color2 = (0x3F80 & $fillColors) >> 7;
$style->getFill()->getEndColor()->setRGB(Xls\Color::map($color2, $this->palette, $this->version)['rgb']);
}
}
private function getCFProtectionStyle(string $options, Style $style): void
{
}
/**
* @return null|float|int|string
*/
private function readCFFormula(string $recordData, int $offset, int $size)
{
try {
$formula = substr($recordData, $offset, $size);
$formula = pack('v', $size) . $formula; // prepend the length
$formula = $this->getFormulaFromStructure($formula);
if (is_numeric($formula)) {
return (strpos($formula, '.') !== false) ? (float) $formula : (int) $formula;
}
return $formula;
} catch (PhpSpreadsheetException $e) {
}
return null;
}
/**
* @param null|float|int|string $formula1
* @param null|float|int|string $formula2
*/
private function setCFRules(array $cellRanges, string $type, string $operator, $formula1, $formula2, Style $style): void
{
foreach ($cellRanges as $cellRange) {
$conditional = new Conditional();
$conditional->setConditionType($type);
$conditional->setOperatorType($operator);
if ($formula1 !== null) {
$conditional->addCondition($formula1);
}
if ($formula2 !== null) {
$conditional->addCondition($formula2);
}
$conditional->setStyle($style);
$conditionalStyles = $this->phpSheet->getStyle($cellRange)->getConditionalStyles();
$conditionalStyles[] = $conditional;
$this->phpSheet->getStyle($cellRange)->setConditionalStyles($conditionalStyles);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php 0000644 00000062605 15242753725 0015771 0 ustar 00 securityScanner = XmlScanner::getInstance($this);
}
/** @var string */
private $fileContents = '';
public static function xmlMappings(): array
{
return array_merge(
Style\Fill::FILL_MAPPINGS,
Style\Border::BORDER_MAPPINGS
);
}
/**
* Can the current IReader read the file?
*/
public function canRead(string $filename): bool
{
// 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 = [
'getSecurityScannerOrThrow()->scan($data);
$valid = true;
foreach ($signature as $match) {
// every part of the signature must be present
if (strpos($data, $match) === false) {
$valid = false;
break;
}
}
$this->fileContents = $data;
return $valid;
}
/**
* Check if the file is a valid SimpleXML.
*
* @param string $filename
*
* @return false|SimpleXMLElement
*/
public function trySimpleXMLLoadString($filename)
{
try {
$xml = simplexml_load_string(
$this->getSecurityScannerOrThrow()
->scan($this->fileContents ?: file_get_contents($filename))
);
} catch (\Exception $e) {
throw new Exception('Cannot load invalid XML file: ' . $filename, 0, $e);
}
$this->fileContents = '';
return $xml;
}
/**
* Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
*
* @param string $filename
*
* @return array
*/
public function listWorksheetNames($filename)
{
File::assertFile($filename);
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an Invalid Spreadsheet file.');
}
$worksheetNames = [];
$xml = $this->trySimpleXMLLoadString($filename);
if ($xml === false) {
throw new Exception("Problem reading {$filename}");
}
$xml_ss = $xml->children(self::NAMESPACES_SS);
foreach ($xml_ss->Worksheet as $worksheet) {
$worksheet_ss = self::getAttributes($worksheet, self::NAMESPACES_SS);
$worksheetNames[] = (string) $worksheet_ss['Name'];
}
return $worksheetNames;
}
/**
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
*
* @param string $filename
*
* @return array
*/
public function listWorksheetInfo($filename)
{
File::assertFile($filename);
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an Invalid Spreadsheet file.');
}
$worksheetInfo = [];
$xml = $this->trySimpleXMLLoadString($filename);
if ($xml === false) {
throw new Exception("Problem reading {$filename}");
}
$worksheetID = 1;
$xml_ss = $xml->children(self::NAMESPACES_SS);
foreach ($xml_ss->Worksheet as $worksheet) {
$worksheet_ss = self::getAttributes($worksheet, self::NAMESPACES_SS);
$tmpInfo = [];
$tmpInfo['worksheetName'] = '';
$tmpInfo['lastColumnLetter'] = 'A';
$tmpInfo['lastColumnIndex'] = 0;
$tmpInfo['totalRows'] = 0;
$tmpInfo['totalColumns'] = 0;
$tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}";
if (isset($worksheet_ss['Name'])) {
$tmpInfo['worksheetName'] = (string) $worksheet_ss['Name'];
}
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 string.
*/
public function loadSpreadsheetFromString(string $contents): Spreadsheet
{
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
$spreadsheet->removeSheetByIndex(0);
// Load into this instance
return $this->loadIntoExisting($contents, $spreadsheet, true);
}
/**
* Loads Spreadsheet from file.
*/
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
$spreadsheet->removeSheetByIndex(0);
// Load into this instance
return $this->loadIntoExisting($filename, $spreadsheet);
}
/**
* Loads from file or contents into Spreadsheet instance.
*
* @param string $filename file name if useContents is false else file contents
*/
public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet, bool $useContents = false): Spreadsheet
{
if ($useContents) {
$this->fileContents = $filename;
} else {
File::assertFile($filename);
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an Invalid Spreadsheet file.');
}
}
$xml = $this->trySimpleXMLLoadString($filename);
if ($xml === false) {
throw new Exception("Problem reading {$filename}");
}
$namespaces = $xml->getNamespaces(true);
(new Properties($spreadsheet))->readProperties($xml, $namespaces);
$this->styles = (new Style())->parseStyles($xml, $namespaces);
if (isset($this->styles['Default'])) {
$spreadsheet->getCellXfCollection()[0]->applyFromArray($this->styles['Default']);
}
$worksheetID = 0;
$xml_ss = $xml->children(self::NAMESPACES_SS);
/** @var null|SimpleXMLElement $worksheetx */
foreach ($xml_ss->Worksheet as $worksheetx) {
$worksheet = $worksheetx ?? new SimpleXMLElement('');
$worksheet_ss = self::getAttributes($worksheet, self::NAMESPACES_SS);
if (
isset($this->loadSheetsOnly, $worksheet_ss['Name']) &&
(!in_array($worksheet_ss['Name'], /** @scrutinizer ignore-type */ $this->loadSheetsOnly))
) {
continue;
}
// Create new Worksheet
$spreadsheet->createSheet();
$spreadsheet->setActiveSheetIndex($worksheetID);
$worksheetName = '';
if (isset($worksheet_ss['Name'])) {
$worksheetName = (string) $worksheet_ss['Name'];
// 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 (isset($worksheet_ss['Protected'])) {
$protection = (string) $worksheet_ss['Protected'] === '1';
$spreadsheet->getActiveSheet()->getProtection()->setSheet($protection);
}
// locally scoped defined names
if (isset($worksheet->Names[0])) {
foreach ($worksheet->Names[0] as $definedName) {
$definedName_ss = self::getAttributes($definedName, self::NAMESPACES_SS);
$name = (string) $definedName_ss['Name'];
$definedValue = (string) $definedName_ss['RefersTo'];
$convertedValue = AddressHelper::convertFormulaToA1($definedValue);
if ($convertedValue[0] === '=') {
$convertedValue = substr($convertedValue, 1);
}
$spreadsheet->addDefinedName(DefinedName::createInstance($name, $spreadsheet->getActiveSheet(), $convertedValue, true));
}
}
$columnID = 'A';
if (isset($worksheet->Table->Column)) {
foreach ($worksheet->Table->Column as $columnData) {
$columnData_ss = self::getAttributes($columnData, self::NAMESPACES_SS);
$colspan = 0;
if (isset($columnData_ss['Span'])) {
$spanAttr = (string) $columnData_ss['Span'];
if (is_numeric($spanAttr)) {
$colspan = max(0, (int) $spanAttr);
}
}
if (isset($columnData_ss['Index'])) {
$columnID = Coordinate::stringFromColumnIndex((int) $columnData_ss['Index']);
}
$columnWidth = null;
if (isset($columnData_ss['Width'])) {
$columnWidth = $columnData_ss['Width'];
}
$columnVisible = null;
if (isset($columnData_ss['Hidden'])) {
$columnVisible = ((string) $columnData_ss['Hidden']) !== '1';
}
while ($colspan >= 0) {
if (isset($columnWidth)) {
$spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
}
if (isset($columnVisible)) {
$spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setVisible($columnVisible);
}
++$columnID;
--$colspan;
}
}
}
$rowID = 1;
if (isset($worksheet->Table->Row)) {
$additionalMergedCells = 0;
foreach ($worksheet->Table->Row as $rowData) {
$rowHasData = false;
$row_ss = self::getAttributes($rowData, self::NAMESPACES_SS);
if (isset($row_ss['Index'])) {
$rowID = (int) $row_ss['Index'];
}
if (isset($row_ss['Hidden'])) {
$rowVisible = ((string) $row_ss['Hidden']) !== '1';
$spreadsheet->getActiveSheet()->getRowDimension($rowID)->setVisible($rowVisible);
}
$columnID = 'A';
foreach ($rowData->Cell as $cell) {
$cell_ss = self::getAttributes($cell, self::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((string) $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((int) (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, Worksheet::MERGE_CELL_CONTENT_HIDE);
}
$hasCalculatedValue = false;
$cellDataFormula = '';
if (isset($cell_ss['Formula'])) {
$cellDataFormula = $cell_ss['Formula'];
$hasCalculatedValue = true;
}
if (isset($cell->Data)) {
$cellData = $cell->Data;
$cellValue = (string) $cellData;
$type = DataType::TYPE_NULL;
$cellData_ss = self::getAttributes($cellData, self::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':
$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;
$dateTime = new DateTime($cellValue, new DateTimeZone('UTC'));
$cellValue = Date::PHPToExcel($dateTime);
break;
case 'Error':
$type = DataType::TYPE_ERROR;
$hasCalculatedValue = false;
break;
}
}
if ($hasCalculatedValue) {
$type = DataType::TYPE_FORMULA;
$columnNumber = Coordinate::columnIndexFromString($columnID);
$cellDataFormula = AddressHelper::convertFormulaToA1($cellDataFormula, $rowID, $columnNumber);
}
$spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type);
if ($hasCalculatedValue) {
$spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue);
}
$rowHasData = true;
}
if (isset($cell->Comment)) {
$this->parseCellComment($cell->Comment, $spreadsheet, $columnID, $rowID);
}
if (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((float) $rowHeight);
}
}
++$rowID;
}
}
$dataValidations = new Xml\DataValidations();
$dataValidations->loadDataValidations($worksheet, $spreadsheet);
$xmlX = $worksheet->children(Namespaces::URN_EXCEL);
if (isset($xmlX->WorksheetOptions)) {
if (isset($xmlX->WorksheetOptions->FreezePanes)) {
$freezeRow = $freezeColumn = 1;
if (isset($xmlX->WorksheetOptions->SplitHorizontal)) {
$freezeRow = (int) $xmlX->WorksheetOptions->SplitHorizontal + 1;
}
if (isset($xmlX->WorksheetOptions->SplitVertical)) {
$freezeColumn = (int) $xmlX->WorksheetOptions->SplitVertical + 1;
}
$spreadsheet->getActiveSheet()->freezePane(Coordinate::stringFromColumnIndex($freezeColumn) . (string) $freezeRow);
}
(new PageSettings($xmlX))->loadPageSettings($spreadsheet);
if (isset($xmlX->WorksheetOptions->TopRowVisible, $xmlX->WorksheetOptions->LeftColumnVisible)) {
$leftTopRow = (string) $xmlX->WorksheetOptions->TopRowVisible;
$leftTopColumn = (string) $xmlX->WorksheetOptions->LeftColumnVisible;
if (is_numeric($leftTopRow) && is_numeric($leftTopColumn)) {
$leftTopCoordinate = Coordinate::stringFromColumnIndex((int) $leftTopColumn + 1) . (string) ($leftTopRow + 1);
$spreadsheet->getActiveSheet()->setTopLeftCell($leftTopCoordinate);
}
}
$rangeCalculated = false;
if (isset($xmlX->WorksheetOptions->Panes->Pane->RangeSelection)) {
if (1 === preg_match('/^R(\d+)C(\d+):R(\d+)C(\d+)$/', (string) $xmlX->WorksheetOptions->Panes->Pane->RangeSelection, $selectionMatches)) {
$selectedCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
. $selectionMatches[1]
. ':'
. Coordinate::stringFromColumnIndex((int) $selectionMatches[4])
. $selectionMatches[3];
$spreadsheet->getActiveSheet()->setSelectedCells($selectedCell);
$rangeCalculated = true;
}
}
if (!$rangeCalculated) {
if (isset($xmlX->WorksheetOptions->Panes->Pane->ActiveRow)) {
$activeRow = (string) $xmlX->WorksheetOptions->Panes->Pane->ActiveRow;
} else {
$activeRow = 0;
}
if (isset($xmlX->WorksheetOptions->Panes->Pane->ActiveCol)) {
$activeColumn = (string) $xmlX->WorksheetOptions->Panes->Pane->ActiveCol;
} else {
$activeColumn = 0;
}
if (is_numeric($activeRow) && is_numeric($activeColumn)) {
$selectedCell = Coordinate::stringFromColumnIndex((int) $activeColumn + 1) . (string) ($activeRow + 1);
$spreadsheet->getActiveSheet()->setSelectedCells($selectedCell);
}
}
}
++$worksheetID;
}
// Globally scoped defined names
$activeSheetIndex = 0;
if (isset($xml->ExcelWorkbook->ActiveSheet)) {
$activeSheetIndex = (int) (string) $xml->ExcelWorkbook->ActiveSheet;
}
$activeWorksheet = $spreadsheet->setActiveSheetIndex($activeSheetIndex);
if (isset($xml->Names[0])) {
foreach ($xml->Names[0] as $definedName) {
$definedName_ss = self::getAttributes($definedName, self::NAMESPACES_SS);
$name = (string) $definedName_ss['Name'];
$definedValue = (string) $definedName_ss['RefersTo'];
$convertedValue = AddressHelper::convertFormulaToA1($definedValue);
if ($convertedValue[0] === '=') {
$convertedValue = substr($convertedValue, 1);
}
$spreadsheet->addDefinedName(DefinedName::createInstance($name, $activeWorksheet, $convertedValue));
}
}
// Return
return $spreadsheet;
}
protected function parseCellComment(
SimpleXMLElement $comment,
Spreadsheet $spreadsheet,
string $columnID,
int $rowID
): void {
$commentAttributes = $comment->attributes(self::NAMESPACES_SS);
$author = 'unknown';
if (isset($commentAttributes->Author)) {
$author = (string) $commentAttributes->Author;
}
$node = $comment->Data->asXML();
$annotation = strip_tags((string) $node);
$spreadsheet->getActiveSheet()->getComment($columnID . $rowID)
->setAuthor($author)
->setText($this->parseRichText($annotation));
}
protected function parseRichText(string $annotation): RichText
{
$value = new RichText();
$value->createText($annotation);
return $value;
}
private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
{
return ($simple === null)
? new SimpleXMLElement('')
: ($simple->attributes($node) ?? new SimpleXMLElement(''));
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php 0000644 00000012263 15242753725 0017221 0 ustar 00 readFilter = new DefaultReadFilter();
}
public function getReadDataOnly()
{
return $this->readDataOnly;
}
public function setReadDataOnly($readCellValuesOnly)
{
$this->readDataOnly = (bool) $readCellValuesOnly;
return $this;
}
public function getReadEmptyCells()
{
return $this->readEmptyCells;
}
public function setReadEmptyCells($readEmptyCells)
{
$this->readEmptyCells = (bool) $readEmptyCells;
return $this;
}
public function getIncludeCharts()
{
return $this->includeCharts;
}
public function setIncludeCharts($includeCharts)
{
$this->includeCharts = (bool) $includeCharts;
return $this;
}
public function getLoadSheetsOnly()
{
return $this->loadSheetsOnly;
}
public function setLoadSheetsOnly($sheetList)
{
if ($sheetList === null) {
return $this->setLoadAllSheets();
}
$this->loadSheetsOnly = is_array($sheetList) ? $sheetList : [$sheetList];
return $this;
}
public function setLoadAllSheets()
{
$this->loadSheetsOnly = null;
return $this;
}
public function getReadFilter()
{
return $this->readFilter;
}
public function setReadFilter(IReadFilter $readFilter)
{
$this->readFilter = $readFilter;
return $this;
}
public function getSecurityScanner(): ?XmlScanner
{
return $this->securityScanner;
}
public function getSecurityScannerOrThrow(): XmlScanner
{
if ($this->securityScanner === null) {
throw new ReaderException('Security scanner is unexpectedly null');
}
return $this->securityScanner;
}
protected function processFlags(int $flags): void
{
if (((bool) ($flags & self::LOAD_WITH_CHARTS)) === true) {
$this->setIncludeCharts(true);
}
if (((bool) ($flags & self::READ_DATA_ONLY)) === true) {
$this->setReadDataOnly(true);
}
if (((bool) ($flags & self::SKIP_EMPTY_CELLS) || (bool) ($flags & self::IGNORE_EMPTY_CELLS)) === true) {
$this->setReadEmptyCells(false);
}
}
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
throw new PhpSpreadsheetException('Reader classes must implement their own loadSpreadsheetFromFile() method');
}
/**
* Loads Spreadsheet from file.
*
* @param int $flags the optional second parameter flags may be used to identify specific elements
* that should be loaded, but which won't be loaded by default, using these values:
* IReader::LOAD_WITH_CHARTS - Include any charts that are defined in the loaded file
*/
public function load(string $filename, int $flags = 0): Spreadsheet
{
$this->processFlags($flags);
try {
return $this->loadSpreadsheetFromFile($filename);
} catch (ReaderException $e) {
throw $e;
}
}
/**
* Open file for reading.
*/
protected function openFile(string $filename): void
{
$fileHandle = false;
if ($filename) {
File::assertFile($filename);
// Open file
$fileHandle = fopen($filename, 'rb');
}
if ($fileHandle === false) {
throw new ReaderException('Could not open file ' . $filename . ' for reading.');
}
$this->fileHandle = $fileHandle;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php 0000644 00000404626 15242753725 0016172 0 ustar 00 referenceHelper = ReferenceHelper::getInstance();
$this->securityScanner = XmlScanner::getInstance($this);
}
/**
* Can the current IReader read the file?
*/
public function canRead(string $filename): bool
{
if (!File::testFileNoThrow($filename, self::INITIAL_FILE)) {
return false;
}
$result = false;
$this->zip = $zip = new ZipArchive();
if ($zip->open($filename) === true) {
[$workbookBasename] = $this->getWorkbookBaseName();
$result = !empty($workbookBasename);
$zip->close();
}
return $result;
}
/**
* @param mixed $value
*/
public static function testSimpleXml($value): SimpleXMLElement
{
return ($value instanceof SimpleXMLElement) ? $value : new SimpleXMLElement('');
}
public static function getAttributes(?SimpleXMLElement $value, string $ns = ''): SimpleXMLElement
{
return self::testSimpleXml($value === null ? $value : $value->attributes($ns));
}
// Phpstan thinks, correctly, that xpath can return false.
// Scrutinizer thinks it can't.
// Sigh.
private static function xpathNoFalse(SimpleXmlElement $sxml, string $path): array
{
return self::falseToArray($sxml->xpath($path));
}
/**
* @param mixed $value
*/
public static function falseToArray($value): array
{
return is_array($value) ? $value : [];
}
private function loadZip(string $filename, string $ns = '', bool $replaceUnclosedBr = false): SimpleXMLElement
{
$contents = $this->getFromZipArchive($this->zip, $filename);
if ($replaceUnclosedBr) {
$contents = str_replace('
', '
', $contents);
}
$rels = @simplexml_load_string(
$this->getSecurityScannerOrThrow()->scan($contents),
'SimpleXMLElement',
0,
$ns
);
return self::testSimpleXml($rels);
}
// This function is just to identify cases where I'm not sure
// why empty namespace is required.
private function loadZipNonamespace(string $filename, string $ns): SimpleXMLElement
{
$contents = $this->getFromZipArchive($this->zip, $filename);
$rels = simplexml_load_string(
$this->getSecurityScannerOrThrow()->scan($contents),
'SimpleXMLElement',
0,
($ns === '' ? $ns : '')
);
return self::testSimpleXml($rels);
}
private const REL_TO_MAIN = [
Namespaces::PURL_OFFICE_DOCUMENT => Namespaces::PURL_MAIN,
Namespaces::THUMBNAIL => '',
];
private const REL_TO_DRAWING = [
Namespaces::PURL_RELATIONSHIPS => Namespaces::PURL_DRAWING,
];
private const REL_TO_CHART = [
Namespaces::PURL_RELATIONSHIPS => Namespaces::PURL_CHART,
];
/**
* Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
*
* @param string $filename
*
* @return array
*/
public function listWorksheetNames($filename)
{
File::assertFile($filename, self::INITIAL_FILE);
$worksheetNames = [];
$this->zip = $zip = new ZipArchive();
$zip->open($filename);
// The files we're looking at here are small enough that simpleXML is more efficient than XMLReader
$rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
foreach ($rels->Relationship as $relx) {
$rel = self::getAttributes($relx);
$relType = (string) $rel['Type'];
$mainNS = self::REL_TO_MAIN[$relType] ?? Namespaces::MAIN;
if ($mainNS !== '') {
$xmlWorkbook = $this->loadZip((string) $rel['Target'], $mainNS);
if ($xmlWorkbook->sheets) {
foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
// Check if sheet should be skipped
$worksheetNames[] = (string) self::getAttributes($eleSheet)['name'];
}
}
}
}
$zip->close();
return $worksheetNames;
}
/**
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
*
* @param string $filename
*
* @return array
*/
public function listWorksheetInfo($filename)
{
File::assertFile($filename, self::INITIAL_FILE);
$worksheetInfo = [];
$this->zip = $zip = new ZipArchive();
$zip->open($filename);
$rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
foreach ($rels->Relationship as $relx) {
$rel = self::getAttributes($relx);
$relType = (string) $rel['Type'];
$mainNS = self::REL_TO_MAIN[$relType] ?? Namespaces::MAIN;
if ($mainNS !== '') {
$relTarget = (string) $rel['Target'];
$dir = dirname($relTarget);
$namespace = dirname($relType);
$relsWorkbook = $this->loadZip("$dir/_rels/" . basename($relTarget) . '.rels', '');
$worksheets = [];
foreach ($relsWorkbook->Relationship as $elex) {
$ele = self::getAttributes($elex);
if (
((string) $ele['Type'] === "$namespace/worksheet") ||
((string) $ele['Type'] === "$namespace/chartsheet")
) {
$worksheets[(string) $ele['Id']] = $ele['Target'];
}
}
$xmlWorkbook = $this->loadZip($relTarget, $mainNS);
if ($xmlWorkbook->sheets) {
$dir = dirname($relTarget);
/** @var SimpleXMLElement $eleSheet */
foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
$tmpInfo = [
'worksheetName' => (string) self::getAttributes($eleSheet)['name'],
'lastColumnLetter' => 'A',
'lastColumnIndex' => 0,
'totalRows' => 0,
'totalColumns' => 0,
];
$fileWorksheet = (string) $worksheets[(string) self::getArrayItem(self::getAttributes($eleSheet, $namespace), 'id')];
$fileWorksheetPath = strpos($fileWorksheet, '/') === 0 ? substr($fileWorksheet, 1) : "$dir/$fileWorksheet";
$xml = new XMLReader();
$xml->xml(
$this->getSecurityScannerOrThrow()
->scan(
$this->getFromZipArchive(
$this->zip,
$fileWorksheetPath
)
)
);
$xml->setParserProperty(2, true);
$currCells = 0;
while ($xml->read()) {
if ($xml->localName == 'row' && $xml->nodeType == XMLReader::ELEMENT && $xml->namespaceURI === $mainNS) {
$row = $xml->getAttribute('r');
$tmpInfo['totalRows'] = $row;
$tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
$currCells = 0;
} elseif ($xml->localName == 'c' && $xml->nodeType == XMLReader::ELEMENT && $xml->namespaceURI === $mainNS) {
$cell = $xml->getAttribute('r');
$currCells = $cell ? max($currCells, Coordinate::indexesFromString($cell)[0]) : ($currCells + 1);
}
}
$tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
$xml->close();
$tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
$tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
$worksheetInfo[] = $tmpInfo;
}
}
}
}
$zip->close();
return $worksheetInfo;
}
private static function castToBoolean(SimpleXMLElement $c): bool
{
$value = isset($c->v) ? (string) $c->v : null;
if ($value == '0') {
return false;
} elseif ($value == '1') {
return true;
}
return (bool) $c->v;
}
private static function castToError(?SimpleXMLElement $c): ?string
{
return isset($c, $c->v) ? (string) $c->v : null;
}
private static function castToString(?SimpleXMLElement $c): ?string
{
return isset($c, $c->v) ? (string) $c->v : null;
}
/**
* @param mixed $value
* @param mixed $calculatedValue
*/
private function castToFormula(?SimpleXMLElement $c, string $r, string &$cellDataType, &$value, &$calculatedValue, string $castBaseType, bool $updateSharedCells = true): void
{
if ($c === null) {
return;
}
$attr = $c->f->attributes();
$cellDataType = DataType::TYPE_FORMULA;
$value = "={$c->f}";
$calculatedValue = self::$castBaseType($c);
// Shared formula?
if (isset($attr['t']) && strtolower((string) $attr['t']) == 'shared') {
$instance = (string) $attr['si'];
if (!isset($this->sharedFormulae[(string) $attr['si']])) {
$this->sharedFormulae[$instance] = new SharedFormula($r, $value);
} elseif ($updateSharedCells === true) {
// It's only worth the overhead of adjusting the shared formula for this cell if we're actually loading
// the cell, which may not be the case if we're using a read filter.
$master = Coordinate::indexesFromString($this->sharedFormulae[$instance]->master());
$current = Coordinate::indexesFromString($r);
$difference = [0, 0];
$difference[0] = $current[0] - $master[0];
$difference[1] = $current[1] - $master[1];
$value = $this->referenceHelper->updateFormulaReferences($this->sharedFormulae[$instance]->formula(), 'A1', $difference[0], $difference[1]);
}
}
}
/**
* @param string $fileName
*/
private function fileExistsInArchive(ZipArchive $archive, $fileName = ''): bool
{
// Root-relative paths
if (strpos($fileName, '//') !== false) {
$fileName = substr($fileName, strpos($fileName, '//') + 1);
}
$fileName = File::realpath($fileName);
// Sadly, some 3rd party xlsx generators don't use consistent case for filenaming
// so we need to load case-insensitively from the zip file
// Apache POI fixes
$contents = $archive->locateName($fileName, ZipArchive::FL_NOCASE);
if ($contents === false) {
$contents = $archive->locateName(substr($fileName, 1), ZipArchive::FL_NOCASE);
}
return $contents !== false;
}
/**
* @param string $fileName
*
* @return string
*/
private function getFromZipArchive(ZipArchive $archive, $fileName = '')
{
// Root-relative paths
if (strpos($fileName, '//') !== false) {
$fileName = substr($fileName, strpos($fileName, '//') + 1);
}
// Relative paths generated by dirname($filename) when $filename
// has no path (i.e.files in root of the zip archive)
$fileName = (string) preg_replace('/^\.\//', '', $fileName);
$fileName = File::realpath($fileName);
// Sadly, some 3rd party xlsx generators don't use consistent case for filenaming
// so we need to load case-insensitively from the zip file
$contents = $archive->getFromName($fileName, 0, ZipArchive::FL_NOCASE);
// Apache POI fixes
if ($contents === false) {
$contents = $archive->getFromName(substr($fileName, 1), 0, ZipArchive::FL_NOCASE);
}
// Has the file been saved with Windoze directory separators rather than unix?
if ($contents === false) {
$contents = $archive->getFromName(str_replace('/', '\\', $fileName), 0, ZipArchive::FL_NOCASE);
}
return ($contents === false) ? '' : $contents;
}
/**
* Loads Spreadsheet from file.
*/
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
File::assertFile($filename, self::INITIAL_FILE);
// Initialisations
$excel = new Spreadsheet();
$excel->removeSheetByIndex(0);
$addingFirstCellStyleXf = true;
$addingFirstCellXf = true;
$unparsedLoadedData = [];
$this->zip = $zip = new ZipArchive();
$zip->open($filename);
// Read the theme first, because we need the colour scheme when reading the styles
[$workbookBasename, $xmlNamespaceBase] = $this->getWorkbookBaseName();
$drawingNS = self::REL_TO_DRAWING[$xmlNamespaceBase] ?? Namespaces::DRAWINGML;
$chartNS = self::REL_TO_CHART[$xmlNamespaceBase] ?? Namespaces::CHART;
$wbRels = $this->loadZip("xl/_rels/{$workbookBasename}.rels", Namespaces::RELATIONSHIPS);
$theme = null;
$this->styleReader = new Styles();
foreach ($wbRels->Relationship as $relx) {
$rel = self::getAttributes($relx);
$relTarget = (string) $rel['Target'];
if (substr($relTarget, 0, 4) === '/xl/') {
$relTarget = substr($relTarget, 4);
}
switch ($rel['Type']) {
case "$xmlNamespaceBase/theme":
$themeOrderArray = ['lt1', 'dk1', 'lt2', 'dk2'];
$themeOrderAdditional = count($themeOrderArray);
$xmlTheme = $this->loadZip("xl/{$relTarget}", $drawingNS);
$xmlThemeName = self::getAttributes($xmlTheme);
$xmlTheme = $xmlTheme->children($drawingNS);
$themeName = (string) $xmlThemeName['name'];
$colourScheme = self::getAttributes($xmlTheme->themeElements->clrScheme);
$colourSchemeName = (string) $colourScheme['name'];
$excel->getTheme()->setThemeColorName($colourSchemeName);
$colourScheme = $xmlTheme->themeElements->clrScheme->children($drawingNS);
$themeColours = [];
foreach ($colourScheme as $k => $xmlColour) {
$themePos = array_search($k, $themeOrderArray);
if ($themePos === false) {
$themePos = $themeOrderAdditional++;
}
if (isset($xmlColour->sysClr)) {
$xmlColourData = self::getAttributes($xmlColour->sysClr);
$themeColours[$themePos] = (string) $xmlColourData['lastClr'];
$excel->getTheme()->setThemeColor($k, (string) $xmlColourData['lastClr']);
} elseif (isset($xmlColour->srgbClr)) {
$xmlColourData = self::getAttributes($xmlColour->srgbClr);
$themeColours[$themePos] = (string) $xmlColourData['val'];
$excel->getTheme()->setThemeColor($k, (string) $xmlColourData['val']);
}
}
$theme = new Theme($themeName, $colourSchemeName, $themeColours);
$this->styleReader->setTheme($theme);
$fontScheme = self::getAttributes($xmlTheme->themeElements->fontScheme);
$fontSchemeName = (string) $fontScheme['name'];
$excel->getTheme()->setThemeFontName($fontSchemeName);
$majorFonts = [];
$minorFonts = [];
$fontScheme = $xmlTheme->themeElements->fontScheme->children($drawingNS);
$majorLatin = self::getAttributes($fontScheme->majorFont->latin)['typeface'] ?? '';
$majorEastAsian = self::getAttributes($fontScheme->majorFont->ea)['typeface'] ?? '';
$majorComplexScript = self::getAttributes($fontScheme->majorFont->cs)['typeface'] ?? '';
$minorLatin = self::getAttributes($fontScheme->minorFont->latin)['typeface'] ?? '';
$minorEastAsian = self::getAttributes($fontScheme->minorFont->ea)['typeface'] ?? '';
$minorComplexScript = self::getAttributes($fontScheme->minorFont->cs)['typeface'] ?? '';
foreach ($fontScheme->majorFont->font as $xmlFont) {
$fontAttributes = self::getAttributes($xmlFont);
$script = (string) ($fontAttributes['script'] ?? '');
if (!empty($script)) {
$majorFonts[$script] = (string) ($fontAttributes['typeface'] ?? '');
}
}
foreach ($fontScheme->minorFont->font as $xmlFont) {
$fontAttributes = self::getAttributes($xmlFont);
$script = (string) ($fontAttributes['script'] ?? '');
if (!empty($script)) {
$minorFonts[$script] = (string) ($fontAttributes['typeface'] ?? '');
}
}
$excel->getTheme()->setMajorFontValues($majorLatin, $majorEastAsian, $majorComplexScript, $majorFonts);
$excel->getTheme()->setMinorFontValues($minorLatin, $minorEastAsian, $minorComplexScript, $minorFonts);
break;
}
}
$rels = $this->loadZip(self::INITIAL_FILE, Namespaces::RELATIONSHIPS);
$propertyReader = new PropertyReader($this->getSecurityScannerOrThrow(), $excel->getProperties());
$chartDetails = [];
foreach ($rels->Relationship as $relx) {
$rel = self::getAttributes($relx);
$relTarget = (string) $rel['Target'];
// issue 3553
if ($relTarget[0] === '/') {
$relTarget = substr($relTarget, 1);
}
$relType = (string) $rel['Type'];
$mainNS = self::REL_TO_MAIN[$relType] ?? Namespaces::MAIN;
switch ($relType) {
case Namespaces::CORE_PROPERTIES:
$propertyReader->readCoreProperties($this->getFromZipArchive($zip, $relTarget));
break;
case "$xmlNamespaceBase/extended-properties":
$propertyReader->readExtendedProperties($this->getFromZipArchive($zip, $relTarget));
break;
case "$xmlNamespaceBase/custom-properties":
$propertyReader->readCustomProperties($this->getFromZipArchive($zip, $relTarget));
break;
//Ribbon
case Namespaces::EXTENSIBILITY:
$customUI = $relTarget;
if ($customUI) {
$this->readRibbon($excel, $customUI, $zip);
}
break;
case "$xmlNamespaceBase/officeDocument":
$dir = dirname($relTarget);
// Do not specify namespace in next stmt - do it in Xpath
$relsWorkbook = $this->loadZip("$dir/_rels/" . basename($relTarget) . '.rels', '');
$relsWorkbook->registerXPathNamespace('rel', Namespaces::RELATIONSHIPS);
$worksheets = [];
$macros = $customUI = null;
foreach ($relsWorkbook->Relationship as $elex) {
$ele = self::getAttributes($elex);
switch ($ele['Type']) {
case Namespaces::WORKSHEET:
case Namespaces::PURL_WORKSHEET:
$worksheets[(string) $ele['Id']] = $ele['Target'];
break;
case Namespaces::CHARTSHEET:
if ($this->includeCharts === true) {
$worksheets[(string) $ele['Id']] = $ele['Target'];
}
break;
// a vbaProject ? (: some macros)
case Namespaces::VBA:
$macros = $ele['Target'];
break;
}
}
if ($macros !== null) {
$macrosCode = $this->getFromZipArchive($zip, 'xl/vbaProject.bin'); //vbaProject.bin always in 'xl' dir and always named vbaProject.bin
if ($macrosCode !== false) {
$excel->setMacrosCode($macrosCode);
$excel->setHasMacros(true);
//short-circuit : not reading vbaProject.bin.rel to get Signature =>allways vbaProjectSignature.bin in 'xl' dir
$Certificate = $this->getFromZipArchive($zip, 'xl/vbaProjectSignature.bin');
if ($Certificate !== false) {
$excel->setMacrosCertificate($Certificate);
}
}
}
$relType = "rel:Relationship[@Type='"
. "$xmlNamespaceBase/styles"
. "']";
$xpath = self::getArrayItem(self::xpathNoFalse($relsWorkbook, $relType));
if ($xpath === null) {
$xmlStyles = self::testSimpleXml(null);
} else {
$xmlStyles = $this->loadZip("$dir/$xpath[Target]", $mainNS);
}
$palette = self::extractPalette($xmlStyles);
$this->styleReader->setWorkbookPalette($palette);
$fills = self::extractStyles($xmlStyles, 'fills', 'fill');
$fonts = self::extractStyles($xmlStyles, 'fonts', 'font');
$borders = self::extractStyles($xmlStyles, 'borders', 'border');
$xfTags = self::extractStyles($xmlStyles, 'cellXfs', 'xf');
$cellXfTags = self::extractStyles($xmlStyles, 'cellStyleXfs', 'xf');
$styles = [];
$cellStyles = [];
$numFmts = null;
if (/*$xmlStyles && */ $xmlStyles->numFmts[0]) {
$numFmts = $xmlStyles->numFmts[0];
}
if (isset($numFmts) && ($numFmts !== null)) {
$numFmts->registerXPathNamespace('sml', $mainNS);
}
$this->styleReader->setNamespace($mainNS);
if (!$this->readDataOnly/* && $xmlStyles*/) {
foreach ($xfTags as $xfTag) {
$xf = self::getAttributes($xfTag);
$numFmt = null;
if ($xf['numFmtId']) {
if (isset($numFmts)) {
$tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
if (isset($tmpNumFmt['formatCode'])) {
$numFmt = (string) $tmpNumFmt['formatCode'];
}
}
// We shouldn't override any of the built-in MS Excel values (values below id 164)
// But there's a lot of naughty homebrew xlsx writers that do use "reserved" id values that aren't actually used
// So we make allowance for them rather than lose formatting masks
if (
$numFmt === null &&
(int) $xf['numFmtId'] < 164 &&
NumberFormat::builtInFormatCode((int) $xf['numFmtId']) !== ''
) {
$numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
}
}
$quotePrefix = (bool) (string) ($xf['quotePrefix'] ?? '');
$style = (object) [
'numFmt' => $numFmt ?? NumberFormat::FORMAT_GENERAL,
'font' => $fonts[(int) ($xf['fontId'])],
'fill' => $fills[(int) ($xf['fillId'])],
'border' => $borders[(int) ($xf['borderId'])],
'alignment' => $xfTag->alignment,
'protection' => $xfTag->protection,
'quotePrefix' => $quotePrefix,
];
$styles[] = $style;
// add style to cellXf collection
$objStyle = new Style();
$this->styleReader->readStyle($objStyle, $style);
if ($addingFirstCellXf) {
$excel->removeCellXfByIndex(0); // remove the default style
$addingFirstCellXf = false;
}
$excel->addCellXf($objStyle);
}
foreach ($cellXfTags as $xfTag) {
$xf = self::getAttributes($xfTag);
$numFmt = NumberFormat::FORMAT_GENERAL;
if ($numFmts && $xf['numFmtId']) {
$tmpNumFmt = self::getArrayItem($numFmts->xpath("sml:numFmt[@numFmtId=$xf[numFmtId]]"));
if (isset($tmpNumFmt['formatCode'])) {
$numFmt = (string) $tmpNumFmt['formatCode'];
} elseif ((int) $xf['numFmtId'] < 165) {
$numFmt = NumberFormat::builtInFormatCode((int) $xf['numFmtId']);
}
}
$quotePrefix = (bool) (string) ($xf['quotePrefix'] ?? '');
$cellStyle = (object) [
'numFmt' => $numFmt,
'font' => $fonts[(int) ($xf['fontId'])],
'fill' => $fills[((int) $xf['fillId'])],
'border' => $borders[(int) ($xf['borderId'])],
'alignment' => $xfTag->alignment,
'protection' => $xfTag->protection,
'quotePrefix' => $quotePrefix,
];
$cellStyles[] = $cellStyle;
// add style to cellStyleXf collection
$objStyle = new Style();
$this->styleReader->readStyle($objStyle, $cellStyle);
if ($addingFirstCellStyleXf) {
$excel->removeCellStyleXfByIndex(0); // remove the default style
$addingFirstCellStyleXf = false;
}
$excel->addCellStyleXf($objStyle);
}
}
$this->styleReader->setStyleXml($xmlStyles);
$this->styleReader->setNamespace($mainNS);
$this->styleReader->setStyleBaseData($theme, $styles, $cellStyles);
$dxfs = $this->styleReader->dxfs($this->readDataOnly);
$styles = $this->styleReader->styles();
// Read content after setting the styles
$sharedStrings = [];
$relType = "rel:Relationship[@Type='"
//. Namespaces::SHARED_STRINGS
. "$xmlNamespaceBase/sharedStrings"
. "']";
$xpath = self::getArrayItem($relsWorkbook->xpath($relType));
if ($xpath) {
$xmlStrings = $this->loadZip("$dir/$xpath[Target]", $mainNS);
if (isset($xmlStrings->si)) {
foreach ($xmlStrings->si as $val) {
if (isset($val->t)) {
$sharedStrings[] = StringHelper::controlCharacterOOXML2PHP((string) $val->t);
} elseif (isset($val->r)) {
$sharedStrings[] = $this->parseRichText($val);
}
}
}
}
$xmlWorkbook = $this->loadZipNoNamespace($relTarget, $mainNS);
$xmlWorkbookNS = $this->loadZip($relTarget, $mainNS);
// Set base date
if ($xmlWorkbookNS->workbookPr) {
Date::setExcelCalendar(Date::CALENDAR_WINDOWS_1900);
$attrs1904 = self::getAttributes($xmlWorkbookNS->workbookPr);
if (isset($attrs1904['date1904'])) {
if (self::boolean((string) $attrs1904['date1904'])) {
Date::setExcelCalendar(Date::CALENDAR_MAC_1904);
}
}
}
// Set protection
$this->readProtection($excel, $xmlWorkbook);
$sheetId = 0; // keep track of new sheet id in final workbook
$oldSheetId = -1; // keep track of old sheet id in final workbook
$countSkippedSheets = 0; // keep track of number of skipped sheets
$mapSheetId = []; // mapping of sheet ids from old to new
$charts = $chartDetails = [];
if ($xmlWorkbookNS->sheets) {
/** @var SimpleXMLElement $eleSheet */
foreach ($xmlWorkbookNS->sheets->sheet as $eleSheet) {
$eleSheetAttr = self::getAttributes($eleSheet);
++$oldSheetId;
// Check if sheet should be skipped
if (is_array($this->loadSheetsOnly) && !in_array((string) $eleSheetAttr['name'], $this->loadSheetsOnly)) {
++$countSkippedSheets;
$mapSheetId[$oldSheetId] = null;
continue;
}
$sheetReferenceId = (string) self::getArrayItem(self::getAttributes($eleSheet, $xmlNamespaceBase), 'id');
if (isset($worksheets[$sheetReferenceId]) === false) {
++$countSkippedSheets;
$mapSheetId[$oldSheetId] = null;
continue;
}
// Map old sheet id in original workbook to new sheet id.
// They will differ if loadSheetsOnly() is being used
$mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets;
// Load sheet
$docSheet = $excel->createSheet();
// 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
$docSheet->setTitle((string) $eleSheetAttr['name'], false, false);
$fileWorksheet = (string) $worksheets[$sheetReferenceId];
$xmlSheet = $this->loadZipNoNamespace("$dir/$fileWorksheet", $mainNS);
$xmlSheetNS = $this->loadZip("$dir/$fileWorksheet", $mainNS);
// Shared Formula table is unique to each Worksheet, so we need to reset it here
$this->sharedFormulae = [];
if (isset($eleSheetAttr['state']) && (string) $eleSheetAttr['state'] != '') {
$docSheet->setSheetState((string) $eleSheetAttr['state']);
}
if ($xmlSheetNS) {
$xmlSheetMain = $xmlSheetNS->children($mainNS);
// Setting Conditional Styles adjusts selected cells, so we need to execute this
// before reading the sheet view data to get the actual selected cells
if (!$this->readDataOnly && ($xmlSheet->conditionalFormatting)) {
(new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->load();
}
if (!$this->readDataOnly && $xmlSheet->extLst) {
(new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->loadFromExt($this->styleReader);
}
if (isset($xmlSheetMain->sheetViews, $xmlSheetMain->sheetViews->sheetView)) {
$sheetViews = new SheetViews($xmlSheetMain->sheetViews->sheetView, $docSheet);
$sheetViews->load();
}
$sheetViewOptions = new SheetViewOptions($docSheet, $xmlSheetNS);
$sheetViewOptions->load($this->getReadDataOnly(), $this->styleReader);
(new ColumnAndRowAttributes($docSheet, $xmlSheetNS))
->load($this->getReadFilter(), $this->getReadDataOnly());
}
if ($xmlSheetNS && $xmlSheetNS->sheetData && $xmlSheetNS->sheetData->row) {
$cIndex = 1; // Cell Start from 1
foreach ($xmlSheetNS->sheetData->row as $row) {
$rowIndex = 1;
foreach ($row->c as $c) {
$cAttr = self::getAttributes($c);
$r = (string) $cAttr['r'];
if ($r == '') {
$r = Coordinate::stringFromColumnIndex($rowIndex) . $cIndex;
}
$cellDataType = (string) $cAttr['t'];
$value = null;
$calculatedValue = null;
// Read cell?
if ($this->getReadFilter() !== null) {
$coordinates = Coordinate::coordinateFromString($r);
if (!$this->getReadFilter()->readCell($coordinates[0], (int) $coordinates[1], $docSheet->getTitle())) {
// Normally, just testing for the f attribute should identify this cell as containing a formula
// that we need to read, even though it is outside of the filter range, in case it is a shared formula.
// But in some cases, this attribute isn't set; so we need to delve a level deeper and look at
// whether or not the cell has a child formula element that is shared.
if (isset($cAttr->f) || (isset($c->f, $c->f->attributes()['t']) && strtolower((string) $c->f->attributes()['t']) === 'shared')) {
$this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError', false);
}
++$rowIndex;
continue;
}
}
// Read cell!
switch ($cellDataType) {
case 's':
if ((string) $c->v != '') {
$value = $sharedStrings[(int) ($c->v)];
if ($value instanceof RichText) {
$value = clone $value;
}
} else {
$value = '';
}
break;
case 'b':
if (!isset($c->f)) {
if (isset($c->v)) {
$value = self::castToBoolean($c);
} else {
$value = null;
$cellDataType = DATATYPE::TYPE_NULL;
}
} else {
// Formula
$this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToBoolean');
if (isset($c->f['t'])) {
$att = $c->f;
$docSheet->getCell($r)->setFormulaAttributes($att);
}
}
break;
case 'inlineStr':
if (isset($c->f)) {
$this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError');
} else {
$value = $this->parseRichText($c->is);
}
break;
case 'e':
if (!isset($c->f)) {
$value = self::castToError($c);
} else {
// Formula
$this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToError');
}
break;
default:
if (!isset($c->f)) {
$value = self::castToString($c);
} else {
// Formula
$this->castToFormula($c, $r, $cellDataType, $value, $calculatedValue, 'castToString');
if (isset($c->f['t'])) {
$attributes = $c->f['t'];
$docSheet->getCell($r)->setFormulaAttributes(['t' => (string) $attributes]);
}
}
break;
}
// read empty cells or the cells are not empty
if ($this->readEmptyCells || ($value !== null && $value !== '')) {
// Rich text?
if ($value instanceof RichText && $this->readDataOnly) {
$value = $value->getPlainText();
}
$cell = $docSheet->getCell($r);
// Assign value
if ($cellDataType != '') {
// it is possible, that datatype is numeric but with an empty string, which result in an error
if ($cellDataType === DataType::TYPE_NUMERIC && ($value === '' || $value === null)) {
$cellDataType = DataType::TYPE_NULL;
}
if ($cellDataType !== DataType::TYPE_NULL) {
$cell->setValueExplicit($value, $cellDataType);
}
} else {
$cell->setValue($value);
}
if ($calculatedValue !== null) {
$cell->setCalculatedValue($calculatedValue);
}
// Style information?
if ($cAttr['s'] && !$this->readDataOnly) {
// no style index means 0, it seems
$cell->setXfIndex(isset($styles[(int) ($cAttr['s'])]) ?
(int) ($cAttr['s']) : 0);
// issue 3495
if ($cell->getDataType() === DataType::TYPE_FORMULA) {
$cell->getStyle()->setQuotePrefix(false);
}
}
}
++$rowIndex;
}
++$cIndex;
}
}
if ($xmlSheetNS && $xmlSheetNS->ignoredErrors) {
foreach ($xmlSheetNS->ignoredErrors->ignoredError as $ignoredErrorx) {
$ignoredError = self::testSimpleXml($ignoredErrorx);
$this->processIgnoredErrors($ignoredError, $docSheet);
}
}
if (!$this->readDataOnly && $xmlSheetNS && $xmlSheetNS->sheetProtection) {
$protAttr = $xmlSheetNS->sheetProtection->attributes() ?? [];
foreach ($protAttr as $key => $value) {
$method = 'set' . ucfirst($key);
$docSheet->getProtection()->$method(self::boolean((string) $value));
}
}
if ($xmlSheet) {
$this->readSheetProtection($docSheet, $xmlSheet);
}
if ($this->readDataOnly === false) {
$this->readAutoFilter($xmlSheet, $docSheet);
$this->readTables($xmlSheet, $docSheet, $dir, $fileWorksheet, $zip);
}
if ($xmlSheetNS && $xmlSheetNS->mergeCells && $xmlSheetNS->mergeCells->mergeCell && !$this->readDataOnly) {
foreach ($xmlSheetNS->mergeCells->mergeCell as $mergeCellx) {
/** @scrutinizer ignore-call */
$mergeCell = $mergeCellx->attributes();
$mergeRef = (string) ($mergeCell['ref'] ?? '');
if (strpos($mergeRef, ':') !== false) {
$docSheet->mergeCells($mergeRef, Worksheet::MERGE_CELL_CONTENT_HIDE);
}
}
}
if ($xmlSheet && !$this->readDataOnly) {
$unparsedLoadedData = (new PageSetup($docSheet, $xmlSheet))->load($unparsedLoadedData);
}
if ($xmlSheet !== false && isset($xmlSheet->extLst, $xmlSheet->extLst->ext, $xmlSheet->extLst->ext['uri']) && ($xmlSheet->extLst->ext['uri'] == '{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}')) {
// Create dataValidations node if does not exists, maybe is better inside the foreach ?
if (!$xmlSheet->dataValidations) {
$xmlSheet->addChild('dataValidations');
}
foreach ($xmlSheet->extLst->ext->children(Namespaces::DATA_VALIDATIONS1)->dataValidations->dataValidation as $item) {
$item = self::testSimpleXml($item);
$node = self::testSimpleXml($xmlSheet->dataValidations)->addChild('dataValidation');
foreach ($item->attributes() ?? [] as $attr) {
$node->addAttribute($attr->getName(), $attr);
}
$node->addAttribute('sqref', $item->children(Namespaces::DATA_VALIDATIONS2)->sqref);
if (isset($item->formula1)) {
$childNode = $node->addChild('formula1');
if ($childNode !== null) { // null should never happen
$childNode[0] = (string) $item->formula1->children(Namespaces::DATA_VALIDATIONS2)->f; // @phpstan-ignore-line
}
}
}
}
if ($xmlSheet && $xmlSheet->dataValidations && !$this->readDataOnly) {
(new DataValidations($docSheet, $xmlSheet))->load();
}
// unparsed sheet AlternateContent
if ($xmlSheet && !$this->readDataOnly) {
$mc = $xmlSheet->children(Namespaces::COMPATIBILITY);
if ($mc->AlternateContent) {
foreach ($mc->AlternateContent as $alternateContent) {
$alternateContent = self::testSimpleXml($alternateContent);
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['AlternateContents'][] = $alternateContent->asXML();
}
}
}
// Add hyperlinks
if (!$this->readDataOnly) {
$hyperlinkReader = new Hyperlinks($docSheet);
// Locate hyperlink relations
$relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
if ($zip->locateName($relationsFileName)) {
$relsWorksheet = $this->loadZip($relationsFileName, Namespaces::RELATIONSHIPS);
$hyperlinkReader->readHyperlinks($relsWorksheet);
}
// Loop through hyperlinks
if ($xmlSheetNS && $xmlSheetNS->children($mainNS)->hyperlinks) {
$hyperlinkReader->setHyperlinks($xmlSheetNS->children($mainNS)->hyperlinks);
}
}
// Add comments
$comments = [];
$vmlComments = [];
if (!$this->readDataOnly) {
// Locate comment relations
$commentRelations = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
if ($zip->locateName($commentRelations)) {
$relsWorksheet = $this->loadZip($commentRelations, Namespaces::RELATIONSHIPS);
foreach ($relsWorksheet->Relationship as $elex) {
$ele = self::getAttributes($elex);
if ($ele['Type'] == Namespaces::COMMENTS) {
$comments[(string) $ele['Id']] = (string) $ele['Target'];
}
if ($ele['Type'] == Namespaces::VML) {
$vmlComments[(string) $ele['Id']] = (string) $ele['Target'];
}
}
}
// Loop through comments
foreach ($comments as $relName => $relPath) {
// Load comments file
$relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
// okay to ignore namespace - using xpath
$commentsFile = $this->loadZip($relPath, '');
// Utility variables
$authors = [];
$commentsFile->registerXpathNamespace('com', $mainNS);
$authorPath = self::xpathNoFalse($commentsFile, 'com:authors/com:author');
foreach ($authorPath as $author) {
$authors[] = (string) $author;
}
// Loop through contents
$contentPath = self::xpathNoFalse($commentsFile, 'com:commentList/com:comment');
foreach ($contentPath as $comment) {
$commentx = $comment->attributes();
$commentModel = $docSheet->getComment((string) $commentx['ref']);
if (isset($commentx['authorId'])) {
$commentModel->setAuthor($authors[(int) $commentx['authorId']]);
}
$commentModel->setText($this->parseRichText($comment->children($mainNS)->text));
}
}
// later we will remove from it real vmlComments
$unparsedVmlDrawings = $vmlComments;
$vmlDrawingContents = [];
// Loop through VML comments
foreach ($vmlComments as $relName => $relPath) {
// Load VML comments file
$relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
try {
// no namespace okay - processed with Xpath
$vmlCommentsFile = $this->loadZip($relPath, '', true);
$vmlCommentsFile->registerXPathNamespace('v', Namespaces::URN_VML);
} catch (Throwable $ex) {
//Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData
continue;
}
// Locate VML drawings image relations
$drowingImages = [];
$VMLDrawingsRelations = dirname($relPath) . '/_rels/' . basename($relPath) . '.rels';
$vmlDrawingContents[$relName] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $relPath));
if ($zip->locateName($VMLDrawingsRelations)) {
$relsVMLDrawing = $this->loadZip($VMLDrawingsRelations, Namespaces::RELATIONSHIPS);
foreach ($relsVMLDrawing->Relationship as $elex) {
$ele = self::getAttributes($elex);
if ($ele['Type'] == Namespaces::IMAGE) {
$drowingImages[(string) $ele['Id']] = (string) $ele['Target'];
}
}
}
$shapes = self::xpathNoFalse($vmlCommentsFile, '//v:shape');
foreach ($shapes as $shape) {
$shape->registerXPathNamespace('v', Namespaces::URN_VML);
if (isset($shape['style'])) {
$style = (string) $shape['style'];
$fillColor = strtoupper(substr((string) $shape['fillcolor'], 1));
$column = null;
$row = null;
$fillImageRelId = null;
$fillImageTitle = '';
$clientData = $shape->xpath('.//x:ClientData');
if (is_array($clientData) && !empty($clientData)) {
$clientData = $clientData[0];
if (isset($clientData['ObjectType']) && (string) $clientData['ObjectType'] == 'Note') {
$temp = $clientData->xpath('.//x:Row');
if (is_array($temp)) {
$row = $temp[0];
}
$temp = $clientData->xpath('.//x:Column');
if (is_array($temp)) {
$column = $temp[0];
}
}
}
$fillImageRelNode = $shape->xpath('.//v:fill/@o:relid');
if (is_array($fillImageRelNode) && !empty($fillImageRelNode)) {
$fillImageRelNode = $fillImageRelNode[0];
if (isset($fillImageRelNode['relid'])) {
$fillImageRelId = (string) $fillImageRelNode['relid'];
}
}
$fillImageTitleNode = $shape->xpath('.//v:fill/@o:title');
if (is_array($fillImageTitleNode) && !empty($fillImageTitleNode)) {
$fillImageTitleNode = $fillImageTitleNode[0];
if (isset($fillImageTitleNode['title'])) {
$fillImageTitle = (string) $fillImageTitleNode['title'];
}
}
if (($column !== null) && ($row !== null)) {
// Set comment properties
$comment = $docSheet->getComment([$column + 1, $row + 1]);
$comment->getFillColor()->setRGB($fillColor);
if (isset($drowingImages[$fillImageRelId])) {
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$objDrawing->setName($fillImageTitle);
$imagePath = str_replace('../', 'xl/', $drowingImages[$fillImageRelId]);
$objDrawing->setPath(
'zip://' . File::realpath($filename) . '#' . $imagePath,
true,
$zip
);
$comment->setBackgroundImage($objDrawing);
}
// Parse style
$styleArray = explode(';', str_replace(' ', '', $style));
foreach ($styleArray as $stylePair) {
$stylePair = explode(':', $stylePair);
if ($stylePair[0] == 'margin-left') {
$comment->setMarginLeft($stylePair[1]);
}
if ($stylePair[0] == 'margin-top') {
$comment->setMarginTop($stylePair[1]);
}
if ($stylePair[0] == 'width') {
$comment->setWidth($stylePair[1]);
}
if ($stylePair[0] == 'height') {
$comment->setHeight($stylePair[1]);
}
if ($stylePair[0] == 'visibility') {
$comment->setVisible($stylePair[1] == 'visible');
}
}
unset($unparsedVmlDrawings[$relName]);
}
}
}
}
// unparsed vmlDrawing
if ($unparsedVmlDrawings) {
foreach ($unparsedVmlDrawings as $rId => $relPath) {
$rId = substr($rId, 3); // rIdXXX
$unparsedVmlDrawing = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['vmlDrawings'];
$unparsedVmlDrawing[$rId] = [];
$unparsedVmlDrawing[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $relPath);
$unparsedVmlDrawing[$rId]['relFilePath'] = $relPath;
$unparsedVmlDrawing[$rId]['content'] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $unparsedVmlDrawing[$rId]['filePath']));
unset($unparsedVmlDrawing);
}
}
// Header/footer images
if ($xmlSheetNS && $xmlSheetNS->legacyDrawingHF) {
$vmlHfRid = '';
$vmlHfRidAttr = $xmlSheetNS->legacyDrawingHF->attributes(Namespaces::SCHEMA_OFFICE_DOCUMENT);
if ($vmlHfRidAttr !== null && isset($vmlHfRidAttr['id'])) {
$vmlHfRid = (string) $vmlHfRidAttr['id'][0];
}
if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
$relsWorksheet = $this->loadZipNoNamespace(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels', Namespaces::RELATIONSHIPS);
$vmlRelationship = '';
foreach ($relsWorksheet->Relationship as $ele) {
if ((string) $ele['Type'] == Namespaces::VML && (string) $ele['Id'] === $vmlHfRid) {
$vmlRelationship = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
break;
}
}
if ($vmlRelationship != '') {
// Fetch linked images
$relsVML = $this->loadZipNoNamespace(dirname($vmlRelationship) . '/_rels/' . basename($vmlRelationship) . '.rels', Namespaces::RELATIONSHIPS);
$drawings = [];
if (isset($relsVML->Relationship)) {
foreach ($relsVML->Relationship as $ele) {
if ($ele['Type'] == Namespaces::IMAGE) {
$drawings[(string) $ele['Id']] = self::dirAdd($vmlRelationship, $ele['Target']);
}
}
}
// Fetch VML document
$vmlDrawing = $this->loadZipNoNamespace($vmlRelationship, '');
$vmlDrawing->registerXPathNamespace('v', Namespaces::URN_VML);
$hfImages = [];
$shapes = self::xpathNoFalse($vmlDrawing, '//v:shape');
foreach ($shapes as $idx => $shape) {
$shape->registerXPathNamespace('v', Namespaces::URN_VML);
$imageData = $shape->xpath('//v:imagedata');
if (empty($imageData)) {
continue;
}
$imageData = $imageData[$idx];
$imageData = self::getAttributes($imageData, Namespaces::URN_MSOFFICE);
$style = self::toCSSArray((string) $shape['style']);
if (array_key_exists((string) $imageData['relid'], $drawings)) {
$shapeId = (string) $shape['id'];
$hfImages[$shapeId] = new HeaderFooterDrawing();
if (isset($imageData['title'])) {
$hfImages[$shapeId]->setName((string) $imageData['title']);
}
$hfImages[$shapeId]->setPath('zip://' . File::realpath($filename) . '#' . $drawings[(string) $imageData['relid']], false, $zip);
$hfImages[$shapeId]->setResizeProportional(false);
$hfImages[$shapeId]->setWidth($style['width']);
$hfImages[$shapeId]->setHeight($style['height']);
if (isset($style['margin-left'])) {
$hfImages[$shapeId]->setOffsetX($style['margin-left']);
}
$hfImages[$shapeId]->setOffsetY($style['margin-top']);
$hfImages[$shapeId]->setResizeProportional(true);
}
}
$docSheet->getHeaderFooter()->setImages($hfImages);
}
}
}
}
// TODO: Autoshapes from twoCellAnchors!
$drawingFilename = dirname("$dir/$fileWorksheet")
. '/_rels/'
. basename($fileWorksheet)
. '.rels';
if (substr($drawingFilename, 0, 7) === 'xl//xl/') {
$drawingFilename = substr($drawingFilename, 4);
}
if (substr($drawingFilename, 0, 8) === '/xl//xl/') {
$drawingFilename = substr($drawingFilename, 5);
}
if ($zip->locateName($drawingFilename)) {
$relsWorksheet = $this->loadZipNoNamespace($drawingFilename, Namespaces::RELATIONSHIPS);
$drawings = [];
foreach ($relsWorksheet->Relationship as $ele) {
if ((string) $ele['Type'] === "$xmlNamespaceBase/drawing") {
$eleTarget = (string) $ele['Target'];
if (substr($eleTarget, 0, 4) === '/xl/') {
$drawings[(string) $ele['Id']] = substr($eleTarget, 1);
} else {
$drawings[(string) $ele['Id']] = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
}
}
}
if ($xmlSheetNS->drawing && !$this->readDataOnly) {
$unparsedDrawings = [];
$fileDrawing = null;
foreach ($xmlSheetNS->drawing as $drawing) {
$drawingRelId = (string) self::getArrayItem(self::getAttributes($drawing, $xmlNamespaceBase), 'id');
$fileDrawing = $drawings[$drawingRelId];
$drawingFilename = dirname($fileDrawing) . '/_rels/' . basename($fileDrawing) . '.rels';
$relsDrawing = $this->loadZipNoNamespace($drawingFilename, $xmlNamespaceBase);
$images = [];
$hyperlinks = [];
if ($relsDrawing && $relsDrawing->Relationship) {
foreach ($relsDrawing->Relationship as $ele) {
$eleType = (string) $ele['Type'];
if ($eleType === Namespaces::HYPERLINK) {
$hyperlinks[(string) $ele['Id']] = (string) $ele['Target'];
}
if ($eleType === "$xmlNamespaceBase/image") {
$eleTarget = (string) $ele['Target'];
if (substr($eleTarget, 0, 4) === '/xl/') {
$eleTarget = substr($eleTarget, 1);
$images[(string) $ele['Id']] = $eleTarget;
} else {
$images[(string) $ele['Id']] = self::dirAdd($fileDrawing, $eleTarget);
}
} elseif ($eleType === "$xmlNamespaceBase/chart") {
if ($this->includeCharts) {
$eleTarget = (string) $ele['Target'];
if (substr($eleTarget, 0, 4) === '/xl/') {
$index = substr($eleTarget, 1);
} else {
$index = self::dirAdd($fileDrawing, $eleTarget);
}
$charts[$index] = [
'id' => (string) $ele['Id'],
'sheet' => $docSheet->getTitle(),
];
}
}
}
}
$xmlDrawing = $this->loadZipNoNamespace($fileDrawing, '');
$xmlDrawingChildren = $xmlDrawing->children(Namespaces::SPREADSHEET_DRAWING);
if ($xmlDrawingChildren->oneCellAnchor) {
foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) {
$oneCellAnchor = self::testSimpleXml($oneCellAnchor);
if ($oneCellAnchor->pic->blipFill) {
/** @var SimpleXMLElement $blip */
$blip = $oneCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip;
/** @var SimpleXMLElement $xfrm */
$xfrm = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm;
/** @var SimpleXMLElement $outerShdw */
$outerShdw = $oneCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
$objDrawing->setName((string) self::getArrayItem(self::getAttributes($oneCellAnchor->pic->nvPicPr->cNvPr), 'name'));
$objDrawing->setDescription((string) self::getArrayItem(self::getAttributes($oneCellAnchor->pic->nvPicPr->cNvPr), 'descr'));
$embedImageKey = (string) self::getArrayItem(
self::getAttributes($blip, $xmlNamespaceBase),
'embed'
);
if (isset($images[$embedImageKey])) {
$objDrawing->setPath(
'zip://' . File::realpath($filename) . '#' .
$images[$embedImageKey],
false,
$zip
);
} else {
$linkImageKey = (string) self::getArrayItem(
$blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
'link'
);
if (isset($images[$linkImageKey])) {
$url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
$objDrawing->setPath($url, false);
}
if ($objDrawing->getPath() === '') {
continue;
}
}
$objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((int) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1));
$objDrawing->setOffsetX((int) Drawing::EMUToPixels($oneCellAnchor->from->colOff));
$objDrawing->setOffsetY(Drawing::EMUToPixels($oneCellAnchor->from->rowOff));
$objDrawing->setResizeProportional(false);
$objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($oneCellAnchor->ext), 'cx')));
$objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($oneCellAnchor->ext), 'cy')));
if ($xfrm) {
$objDrawing->setRotation((int) Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($xfrm), 'rot')));
}
if ($outerShdw) {
$shadow = $objDrawing->getShadow();
$shadow->setVisible(true);
$shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($outerShdw), 'blurRad')));
$shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($outerShdw), 'dist')));
$shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($outerShdw), 'dir')));
$shadow->setAlignment((string) self::getArrayItem(self::getAttributes($outerShdw), 'algn'));
$clr = $outerShdw->srgbClr ?? $outerShdw->prstClr;
$shadow->getColor()->setRGB(self::getArrayItem(self::getAttributes($clr), 'val'));
$shadow->setAlpha(self::getArrayItem(self::getAttributes($clr->alpha), 'val') / 1000);
}
$this->readHyperLinkDrawing($objDrawing, $oneCellAnchor, $hyperlinks);
$objDrawing->setWorksheet($docSheet);
} elseif ($this->includeCharts && $oneCellAnchor->graphicFrame) {
// Exported XLSX from Google Sheets positions charts with a oneCellAnchor
$coordinates = Coordinate::stringFromColumnIndex(((int) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1);
$offsetX = Drawing::EMUToPixels($oneCellAnchor->from->colOff);
$offsetY = Drawing::EMUToPixels($oneCellAnchor->from->rowOff);
$width = Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($oneCellAnchor->ext), 'cx'));
$height = Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($oneCellAnchor->ext), 'cy'));
$graphic = $oneCellAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
/** @var SimpleXMLElement $chartRef */
$chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
$thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
$chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
'fromCoordinate' => $coordinates,
'fromOffsetX' => $offsetX,
'fromOffsetY' => $offsetY,
'width' => $width,
'height' => $height,
'worksheetTitle' => $docSheet->getTitle(),
'oneCellAnchor' => true,
];
}
}
}
if ($xmlDrawingChildren->twoCellAnchor) {
foreach ($xmlDrawingChildren->twoCellAnchor as $twoCellAnchor) {
$twoCellAnchor = self::testSimpleXml($twoCellAnchor);
if ($twoCellAnchor->pic->blipFill) {
$blip = $twoCellAnchor->pic->blipFill->children(Namespaces::DRAWINGML)->blip;
$xfrm = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->xfrm;
$outerShdw = $twoCellAnchor->pic->spPr->children(Namespaces::DRAWINGML)->effectLst->outerShdw;
$objDrawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
/** @scrutinizer ignore-call */
$editAs = $twoCellAnchor->attributes();
if (isset($editAs, $editAs['editAs'])) {
$objDrawing->setEditAs($editAs['editAs']);
}
$objDrawing->setName((string) self::getArrayItem(self::getAttributes($twoCellAnchor->pic->nvPicPr->cNvPr), 'name'));
$objDrawing->setDescription((string) self::getArrayItem(self::getAttributes($twoCellAnchor->pic->nvPicPr->cNvPr), 'descr'));
$embedImageKey = (string) self::getArrayItem(
self::getAttributes($blip, $xmlNamespaceBase),
'embed'
);
if (isset($images[$embedImageKey])) {
$objDrawing->setPath(
'zip://' . File::realpath($filename) . '#' .
$images[$embedImageKey],
false,
$zip
);
} else {
$linkImageKey = (string) self::getArrayItem(
$blip->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'),
'link'
);
if (isset($images[$linkImageKey])) {
$url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
$objDrawing->setPath($url, false);
}
if ($objDrawing->getPath() === '') {
continue;
}
}
$objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1));
$objDrawing->setOffsetX(Drawing::EMUToPixels($twoCellAnchor->from->colOff));
$objDrawing->setOffsetY(Drawing::EMUToPixels($twoCellAnchor->from->rowOff));
$objDrawing->setCoordinates2(Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->to->col) + 1) . ($twoCellAnchor->to->row + 1));
$objDrawing->setOffsetX2(Drawing::EMUToPixels($twoCellAnchor->to->colOff));
$objDrawing->setOffsetY2(Drawing::EMUToPixels($twoCellAnchor->to->rowOff));
$objDrawing->setResizeProportional(false);
if ($xfrm) {
$objDrawing->setWidth(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($xfrm->ext), 'cx')));
$objDrawing->setHeight(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($xfrm->ext), 'cy')));
$objDrawing->setRotation(Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($xfrm), 'rot')));
}
if ($outerShdw) {
$shadow = $objDrawing->getShadow();
$shadow->setVisible(true);
$shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($outerShdw), 'blurRad')));
$shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem(self::getAttributes($outerShdw), 'dist')));
$shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem(self::getAttributes($outerShdw), 'dir')));
$shadow->setAlignment((string) self::getArrayItem(self::getAttributes($outerShdw), 'algn'));
$clr = $outerShdw->srgbClr ?? $outerShdw->prstClr;
$shadow->getColor()->setRGB(self::getArrayItem(self::getAttributes($clr), 'val'));
$shadow->setAlpha(self::getArrayItem(self::getAttributes($clr->alpha), 'val') / 1000);
}
$this->readHyperLinkDrawing($objDrawing, $twoCellAnchor, $hyperlinks);
$objDrawing->setWorksheet($docSheet);
} elseif (($this->includeCharts) && ($twoCellAnchor->graphicFrame)) {
$fromCoordinate = Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1);
$fromOffsetX = Drawing::EMUToPixels($twoCellAnchor->from->colOff);
$fromOffsetY = Drawing::EMUToPixels($twoCellAnchor->from->rowOff);
$toCoordinate = Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->to->col) + 1) . ($twoCellAnchor->to->row + 1);
$toOffsetX = Drawing::EMUToPixels($twoCellAnchor->to->colOff);
$toOffsetY = Drawing::EMUToPixels($twoCellAnchor->to->rowOff);
$graphic = $twoCellAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
/** @var SimpleXMLElement $chartRef */
$chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
$thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
$chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
'fromCoordinate' => $fromCoordinate,
'fromOffsetX' => $fromOffsetX,
'fromOffsetY' => $fromOffsetY,
'toCoordinate' => $toCoordinate,
'toOffsetX' => $toOffsetX,
'toOffsetY' => $toOffsetY,
'worksheetTitle' => $docSheet->getTitle(),
];
}
}
}
if ($xmlDrawingChildren->absoluteAnchor) {
foreach ($xmlDrawingChildren->absoluteAnchor as $absoluteAnchor) {
if (($this->includeCharts) && ($absoluteAnchor->graphicFrame)) {
$graphic = $absoluteAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
/** @var SimpleXMLElement $chartRef */
$chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
$thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
$width = Drawing::EMUToPixels((int) self::getArrayItem(self::getAttributes($absoluteAnchor->ext), 'cx')[0]);
$height = Drawing::EMUToPixels((int) self::getArrayItem(self::getAttributes($absoluteAnchor->ext), 'cy')[0]);
$chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
'fromCoordinate' => 'A1',
'fromOffsetX' => 0,
'fromOffsetY' => 0,
'width' => $width,
'height' => $height,
'worksheetTitle' => $docSheet->getTitle(),
];
}
}
}
if (empty($relsDrawing) && $xmlDrawing->count() == 0) {
// Save Drawing without rels and children as unparsed
$unparsedDrawings[$drawingRelId] = $xmlDrawing->asXML();
}
}
// store original rId of drawing files
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'] = [];
foreach ($relsWorksheet->Relationship as $ele) {
if ((string) $ele['Type'] === "$xmlNamespaceBase/drawing") {
$drawingRelId = (string) $ele['Id'];
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = $drawingRelId;
if (isset($unparsedDrawings[$drawingRelId])) {
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['Drawings'][$drawingRelId] = $unparsedDrawings[$drawingRelId];
}
}
}
if ($xmlSheet->legacyDrawing && !$this->readDataOnly) {
foreach ($xmlSheet->legacyDrawing as $drawing) {
$drawingRelId = (string) self::getArrayItem(self::getAttributes($drawing, $xmlNamespaceBase), 'id');
if (isset($vmlDrawingContents[$drawingRelId])) {
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['legacyDrawing'] = $vmlDrawingContents[$drawingRelId];
}
}
}
// unparsed drawing AlternateContent
$xmlAltDrawing = $this->loadZip((string) $fileDrawing, Namespaces::COMPATIBILITY);
if ($xmlAltDrawing->AlternateContent) {
foreach ($xmlAltDrawing->AlternateContent as $alternateContent) {
$alternateContent = self::testSimpleXml($alternateContent);
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingAlternateContents'][] = $alternateContent->asXML();
}
}
}
}
$this->readFormControlProperties($excel, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
$this->readPrinterSettings($excel, $dir, $fileWorksheet, $docSheet, $unparsedLoadedData);
// Loop through definedNames
if ($xmlWorkbook->definedNames) {
foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
// Extract range
$extractedRange = (string) $definedName;
if (($spos = strpos($extractedRange, '!')) !== false) {
$extractedRange = substr($extractedRange, 0, $spos) . str_replace('$', '', substr($extractedRange, $spos));
} else {
$extractedRange = str_replace('$', '', $extractedRange);
}
// Valid range?
if ($extractedRange == '') {
continue;
}
// Some definedNames are only applicable if we are on the same sheet...
if ((string) $definedName['localSheetId'] != '' && (string) $definedName['localSheetId'] == $oldSheetId) {
// Switch on type
switch ((string) $definedName['name']) {
case '_xlnm._FilterDatabase':
if ((string) $definedName['hidden'] !== '1') {
$extractedRange = explode(',', $extractedRange);
foreach ($extractedRange as $range) {
$autoFilterRange = $range;
if (strpos($autoFilterRange, ':') !== false) {
$docSheet->getAutoFilter()->setRange($autoFilterRange);
}
}
}
break;
case '_xlnm.Print_Titles':
// Split $extractedRange
$extractedRange = explode(',', $extractedRange);
// Set print titles
foreach ($extractedRange as $range) {
$matches = [];
$range = str_replace('$', '', $range);
// check for repeating columns, e g. 'A:A' or 'A:D'
if (preg_match('/!?([A-Z]+)\:([A-Z]+)$/', $range, $matches)) {
$docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$matches[1], $matches[2]]);
} elseif (preg_match('/!?(\d+)\:(\d+)$/', $range, $matches)) {
// check for repeating rows, e.g. '1:1' or '1:5'
$docSheet->getPageSetup()->setRowsToRepeatAtTop([$matches[1], $matches[2]]);
}
}
break;
case '_xlnm.Print_Area':
$rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) ?: [];
$newRangeSets = [];
foreach ($rangeSets as $rangeSet) {
[, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
if (empty($rangeSet)) {
continue;
}
if (strpos($rangeSet, ':') === false) {
$rangeSet = $rangeSet . ':' . $rangeSet;
}
$newRangeSets[] = str_replace('$', '', $rangeSet);
}
if (count($newRangeSets) > 0) {
$docSheet->getPageSetup()->setPrintArea(implode(',', $newRangeSets));
}
break;
default:
break;
}
}
}
}
// Next sheet id
++$sheetId;
}
// Loop through definedNames
if ($xmlWorkbook->definedNames) {
foreach ($xmlWorkbook->definedNames->definedName as $definedName) {
// Extract range
$extractedRange = (string) $definedName;
// Valid range?
if ($extractedRange == '') {
continue;
}
// Some definedNames are only applicable if we are on the same sheet...
if ((string) $definedName['localSheetId'] != '') {
// Local defined name
// Switch on type
switch ((string) $definedName['name']) {
case '_xlnm._FilterDatabase':
case '_xlnm.Print_Titles':
case '_xlnm.Print_Area':
break;
default:
if ($mapSheetId[(int) $definedName['localSheetId']] !== null) {
$range = Worksheet::extractSheetTitle((string) $definedName, true);
$scope = $excel->getSheet($mapSheetId[(int) $definedName['localSheetId']]);
if (strpos((string) $definedName, '!') !== false) {
$range[0] = str_replace("''", "'", $range[0]);
$range[0] = str_replace("'", '', $range[0]);
if ($worksheet = $excel->getSheetByName($range[0])) { // @phpstan-ignore-line
$excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $worksheet, $extractedRange, true, $scope));
} else {
$excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $scope, $extractedRange, true, $scope));
}
} else {
$excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $scope, $extractedRange, true));
}
}
break;
}
} elseif (!isset($definedName['localSheetId'])) {
$definedRange = (string) $definedName;
// "Global" definedNames
$locatedSheet = null;
if (strpos((string) $definedName, '!') !== false) {
// Modify range, and extract the first worksheet reference
// Need to split on a comma or a space if not in quotes, and extract the first part.
$definedNameValueParts = preg_split("/[ ,](?=([^']*'[^']*')*[^']*$)/miuU", $definedRange);
// Extract sheet name
[$extractedSheetName] = Worksheet::extractSheetTitle((string) $definedNameValueParts[0], true); // @phpstan-ignore-line
$extractedSheetName = trim($extractedSheetName, "'");
// Locate sheet
$locatedSheet = $excel->getSheetByName($extractedSheetName);
}
if ($locatedSheet === null && !DefinedName::testIfFormula($definedRange)) {
$definedRange = '#REF!';
}
$excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $locatedSheet, $definedRange, false));
}
}
}
}
(new WorkbookView($excel))->viewSettings($xmlWorkbook, $mainNS, $mapSheetId, $this->readDataOnly);
break;
}
}
if (!$this->readDataOnly) {
$contentTypes = $this->loadZip('[Content_Types].xml');
// Default content types
foreach ($contentTypes->Default as $contentType) {
switch ($contentType['ContentType']) {
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings':
$unparsedLoadedData['default_content_types'][(string) $contentType['Extension']] = (string) $contentType['ContentType'];
break;
}
}
// Override content types
foreach ($contentTypes->Override as $contentType) {
switch ($contentType['ContentType']) {
case 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml':
if ($this->includeCharts) {
$chartEntryRef = ltrim((string) $contentType['PartName'], '/');
$chartElements = $this->loadZip($chartEntryRef);
$chartReader = new Chart($chartNS, $drawingNS);
$objChart = $chartReader->readChart($chartElements, basename($chartEntryRef, '.xml'));
if (isset($charts[$chartEntryRef])) {
$chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id'];
if (isset($chartDetails[$chartPositionRef])) {
$excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart); // @phpstan-ignore-line
$objChart->setWorksheet($excel->getSheetByName($charts[$chartEntryRef]['sheet']));
// For oneCellAnchor or absoluteAnchor positioned charts,
// toCoordinate is not in the data. Does it need to be calculated?
if (array_key_exists('toCoordinate', $chartDetails[$chartPositionRef])) {
// twoCellAnchor
$objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']);
$objChart->setBottomRightPosition($chartDetails[$chartPositionRef]['toCoordinate'], $chartDetails[$chartPositionRef]['toOffsetX'], $chartDetails[$chartPositionRef]['toOffsetY']);
} else {
// oneCellAnchor or absoluteAnchor (e.g. Chart sheet)
$objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']);
$objChart->setBottomRightPosition('', $chartDetails[$chartPositionRef]['width'], $chartDetails[$chartPositionRef]['height']);
if (array_key_exists('oneCellAnchor', $chartDetails[$chartPositionRef])) {
$objChart->setOneCellAnchor($chartDetails[$chartPositionRef]['oneCellAnchor']);
}
}
}
}
}
break;
// unparsed
case 'application/vnd.ms-excel.controlproperties+xml':
$unparsedLoadedData['override_content_types'][(string) $contentType['PartName']] = (string) $contentType['ContentType'];
break;
}
}
}
$excel->setUnparsedLoadedData($unparsedLoadedData);
$zip->close();
return $excel;
}
/**
* @return RichText
*/
private function parseRichText(?SimpleXMLElement $is)
{
$value = new RichText();
if (isset($is->t)) {
$value->createText(StringHelper::controlCharacterOOXML2PHP((string) $is->t));
} elseif ($is !== null) {
if (is_object($is->r)) {
/** @var SimpleXMLElement $run */
foreach ($is->r as $run) {
if (!isset($run->rPr)) {
$value->createText(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
} else {
$objText = $value->createTextRun(StringHelper::controlCharacterOOXML2PHP((string) $run->t));
$objFont = $objText->getFont() ?? new StyleFont();
if (isset($run->rPr->rFont)) {
$attr = $run->rPr->rFont->attributes();
if (isset($attr['val'])) {
$objFont->setName((string) $attr['val']);
}
}
if (isset($run->rPr->sz)) {
$attr = $run->rPr->sz->attributes();
if (isset($attr['val'])) {
$objFont->setSize((float) $attr['val']);
}
}
if (isset($run->rPr->color)) {
$objFont->setColor(new Color($this->styleReader->readColor($run->rPr->color)));
}
if (isset($run->rPr->b)) {
$attr = $run->rPr->b->attributes();
if (
(isset($attr['val']) && self::boolean((string) $attr['val'])) ||
(!isset($attr['val']))
) {
$objFont->setBold(true);
}
}
if (isset($run->rPr->i)) {
$attr = $run->rPr->i->attributes();
if (
(isset($attr['val']) && self::boolean((string) $attr['val'])) ||
(!isset($attr['val']))
) {
$objFont->setItalic(true);
}
}
if (isset($run->rPr->vertAlign)) {
$attr = $run->rPr->vertAlign->attributes();
if (isset($attr['val'])) {
$vertAlign = strtolower((string) $attr['val']);
if ($vertAlign == 'superscript') {
$objFont->setSuperscript(true);
}
if ($vertAlign == 'subscript') {
$objFont->setSubscript(true);
}
}
}
if (isset($run->rPr->u)) {
$attr = $run->rPr->u->attributes();
if (!isset($attr['val'])) {
$objFont->setUnderline(\PhpOffice\PhpSpreadsheet\Style\Font::UNDERLINE_SINGLE);
} else {
$objFont->setUnderline((string) $attr['val']);
}
}
if (isset($run->rPr->strike)) {
$attr = $run->rPr->strike->attributes();
if (
(isset($attr['val']) && self::boolean((string) $attr['val'])) ||
(!isset($attr['val']))
) {
$objFont->setStrikethrough(true);
}
}
}
}
}
}
return $value;
}
private function readRibbon(Spreadsheet $excel, string $customUITarget, ZipArchive $zip): void
{
$baseDir = dirname($customUITarget);
$nameCustomUI = basename($customUITarget);
// get the xml file (ribbon)
$localRibbon = $this->getFromZipArchive($zip, $customUITarget);
$customUIImagesNames = [];
$customUIImagesBinaries = [];
// something like customUI/_rels/customUI.xml.rels
$pathRels = $baseDir . '/_rels/' . $nameCustomUI . '.rels';
$dataRels = $this->getFromZipArchive($zip, $pathRels);
if ($dataRels) {
// exists and not empty if the ribbon have some pictures (other than internal MSO)
$UIRels = simplexml_load_string(
$this->getSecurityScannerOrThrow()
->scan($dataRels)
);
if (false !== $UIRels) {
// we need to save id and target to avoid parsing customUI.xml and "guess" if it's a pseudo callback who load the image
foreach ($UIRels->Relationship as $ele) {
if ((string) $ele['Type'] === Namespaces::SCHEMA_OFFICE_DOCUMENT . '/image') {
// an image ?
$customUIImagesNames[(string) $ele['Id']] = (string) $ele['Target'];
$customUIImagesBinaries[(string) $ele['Target']] = $this->getFromZipArchive($zip, $baseDir . '/' . (string) $ele['Target']);
}
}
}
}
if ($localRibbon) {
$excel->setRibbonXMLData($customUITarget, $localRibbon);
if (count($customUIImagesNames) > 0 && count($customUIImagesBinaries) > 0) {
$excel->setRibbonBinObjects($customUIImagesNames, $customUIImagesBinaries);
} else {
$excel->setRibbonBinObjects(null, null);
}
} else {
$excel->setRibbonXMLData(null, null);
$excel->setRibbonBinObjects(null, null);
}
}
/**
* @param null|array|bool|SimpleXMLElement $array
* @param int|string $key
*
* @return mixed
*/
private static function getArrayItem($array, $key = 0)
{
return ($array === null || is_bool($array)) ? null : ($array[$key] ?? null);
}
/**
* @param null|SimpleXMLElement|string $base
* @param null|SimpleXMLElement|string $add
*/
private static function dirAdd($base, $add): string
{
$base = (string) $base;
$add = (string) $add;
return (string) preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
}
private static function toCSSArray(string $style): array
{
$style = self::stripWhiteSpaceFromStyleString($style);
$temp = explode(';', $style);
$style = [];
foreach ($temp as $item) {
$item = explode(':', $item);
if (strpos($item[1], 'px') !== false) {
$item[1] = str_replace('px', '', $item[1]);
}
if (strpos($item[1], 'pt') !== false) {
$item[1] = str_replace('pt', '', $item[1]);
$item[1] = (string) Font::fontSizeToPixels((int) $item[1]);
}
if (strpos($item[1], 'in') !== false) {
$item[1] = str_replace('in', '', $item[1]);
$item[1] = (string) Font::inchSizeToPixels((int) $item[1]);
}
if (strpos($item[1], 'cm') !== false) {
$item[1] = str_replace('cm', '', $item[1]);
$item[1] = (string) Font::centimeterSizeToPixels((int) $item[1]);
}
$style[$item[0]] = $item[1];
}
return $style;
}
public static function stripWhiteSpaceFromStyleString(string $string): string
{
return trim(str_replace(["\r", "\n", ' '], '', $string), ';');
}
private static function boolean(string $value): bool
{
if (is_numeric($value)) {
return (bool) $value;
}
return $value === 'true' || $value === 'TRUE';
}
/**
* @param array $hyperlinks
*/
private function readHyperLinkDrawing(\PhpOffice\PhpSpreadsheet\Worksheet\Drawing $objDrawing, SimpleXMLElement $cellAnchor, $hyperlinks): void
{
$hlinkClick = $cellAnchor->pic->nvPicPr->cNvPr->children(Namespaces::DRAWINGML)->hlinkClick;
if ($hlinkClick->count() === 0) {
return;
}
$hlinkId = (string) self::getAttributes($hlinkClick, Namespaces::SCHEMA_OFFICE_DOCUMENT)['id'];
$hyperlink = new Hyperlink(
$hyperlinks[$hlinkId],
(string) self::getArrayItem(self::getAttributes($cellAnchor->pic->nvPicPr->cNvPr), 'name')
);
$objDrawing->setHyperlink($hyperlink);
}
private function readProtection(Spreadsheet $excel, SimpleXMLElement $xmlWorkbook): void
{
if (!$xmlWorkbook->workbookProtection) {
return;
}
$excel->getSecurity()->setLockRevision(self::getLockValue($xmlWorkbook->workbookProtection, 'lockRevision'));
$excel->getSecurity()->setLockStructure(self::getLockValue($xmlWorkbook->workbookProtection, 'lockStructure'));
$excel->getSecurity()->setLockWindows(self::getLockValue($xmlWorkbook->workbookProtection, 'lockWindows'));
if ($xmlWorkbook->workbookProtection['revisionsPassword']) {
$excel->getSecurity()->setRevisionsPassword(
(string) $xmlWorkbook->workbookProtection['revisionsPassword'],
true
);
}
if ($xmlWorkbook->workbookProtection['workbookPassword']) {
$excel->getSecurity()->setWorkbookPassword(
(string) $xmlWorkbook->workbookProtection['workbookPassword'],
true
);
}
}
private static function getLockValue(SimpleXmlElement $protection, string $key): ?bool
{
$returnValue = null;
$protectKey = $protection[$key];
if (!empty($protectKey)) {
$protectKey = (string) $protectKey;
$returnValue = $protectKey !== 'false' && (bool) $protectKey;
}
return $returnValue;
}
private function readFormControlProperties(Spreadsheet $excel, string $dir, string $fileWorksheet, Worksheet $docSheet, array &$unparsedLoadedData): void
{
$zip = $this->zip;
if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
return;
}
$filename = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
$relsWorksheet = $this->loadZipNoNamespace($filename, Namespaces::RELATIONSHIPS);
$ctrlProps = [];
foreach ($relsWorksheet->Relationship as $ele) {
if ((string) $ele['Type'] === Namespaces::SCHEMA_OFFICE_DOCUMENT . '/ctrlProp') {
$ctrlProps[(string) $ele['Id']] = $ele;
}
}
$unparsedCtrlProps = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['ctrlProps'];
foreach ($ctrlProps as $rId => $ctrlProp) {
$rId = substr($rId, 3); // rIdXXX
$unparsedCtrlProps[$rId] = [];
$unparsedCtrlProps[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $ctrlProp['Target']);
$unparsedCtrlProps[$rId]['relFilePath'] = (string) $ctrlProp['Target'];
$unparsedCtrlProps[$rId]['content'] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $unparsedCtrlProps[$rId]['filePath']));
}
unset($unparsedCtrlProps);
}
private function readPrinterSettings(Spreadsheet $excel, string $dir, string $fileWorksheet, Worksheet $docSheet, array &$unparsedLoadedData): void
{
$zip = $this->zip;
if (!$zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
return;
}
$filename = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
$relsWorksheet = $this->loadZipNoNamespace($filename, Namespaces::RELATIONSHIPS);
$sheetPrinterSettings = [];
foreach ($relsWorksheet->Relationship as $ele) {
if ((string) $ele['Type'] === Namespaces::SCHEMA_OFFICE_DOCUMENT . '/printerSettings') {
$sheetPrinterSettings[(string) $ele['Id']] = $ele;
}
}
$unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings'];
foreach ($sheetPrinterSettings as $rId => $printerSettings) {
$rId = substr($rId, 3); // rIdXXX
if (substr($rId, -2) !== 'ps') {
$rId = $rId . 'ps'; // rIdXXX, add 'ps' suffix to avoid identical resource identifier collision with unparsed vmlDrawing
}
$unparsedPrinterSettings[$rId] = [];
$unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']);
$unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target'];
$unparsedPrinterSettings[$rId]['content'] = $this->getSecurityScannerOrThrow()->scan($this->getFromZipArchive($zip, $unparsedPrinterSettings[$rId]['filePath']));
}
unset($unparsedPrinterSettings);
}
private function getWorkbookBaseName(): array
{
$workbookBasename = '';
$xmlNamespaceBase = '';
// check if it is an OOXML archive
$rels = $this->loadZip(self::INITIAL_FILE);
foreach ($rels->children(Namespaces::RELATIONSHIPS)->Relationship as $rel) {
$rel = self::getAttributes($rel);
$type = (string) $rel['Type'];
switch ($type) {
case Namespaces::OFFICE_DOCUMENT:
case Namespaces::PURL_OFFICE_DOCUMENT:
$basename = basename((string) $rel['Target']);
$xmlNamespaceBase = dirname($type);
if (preg_match('/workbook.*\.xml/', $basename)) {
$workbookBasename = $basename;
}
break;
}
}
return [$workbookBasename, $xmlNamespaceBase];
}
private function readSheetProtection(Worksheet $docSheet, SimpleXMLElement $xmlSheet): void
{
if ($this->readDataOnly || !$xmlSheet->sheetProtection) {
return;
}
$algorithmName = (string) $xmlSheet->sheetProtection['algorithmName'];
$protection = $docSheet->getProtection();
$protection->setAlgorithm($algorithmName);
if ($algorithmName) {
$protection->setPassword((string) $xmlSheet->sheetProtection['hashValue'], true);
$protection->setSalt((string) $xmlSheet->sheetProtection['saltValue']);
$protection->setSpinCount((int) $xmlSheet->sheetProtection['spinCount']);
} else {
$protection->setPassword((string) $xmlSheet->sheetProtection['password'], true);
}
if ($xmlSheet->protectedRanges->protectedRange) {
foreach ($xmlSheet->protectedRanges->protectedRange as $protectedRange) {
$docSheet->protectCells((string) $protectedRange['sqref'], (string) $protectedRange['password'], true);
}
}
}
private function readAutoFilter(
SimpleXMLElement $xmlSheet,
Worksheet $docSheet
): void {
if ($xmlSheet && $xmlSheet->autoFilter) {
(new AutoFilter($docSheet, $xmlSheet))->load();
}
}
private function readTables(
SimpleXMLElement $xmlSheet,
Worksheet $docSheet,
string $dir,
string $fileWorksheet,
ZipArchive $zip
): void {
if ($xmlSheet && $xmlSheet->tableParts && (int) $xmlSheet->tableParts['count'] > 0) {
$this->readTablesInTablesFile($xmlSheet, $dir, $fileWorksheet, $zip, $docSheet);
}
}
private function readTablesInTablesFile(
SimpleXMLElement $xmlSheet,
string $dir,
string $fileWorksheet,
ZipArchive $zip,
Worksheet $docSheet
): void {
foreach ($xmlSheet->tableParts->tablePart as $tablePart) {
$relation = self::getAttributes($tablePart, Namespaces::SCHEMA_OFFICE_DOCUMENT);
$tablePartRel = (string) $relation['id'];
$relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
if ($zip->locateName($relationsFileName)) {
$relsTableReferences = $this->loadZip($relationsFileName, Namespaces::RELATIONSHIPS);
foreach ($relsTableReferences->Relationship as $relationship) {
$relationshipAttributes = self::getAttributes($relationship, '');
if ((string) $relationshipAttributes['Id'] === $tablePartRel) {
$relationshipFileName = (string) $relationshipAttributes['Target'];
$relationshipFilePath = dirname("$dir/$fileWorksheet") . '/' . $relationshipFileName;
$relationshipFilePath = File::realpath($relationshipFilePath);
if ($this->fileExistsInArchive($this->zip, $relationshipFilePath)) {
$tableXml = $this->loadZip($relationshipFilePath);
(new TableReader($docSheet, $tableXml))->load();
}
}
}
}
}
}
private static function extractStyles(?SimpleXMLElement $sxml, string $node1, string $node2): array
{
$array = [];
if ($sxml && $sxml->{$node1}->{$node2}) {
foreach ($sxml->{$node1}->{$node2} as $node) {
$array[] = $node;
}
}
return $array;
}
private static function extractPalette(?SimpleXMLElement $sxml): array
{
$array = [];
if ($sxml && $sxml->colors->indexedColors) {
foreach ($sxml->colors->indexedColors->rgbColor as $node) {
if ($node !== null) {
$attr = $node->attributes();
if (isset($attr['rgb'])) {
$array[] = (string) $attr['rgb'];
}
}
}
}
return $array;
}
private function processIgnoredErrors(SimpleXMLElement $xml, Worksheet $sheet): void
{
$attributes = self::getAttributes($xml);
$sqref = (string) ($attributes['sqref'] ?? '');
$numberStoredAsText = (string) ($attributes['numberStoredAsText'] ?? '');
$formula = (string) ($attributes['formula'] ?? '');
$twoDigitTextYear = (string) ($attributes['twoDigitTextYear'] ?? '');
$evalError = (string) ($attributes['evalError'] ?? '');
if (!empty($sqref)) {
$explodedSqref = explode(' ', $sqref);
$pattern1 = '/^([A-Z]{1,3})([0-9]{1,7})(:([A-Z]{1,3})([0-9]{1,7}))?$/';
foreach ($explodedSqref as $sqref1) {
if (preg_match($pattern1, $sqref1, $matches) === 1) {
$firstRow = $matches[2];
$firstCol = $matches[1];
if (array_key_exists(3, $matches)) {
$lastCol = $matches[4];
$lastRow = $matches[5];
} else {
$lastCol = $firstCol;
$lastRow = $firstRow;
}
++$lastCol;
for ($row = $firstRow; $row <= $lastRow; ++$row) {
for ($col = $firstCol; $col !== $lastCol; ++$col) {
if ($numberStoredAsText === '1') {
$sheet->getCell("$col$row")->getIgnoredErrors()->setNumberStoredAsText(true);
}
if ($formula === '1') {
$sheet->getCell("$col$row")->getIgnoredErrors()->setFormula(true);
}
if ($twoDigitTextYear === '1') {
$sheet->getCell("$col$row")->getIgnoredErrors()->setTwoDigitTextYear(true);
}
if ($evalError === '1') {
$sheet->getCell("$col$row")->getIgnoredErrors()->setEvalError(true);
}
}
}
}
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php 0000644 00000047211 15242753725 0015756 0 ustar 00 openFile($filename);
} catch (ReaderException $e) {
return false;
}
// Read sample data (first 2 KB will do)
$data = (string) 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;
}
private function canReadOrBust(string $filename): void
{
if (!$this->canRead($filename)) {
throw new ReaderException($filename . ' is an Invalid SYLK file.');
}
$this->openFile($filename);
}
/**
* Set input encoding.
*
* @deprecated no use is made of this property
*
* @param string $inputEncoding Input encoding, eg: 'ANSI'
*
* @return $this
*
* @codeCoverageIgnore
*/
public function setInputEncoding($inputEncoding)
{
$this->inputEncoding = $inputEncoding;
return $this;
}
/**
* Get input encoding.
*
* @deprecated no use is made of this property
*
* @return string
*
* @codeCoverageIgnore
*/
public function getInputEncoding()
{
return $this->inputEncoding;
}
/**
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
*
* @param string $filename
*
* @return array
*/
public function listWorksheetInfo($filename)
{
// Open file
$this->canReadOrBust($filename);
$fileHandle = $this->fileHandle;
rewind($fileHandle);
$worksheetInfo = [];
$worksheetInfo[0]['worksheetName'] = basename($filename, '.slk');
// loop through one row (line) at a time in the file
$rowIndex = 0;
$columnIndex = 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 == 'B') {
foreach ($rowData as $rowDatum) {
switch ($rowDatum[0]) {
case 'X':
$columnIndex = (int) substr($rowDatum, 1) - 1;
break;
case 'Y':
$rowIndex = substr($rowDatum, 1);
break;
}
}
break;
}
}
$worksheetInfo[0]['lastColumnIndex'] = $columnIndex;
$worksheetInfo[0]['totalRows'] = $rowIndex;
$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.
*/
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
// Load into this instance
return $this->loadIntoExisting($filename, $spreadsheet);
}
private const COLOR_ARRAY = [
'FF00FFFF', // 0 - cyan
'FF000000', // 1 - black
'FFFFFFFF', // 2 - white
'FFFF0000', // 3 - red
'FF00FF00', // 4 - green
'FF0000FF', // 5 - blue
'FFFFFF00', // 6 - yellow
'FFFF00FF', // 7 - magenta
];
private const FONT_STYLE_MAPPINGS = [
'B' => 'bold',
'I' => 'italic',
'U' => 'underline',
];
private function processFormula(string $rowDatum, bool &$hasCalculatedValue, string &$cellDataFormula, string $row, string $column): void
{
$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
$key = $key === false;
if ($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 = (int) $row + (int) 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 = (int) $column + (int) trim($columnReference, '[]');
}
$A1CellReference = Coordinate::stringFromColumnIndex((int) $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;
}
private function processCRecord(array $rowData, Spreadsheet &$spreadsheet, string &$row, string &$column): void
{
// Read cell value data
$hasCalculatedValue = false;
$cellDataFormula = $cellData = '';
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':
$this->processFormula($rowDatum, $hasCalculatedValue, $cellDataFormula, $row, $column);
break;
case 'A':
$comment = substr($rowDatum, 1);
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
$spreadsheet->getActiveSheet()
->getComment("$columnLetter$row")
->getText()
->createText($comment);
break;
}
}
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
$cellData = Calculation::unwrapResult($cellData);
// Set cell value
$this->processCFinal($spreadsheet, $hasCalculatedValue, $cellDataFormula, $cellData, "$columnLetter$row");
}
private function processCFinal(Spreadsheet &$spreadsheet, bool $hasCalculatedValue, string $cellDataFormula, string $cellData, string $coordinate): void
{
// Set cell value
$spreadsheet->getActiveSheet()->getCell($coordinate)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData);
if ($hasCalculatedValue) {
$cellData = Calculation::unwrapResult($cellData);
$spreadsheet->getActiveSheet()->getCell($coordinate)->setCalculatedValue($cellData);
}
}
private function processFRecord(array $rowData, Spreadsheet &$spreadsheet, string &$row, string &$column): void
{
// Read cell formatting
$formatStyle = $columnWidth = '';
$startCol = $endCol = '';
$fontStyle = '';
$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':
$this->styleSettings($rowDatum, $styleData, $fontStyle);
break;
}
}
$this->addFormats($spreadsheet, $formatStyle, $row, $column);
$this->addFonts($spreadsheet, $fontStyle, $row, $column);
$this->addStyle($spreadsheet, $styleData, $row, $column);
$this->addWidth($spreadsheet, $columnWidth, $startCol, $endCol);
}
private const STYLE_SETTINGS_FONT = ['D' => 'bold', 'I' => 'italic'];
private const STYLE_SETTINGS_BORDER = [
'B' => 'bottom',
'L' => 'left',
'R' => 'right',
'T' => 'top',
];
private function styleSettings(string $rowDatum, array &$styleData, string &$fontStyle): void
{
$styleSettings = substr($rowDatum, 1);
$iMax = strlen($styleSettings);
for ($i = 0; $i < $iMax; ++$i) {
$char = $styleSettings[$i];
if (array_key_exists($char, self::STYLE_SETTINGS_FONT)) {
$styleData['font'][self::STYLE_SETTINGS_FONT[$char]] = true;
} elseif (array_key_exists($char, self::STYLE_SETTINGS_BORDER)) {
$styleData['borders'][self::STYLE_SETTINGS_BORDER[$char]]['borderStyle'] = Border::BORDER_THIN;
} elseif ($char == 'S') {
$styleData['fill']['fillType'] = \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_PATTERN_GRAY125;
} elseif ($char == 'M') {
if (preg_match('/M([1-9]\\d*)/', $styleSettings, $matches)) {
$fontStyle = $matches[1];
}
}
}
}
private function addFormats(Spreadsheet &$spreadsheet, string $formatStyle, string $row, string $column): void
{
if ($formatStyle && $column > '' && $row > '') {
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
if (isset($this->formats[$formatStyle])) {
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->formats[$formatStyle]);
}
}
}
private function addFonts(Spreadsheet &$spreadsheet, string $fontStyle, string $row, string $column): void
{
if ($fontStyle && $column > '' && $row > '') {
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
if (isset($this->fonts[$fontStyle])) {
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->fonts[$fontStyle]);
}
}
}
private function addStyle(Spreadsheet &$spreadsheet, array $styleData, string $row, string $column): void
{
if ((!empty($styleData)) && $column > '' && $row > '') {
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData);
}
}
private function addWidth(Spreadsheet $spreadsheet, string $columnWidth, string $startCol, string $endCol): void
{
if ($columnWidth > '') {
if ($startCol == $endCol) {
$startCol = Coordinate::stringFromColumnIndex((int) $startCol);
$spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth);
} else {
$startCol = Coordinate::stringFromColumnIndex((int) $startCol);
$endCol = Coordinate::stringFromColumnIndex((int) $endCol);
$spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth);
do {
$spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth((float) $columnWidth);
} while ($startCol !== $endCol);
}
}
}
private function processPRecord(array $rowData, Spreadsheet &$spreadsheet): void
{
// Read shared styles
$formatArray = [];
$fromFormats = ['\-', '\ '];
$toFormats = ['-', ' '];
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 'M':
$formatArray['font']['size'] = ((float) substr($rowDatum, 1)) / 20;
break;
case 'L':
$this->processPColors($rowDatum, $formatArray);
break;
case 'S':
$this->processPFontStyles($rowDatum, $formatArray);
break;
}
}
$this->processPFinal($spreadsheet, $formatArray);
}
private function processPColors(string $rowDatum, array &$formatArray): void
{
if (preg_match('/L([1-9]\\d*)/', $rowDatum, $matches)) {
$fontColor = $matches[1] % 8;
$formatArray['font']['color']['argb'] = self::COLOR_ARRAY[$fontColor];
}
}
private function processPFontStyles(string $rowDatum, array &$formatArray): void
{
$styleSettings = substr($rowDatum, 1);
$iMax = strlen($styleSettings);
for ($i = 0; $i < $iMax; ++$i) {
if (array_key_exists($styleSettings[$i], self::FONT_STYLE_MAPPINGS)) {
$formatArray['font'][self::FONT_STYLE_MAPPINGS[$styleSettings[$i]]] = true;
}
}
}
private function processPFinal(Spreadsheet &$spreadsheet, array $formatArray): void
{
if (array_key_exists('numberFormat', $formatArray)) {
$this->formats['P' . $this->format] = $formatArray;
++$this->format;
} elseif (array_key_exists('font', $formatArray)) {
++$this->fontcount;
$this->fonts[$this->fontcount] = $formatArray;
if ($this->fontcount === 1) {
$spreadsheet->getDefaultStyle()->applyFromArray($formatArray);
}
}
}
/**
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
*
* @param string $filename
*
* @return Spreadsheet
*/
public function loadIntoExisting($filename, Spreadsheet $spreadsheet)
{
// Open file
$this->canReadOrBust($filename);
$fileHandle = $this->fileHandle;
rewind($fileHandle);
// Create new Worksheets
while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
$spreadsheet->createSheet();
}
$spreadsheet->setActiveSheetIndex($this->sheetIndex);
$spreadsheet->getActiveSheet()->setTitle(substr(basename($filename, '.slk'), 0, Worksheet::SHEET_TITLE_MAXIMUM_LENGTH));
// Loop through file
$column = $row = '';
// loop through one row (line) at a time in the file
while (($rowDataTxt = fgets($fileHandle)) !== false) {
// convert SYLK encoded $rowData to UTF-8
$rowDataTxt = StringHelper::SYLKtoUTF8($rowDataTxt);
// 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($rowDataTxt)))));
$dataType = array_shift($rowData);
if ($dataType == 'P') {
// Read shared styles
$this->processPRecord($rowData, $spreadsheet);
} elseif ($dataType == 'C') {
// Read cell value data
$this->processCRecord($rowData, $spreadsheet, $row, $column);
} elseif ($dataType == 'F') {
// Read cell formatting
$this->processFRecord($rowData, $spreadsheet, $row, $column);
} else {
$this->columnRowFromRowData($rowData, $column, $row);
}
}
// Close file
fclose($fileHandle);
// Return
return $spreadsheet;
}
private function columnRowFromRowData(array $rowData, string &$column, string &$row): void
{
foreach ($rowData as $rowDatum) {
$char0 = $rowDatum[0];
if ($char0 === 'X' || $char0 == 'C') {
$column = substr($rowDatum, 1);
} elseif ($char0 === 'Y' || $char0 == 'R') {
$row = substr($rowDatum, 1);
}
}
}
/**
* Get sheet index.
*
* @return int
*/
public function getSheetIndex()
{
return $this->sheetIndex;
}
/**
* Set sheet index.
*
* @param int $sheetIndex Sheet index
*
* @return $this
*/
public function setSheetIndex($sheetIndex)
{
$this->sheetIndex = $sheetIndex;
return $this;
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php 0000644 00000053161 15242753725 0016777 0 ustar 00 [
'10' => DataType::TYPE_NULL,
'20' => DataType::TYPE_BOOL,
'30' => DataType::TYPE_NUMERIC, // Integer doesn't exist in Excel
'40' => DataType::TYPE_NUMERIC, // Float
'50' => DataType::TYPE_ERROR,
'60' => DataType::TYPE_STRING,
//'70': // Cell Range
//'80': // Array
],
];
/**
* 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?
*/
public function canRead(string $filename): bool
{
$data = null;
if (File::testFileNoThrow($filename)) {
$data = $this->gzfileGetContents($filename);
if (strpos($data, self::NAMESPACE_GNM) === false) {
$data = '';
}
}
return !empty($data);
}
private static function matchXml(XMLReader $xml, string $expectedLocalName): bool
{
return $xml->namespaceURI === self::NAMESPACE_GNM
&& $xml->localName === $expectedLocalName
&& $xml->nodeType === XMLReader::ELEMENT;
}
/**
* Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
*
* @param string $filename
*
* @return array
*/
public function listWorksheetNames($filename)
{
File::assertFile($filename);
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an invalid Gnumeric file.');
}
$xml = new XMLReader();
$contents = $this->gzfileGetContents($filename);
$xml->xml($contents);
$xml->setParserProperty(2, true);
$worksheetNames = [];
while ($xml->read()) {
if (self::matchXml($xml, 'SheetName')) {
$xml->read(); // Move onto the value node
$worksheetNames[] = (string) $xml->value;
} elseif (self::matchXml($xml, '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 $filename
*
* @return array
*/
public function listWorksheetInfo($filename)
{
File::assertFile($filename);
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an invalid Gnumeric file.');
}
$xml = new XMLReader();
$contents = $this->gzfileGetContents($filename);
$xml->xml($contents);
$xml->setParserProperty(2, true);
$worksheetInfo = [];
while ($xml->read()) {
if (self::matchXml($xml, 'Sheet')) {
$tmpInfo = [
'worksheetName' => '',
'lastColumnLetter' => 'A',
'lastColumnIndex' => 0,
'totalRows' => 0,
'totalColumns' => 0,
];
while ($xml->read()) {
if (self::matchXml($xml, 'Name')) {
$xml->read(); // Move onto the value node
$tmpInfo['worksheetName'] = (string) $xml->value;
} elseif (self::matchXml($xml, 'MaxCol')) {
$xml->read(); // Move onto the value node
$tmpInfo['lastColumnIndex'] = (int) $xml->value;
$tmpInfo['totalColumns'] = (int) $xml->value + 1;
} elseif (self::matchXml($xml, 'MaxRow')) {
$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)
{
$data = '';
$contents = @file_get_contents($filename);
if ($contents !== false) {
if (substr($contents, 0, 2) === "\x1f\x8b") {
// Check if gzlib functions are available
if (function_exists('gzdecode')) {
$contents = @gzdecode($contents);
if ($contents !== false) {
$data = $contents;
}
}
} else {
$data = $contents;
}
}
if ($data !== '') {
$data = $this->getSecurityScannerOrThrow()->scan($data);
}
return $data;
}
public static function gnumericMappings(): array
{
return array_merge(self::$mappings, Styles::$mappings);
}
private function processComments(SimpleXMLElement $sheet): void
{
if ((!$this->readDataOnly) && (isset($sheet->Objects))) {
foreach ($sheet->Objects->children(self::NAMESPACE_GNM) as $key => $comment) {
$commentAttributes = $comment->attributes();
// Only comment objects are handled at the moment
if ($commentAttributes && $commentAttributes->Text) {
$this->spreadsheet->getActiveSheet()->getComment((string) $commentAttributes->ObjectBound)
->setAuthor((string) $commentAttributes->Author)
->setText($this->parseRichText((string) $commentAttributes->Text));
}
}
}
}
/**
* @param mixed $value
*/
private static function testSimpleXml($value): SimpleXMLElement
{
return ($value instanceof SimpleXMLElement) ? $value : new SimpleXMLElement('');
}
/**
* Loads Spreadsheet from file.
*/
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
$spreadsheet->removeSheetByIndex(0);
// Load into this instance
return $this->loadIntoExisting($filename, $spreadsheet);
}
/**
* Loads from file into Spreadsheet instance.
*/
public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
{
$this->spreadsheet = $spreadsheet;
File::assertFile($filename);
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an invalid Gnumeric file.');
}
$gFileData = $this->gzfileGetContents($filename);
$xml2 = simplexml_load_string($gFileData);
$xml = self::testSimpleXml($xml2);
$gnmXML = $xml->children(self::NAMESPACE_GNM);
(new Properties($this->spreadsheet))->readProperties($xml, $gnmXML);
$worksheetID = 0;
foreach ($gnmXML->Sheets->Sheet as $sheetOrNull) {
$sheet = self::testSimpleXml($sheetOrNull);
$worksheetName = (string) $sheet->Name;
if (is_array($this->loadSheetsOnly) && !in_array($worksheetName, $this->loadSheetsOnly, true)) {
continue;
}
$maxRow = $maxCol = 0;
// Create new Worksheet
$this->spreadsheet->createSheet();
$this->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
$this->spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
$visibility = $sheet->attributes()['Visibility'] ?? 'GNM_SHEET_VISIBILITY_VISIBLE';
if ((string) $visibility !== 'GNM_SHEET_VISIBILITY_VISIBLE') {
$this->spreadsheet->getActiveSheet()->setSheetState(Worksheet::SHEETSTATE_HIDDEN);
}
if (!$this->readDataOnly) {
(new PageSetup($this->spreadsheet))
->printInformation($sheet)
->sheetMargins($sheet);
}
foreach ($sheet->Cells->Cell as $cellOrNull) {
$cell = self::testSimpleXml($cellOrNull);
$cellAttributes = self::testSimpleXml($cell->attributes());
$row = (int) $cellAttributes->Row + 1;
$column = (int) $cellAttributes->Col;
$maxRow = max($maxRow, $row);
$maxCol = max($maxCol, $column);
$column = Coordinate::stringFromColumnIndex($column + 1);
// Read cell?
if ($this->getReadFilter() !== null) {
if (!$this->getReadFilter()->readCell($column, $row, $worksheetName)) {
continue;
}
}
$this->loadCell($cell, $worksheetName, $cellAttributes, $column, $row);
}
if ($sheet->Styles !== null) {
(new Styles($this->spreadsheet, $this->readDataOnly))->read($sheet, $maxRow, $maxCol);
}
$this->processComments($sheet);
$this->processColumnWidths($sheet, $maxCol);
$this->processRowHeights($sheet, $maxRow);
$this->processMergedCells($sheet);
$this->processAutofilter($sheet);
$this->setSelectedCells($sheet);
++$worksheetID;
}
$this->processDefinedNames($gnmXML);
$this->setSelectedSheet($gnmXML);
// Return
return $this->spreadsheet;
}
private function setSelectedSheet(SimpleXMLElement $gnmXML): void
{
if (isset($gnmXML->UIData)) {
$attributes = self::testSimpleXml($gnmXML->UIData->attributes());
$selectedSheet = (int) $attributes['SelectedTab'];
$this->spreadsheet->setActiveSheetIndex($selectedSheet);
}
}
private function setSelectedCells(?SimpleXMLElement $sheet): void
{
if ($sheet !== null && isset($sheet->Selections)) {
foreach ($sheet->Selections as $selection) {
$startCol = (int) ($selection->StartCol ?? 0);
$startRow = (int) ($selection->StartRow ?? 0) + 1;
$endCol = (int) ($selection->EndCol ?? $startCol);
$endRow = (int) ($selection->endRow ?? 0) + 1;
$startColumn = Coordinate::stringFromColumnIndex($startCol + 1);
$endColumn = Coordinate::stringFromColumnIndex($endCol + 1);
$startCell = "{$startColumn}{$startRow}";
$endCell = "{$endColumn}{$endRow}";
$selectedRange = $startCell . (($endCell !== $startCell) ? ':' . $endCell : '');
$this->spreadsheet->getActiveSheet()->setSelectedCell($selectedRange);
break;
}
}
}
private function processMergedCells(?SimpleXMLElement $sheet): void
{
// Handle Merged Cells in this worksheet
if ($sheet !== null && isset($sheet->MergedRegions)) {
foreach ($sheet->MergedRegions->Merge as $mergeCells) {
if (strpos((string) $mergeCells, ':') !== false) {
$this->spreadsheet->getActiveSheet()->mergeCells($mergeCells, Worksheet::MERGE_CELL_CONTENT_HIDE);
}
}
}
}
private function processAutofilter(?SimpleXMLElement $sheet): void
{
if ($sheet !== null && isset($sheet->Filters)) {
foreach ($sheet->Filters->Filter as $autofilter) {
if ($autofilter !== null) {
$attributes = $autofilter->attributes();
if (isset($attributes['Area'])) {
$this->spreadsheet->getActiveSheet()->setAutoFilter((string) $attributes['Area']);
}
}
}
}
}
private function setColumnWidth(int $whichColumn, float $defaultWidth): void
{
$columnDimension = $this->spreadsheet->getActiveSheet()
->getColumnDimension(Coordinate::stringFromColumnIndex($whichColumn + 1));
if ($columnDimension !== null) {
$columnDimension->setWidth($defaultWidth);
}
}
private function setColumnInvisible(int $whichColumn): void
{
$columnDimension = $this->spreadsheet->getActiveSheet()
->getColumnDimension(Coordinate::stringFromColumnIndex($whichColumn + 1));
if ($columnDimension !== null) {
$columnDimension->setVisible(false);
}
}
private function processColumnLoop(int $whichColumn, int $maxCol, ?SimpleXMLElement $columnOverride, float $defaultWidth): int
{
$columnOverride = self::testSimpleXml($columnOverride);
$columnAttributes = self::testSimpleXml($columnOverride->attributes());
$column = $columnAttributes['No'];
$columnWidth = ((float) $columnAttributes['Unit']) / 5.4;
$hidden = (isset($columnAttributes['Hidden'])) && ((string) $columnAttributes['Hidden'] == '1');
$columnCount = (int) ($columnAttributes['Count'] ?? 1);
while ($whichColumn < $column) {
$this->setColumnWidth($whichColumn, $defaultWidth);
++$whichColumn;
}
while (($whichColumn < ($column + $columnCount)) && ($whichColumn <= $maxCol)) {
$this->setColumnWidth($whichColumn, $columnWidth);
if ($hidden) {
$this->setColumnInvisible($whichColumn);
}
++$whichColumn;
}
return $whichColumn;
}
private function processColumnWidths(?SimpleXMLElement $sheet, int $maxCol): void
{
if ((!$this->readDataOnly) && $sheet !== null && (isset($sheet->Cols))) {
// Column Widths
$defaultWidth = 0;
$columnAttributes = $sheet->Cols->attributes();
if ($columnAttributes !== null) {
$defaultWidth = $columnAttributes['DefaultSizePts'] / 5.4;
}
$whichColumn = 0;
foreach ($sheet->Cols->ColInfo as $columnOverride) {
$whichColumn = $this->processColumnLoop($whichColumn, $maxCol, $columnOverride, $defaultWidth);
}
while ($whichColumn <= $maxCol) {
$this->setColumnWidth($whichColumn, $defaultWidth);
++$whichColumn;
}
}
}
private function setRowHeight(int $whichRow, float $defaultHeight): void
{
$rowDimension = $this->spreadsheet->getActiveSheet()->getRowDimension($whichRow);
if ($rowDimension !== null) {
$rowDimension->setRowHeight($defaultHeight);
}
}
private function setRowInvisible(int $whichRow): void
{
$rowDimension = $this->spreadsheet->getActiveSheet()->getRowDimension($whichRow);
if ($rowDimension !== null) {
$rowDimension->setVisible(false);
}
}
private function processRowLoop(int $whichRow, int $maxRow, ?SimpleXMLElement $rowOverride, float $defaultHeight): int
{
$rowOverride = self::testSimpleXml($rowOverride);
$rowAttributes = self::testSimpleXml($rowOverride->attributes());
$row = $rowAttributes['No'];
$rowHeight = (float) $rowAttributes['Unit'];
$hidden = (isset($rowAttributes['Hidden'])) && ((string) $rowAttributes['Hidden'] == '1');
$rowCount = (int) ($rowAttributes['Count'] ?? 1);
while ($whichRow < $row) {
++$whichRow;
$this->setRowHeight($whichRow, $defaultHeight);
}
while (($whichRow < ($row + $rowCount)) && ($whichRow < $maxRow)) {
++$whichRow;
$this->setRowHeight($whichRow, $rowHeight);
if ($hidden) {
$this->setRowInvisible($whichRow);
}
}
return $whichRow;
}
private function processRowHeights(?SimpleXMLElement $sheet, int $maxRow): void
{
if ((!$this->readDataOnly) && $sheet !== null && (isset($sheet->Rows))) {
// Row Heights
$defaultHeight = 0;
$rowAttributes = $sheet->Rows->attributes();
if ($rowAttributes !== null) {
$defaultHeight = (float) $rowAttributes['DefaultSizePts'];
}
$whichRow = 0;
foreach ($sheet->Rows->RowInfo as $rowOverride) {
$whichRow = $this->processRowLoop($whichRow, $maxRow, $rowOverride, $defaultHeight);
}
// never executed, I can't figure out any circumstances
// under which it would be executed, and, even if
// such exist, I'm not convinced this is needed.
//while ($whichRow < $maxRow) {
// ++$whichRow;
// $this->spreadsheet->getActiveSheet()->getRowDimension($whichRow)->setRowHeight($defaultHeight);
//}
}
}
private function processDefinedNames(?SimpleXMLElement $gnmXML): void
{
// Loop through definedNames (global named ranges)
if ($gnmXML !== null && isset($gnmXML->Names)) {
foreach ($gnmXML->Names->Name as $definedName) {
$name = (string) $definedName->name;
$value = (string) $definedName->value;
if (stripos($value, '#REF!') !== false) {
continue;
}
[$worksheetName] = Worksheet::extractSheetTitle($value, true);
$worksheetName = trim($worksheetName, "'");
$worksheet = $this->spreadsheet->getSheetByName($worksheetName);
// Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet
if ($worksheet !== null) {
$this->spreadsheet->addDefinedName(DefinedName::createInstance($name, $worksheet, $value));
}
}
}
}
private function parseRichText(string $is): RichText
{
$value = new RichText();
$value->createText($is);
return $value;
}
private function loadCell(
SimpleXMLElement $cell,
string $worksheetName,
SimpleXMLElement $cellAttributes,
string $column,
int $row
): void {
$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 {
$vtype = (string) $ValueType;
if (array_key_exists($vtype, self::$mappings['dataType'])) {
$type = self::$mappings['dataType'][$vtype];
}
if ($vtype === '20') { // Boolean
$cell = $cell == 'TRUE';
}
}
$this->spreadsheet->getActiveSheet()->getCell($column . $row)->setValueExplicit((string) $cell, $type);
if (isset($cellAttributes->ValueFormat)) {
$this->spreadsheet->getActiveSheet()->getCell($column . $row)
->getStyle()->getNumberFormat()
->setFormatCode((string) $cellAttributes->ValueFormat);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/IReadFilter.php 0000644 00000000640 15242753725 0017352 0 ustar 00 [
'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
];
/** @var array */
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.
*/
public function canRead(string $filename): bool
{
// Check if file exists
try {
$this->openFile($filename);
} 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(): string
{
fseek($this->fileHandle, 0);
return (string) fread($this->fileHandle, self::TEST_SAMPLE_SIZE);
}
private function readEnding(): string
{
$meta = stream_get_meta_data($this->fileHandle);
$filename = $meta['uri'];
$size = (int) filesize($filename);
if ($size === 0) {
return '';
}
$blockSize = self::TEST_SAMPLE_SIZE;
if ($size < $blockSize) {
$blockSize = $size;
}
fseek($this->fileHandle, $size - $blockSize);
return (string) fread($this->fileHandle, $blockSize);
}
private static function startsWithTag(string $data): bool
{
return '<' === substr(trim($data), 0, 1);
}
private static function endsWithTag(string $data): bool
{
return '>' === substr(trim($data), -1, 1);
}
private static function containsTags(string $data): bool
{
return strlen($data) !== strlen(strip_tags($data));
}
/**
* Loads Spreadsheet from file.
*/
public function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
// Load into this instance
return $this->loadIntoExisting($filename, $spreadsheet);
}
/**
* Set input encoding.
*
* @param string $inputEncoding Input encoding, eg: 'ANSI'
*
* @return $this
*
* @codeCoverageIgnore
*
* @deprecated no use is made of this property
*/
public function setInputEncoding($inputEncoding)
{
$this->inputEncoding = $inputEncoding;
return $this;
}
/**
* Get input encoding.
*
* @return string
*
* @codeCoverageIgnore
*
* @deprecated no use is made of this property
*/
public function getInputEncoding()
{
return $this->inputEncoding;
}
// Data Array used for testing only, should write to Spreadsheet object on completion of tests
/** @var array */
protected $dataArray = [];
/** @var int */
protected $tableLevel = 0;
/** @var array */
protected $nestedColumn = ['A'];
protected function setTableStartColumn(string $column): string
{
if ($this->tableLevel == 0) {
$column = 'A';
}
++$this->tableLevel;
$this->nestedColumn[$this->tableLevel] = $column;
return $this->nestedColumn[$this->tableLevel];
}
protected function getTableStartColumn(): string
{
return $this->nestedColumn[$this->tableLevel];
}
protected function releaseTableStartColumn(): string
{
--$this->tableLevel;
return array_pop($this->nestedColumn);
}
/**
* Flush cell.
*
* @param string $column
* @param int|string $row
* @param mixed $cellContent
*/
protected function flushCell(Worksheet $sheet, $column, $row, &$cellContent, array $attributeArray): 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
// Set cell value explicitly if there is data-type attribute
if (isset($attributeArray['data-type'])) {
$datatype = $attributeArray['data-type'];
if (in_array($datatype, [DataType::TYPE_STRING, DataType::TYPE_STRING2, DataType::TYPE_INLINE])) {
//Prevent to Excel treat string with beginning equal sign or convert big numbers to scientific number
if (substr($cellContent, 0, 1) === '=') {
$sheet->getCell($column . $row)
->getStyle()
->setQuotePrefix(true);
}
}
//catching the Exception and ignoring the invalid data types
try {
$sheet->setCellValueExplicit($column . $row, $cellContent, $attributeArray['data-type']);
} catch (\PhpOffice\PhpSpreadsheet\Exception $exception) {
$sheet->setCellValue($column . $row, $cellContent);
}
} else {
$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) '';
}
private function processDomElementBody(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child): void
{
$attributeArray = [];
foreach ($child->attributes as $attribute) {
$attributeArray[$attribute->name] = $attribute->value;
}
if ($child->nodeName === 'body') {
$row = 1;
$column = 'A';
$cellContent = '';
$this->tableLevel = 0;
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
} else {
$this->processDomElementTitle($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private function processDomElementTitle(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === 'title') {
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
$sheet->setTitle($cellContent, true, true);
$cellContent = '';
} else {
$this->processDomElementSpanEtc($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private const SPAN_ETC = ['span', 'div', 'font', 'i', 'em', 'strong', 'b'];
private function processDomElementSpanEtc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if (in_array((string) $child->nodeName, self::SPAN_ETC, true)) {
if (isset($attributeArray['class']) && $attributeArray['class'] === 'comment') {
$sheet->getComment($column . $row)
->getText()
->createTextRun($child->textContent);
} else {
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
}
if (isset($this->formats[$child->nodeName])) {
$sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
}
} else {
$this->processDomElementHr($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private function processDomElementHr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === 'hr') {
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
++$row;
if (isset($this->formats[$child->nodeName])) {
$sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
}
++$row;
}
// fall through to br
$this->processDomElementBr($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
private function processDomElementBr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === 'br' || $child->nodeName === 'hr') {
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, $attributeArray);
++$row;
}
} else {
$this->processDomElementA($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private function processDomElementA(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === '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.
}
}
}
// no idea why this should be needed
//$cellContent .= ' ';
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
} else {
$this->processDomElementH1Etc($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private const H1_ETC = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', 'p'];
private function processDomElementH1Etc(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if (in_array((string) $child->nodeName, self::H1_ETC, true)) {
if ($this->tableLevel > 0) {
// If we're inside a table, replace with a \n
$cellContent .= $cellContent ? "\n" : '';
$sheet->getStyle($column . $row)->getAlignment()->setWrapText(true);
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
} else {
if ($cellContent > '') {
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
++$row;
}
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
if (isset($this->formats[$child->nodeName])) {
$sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
}
++$row;
$column = 'A';
}
} else {
$this->processDomElementLi($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private function processDomElementLi(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === 'li') {
if ($this->tableLevel > 0) {
// If we're inside a table, replace with a \n
$cellContent .= $cellContent ? "\n" : '';
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
} else {
if ($cellContent > '') {
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
}
++$row;
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
$column = 'A';
}
} else {
$this->processDomElementImg($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private function processDomElementImg(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === 'img') {
$this->insertImage($sheet, $column, $row, $attributeArray);
} else {
$this->processDomElementTable($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private string $currentColumn = 'A';
private function processDomElementTable(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === 'table') {
$this->currentColumn = 'A';
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
$column = $this->setTableStartColumn($column);
if ($this->tableLevel > 1 && $row > 1) {
--$row;
}
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
$column = $this->releaseTableStartColumn();
if ($this->tableLevel > 1) {
++$column;
} else {
++$row;
}
} else {
$this->processDomElementTr($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private function processDomElementTr(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName === 'col') {
$this->applyInlineStyle($sheet, -1, $this->currentColumn, $attributeArray);
++$this->currentColumn;
} elseif ($child->nodeName === 'tr') {
$column = $this->getTableStartColumn();
$cellContent = '';
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
if (isset($attributeArray['height'])) {
$sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
}
++$row;
} else {
$this->processDomElementThTdOther($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private function processDomElementThTdOther(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
if ($child->nodeName !== 'td' && $child->nodeName !== 'th') {
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
} else {
$this->processDomElementThTd($sheet, $row, $column, $cellContent, $child, $attributeArray);
}
}
private function processDomElementBgcolor(Worksheet $sheet, int $row, string $column, array $attributeArray): void
{
if (isset($attributeArray['bgcolor'])) {
$sheet->getStyle("$column$row")->applyFromArray(
[
'fill' => [
'fillType' => Fill::FILL_SOLID,
'color' => ['rgb' => $this->getStyleColor($attributeArray['bgcolor'])],
],
]
);
}
}
private function processDomElementWidth(Worksheet $sheet, string $column, array $attributeArray): void
{
if (isset($attributeArray['width'])) {
$sheet->getColumnDimension($column)->setWidth((new CssDimension($attributeArray['width']))->width());
}
}
private function processDomElementHeight(Worksheet $sheet, int $row, array $attributeArray): void
{
if (isset($attributeArray['height'])) {
$sheet->getRowDimension($row)->setRowHeight((new CssDimension($attributeArray['height']))->height());
}
}
private function processDomElementAlign(Worksheet $sheet, int $row, string $column, array $attributeArray): void
{
if (isset($attributeArray['align'])) {
$sheet->getStyle($column . $row)->getAlignment()->setHorizontal($attributeArray['align']);
}
}
private function processDomElementVAlign(Worksheet $sheet, int $row, string $column, array $attributeArray): void
{
if (isset($attributeArray['valign'])) {
$sheet->getStyle($column . $row)->getAlignment()->setVertical($attributeArray['valign']);
}
}
private function processDomElementDataFormat(Worksheet $sheet, int $row, string $column, array $attributeArray): void
{
if (isset($attributeArray['data-format'])) {
$sheet->getStyle($column . $row)->getNumberFormat()->setFormatCode($attributeArray['data-format']);
}
}
private function processDomElementThTd(Worksheet $sheet, int &$row, string &$column, string &$cellContent, DOMElement $child, array &$attributeArray): void
{
while (isset($this->rowspan[$column . $row])) {
++$column;
}
$this->processDomElement($child, $sheet, $row, $column, $cellContent);
// apply inline style
$this->applyInlineStyle($sheet, $row, $column, $attributeArray);
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
$this->processDomElementBgcolor($sheet, $row, $column, $attributeArray);
$this->processDomElementWidth($sheet, $column, $attributeArray);
$this->processDomElementHeight($sheet, $row, $attributeArray);
$this->processDomElementAlign($sheet, $row, $column, $attributeArray);
$this->processDomElementVAlign($sheet, $row, $column, $attributeArray);
$this->processDomElementDataFormat($sheet, $row, $column, $attributeArray);
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;
}
++$column;
}
protected function processDomElement(DOMNode $element, Worksheet $sheet, int &$row, string &$column, string &$cellContent): void
{
foreach ($element->childNodes as $child) {
if ($child instanceof DOMText) {
$domText = (string) 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) {
$this->processDomElementBody($sheet, $row, $column, $cellContent, $child);
}
}
}
/**
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
*
* @param string $filename
*
* @return Spreadsheet
*/
public function loadIntoExisting($filename, Spreadsheet $spreadsheet)
{
// Validate
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an Invalid HTML file.');
}
// Create a new DOM object
$dom = new DOMDocument();
// Reload the HTML file into the DOM object
try {
$convert = $this->getSecurityScannerOrThrow()->scanFile($filename);
$lowend = "\u{80}";
$highend = "\u{10ffff}";
$regexp = "/[$lowend-$highend]/u";
/** @var callable */
$callback = [self::class, 'replaceNonAscii'];
$convert = preg_replace_callback($regexp, $callback, $convert);
$loaded = ($convert === null) ? false : $dom->loadHTML($convert);
} catch (Throwable $e) {
$loaded = false;
}
if ($loaded === false) {
throw new Exception('Failed to load ' . $filename . ' as a DOM Document', 0, $e ?? null);
}
self::loadProperties($dom, $spreadsheet);
return $this->loadDocument($dom, $spreadsheet);
}
private static function loadProperties(DOMDocument $dom, Spreadsheet $spreadsheet): void
{
$properties = $spreadsheet->getProperties();
foreach ($dom->getElementsByTagName('meta') as $meta) {
$metaContent = (string) $meta->getAttribute('content');
if ($metaContent !== '') {
$metaName = (string) $meta->getAttribute('name');
switch ($metaName) {
case 'author':
$properties->setCreator($metaContent);
break;
case 'category':
$properties->setCategory($metaContent);
break;
case 'company':
$properties->setCompany($metaContent);
break;
case 'created':
$properties->setCreated($metaContent);
break;
case 'description':
$properties->setDescription($metaContent);
break;
case 'keywords':
$properties->setKeywords($metaContent);
break;
case 'lastModifiedBy':
$properties->setLastModifiedBy($metaContent);
break;
case 'manager':
$properties->setManager($metaContent);
break;
case 'modified':
$properties->setModified($metaContent);
break;
case 'subject':
$properties->setSubject($metaContent);
break;
case 'title':
$properties->setTitle($metaContent);
break;
default:
if (preg_match('/^custom[.](bool|date|float|int|string)[.](.+)$/', $metaName, $matches) === 1) {
switch ($matches[1]) {
case 'bool':
$properties->setCustomProperty($matches[2], (bool) $metaContent, Properties::PROPERTY_TYPE_BOOLEAN);
break;
case 'float':
$properties->setCustomProperty($matches[2], (float) $metaContent, Properties::PROPERTY_TYPE_FLOAT);
break;
case 'int':
$properties->setCustomProperty($matches[2], (int) $metaContent, Properties::PROPERTY_TYPE_INTEGER);
break;
case 'date':
$properties->setCustomProperty($matches[2], $metaContent, Properties::PROPERTY_TYPE_DATE);
break;
default: // string
$properties->setCustomProperty($matches[2], $metaContent, Properties::PROPERTY_TYPE_STRING);
}
}
}
}
}
if (!empty($dom->baseURI)) {
$properties->setHyperlinkBase($dom->baseURI);
}
}
private static function replaceNonAscii(array $matches): string
{
return '' . mb_ord($matches[0], 'UTF-8') . ';';
}
/**
* 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
try {
$convert = $this->getSecurityScannerOrThrow()->scan($content);
$lowend = "\u{80}";
$highend = "\u{10ffff}";
$regexp = "/[$lowend-$highend]/u";
/** @var callable */
$callback = [self::class, 'replaceNonAscii'];
$convert = preg_replace_callback($regexp, $callback, $convert);
$loaded = ($convert === null) ? false : $dom->loadHTML($convert);
} catch (Throwable $e) {
$loaded = false;
}
if ($loaded === false) {
throw new Exception('Failed to load content as a DOM Document', 0, $e ?? null);
}
$spreadsheet = $spreadsheet ?? new Spreadsheet();
self::loadProperties($dom, $spreadsheet);
return $this->loadDocument($dom, $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 $sheetIndex Sheet index
*
* @return $this
*/
public function setSheetIndex($sheetIndex)
{
$this->sheetIndex = $sheetIndex;
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 int $row
* @param string $column
* @param array $attributeArray
*/
private function applyInlineStyle(Worksheet &$sheet, $row, $column, $attributeArray): void
{
if (!isset($attributeArray['style'])) {
return;
}
if ($row <= 0 || $column === '') {
$cellStyle = new Style();
} elseif (isset($attributeArray['rowspan'], $attributeArray['colspan'])) {
$columnTo = $column;
for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
++$columnTo;
}
$range = $column . $row . ':' . $columnTo . ($row + (int) $attributeArray['rowspan'] - 1);
$cellStyle = $sheet->getStyle($range);
} elseif (isset($attributeArray['rowspan'])) {
$range = $column . $row . ':' . $column . ($row + (int) $attributeArray['rowspan'] - 1);
$cellStyle = $sheet->getStyle($range);
} elseif (isset($attributeArray['colspan'])) {
$columnTo = $column;
for ($i = 0; $i < (int) $attributeArray['colspan'] - 1; ++$i) {
++$columnTo;
}
$range = $column . $row . ':' . $columnTo . $row;
$cellStyle = $sheet->getStyle($range);
} else {
$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;
$styleValueString = (string) $styleValue;
if (!$styleName) {
continue;
}
switch ($styleName) {
case 'background':
case 'background-color':
$styleColor = $this->getStyleColor($styleValueString);
if (!$styleColor) {
continue 2;
}
$cellStyle->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => $styleColor]]]);
break;
case 'color':
$styleColor = $this->getStyleColor($styleValueString);
if (!$styleColor) {
continue 2;
}
$cellStyle->applyFromArray(['font' => ['color' => ['rgb' => $styleColor]]]);
break;
case 'border':
$this->setBorderStyle($cellStyle, $styleValueString, 'allBorders');
break;
case 'border-top':
$this->setBorderStyle($cellStyle, $styleValueString, 'top');
break;
case 'border-bottom':
$this->setBorderStyle($cellStyle, $styleValueString, 'bottom');
break;
case 'border-left':
$this->setBorderStyle($cellStyle, $styleValueString, 'left');
break;
case 'border-right':
$this->setBorderStyle($cellStyle, $styleValueString, '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('\'', '', $styleValueString));
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($styleValueString);
break;
case 'vertical-align':
$cellStyle->getAlignment()->setVertical($styleValueString);
break;
case 'width':
if ($column !== '') {
$sheet->getColumnDimension($column)->setWidth(
(new CssDimension($styleValue ?? ''))->width()
);
}
break;
case 'height':
if ($row > 0) {
$sheet->getRowDimension($row)->setRowHeight(
(new CssDimension($styleValue ?? ''))->height()
);
}
break;
case 'word-wrap':
$cellStyle->getAlignment()->setWrapText(
$styleValue === 'break-word'
);
break;
case 'text-indent':
$cellStyle->getAlignment()->setIndent(
(int) str_replace(['px'], '', $styleValueString)
);
break;
}
}
}
/**
* Check if has #, so we can get clean hex.
*
* @param mixed $value
*
* @return null|string
*/
public function getStyleColor($value)
{
$value = (string) $value;
if (strpos($value, '#') === 0) {
return substr($value, 1);
}
return \PhpOffice\PhpSpreadsheet\Helper\Html::colourNameLookup($value);
}
/**
* @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 = $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
);
}
private const BORDER_MAPPINGS = [
'dash-dot' => Border::BORDER_DASHDOT,
'dash-dot-dot' => Border::BORDER_DASHDOTDOT,
'dashed' => Border::BORDER_DASHED,
'dotted' => Border::BORDER_DOTTED,
'double' => Border::BORDER_DOUBLE,
'hair' => Border::BORDER_HAIR,
'medium' => Border::BORDER_MEDIUM,
'medium-dashed' => Border::BORDER_MEDIUMDASHED,
'medium-dash-dot' => Border::BORDER_MEDIUMDASHDOT,
'medium-dash-dot-dot' => Border::BORDER_MEDIUMDASHDOTDOT,
'none' => Border::BORDER_NONE,
'slant-dash-dot' => Border::BORDER_SLANTDASHDOT,
'solid' => Border::BORDER_THIN,
'thick' => Border::BORDER_THICK,
];
public static function getBorderMappings(): array
{
return self::BORDER_MAPPINGS;
}
/**
* Map html border style to PhpSpreadsheet border style.
*
* @param string $style
*
* @return null|string
*/
public function getBorderStyle($style)
{
return self::BORDER_MAPPINGS[$style] ?? 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 {
$borderArray = explode(' ', $styleValue);
$borderCount = count($borderArray);
if ($borderCount >= 3) {
$borderStyle = $borderArray[1];
$color = $borderArray[2];
} else {
$borderStyle = $borderArray[0];
$color = $borderArray[1] ?? null;
}
}
$cellStyle->applyFromArray([
'borders' => [
$type => [
'borderStyle' => $this->getBorderStyle($borderStyle),
'color' => ['rgb' => $this->getStyleColor($color)],
],
],
]);
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/DefaultReadFilter.php 0000644 00000000731 15242753725 0020547 0 ustar 00 securityScanner = XmlScanner::getInstance($this);
}
/**
* Can the current IReader read the file?
*/
public function canRead(string $filename): bool
{
$mimeType = 'UNKNOWN';
// Load file
if (File::testFileNoThrow($filename, '')) {
$zip = new ZipArchive();
if ($zip->open($filename) === true) {
// check if it is an OOXML archive
$stat = $zip->statName('mimetype');
if (!empty($stat) && ($stat['size'] <= 255)) {
$mimeType = $zip->getFromName($stat['name']);
} elseif ($zip->statName('META-INF/manifest.xml')) {
$xml = simplexml_load_string(
$this->getSecurityScannerOrThrow()
->scan(
$zip->getFromName(
'META-INF/manifest.xml'
)
)
);
if ($xml !== false) {
$namespacesContent = $xml->getNamespaces(true);
if (isset($namespacesContent['manifest'])) {
$manifest = $xml->children($namespacesContent['manifest']);
foreach ($manifest as $manifestDataSet) {
/** @scrutinizer ignore-call */
$manifestAttributes = $manifestDataSet->attributes($namespacesContent['manifest']);
if ($manifestAttributes && $manifestAttributes->{'full-path'} == '/') {
$mimeType = (string) $manifestAttributes->{'media-type'};
break;
}
}
}
}
}
$zip->close();
}
}
return $mimeType === 'application/vnd.oasis.opendocument.spreadsheet';
}
/**
* Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object.
*
* @param string $filename
*
* @return string[]
*/
public function listWorksheetNames($filename)
{
File::assertFile($filename, self::INITIAL_FILE);
$worksheetNames = [];
$xml = new XMLReader();
$xml->xml(
$this->getSecurityScannerOrThrow()
->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE)
);
$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 (self::getXmlName($xml) !== '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()) {
$xmlName = self::getXmlName($xml);
if ($xmlName == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
// Loop through each table:table node reading the table:name attribute for each worksheet name
do {
$worksheetName = $xml->getAttribute('table:name');
if (!empty($worksheetName)) {
$worksheetNames[] = $worksheetName;
}
$xml->next();
} while (self::getXmlName($xml) == 'table:table' && $xml->nodeType == XMLReader::ELEMENT);
}
}
}
return $worksheetNames;
}
/**
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
*
* @param string $filename
*
* @return array
*/
public function listWorksheetInfo($filename)
{
File::assertFile($filename, self::INITIAL_FILE);
$worksheetInfo = [];
$xml = new XMLReader();
$xml->xml(
$this->getSecurityScannerOrThrow()
->scanFile('zip://' . realpath($filename) . '#' . self::INITIAL_FILE)
);
$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 (self::getXmlName($xml) !== '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 (self::getXmlName($xml) == '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 (self::getXmlName($xml) == '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 {
$doread = true;
if (self::getXmlName($xml) == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
if (!$xml->isEmptyElement) {
++$currCells;
$xml->next();
$doread = false;
}
} elseif (self::getXmlName($xml) == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
$mergeSize = $xml->getAttribute('table:number-columns-repeated');
$currCells += (int) $mergeSize;
}
if ($doread) {
$xml->read();
}
} while (self::getXmlName($xml) != 'table:table-row');
}
} while (self::getXmlName($xml) != 'table:table');
$tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
$tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
$tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
$worksheetInfo[] = $tmpInfo;
}
}
}
return $worksheetInfo;
}
/**
* Counteract Phpstan caching.
*
* @phpstan-impure
*/
private static function getXmlName(XMLReader $xml): string
{
return $xml->name;
}
/**
* Loads PhpSpreadsheet from file.
*/
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
// Load into this instance
return $this->loadIntoExisting($filename, $spreadsheet);
}
/**
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
*
* @param string $filename
*
* @return Spreadsheet
*/
public function loadIntoExisting($filename, Spreadsheet $spreadsheet)
{
File::assertFile($filename, self::INITIAL_FILE);
$zip = new ZipArchive();
$zip->open($filename);
// Meta
$xml = @simplexml_load_string(
$this->getSecurityScannerOrThrow()
->scan($zip->getFromName('meta.xml'))
);
if ($xml === false) {
throw new Exception('Unable to read data from {$pFilename}');
}
$namespacesMeta = $xml->getNamespaces(true);
(new DocumentProperties($spreadsheet))->load($xml, $namespacesMeta);
// Styles
$dom = new DOMDocument('1.01', 'UTF-8');
$dom->loadXML(
$this->getSecurityScannerOrThrow()
->scan($zip->getFromName('styles.xml'))
);
$pageSettings = new PageSettings($dom);
// Main Content
$dom = new DOMDocument('1.01', 'UTF-8');
$dom->loadXML(
$this->getSecurityScannerOrThrow()
->scan($zip->getFromName(self::INITIAL_FILE))
);
$officeNs = $dom->lookupNamespaceUri('office');
$tableNs = $dom->lookupNamespaceUri('table');
$textNs = $dom->lookupNamespaceUri('text');
$xlinkNs = $dom->lookupNamespaceUri('xlink');
$styleNs = $dom->lookupNamespaceUri('style');
$pageSettings->readStyleCrossReferences($dom);
$autoFilterReader = new AutoFilter($spreadsheet, $tableNs);
$definedNameReader = new DefinedNames($spreadsheet, $tableNs);
$columnWidths = [];
$automaticStyle0 = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles')->item(0);
$automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'style');
foreach ($automaticStyles as $automaticStyle) {
$styleName = $automaticStyle->getAttributeNS($styleNs, 'name');
$styleFamily = $automaticStyle->getAttributeNS($styleNs, 'family');
if ($styleFamily === 'table-column') {
$tcprops = $automaticStyle->getElementsByTagNameNS($styleNs, 'table-column-properties');
if ($tcprops !== null) {
$tcprop = $tcprops->item(0);
if ($tcprop !== null) {
$columnWidth = $tcprop->getAttributeNs($styleNs, 'column-width');
$columnWidths[$styleName] = $columnWidth;
}
}
}
}
// Content
$item0 = $dom->getElementsByTagNameNS($officeNs, 'body')->item(0);
$spreadsheets = ($item0 === null) ? [] : $item0->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 (
$this->loadSheetsOnly !== null
&& $worksheetName
&& !in_array($worksheetName, $this->loadSheetsOnly)
) {
continue;
}
$worksheetStyleName = $worksheetDataSet->getAttributeNS($tableNs, 'style-name');
// Create sheet
if ($worksheetID > 0) {
$spreadsheet->createSheet(); // First sheet is added by default
}
$spreadsheet->setActiveSheetIndex($worksheetID);
if ($worksheetName || is_numeric($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((string) $worksheetName, false, false);
}
// Go through every child of table element
$rowID = 1;
$tableColumnIndex = 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-column':
if ($childNode->hasAttributeNS($tableNs, 'number-columns-repeated')) {
$rowRepeats = (int) $childNode->getAttributeNS($tableNs, 'number-columns-repeated');
} else {
$rowRepeats = 1;
}
$tableStyleName = $childNode->getAttributeNS($tableNs, 'style-name');
if (isset($columnWidths[$tableStyleName])) {
$columnWidth = new HelperDimension($columnWidths[$tableStyleName]);
$tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex);
for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0; --$rowRepeats2) {
$spreadsheet->getActiveSheet()
->getColumnDimension($tableColumnString)
->setWidth($columnWidth->toUnit('cm'), 'cm');
++$tableColumnString;
}
}
$tableColumnIndex += $rowRepeats;
break;
case 'table-row':
if ($childNode->hasAttributeNS($tableNs, 'number-rows-repeated')) {
$rowRepeats = (int) $childNode->getAttributeNS($tableNs, 'number-rows-repeated');
} else {
$rowRepeats = 1;
}
$columnID = 'A';
/** @var DOMElement $cellData */
foreach ($childNode->childNodes as $cellData) {
if ($this->getReadFilter() !== null) {
if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
$colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
} else {
$colRepeats = 1;
}
for ($i = 0; $i < $colRepeats; ++$i) {
++$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 && $annotation->item(0) !== null) {
$textNode = $annotation->item(0)->getElementsByTagNameNS($textNs, 'p');
if ($textNode->length > 0 && $textNode->item(0) !== null) {
$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 && $link->item(0) !== null) {
$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');
// percentage should always be float
//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_INTEGER;
break;
case 'float':
$type = DataType::TYPE_NUMERIC;
$dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
if (floor($dataValue) == $dataValue) {
if ($dataValue == (int) $dataValue) {
$dataValue = (int) $dataValue;
}
}
break;
case 'date':
$type = DataType::TYPE_NUMERIC;
$value = $cellData->getAttributeNS($officeNs, 'date-value');
$dataValue = Date::convertIsoDate($value);
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(':', /** @scrutinizer ignore-type */ 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);
$cellDataFormula = FormulaTranslator::convertToExcelFormulaValue($cellDataFormula);
}
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
$this->processMergedCells($cellData, $tableNs, $type, $columnID, $rowID, $spreadsheet);
++$columnID;
}
$rowID += $rowRepeats;
break;
}
}
$pageSettings->setVisibilityForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName);
$pageSettings->setPrintSettingsForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName);
++$worksheetID;
}
$autoFilterReader->read($workbookData);
$definedNameReader->read($workbookData);
}
$spreadsheet->setActiveSheetIndex(0);
if ($zip->locateName('settings.xml') !== false) {
$this->processSettings($zip, $spreadsheet);
}
// Return
return $spreadsheet;
}
private function processSettings(ZipArchive $zip, Spreadsheet $spreadsheet): void
{
$dom = new DOMDocument('1.01', 'UTF-8');
$dom->loadXML(
$this->getSecurityScannerOrThrow()
->scan($zip->getFromName('settings.xml'))
);
//$xlinkNs = $dom->lookupNamespaceUri('xlink');
$configNs = $dom->lookupNamespaceUri('config');
//$oooNs = $dom->lookupNamespaceUri('ooo');
$officeNs = $dom->lookupNamespaceUri('office');
$settings = $dom->getElementsByTagNameNS($officeNs, 'settings')
->item(0);
if ($settings !== null) {
$this->lookForActiveSheet($settings, $spreadsheet, $configNs);
$this->lookForSelectedCells($settings, $spreadsheet, $configNs);
}
}
private function lookForActiveSheet(DOMElement $settings, Spreadsheet $spreadsheet, string $configNs): void
{
/** @var DOMElement $t */
foreach ($settings->getElementsByTagNameNS($configNs, 'config-item') as $t) {
if ($t->getAttributeNs($configNs, 'name') === 'ActiveTable') {
try {
$spreadsheet->setActiveSheetIndexByName($t->nodeValue ?? '');
} catch (Throwable $e) {
// do nothing
}
break;
}
}
}
private function lookForSelectedCells(DOMElement $settings, Spreadsheet $spreadsheet, string $configNs): void
{
/** @var DOMElement $t */
foreach ($settings->getElementsByTagNameNS($configNs, 'config-item-map-named') as $t) {
if ($t->getAttributeNs($configNs, 'name') === 'Tables') {
foreach ($t->getElementsByTagNameNS($configNs, 'config-item-map-entry') as $ws) {
$setRow = $setCol = '';
$wsname = $ws->getAttributeNs($configNs, 'name');
foreach ($ws->getElementsByTagNameNS($configNs, 'config-item') as $configItem) {
$attrName = $configItem->getAttributeNs($configNs, 'name');
if ($attrName === 'CursorPositionX') {
$setCol = $configItem->nodeValue;
}
if ($attrName === 'CursorPositionY') {
$setRow = $configItem->nodeValue;
}
}
$this->setSelected($spreadsheet, $wsname, "$setCol", "$setRow");
}
break;
}
}
}
private function setSelected(Spreadsheet $spreadsheet, string $wsname, string $setCol, string $setRow): void
{
if (is_numeric($setCol) && is_numeric($setRow)) {
$sheet = $spreadsheet->getSheetByName($wsname);
if ($sheet !== null) {
$sheet->setSelectedCells([(int) $setCol + 1, (int) $setRow + 1]);
}
}
}
/**
* 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?
$attributes = $child->attributes;
/** @var ?DOMAttr $cAttr */
$cAttr = ($attributes === null) ? null : $attributes->getNamedItem('c');
$multiplier = self::getMultiplier($cAttr);
$str .= str_repeat(' ', $multiplier);
}
if ($child->hasChildNodes()) {
$str .= $this->scanElementForText($child);
}
}
return $str;
}
private static function getMultiplier(?DOMAttr $cAttr): int
{
if ($cAttr) {
$multiplier = (int) $cAttr->nodeValue;
} else {
$multiplier = 1;
}
return $multiplier;
}
/**
* @param string $is
*
* @return RichText
*/
private function parseRichText($is)
{
$value = new RichText();
$value->createText($is);
return $value;
}
private function processMergedCells(
DOMElement $cellData,
string $tableNs,
string $type,
string $columnID,
int $rowID,
Spreadsheet $spreadsheet
): void {
if (
$cellData->hasAttributeNS($tableNs, 'number-columns-spanned')
|| $cellData->hasAttributeNS($tableNs, 'number-rows-spanned')
) {
if (($type !== DataType::TYPE_NULL) || ($this->readDataOnly === false)) {
$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, Worksheet::MERGE_CELL_CONTENT_HIDE);
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php 0000644 00000053740 15242753725 0015764 0 ustar 00 inputEncoding = $encoding;
return $this;
}
public function getInputEncoding(): string
{
return $this->inputEncoding;
}
public function setFallbackEncoding(string $fallbackEncoding): self
{
$this->fallbackEncoding = $fallbackEncoding;
return $this;
}
public function getFallbackEncoding(): string
{
return $this->fallbackEncoding;
}
/**
* Move filepointer past any BOM marker.
*/
protected function skipBOM(): void
{
rewind($this->fileHandle);
if (fgets($this->fileHandle, self::UTF8_BOM_LEN + 1) !== self::UTF8_BOM) {
rewind($this->fileHandle);
}
}
/**
* 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;
}
$inferenceEngine = new Delimiter($this->fileHandle, $this->escapeCharacter ?? self::$defaultEscapeCharacter, $this->enclosure);
// If number of lines is 0, nothing to infer : fall back to the default
if ($inferenceEngine->linesCounted() === 0) {
$this->delimiter = $inferenceEngine->getDefaultDelimiter();
$this->skipBOM();
return;
}
$this->delimiter = $inferenceEngine->infer();
// If no delimiter could be detected, fall back to the default
if ($this->delimiter === null) {
$this->delimiter = $inferenceEngine->getDefaultDelimiter();
}
$this->skipBOM();
}
/**
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
*/
public function listWorksheetInfo(string $filename): array
{
// Open file
$this->openFileOrMemory($filename);
$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
$rowData = self::getCsv($fileHandle, 0, $this->delimiter ?? '', $this->enclosure, $this->escapeCharacter);
while (is_array($rowData)) {
++$worksheetInfo[0]['totalRows'];
$worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], count($rowData) - 1);
$rowData = self::getCsv($fileHandle, 0, $this->delimiter ?? '', $this->enclosure, $this->escapeCharacter);
}
$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.
*/
protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
{
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
// Load into this instance
return $this->loadIntoExisting($filename, $spreadsheet);
}
/**
* Loads Spreadsheet from string.
*/
public function loadSpreadsheetFromString(string $contents): Spreadsheet
{
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
// Load into this instance
return $this->loadStringOrFile('data://text/plain,' . urlencode($contents), $spreadsheet, true);
}
private function openFileOrMemory(string $filename): void
{
// Open file
$fhandle = $this->canRead($filename);
if (!$fhandle) {
throw new Exception($filename . ' is an Invalid Spreadsheet file.');
}
if ($this->inputEncoding === 'UTF-8') {
$encoding = self::guessEncodingBom($filename);
if ($encoding !== '') {
$this->inputEncoding = $encoding;
}
}
if ($this->inputEncoding === self::GUESS_ENCODING) {
$this->inputEncoding = self::guessEncoding($filename, $this->fallbackEncoding);
}
$this->openFile($filename);
if ($this->inputEncoding !== 'UTF-8') {
fclose($this->fileHandle);
$entireFile = file_get_contents($filename);
$fileHandle = fopen('php://memory', 'r+b');
if ($fileHandle !== false && $entireFile !== false) {
$this->fileHandle = $fileHandle;
$data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
fwrite($this->fileHandle, $data);
$this->skipBOM();
}
}
}
public function setTestAutoDetect(bool $value): self
{
$this->testAutodetect = $value;
return $this;
}
private function setAutoDetect(?string $value): ?string
{
$retVal = null;
if ($value !== null && $this->testAutodetect && PHP_VERSION_ID < 90000) {
$retVal2 = @ini_set('auto_detect_line_endings', $value);
if (is_string($retVal2)) {
$retVal = $retVal2;
}
}
return $retVal;
}
public function castFormattedNumberToNumeric(
bool $castFormattedNumberToNumeric,
bool $preserveNumericFormatting = false
): void {
$this->castFormattedNumberToNumeric = $castFormattedNumberToNumeric;
$this->preserveNumericFormatting = $preserveNumericFormatting;
}
/**
* Open data uri for reading.
*/
private function openDataUri(string $filename): void
{
$fileHandle = fopen($filename, 'rb');
if ($fileHandle === false) {
// @codeCoverageIgnoreStart
throw new ReaderException('Could not open file ' . $filename . ' for reading.');
// @codeCoverageIgnoreEnd
}
$this->fileHandle = $fileHandle;
}
/**
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
*/
public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet): Spreadsheet
{
return $this->loadStringOrFile($filename, $spreadsheet, false);
}
/**
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
*/
private function loadStringOrFile(string $filename, Spreadsheet $spreadsheet, bool $dataUri): Spreadsheet
{
// Deprecated in Php8.1
$iniset = $this->setAutoDetect('1');
try {
$this->loadStringOrFile2($filename, $spreadsheet, $dataUri);
$this->setAutoDetect($iniset);
} catch (Throwable $e) {
$this->setAutoDetect($iniset);
throw $e;
}
return $spreadsheet;
}
private function loadStringOrFile2(string $filename, Spreadsheet $spreadsheet, bool $dataUri): void
{
// Open file
if ($dataUri) {
$this->openDataUri($filename);
} else {
$this->openFileOrMemory($filename);
}
$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
$rowData = self::getCsv($fileHandle, 0, $this->delimiter ?? '', $this->enclosure, $this->escapeCharacter);
$valueBinder = Cell::getValueBinder();
$preserveBooleanString = method_exists($valueBinder, 'getBooleanConversion') && $valueBinder->getBooleanConversion();
while (is_array($rowData)) {
$noOutputYet = true;
$columnLetter = 'A';
foreach ($rowData as $rowDatum) {
$this->convertBoolean($rowDatum, $preserveBooleanString);
$numberFormatMask = $this->convertFormattedNumber($rowDatum);
if (($rowDatum !== '' || $this->preserveNullString) && $this->readFilter->readCell($columnLetter, $currentRow)) {
if ($this->contiguous) {
if ($noOutputYet) {
$noOutputYet = false;
++$outRow;
}
} else {
$outRow = $currentRow;
}
// Set basic styling for the value (Note that this could be overloaded by styling in a value binder)
$sheet->getCell($columnLetter . $outRow)->getStyle()
->getNumberFormat()
->setFormatCode($numberFormatMask);
// Set cell value
$sheet->getCell($columnLetter . $outRow)->setValue($rowDatum);
}
++$columnLetter;
}
$rowData = self::getCsv($fileHandle, 0, $this->delimiter ?? '', $this->enclosure, $this->escapeCharacter);
++$currentRow;
}
// Close file
fclose($fileHandle);
}
/**
* Convert string true/false to boolean, and null to null-string.
*
* @param mixed $rowDatum
*/
private function convertBoolean(&$rowDatum, bool $preserveBooleanString): void
{
if (is_string($rowDatum) && !$preserveBooleanString) {
if (strcasecmp(Calculation::getTRUE(), $rowDatum) === 0 || strcasecmp('true', $rowDatum) === 0) {
$rowDatum = true;
} elseif (strcasecmp(Calculation::getFALSE(), $rowDatum) === 0 || strcasecmp('false', $rowDatum) === 0) {
$rowDatum = false;
}
} else {
$rowDatum = $rowDatum ?? '';
}
}
/**
* Convert numeric strings to int or float values.
*
* @param mixed $rowDatum
*/
private function convertFormattedNumber(&$rowDatum): string
{
$numberFormatMask = NumberFormat::FORMAT_GENERAL;
if ($this->castFormattedNumberToNumeric === true && is_string($rowDatum)) {
$numeric = str_replace(
[StringHelper::getThousandsSeparator(), StringHelper::getDecimalSeparator()],
['', '.'],
$rowDatum
);
if (is_numeric($numeric)) {
$decimalPos = strpos($rowDatum, StringHelper::getDecimalSeparator());
if ($this->preserveNumericFormatting === true) {
$numberFormatMask = (strpos($rowDatum, StringHelper::getThousandsSeparator()) !== false)
? '#,##0' : '0';
if ($decimalPos !== false) {
$decimals = strlen($rowDatum) - $decimalPos - 1;
$numberFormatMask .= '.' . str_repeat('0', min($decimals, 6));
}
}
$rowDatum = ($decimalPos !== false) ? (float) $numeric : (int) $numeric;
}
}
return $numberFormatMask;
}
public function getDelimiter(): ?string
{
return $this->delimiter;
}
public function setDelimiter(?string $delimiter): self
{
$this->delimiter = $delimiter;
return $this;
}
public function getEnclosure(): string
{
return $this->enclosure;
}
public function setEnclosure(string $enclosure): self
{
if ($enclosure == '') {
$enclosure = '"';
}
$this->enclosure = $enclosure;
return $this;
}
public function getSheetIndex(): int
{
return $this->sheetIndex;
}
public function setSheetIndex(int $indexValue): self
{
$this->sheetIndex = $indexValue;
return $this;
}
public function setContiguous(bool $contiguous): self
{
$this->contiguous = $contiguous;
return $this;
}
public function getContiguous(): bool
{
return $this->contiguous;
}
/**
* Php9 intends to drop support for this parameter in fgetcsv.
* Not yet ready to mark deprecated in order to give users
* a migration path.
*/
public function setEscapeCharacter(string $escapeCharacter): self
{
if (PHP_VERSION_ID >= 90000 && $escapeCharacter !== '') {
throw new ReaderException('Escape character must be null string for Php9+');
}
$this->escapeCharacter = $escapeCharacter;
return $this;
}
public function getEscapeCharacter(): string
{
return $this->escapeCharacter ?? self::$defaultEscapeCharacter;
}
/**
* Can the current IReader read the file?
*/
public function canRead(string $filename): bool
{
// Check if file exists
try {
$this->openFile($filename);
} catch (ReaderException $e) {
return false;
}
fclose($this->fileHandle);
// Trust file extension if any
$extension = strtolower(/** @scrutinizer ignore-type */ pathinfo($filename, PATHINFO_EXTENSION));
if (in_array($extension, ['csv', 'tsv'])) {
return true;
}
// Attempt to guess mimetype
$type = mime_content_type($filename);
$supportedTypes = [
'application/csv',
'text/csv',
'text/plain',
'inode/x-empty',
];
return in_array($type, $supportedTypes, true);
}
private static function guessEncodingTestNoBom(string &$encoding, string &$contents, string $compare, string $setEncoding): void
{
if ($encoding === '') {
$pos = strpos($contents, $compare);
if ($pos !== false && $pos % strlen($compare) === 0) {
$encoding = $setEncoding;
}
}
}
private static function guessEncodingNoBom(string $filename): string
{
$encoding = '';
$contents = file_get_contents($filename);
self::guessEncodingTestNoBom($encoding, $contents, self::UTF32BE_LF, 'UTF-32BE');
self::guessEncodingTestNoBom($encoding, $contents, self::UTF32LE_LF, 'UTF-32LE');
self::guessEncodingTestNoBom($encoding, $contents, self::UTF16BE_LF, 'UTF-16BE');
self::guessEncodingTestNoBom($encoding, $contents, self::UTF16LE_LF, 'UTF-16LE');
if ($encoding === '' && preg_match('//u', $contents) === 1) {
$encoding = 'UTF-8';
}
return $encoding;
}
private static function guessEncodingTestBom(string &$encoding, string $first4, string $compare, string $setEncoding): void
{
if ($encoding === '') {
if ($compare === substr($first4, 0, strlen($compare))) {
$encoding = $setEncoding;
}
}
}
public static function guessEncodingBom(string $filename, ?string $convertString = null): string
{
$encoding = '';
$first4 = $convertString ?? (string) file_get_contents($filename, false, null, 0, 4);
self::guessEncodingTestBom($encoding, $first4, self::UTF8_BOM, 'UTF-8');
self::guessEncodingTestBom($encoding, $first4, self::UTF16BE_BOM, 'UTF-16BE');
self::guessEncodingTestBom($encoding, $first4, self::UTF32BE_BOM, 'UTF-32BE');
self::guessEncodingTestBom($encoding, $first4, self::UTF32LE_BOM, 'UTF-32LE');
self::guessEncodingTestBom($encoding, $first4, self::UTF16LE_BOM, 'UTF-16LE');
return $encoding;
}
public static function guessEncoding(string $filename, string $dflt = self::DEFAULT_FALLBACK_ENCODING): string
{
$encoding = self::guessEncodingBom($filename);
if ($encoding === '') {
$encoding = self::guessEncodingNoBom($filename);
}
return ($encoding === '') ? $dflt : $encoding;
}
public function setPreserveNullString(bool $value): self
{
$this->preserveNullString = $value;
return $this;
}
public function getPreserveNullString(): bool
{
return $this->preserveNullString;
}
/**
* Php8.4 deprecates use of anything other than null string
* as escape Character.
*
* @param resource $stream
*
* @return array|false
*/
private static function getCsv(
$stream,
?int $length = null,
string $separator = ',',
string $enclosure = '"',
?string $escape = null
) {
$escape = $escape ?? self::$defaultEscapeCharacter;
if (PHP_VERSION_ID >= 80400 && $escape !== '') {
return @fgetcsv($stream, $length, $separator, $enclosure, $escape);
}
return fgetcsv($stream, $length, $separator, $enclosure, $escape);
}
public static function affectedByPhp9(
string $filename,
string $inputEncoding = 'UTF-8',
?string $delimiter = null,
string $enclosure = '"',
string $escapeCharacter = '\\'
): bool {
if (PHP_VERSION_ID < 70400 || PHP_VERSION_ID >= 90000) {
throw new ReaderException('Function valid only for Php7.4 or Php8'); // @codeCoverageIgnore
}
$reader1 = new self();
$reader1->setInputEncoding($inputEncoding)
->setTestAutoDetect(true)
->setEscapeCharacter($escapeCharacter)
->setDelimiter($delimiter)
->setEnclosure($enclosure);
$spreadsheet1 = $reader1->load($filename);
$sheet1 = $spreadsheet1->getActiveSheet();
$array1 = $sheet1->toArray(null, false, false);
$spreadsheet1->disconnectWorksheets();
$reader2 = new self();
$reader2->setInputEncoding($inputEncoding)
->setTestAutoDetect(false)
->setEscapeCharacter('')
->setDelimiter($delimiter)
->setEnclosure($enclosure);
$spreadsheet2 = $reader2->load($filename);
$sheet2 = $spreadsheet2->getActiveSheet();
$array2 = $sheet2->toArray(null, false, false);
$spreadsheet2->disconnectWorksheets();
return $array1 !== $array2;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/File.php 0000644 00000004255 15242753725 0017172 0 ustar 00 |
// | Based on OLE::Storage_Lite by Kawai, Takanori |
// +----------------------------------------------------------------------+
//
use PhpOffice\PhpSpreadsheet\Shared\OLE;
use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
/**
* Class for creating File PPS's for OLE containers.
*
* @author Xavier Noguer
*/
class File extends PPS
{
/**
* The constructor.
*
* @param string $name The name of the file (in Unicode)
*
* @see OLE::ascToUcs()
*/
public function __construct($name)
{
parent::__construct(null, $name, OLE::OLE_PPS_TYPE_FILE, null, null, null, null, null, '', []);
}
/**
* Initialization method. Has to be called right after OLE_PPS_File().
*
* @return mixed true on success
*/
public function init()
{
return true;
}
/**
* Append data to PPS.
*
* @param string $data The data to append
*/
public function append($data): void
{
$this->_data .= $data;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php 0000644 00000035273 15242753725 0017242 0 ustar 00 |
// | Based on OLE::Storage_Lite by Kawai, Takanori |
// +----------------------------------------------------------------------+
//
use PhpOffice\PhpSpreadsheet\Shared\OLE;
use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
/**
* Class for creating Root PPS's for OLE containers.
*
* @author Xavier Noguer
*/
class Root extends PPS
{
/**
* @var resource
*/
private $fileHandle;
/**
* @var ?int
*/
private $smallBlockSize;
/**
* @var ?int
*/
private $bigBlockSize;
/**
* @param null|float|int $time_1st A timestamp
* @param null|float|int $time_2nd A timestamp
* @param File[] $raChild
*/
public function __construct($time_1st, $time_2nd, $raChild)
{
parent::__construct(null, OLE::ascToUcs('Root Entry'), OLE::OLE_PPS_TYPE_ROOT, null, null, null, $time_1st, $time_2nd, null, $raChild);
}
/**
* Method for saving the whole OLE container (including files).
* In fact, if called with an empty argument (or '-'), it saves to a
* temporary file and then outputs it's contents to stdout.
* If a resource pointer to a stream created by fopen() is passed
* it will be used, but you have to close such stream by yourself.
*
* @param resource $fileHandle the name of the file or stream where to save the OLE container
*
* @return bool true on success
*/
public function save($fileHandle)
{
$this->fileHandle = $fileHandle;
// Initial Setting for saving
$this->bigBlockSize = (int) (2 ** (
(isset($this->bigBlockSize)) ? self::adjust2($this->bigBlockSize) : 9
));
$this->smallBlockSize = (int) (2 ** (
(isset($this->smallBlockSize)) ? self::adjust2($this->smallBlockSize) : 6
));
// Make an array of PPS's (for Save)
$aList = [];
PPS::savePpsSetPnt($aList, [$this]);
// calculate values for header
[$iSBDcnt, $iBBcnt, $iPPScnt] = $this->calcSize($aList); //, $rhInfo);
// Save Header
$this->saveHeader((int) $iSBDcnt, (int) $iBBcnt, (int) $iPPScnt);
// Make Small Data string (write SBD)
$this->_data = $this->makeSmallData($aList);
// Write BB
$this->saveBigData((int) $iSBDcnt, $aList);
// Write PPS
$this->savePps($aList);
// Write Big Block Depot and BDList and Adding Header informations
$this->saveBbd((int) $iSBDcnt, (int) $iBBcnt, (int) $iPPScnt);
return true;
}
/**
* Calculate some numbers.
*
* @param array $raList Reference to an array of PPS's
*
* @return float[] The array of numbers
*/
private function calcSize(&$raList)
{
// Calculate Basic Setting
[$iSBDcnt, $iBBcnt, $iPPScnt] = [0, 0, 0];
$iSBcnt = 0;
$iCount = count($raList);
for ($i = 0; $i < $iCount; ++$i) {
if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
$raList[$i]->Size = $raList[$i]->getDataLen();
if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
$iSBcnt += floor($raList[$i]->Size / $this->smallBlockSize)
+ (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
} else {
$iBBcnt += (floor($raList[$i]->Size / $this->bigBlockSize) +
(($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
}
}
}
$iSmallLen = $iSBcnt * $this->smallBlockSize;
$iSlCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
$iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt) ? 1 : 0);
$iBBcnt += (floor($iSmallLen / $this->bigBlockSize) +
(($iSmallLen % $this->bigBlockSize) ? 1 : 0));
$iCnt = count($raList);
$iBdCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
$iPPScnt = (floor($iCnt / $iBdCnt) + (($iCnt % $iBdCnt) ? 1 : 0));
return [$iSBDcnt, $iBBcnt, $iPPScnt];
}
/**
* Helper function for caculating a magic value for block sizes.
*
* @param int $i2 The argument
*
* @return float
*
* @see save()
*/
private static function adjust2($i2)
{
$iWk = log($i2) / log(2);
return ($iWk > floor($iWk)) ? floor($iWk) + 1 : $iWk;
}
/**
* Save OLE header.
*
* @param int $iSBDcnt
* @param int $iBBcnt
* @param int $iPPScnt
*/
private function saveHeader($iSBDcnt, $iBBcnt, $iPPScnt): void
{
$FILE = $this->fileHandle;
// Calculate Basic Setting
$iBlCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
$i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;
$iBdExL = 0;
$iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
$iAllW = $iAll;
$iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
$iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);
// Calculate BD count
if ($iBdCnt > $i1stBdL) {
while (1) {
++$iBdExL;
++$iAllW;
$iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
$iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);
if ($iBdCnt <= ($iBdExL * $iBlCnt + $i1stBdL)) {
break;
}
}
}
// Save Header
fwrite(
$FILE,
"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
. "\x00\x00\x00\x00"
. "\x00\x00\x00\x00"
. "\x00\x00\x00\x00"
. "\x00\x00\x00\x00"
. pack('v', 0x3b)
. pack('v', 0x03)
. pack('v', -2)
. pack('v', 9)
. pack('v', 6)
. pack('v', 0)
. "\x00\x00\x00\x00"
. "\x00\x00\x00\x00"
. pack('V', $iBdCnt)
. pack('V', $iBBcnt + $iSBDcnt) //ROOT START
. pack('V', 0)
. pack('V', 0x1000)
. pack('V', $iSBDcnt ? 0 : -2) //Small Block Depot
. pack('V', $iSBDcnt)
);
// Extra BDList Start, Count
if ($iBdCnt < $i1stBdL) {
fwrite(
$FILE,
pack('V', -2) // Extra BDList Start
. pack('V', 0)// Extra BDList Count
);
} else {
fwrite($FILE, pack('V', $iAll + $iBdCnt) . pack('V', $iBdExL));
}
// BDList
for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; ++$i) {
fwrite($FILE, pack('V', $iAll + $i));
}
if ($i < $i1stBdL) {
$jB = $i1stBdL - $i;
for ($j = 0; $j < $jB; ++$j) {
fwrite($FILE, (pack('V', -1)));
}
}
}
/**
* Saving big data (PPS's with data bigger than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
*
* @param int $iStBlk
* @param array $raList Reference to array of PPS's
*/
private function saveBigData($iStBlk, &$raList): void
{
$FILE = $this->fileHandle;
// cycle through PPS's
$iCount = count($raList);
for ($i = 0; $i < $iCount; ++$i) {
if ($raList[$i]->Type != OLE::OLE_PPS_TYPE_DIR) {
$raList[$i]->Size = $raList[$i]->getDataLen();
if (($raList[$i]->Size >= OLE::OLE_DATA_SIZE_SMALL) || (($raList[$i]->Type == OLE::OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) {
fwrite($FILE, $raList[$i]->_data);
if ($raList[$i]->Size % $this->bigBlockSize) {
fwrite($FILE, str_repeat("\x00", $this->bigBlockSize - ($raList[$i]->Size % $this->bigBlockSize)));
}
// Set For PPS
$raList[$i]->startBlock = $iStBlk;
$iStBlk +=
(floor($raList[$i]->Size / $this->bigBlockSize) +
(($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
}
}
}
}
/**
* get small data (PPS's with data smaller than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
*
* @param array $raList Reference to array of PPS's
*
* @return string
*/
private function makeSmallData(&$raList)
{
$sRes = '';
$FILE = $this->fileHandle;
$iSmBlk = 0;
$iCount = count($raList);
for ($i = 0; $i < $iCount; ++$i) {
// Make SBD, small data string
if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
if ($raList[$i]->Size <= 0) {
continue;
}
if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
$iSmbCnt = floor($raList[$i]->Size / $this->smallBlockSize)
+ (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
// Add to SBD
$jB = $iSmbCnt - 1;
for ($j = 0; $j < $jB; ++$j) {
fwrite($FILE, pack('V', $j + $iSmBlk + 1));
}
fwrite($FILE, pack('V', -2));
// Add to Data String(this will be written for RootEntry)
$sRes .= $raList[$i]->_data;
if ($raList[$i]->Size % $this->smallBlockSize) {
$sRes .= str_repeat("\x00", $this->smallBlockSize - ($raList[$i]->Size % $this->smallBlockSize));
}
// Set for PPS
$raList[$i]->startBlock = $iSmBlk;
$iSmBlk += $iSmbCnt;
}
}
}
$iSbCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
if ($iSmBlk % $iSbCnt) {
$iB = $iSbCnt - ($iSmBlk % $iSbCnt);
for ($i = 0; $i < $iB; ++$i) {
fwrite($FILE, pack('V', -1));
}
}
return $sRes;
}
/**
* Saves all the PPS's WKs.
*
* @param array $raList Reference to an array with all PPS's
*/
private function savePps(&$raList): void
{
// Save each PPS WK
$iC = count($raList);
for ($i = 0; $i < $iC; ++$i) {
fwrite($this->fileHandle, $raList[$i]->getPpsWk());
}
// Adjust for Block
$iCnt = count($raList);
$iBCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
if ($iCnt % $iBCnt) {
fwrite($this->fileHandle, str_repeat("\x00", ($iBCnt - ($iCnt % $iBCnt)) * OLE::OLE_PPS_SIZE));
}
}
/**
* Saving Big Block Depot.
*
* @param int $iSbdSize
* @param int $iBsize
* @param int $iPpsCnt
*/
private function saveBbd($iSbdSize, $iBsize, $iPpsCnt): void
{
$FILE = $this->fileHandle;
// Calculate Basic Setting
$iBbCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
$i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;
$iBdExL = 0;
$iAll = $iBsize + $iPpsCnt + $iSbdSize;
$iAllW = $iAll;
$iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
$iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
// Calculate BD count
if ($iBdCnt > $i1stBdL) {
while (1) {
++$iBdExL;
++$iAllW;
$iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
$iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
if ($iBdCnt <= ($iBdExL * $iBbCnt + $i1stBdL)) {
break;
}
}
}
// Making BD
// Set for SBD
if ($iSbdSize > 0) {
for ($i = 0; $i < ($iSbdSize - 1); ++$i) {
fwrite($FILE, pack('V', $i + 1));
}
fwrite($FILE, pack('V', -2));
}
// Set for B
for ($i = 0; $i < ($iBsize - 1); ++$i) {
fwrite($FILE, pack('V', $i + $iSbdSize + 1));
}
fwrite($FILE, pack('V', -2));
// Set for PPS
for ($i = 0; $i < ($iPpsCnt - 1); ++$i) {
fwrite($FILE, pack('V', $i + $iSbdSize + $iBsize + 1));
}
fwrite($FILE, pack('V', -2));
// Set for BBD itself ( 0xFFFFFFFD : BBD)
for ($i = 0; $i < $iBdCnt; ++$i) {
fwrite($FILE, pack('V', 0xFFFFFFFD));
}
// Set for ExtraBDList
for ($i = 0; $i < $iBdExL; ++$i) {
fwrite($FILE, pack('V', 0xFFFFFFFC));
}
// Adjust for Block
if (($iAllW + $iBdCnt) % $iBbCnt) {
$iBlock = ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt));
for ($i = 0; $i < $iBlock; ++$i) {
fwrite($FILE, pack('V', -1));
}
}
// Extra BDList
if ($iBdCnt > $i1stBdL) {
$iN = 0;
$iNb = 0;
for ($i = $i1stBdL; $i < $iBdCnt; $i++, ++$iN) {
if ($iN >= ($iBbCnt - 1)) {
$iN = 0;
++$iNb;
fwrite($FILE, pack('V', $iAll + $iBdCnt + $iNb));
}
fwrite($FILE, pack('V', $iBsize + $iSbdSize + $iPpsCnt + $i));
}
if (($iBdCnt - $i1stBdL) % ($iBbCnt - 1)) {
$iB = ($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1));
for ($i = 0; $i < $iB; ++$i) {
fwrite($FILE, pack('V', -1));
}
}
fwrite($FILE, pack('V', -2));
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php 0000644 00000016031 15242753725 0016306 0 ustar 00 |
// | Based on OLE::Storage_Lite by Kawai, Takanori |
// +----------------------------------------------------------------------+
//
use PhpOffice\PhpSpreadsheet\Shared\OLE;
/**
* Class for creating PPS's for OLE containers.
*
* @author Xavier Noguer
*/
class PPS
{
/**
* The PPS index.
*
* @var int
*/
public $No;
/**
* The PPS name (in Unicode).
*
* @var string
*/
public $Name;
/**
* The PPS type. Dir, Root or File.
*
* @var int
*/
public $Type;
/**
* The index of the previous PPS.
*
* @var int
*/
public $PrevPps;
/**
* The index of the next PPS.
*
* @var int
*/
public $NextPps;
/**
* The index of it's first child if this is a Dir or Root PPS.
*
* @var int
*/
public $DirPps;
/**
* A timestamp.
*
* @var float|int
*/
public $Time1st;
/**
* A timestamp.
*
* @var float|int
*/
public $Time2nd;
/**
* Starting block (small or big) for this PPS's data inside the container.
*
* @var ?int
*/
public $startBlock;
/**
* The size of the PPS's data (in bytes).
*
* @var int
*/
public $Size;
/**
* The PPS's data (only used if it's not using a temporary file).
*
* @var string
*/
public $_data = '';
/**
* Array of child PPS's (only used by Root and Dir PPS's).
*
* @var array
*/
public $children = [];
/**
* Pointer to OLE container.
*
* @var OLE
*/
public $ole;
/**
* The constructor.
*
* @param ?int $No The PPS index
* @param ?string $name The PPS name
* @param ?int $type The PPS type. Dir, Root or File
* @param ?int $prev The index of the previous PPS
* @param ?int $next The index of the next PPS
* @param ?int $dir The index of it's first child if this is a Dir or Root PPS
* @param null|float|int $time_1st A timestamp
* @param null|float|int $time_2nd A timestamp
* @param ?string $data The (usually binary) source data of the PPS
* @param array $children Array containing children PPS for this PPS
*/
public function __construct($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children)
{
$this->No = (int) $No;
$this->Name = (string) $name;
$this->Type = (int) $type;
$this->PrevPps = (int) $prev;
$this->NextPps = (int) $next;
$this->DirPps = (int) $dir;
$this->Time1st = $time_1st ?? 0;
$this->Time2nd = $time_2nd ?? 0;
$this->_data = (string) $data;
$this->children = $children;
$this->Size = strlen((string) $data);
}
/**
* Returns the amount of data saved for this PPS.
*
* @return int The amount of data (in bytes)
*/
public function getDataLen()
{
//if (!isset($this->_data)) {
// return 0;
//}
return strlen($this->_data);
}
/**
* Returns a string with the PPS's WK (What is a WK?).
*
* @return string The binary string
*/
public function getPpsWk()
{
$ret = str_pad($this->Name, 64, "\x00");
$ret .= pack('v', strlen($this->Name) + 2) // 66
. pack('c', $this->Type) // 67
. pack('c', 0x00) //UK // 68
. pack('V', $this->PrevPps) //Prev // 72
. pack('V', $this->NextPps) //Next // 76
. pack('V', $this->DirPps) //Dir // 80
. "\x00\x09\x02\x00" // 84
. "\x00\x00\x00\x00" // 88
. "\xc0\x00\x00\x00" // 92
. "\x00\x00\x00\x46" // 96 // Seems to be ok only for Root
. "\x00\x00\x00\x00" // 100
. OLE::localDateToOLE($this->Time1st) // 108
. OLE::localDateToOLE($this->Time2nd) // 116
. pack('V', $this->startBlock ?? 0) // 120
. pack('V', $this->Size) // 124
. pack('V', 0); // 128
return $ret;
}
/**
* Updates index and pointers to previous, next and children PPS's for this
* PPS. I don't think it'll work with Dir PPS's.
*
* @param array $raList Reference to the array of PPS's for the whole OLE
* container
* @param mixed $to_save
* @param mixed $depth
*
* @return int The index for this PPS
*/
public static function savePpsSetPnt(&$raList, $to_save, $depth = 0)
{
if (!is_array($to_save) || (empty($to_save))) {
return 0xFFFFFFFF;
} elseif (count($to_save) == 1) {
$cnt = count($raList);
// If the first entry, it's the root... Don't clone it!
$raList[$cnt] = ($depth == 0) ? $to_save[0] : clone $to_save[0];
$raList[$cnt]->No = $cnt;
$raList[$cnt]->PrevPps = 0xFFFFFFFF;
$raList[$cnt]->NextPps = 0xFFFFFFFF;
$raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
} else {
$iPos = (int) floor(count($to_save) / 2);
$aPrev = array_slice($to_save, 0, $iPos);
$aNext = array_slice($to_save, $iPos + 1);
$cnt = count($raList);
// If the first entry, it's the root... Don't clone it!
$raList[$cnt] = ($depth == 0) ? $to_save[$iPos] : clone $to_save[$iPos];
$raList[$cnt]->No = $cnt;
$raList[$cnt]->PrevPps = self::savePpsSetPnt($raList, $aPrev, $depth++);
$raList[$cnt]->NextPps = self::savePpsSetPnt($raList, $aNext, $depth++);
$raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
}
return $cnt;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/ChainedBlockStream.php 0000644 00000013474 15242753725 0021336 0 ustar 00 params);
if (!isset($this->params['oleInstanceId'], $this->params['blockId'], $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']])) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error('OLE stream not found', E_USER_WARNING);
}
return false;
}
$this->ole = $GLOBALS['_OLE_INSTANCES'][$this->params['oleInstanceId']];
$blockId = $this->params['blockId'];
$this->data = '';
if (isset($this->params['size']) && $this->params['size'] < $this->ole->bigBlockThreshold && $blockId != $this->ole->root->startBlock) {
// Block id refers to small blocks
$rootPos = $this->ole->getBlockOffset($this->ole->root->startBlock);
while ($blockId != -2) {
$pos = $rootPos + $blockId * $this->ole->bigBlockSize;
$blockId = $this->ole->sbat[$blockId];
fseek($this->ole->_file_handle, $pos);
$this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
}
} else {
// Block id refers to big blocks
while ($blockId != -2) {
$pos = $this->ole->getBlockOffset($blockId);
fseek($this->ole->_file_handle, $pos);
$this->data .= fread($this->ole->_file_handle, $this->ole->bigBlockSize);
$blockId = $this->ole->bbat[$blockId];
}
}
if (isset($this->params['size'])) {
$this->data = substr($this->data, 0, $this->params['size']);
}
if ($options & STREAM_USE_PATH) {
$openedPath = $path;
}
return true;
}
/**
* Implements support for fclose().
*/
public function stream_close(): void // @codingStandardsIgnoreLine
{
$this->ole = null;
unset($GLOBALS['_OLE_INSTANCES']);
}
/**
* Implements support for fread(), fgets() etc.
*
* @param int $count maximum number of bytes to read
*
* @return false|string
*/
public function stream_read($count) // @codingStandardsIgnoreLine
{
if ($this->stream_eof()) {
return false;
}
$s = substr($this->data, (int) $this->pos, $count);
$this->pos += $count;
return $s;
}
/**
* Implements support for feof().
*
* @return bool TRUE if the file pointer is at EOF; otherwise FALSE
*/
public function stream_eof() // @codingStandardsIgnoreLine
{
return $this->pos >= strlen($this->data);
}
/**
* Returns the position of the file pointer, i.e. its offset into the file
* stream. Implements support for ftell().
*
* @return int
*/
public function stream_tell() // @codingStandardsIgnoreLine
{
return $this->pos;
}
/**
* Implements support for fseek().
*
* @param int $offset byte offset
* @param int $whence SEEK_SET, SEEK_CUR or SEEK_END
*
* @return bool
*/
public function stream_seek($offset, $whence) // @codingStandardsIgnoreLine
{
if ($whence == SEEK_SET && $offset >= 0) {
$this->pos = $offset;
} elseif ($whence == SEEK_CUR && -$offset <= $this->pos) {
$this->pos += $offset;
// @phpstan-ignore-next-line
} elseif ($whence == SEEK_END && -$offset <= count(/** @scrutinizer ignore-type */ $this->data)) {
$this->pos = strlen($this->data) + $offset;
} else {
return false;
}
return true;
}
/**
* Implements support for fstat(). Currently the only supported field is
* "size".
*
* @return array
*/
public function stream_stat() // @codingStandardsIgnoreLine
{
return [
'size' => strlen($this->data),
];
}
// Methods used by stream_wrapper_register() that are not implemented:
// bool stream_flush ( void )
// int stream_write ( string data )
// bool rename ( string path_from, string path_to )
// bool mkdir ( string path, int mode, int options )
// bool rmdir ( string path, int options )
// bool dir_opendir ( string path, int options )
// array url_stat ( string path, int flags )
// string dir_readdir ( void )
// bool dir_rewinddir ( void )
// bool dir_closedir ( void )
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php 0000644 00000006030 15242753725 0022046 0 ustar 00 getIntersect() * $this->getSlope() ** ($xValue - $this->xOffset);
}
/**
* Return the X-Value for a specified value of Y.
*
* @param float $yValue Y-Value
*
* @return float X-Value
*/
public function getValueOfXForY($yValue)
{
return log(($yValue + $this->yOffset) / $this->getIntersect()) / log($this->getSlope());
}
/**
* Return the Equation of the best-fit line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return string
*/
public function getEquation($dp = 0)
{
$slope = $this->getSlope($dp);
$intersect = $this->getIntersect($dp);
return 'Y = ' . $intersect . ' * ' . $slope . '^X';
}
/**
* Return the Slope of the line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getSlope($dp = 0)
{
if ($dp != 0) {
return round(exp($this->slope), $dp);
}
return exp($this->slope);
}
/**
* Return the Value of X where it intersects Y = 0.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getIntersect($dp = 0)
{
if ($dp != 0) {
return round(exp($this->intersect), $dp);
}
return exp($this->intersect);
}
/**
* Execute the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
private function exponentialRegression(array $yValues, array $xValues, bool $const): void
{
$adjustedYValues = array_map(
function ($value) {
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
},
$yValues
);
$this->leastSquareFit($adjustedYValues, $xValues, $const);
}
/**
* Define the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
* @param bool $const
*/
public function __construct($yValues, $xValues = [], $const = true)
{
parent::__construct($yValues, $xValues);
if (!$this->error) {
$this->exponentialRegression($yValues, $xValues, (bool) $const);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php 0000644 00000004134 15242753725 0020775 0 ustar 00 getIntersect() + $this->getSlope() * $xValue;
}
/**
* Return the X-Value for a specified value of Y.
*
* @param float $yValue Y-Value
*
* @return float X-Value
*/
public function getValueOfXForY($yValue)
{
return ($yValue - $this->getIntersect()) / $this->getSlope();
}
/**
* Return the Equation of the best-fit line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return string
*/
public function getEquation($dp = 0)
{
$slope = $this->getSlope($dp);
$intersect = $this->getIntersect($dp);
return 'Y = ' . $intersect . ' + ' . $slope . ' * X';
}
/**
* Execute the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
private function linearRegression(array $yValues, array $xValues, bool $const): void
{
$this->leastSquareFit($yValues, $xValues, $const);
}
/**
* Define the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
* @param bool $const
*/
public function __construct($yValues, $xValues = [], $const = true)
{
parent::__construct($yValues, $xValues);
if (!$this->error) {
$this->linearRegression($yValues, $xValues, (bool) $const);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php 0000644 00000011624 15242753725 0017360 0 ustar 00 getGoodnessOfFit();
}
if ($trendType != self::TREND_BEST_FIT_NO_POLY) {
foreach (self::$trendTypePolynomialOrders as $trendMethod) {
$order = (int) substr($trendMethod, -1);
$bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues);
if ($bestFit[$trendMethod]->getError()) {
unset($bestFit[$trendMethod]);
} else {
$bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit();
}
}
}
// Determine which of our Trend lines is the best fit, and then we return the instance of that Trend class
arsort($bestFitValue);
$bestFitType = key($bestFitValue);
return $bestFit[$bestFitType];
default:
return false;
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php 0000644 00000014555 15242753725 0021716 0 ustar 00 slope is specified where an array is expected in several places.
// But it seems that it should always be float.
// This code is probably not exercised at all in unit tests.
class PolynomialBestFit extends BestFit
{
/**
* Algorithm type to use for best-fit
* (Name of this Trend class).
*
* @var string
*/
protected $bestFitType = 'polynomial';
/**
* Polynomial order.
*
* @var int
*/
protected $order = 0;
/**
* Return the order of this polynomial.
*
* @return int
*/
public function getOrder()
{
return $this->order;
}
/**
* Return the Y-Value for a specified value of X.
*
* @param float $xValue X-Value
*
* @return float Y-Value
*/
public function getValueOfYForX($xValue)
{
$retVal = $this->getIntersect();
$slope = $this->getSlope();
// Phpstan and Scrutinizer are both correct - getSlope returns float, not array.
// @phpstan-ignore-next-line
foreach ($slope as $key => $value) {
if ($value != 0.0) {
$retVal += $value * $xValue ** ($key + 1);
}
}
return $retVal;
}
/**
* Return the X-Value for a specified value of Y.
*
* @param float $yValue Y-Value
*
* @return float X-Value
*/
public function getValueOfXForY($yValue)
{
return ($yValue - $this->getIntersect()) / $this->getSlope();
}
/**
* Return the Equation of the best-fit line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return string
*/
public function getEquation($dp = 0)
{
$slope = $this->getSlope($dp);
$intersect = $this->getIntersect($dp);
$equation = 'Y = ' . $intersect;
// Phpstan and Scrutinizer are both correct - getSlope returns float, not array.
// @phpstan-ignore-next-line
foreach ($slope as $key => $value) {
if ($value != 0.0) {
$equation .= ' + ' . $value . ' * X';
if ($key > 0) {
$equation .= '^' . ($key + 1);
}
}
}
return $equation;
}
/**
* Return the Slope of the line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getSlope($dp = 0)
{
if ($dp != 0) {
$coefficients = [];
// Scrutinizer is correct - $this->slope is float, not array.
//* @phpstan-ignore-next-line
foreach ($this->slope as $coefficient) {
$coefficients[] = round($coefficient, $dp);
}
// @phpstan-ignore-next-line
return $coefficients;
}
return $this->slope;
}
/**
* @param int $dp
*
* @return array
*/
public function getCoefficients($dp = 0)
{
// Phpstan and Scrutinizer are both correct - getSlope returns float, not array.
// @phpstan-ignore-next-line
return array_merge([$this->getIntersect($dp)], $this->getSlope($dp));
}
/**
* Execute the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param int $order Order of Polynomial for this regression
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
private function polynomialRegression($order, $yValues, $xValues): void
{
// calculate sums
$x_sum = array_sum($xValues);
$y_sum = array_sum($yValues);
$xx_sum = $xy_sum = $yy_sum = 0;
for ($i = 0; $i < $this->valueCount; ++$i) {
$xy_sum += $xValues[$i] * $yValues[$i];
$xx_sum += $xValues[$i] * $xValues[$i];
$yy_sum += $yValues[$i] * $yValues[$i];
}
/*
* This routine uses logic from the PHP port of polyfit version 0.1
* written by Michael Bommarito and Paul Meagher
*
* The function fits a polynomial function of order $order through
* a series of x-y data points using least squares.
*
*/
$A = [];
$B = [];
for ($i = 0; $i < $this->valueCount; ++$i) {
for ($j = 0; $j <= $order; ++$j) {
$A[$i][$j] = $xValues[$i] ** $j;
}
}
for ($i = 0; $i < $this->valueCount; ++$i) {
$B[$i] = [$yValues[$i]];
}
$matrixA = new Matrix($A);
$matrixB = new Matrix($B);
$C = $matrixA->solve($matrixB);
$coefficients = [];
for ($i = 0; $i < $C->rows; ++$i) {
$r = $C->getValue($i + 1, 1); // row and column are origin-1
if (abs($r) <= 10 ** (-9)) {
$r = 0;
}
$coefficients[] = $r;
}
$this->intersect = array_shift($coefficients);
// Phpstan (and maybe Scrutinizer) are correct
//* @phpstan-ignore-next-line
$this->slope = $coefficients;
$this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, 0, 0, 0);
foreach ($this->xValues as $xKey => $xValue) {
$this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
}
}
/**
* Define the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param int $order Order of Polynomial for this regression
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
public function __construct($order, $yValues, $xValues = [])
{
parent::__construct($yValues, $xValues);
if (!$this->error) {
if ($order < $this->valueCount) {
$this->bestFitType .= '_' . $order;
$this->order = $order;
$this->polynomialRegression($order, $yValues, $xValues);
if (($this->getGoodnessOfFit() < 0.0) || ($this->getGoodnessOfFit() > 1.0)) {
$this->error = true;
}
} else {
$this->error = true;
}
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php 0000644 00000004532 15242753725 0022027 0 ustar 00 getIntersect() + $this->getSlope() * log($xValue - $this->xOffset);
}
/**
* Return the X-Value for a specified value of Y.
*
* @param float $yValue Y-Value
*
* @return float X-Value
*/
public function getValueOfXForY($yValue)
{
return exp(($yValue - $this->getIntersect()) / $this->getSlope());
}
/**
* Return the Equation of the best-fit line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return string
*/
public function getEquation($dp = 0)
{
$slope = $this->getSlope($dp);
$intersect = $this->getIntersect($dp);
return 'Y = ' . $slope . ' * log(' . $intersect . ' * X)';
}
/**
* Execute the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
private function logarithmicRegression(array $yValues, array $xValues, bool $const): void
{
$adjustedYValues = array_map(
function ($value) {
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
},
$yValues
);
$this->leastSquareFit($adjustedYValues, $xValues, $const);
}
/**
* Define the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
* @param bool $const
*/
public function __construct($yValues, $xValues = [], $const = true)
{
parent::__construct($yValues, $xValues);
if (!$this->error) {
$this->logarithmicRegression($yValues, $xValues, (bool) $const);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php 0000644 00000005572 15242753725 0020666 0 ustar 00 getIntersect() * ($xValue - $this->xOffset) ** $this->getSlope();
}
/**
* Return the X-Value for a specified value of Y.
*
* @param float $yValue Y-Value
*
* @return float X-Value
*/
public function getValueOfXForY($yValue)
{
return (($yValue + $this->yOffset) / $this->getIntersect()) ** (1 / $this->getSlope());
}
/**
* Return the Equation of the best-fit line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return string
*/
public function getEquation($dp = 0)
{
$slope = $this->getSlope($dp);
$intersect = $this->getIntersect($dp);
return 'Y = ' . $intersect . ' * X^' . $slope;
}
/**
* Return the Value of X where it intersects Y = 0.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getIntersect($dp = 0)
{
if ($dp != 0) {
return round(exp($this->intersect), $dp);
}
return exp($this->intersect);
}
/**
* Execute the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
private function powerRegression(array $yValues, array $xValues, bool $const): void
{
$adjustedYValues = array_map(
function ($value) {
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
},
$yValues
);
$adjustedXValues = array_map(
function ($value) {
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
},
$xValues
);
$this->leastSquareFit($adjustedYValues, $adjustedXValues, $const);
}
/**
* Define the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
* @param bool $const
*/
public function __construct($yValues, $xValues = [], $const = true)
{
parent::__construct($yValues, $xValues);
if (!$this->error) {
$this->powerRegression($yValues, $xValues, (bool) $const);
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/BestFit.php 0000644 00000030306 15242753725 0017642 0 ustar 00 error;
}
/** @return string */
public function getBestFitType()
{
return $this->bestFitType;
}
/**
* Return the Y-Value for a specified value of X.
*
* @param float $xValue X-Value
*
* @return float Y-Value
*/
abstract public function getValueOfYForX($xValue);
/**
* Return the X-Value for a specified value of Y.
*
* @param float $yValue Y-Value
*
* @return float X-Value
*/
abstract public function getValueOfXForY($yValue);
/**
* Return the original set of X-Values.
*
* @return float[] X-Values
*/
public function getXValues()
{
return $this->xValues;
}
/**
* Return the Equation of the best-fit line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return string
*/
abstract public function getEquation($dp = 0);
/**
* Return the Slope of the line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getSlope($dp = 0)
{
if ($dp != 0) {
return round($this->slope, $dp);
}
return $this->slope;
}
/**
* Return the standard error of the Slope.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getSlopeSE($dp = 0)
{
if ($dp != 0) {
return round($this->slopeSE, $dp);
}
return $this->slopeSE;
}
/**
* Return the Value of X where it intersects Y = 0.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getIntersect($dp = 0)
{
if ($dp != 0) {
return round($this->intersect, $dp);
}
return $this->intersect;
}
/**
* Return the standard error of the Intersect.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getIntersectSE($dp = 0)
{
if ($dp != 0) {
return round($this->intersectSE, $dp);
}
return $this->intersectSE;
}
/**
* Return the goodness of fit for this regression.
*
* @param int $dp Number of places of decimal precision to return
*
* @return float
*/
public function getGoodnessOfFit($dp = 0)
{
if ($dp != 0) {
return round($this->goodnessOfFit, $dp);
}
return $this->goodnessOfFit;
}
/**
* Return the goodness of fit for this regression.
*
* @param int $dp Number of places of decimal precision to return
*
* @return float
*/
public function getGoodnessOfFitPercent($dp = 0)
{
if ($dp != 0) {
return round($this->goodnessOfFit * 100, $dp);
}
return $this->goodnessOfFit * 100;
}
/**
* Return the standard deviation of the residuals for this regression.
*
* @param int $dp Number of places of decimal precision to return
*
* @return float
*/
public function getStdevOfResiduals($dp = 0)
{
if ($dp != 0) {
return round($this->stdevOfResiduals, $dp);
}
return $this->stdevOfResiduals;
}
/**
* @param int $dp Number of places of decimal precision to return
*
* @return float
*/
public function getSSRegression($dp = 0)
{
if ($dp != 0) {
return round($this->SSRegression, $dp);
}
return $this->SSRegression;
}
/**
* @param int $dp Number of places of decimal precision to return
*
* @return float
*/
public function getSSResiduals($dp = 0)
{
if ($dp != 0) {
return round($this->SSResiduals, $dp);
}
return $this->SSResiduals;
}
/**
* @param int $dp Number of places of decimal precision to return
*
* @return float
*/
public function getDFResiduals($dp = 0)
{
if ($dp != 0) {
return round($this->DFResiduals, $dp);
}
return $this->DFResiduals;
}
/**
* @param int $dp Number of places of decimal precision to return
*
* @return float
*/
public function getF($dp = 0)
{
if ($dp != 0) {
return round($this->f, $dp);
}
return $this->f;
}
/**
* @param int $dp Number of places of decimal precision to return
*
* @return float
*/
public function getCovariance($dp = 0)
{
if ($dp != 0) {
return round($this->covariance, $dp);
}
return $this->covariance;
}
/**
* @param int $dp Number of places of decimal precision to return
*
* @return float
*/
public function getCorrelation($dp = 0)
{
if ($dp != 0) {
return round($this->correlation, $dp);
}
return $this->correlation;
}
/**
* @return float[]
*/
public function getYBestFitValues()
{
return $this->yBestFitValues;
}
/** @var mixed */
private static $scrutinizerZeroPointZero = 0.0;
/**
* @param mixed $x
* @param mixed $y
*/
private static function scrutinizerLooseCompare($x, $y): bool
{
return $x == $y;
}
/**
* @param float $sumX
* @param float $sumY
* @param float $sumX2
* @param float $sumY2
* @param float $sumXY
* @param float $meanX
* @param float $meanY
* @param bool|int $const
*/
protected function calculateGoodnessOfFit($sumX, $sumY, $sumX2, $sumY2, $sumXY, $meanX, $meanY, $const): void
{
$SSres = $SScov = $SStot = $SSsex = 0.0;
foreach ($this->xValues as $xKey => $xValue) {
$bestFitY = $this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
$SSres += ($this->yValues[$xKey] - $bestFitY) * ($this->yValues[$xKey] - $bestFitY);
if ($const === true) {
$SStot += ($this->yValues[$xKey] - $meanY) * ($this->yValues[$xKey] - $meanY);
} else {
$SStot += $this->yValues[$xKey] * $this->yValues[$xKey];
}
$SScov += ($this->xValues[$xKey] - $meanX) * ($this->yValues[$xKey] - $meanY);
if ($const === true) {
$SSsex += ($this->xValues[$xKey] - $meanX) * ($this->xValues[$xKey] - $meanX);
} else {
$SSsex += $this->xValues[$xKey] * $this->xValues[$xKey];
}
}
$this->SSResiduals = $SSres;
$this->DFResiduals = $this->valueCount - 1 - ($const === true ? 1 : 0);
if ($this->DFResiduals == 0.0) {
$this->stdevOfResiduals = 0.0;
} else {
$this->stdevOfResiduals = sqrt($SSres / $this->DFResiduals);
}
// Scrutinizer thinks $SSres == $SStot is always true. It is wrong.
if ($SStot == self::$scrutinizerZeroPointZero || self::scrutinizerLooseCompare($SSres, $SStot)) {
$this->goodnessOfFit = 1;
} else {
$this->goodnessOfFit = 1 - ($SSres / $SStot);
}
$this->SSRegression = $this->goodnessOfFit * $SStot;
$this->covariance = $SScov / $this->valueCount;
$this->correlation = ($this->valueCount * $sumXY - $sumX * $sumY) / sqrt(($this->valueCount * $sumX2 - $sumX ** 2) * ($this->valueCount * $sumY2 - $sumY ** 2));
$this->slopeSE = $this->stdevOfResiduals / sqrt($SSsex);
$this->intersectSE = $this->stdevOfResiduals * sqrt(1 / ($this->valueCount - ($sumX * $sumX) / $sumX2));
if ($this->SSResiduals != 0.0) {
if ($this->DFResiduals == 0.0) {
$this->f = 0.0;
} else {
$this->f = $this->SSRegression / ($this->SSResiduals / $this->DFResiduals);
}
} else {
if ($this->DFResiduals == 0.0) {
$this->f = 0.0;
} else {
$this->f = $this->SSRegression / $this->DFResiduals;
}
}
}
/** @return float|int */
private function sumSquares(array $values)
{
return array_sum(
array_map(
function ($value) {
return $value ** 2;
},
$values
)
);
}
/**
* @param float[] $yValues
* @param float[] $xValues
*/
protected function leastSquareFit(array $yValues, array $xValues, bool $const): void
{
// calculate sums
$sumValuesX = array_sum($xValues);
$sumValuesY = array_sum($yValues);
$meanValueX = $sumValuesX / $this->valueCount;
$meanValueY = $sumValuesY / $this->valueCount;
$sumSquaresX = $this->sumSquares($xValues);
$sumSquaresY = $this->sumSquares($yValues);
$mBase = $mDivisor = 0.0;
$xy_sum = 0.0;
for ($i = 0; $i < $this->valueCount; ++$i) {
$xy_sum += $xValues[$i] * $yValues[$i];
if ($const === true) {
$mBase += ($xValues[$i] - $meanValueX) * ($yValues[$i] - $meanValueY);
$mDivisor += ($xValues[$i] - $meanValueX) * ($xValues[$i] - $meanValueX);
} else {
$mBase += $xValues[$i] * $yValues[$i];
$mDivisor += $xValues[$i] * $xValues[$i];
}
}
// calculate slope
$this->slope = $mBase / $mDivisor;
// calculate intersect
$this->intersect = ($const === true) ? $meanValueY - ($this->slope * $meanValueX) : 0.0;
$this->calculateGoodnessOfFit($sumValuesX, $sumValuesY, $sumSquaresX, $sumSquaresY, $xy_sum, $meanValueX, $meanValueY, $const);
}
/**
* Define the regression.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
public function __construct($yValues, $xValues = [])
{
// Calculate number of points
$yValueCount = count($yValues);
$xValueCount = count($xValues);
// Define X Values if necessary
if ($xValueCount === 0) {
$xValues = range(1, $yValueCount);
} elseif ($yValueCount !== $xValueCount) {
// Ensure both arrays of points are the same size
$this->error = true;
}
$this->valueCount = $yValueCount;
$this->xValues = $xValues;
$this->yValues = $yValues;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php 0000644 00000016451 15242753725 0025644 0 ustar 00 parent = $parent;
}
/**
* Get the parent Shape Group Container.
*
* @return SpgrContainer
*/
public function getParent()
{
return $this->parent;
}
/**
* Set whether this is a group shape.
*
* @param bool $value
*/
public function setSpgr($value): void
{
$this->spgr = $value;
}
/**
* Get whether this is a group shape.
*
* @return bool
*/
public function getSpgr()
{
return $this->spgr;
}
/**
* Set the shape type.
*
* @param int $value
*/
public function setSpType($value): void
{
$this->spType = $value;
}
/**
* Get the shape type.
*
* @return int
*/
public function getSpType()
{
return $this->spType;
}
/**
* Set the shape flag.
*
* @param int $value
*/
public function setSpFlag($value): void
{
$this->spFlag = $value;
}
/**
* Get the shape flag.
*
* @return int
*/
public function getSpFlag()
{
return $this->spFlag;
}
/**
* Set the shape index.
*
* @param int $value
*/
public function setSpId($value): void
{
$this->spId = $value;
}
/**
* Get the shape index.
*
* @return int
*/
public function getSpId()
{
return $this->spId;
}
/**
* Set an option for the Shape Group Container.
*
* @param int $property The number specifies the option
* @param mixed $value
*/
public function setOPT($property, $value): void
{
$this->OPT[$property] = $value;
}
/**
* Get an option for the Shape Group Container.
*
* @param int $property The number specifies the option
*
* @return mixed
*/
public function getOPT($property)
{
if (isset($this->OPT[$property])) {
return $this->OPT[$property];
}
return null;
}
/**
* Get the collection of options.
*
* @return array
*/
public function getOPTCollection()
{
return $this->OPT;
}
/**
* Set cell coordinates of upper-left corner of shape.
*
* @param string $value eg: 'A1'
*/
public function setStartCoordinates($value): void
{
$this->startCoordinates = $value;
}
/**
* Get cell coordinates of upper-left corner of shape.
*
* @return string
*/
public function getStartCoordinates()
{
return $this->startCoordinates;
}
/**
* Set offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
*
* @param int $startOffsetX
*/
public function setStartOffsetX($startOffsetX): void
{
$this->startOffsetX = $startOffsetX;
}
/**
* Get offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
*
* @return int
*/
public function getStartOffsetX()
{
return $this->startOffsetX;
}
/**
* Set offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
*
* @param int $startOffsetY
*/
public function setStartOffsetY($startOffsetY): void
{
$this->startOffsetY = $startOffsetY;
}
/**
* Get offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
*
* @return int
*/
public function getStartOffsetY()
{
return $this->startOffsetY;
}
/**
* Set cell coordinates of bottom-right corner of shape.
*
* @param string $value eg: 'A1'
*/
public function setEndCoordinates($value): void
{
$this->endCoordinates = $value;
}
/**
* Get cell coordinates of bottom-right corner of shape.
*
* @return string
*/
public function getEndCoordinates()
{
return $this->endCoordinates;
}
/**
* Set offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
*
* @param int $endOffsetX
*/
public function setEndOffsetX($endOffsetX): void
{
$this->endOffsetX = $endOffsetX;
}
/**
* Get offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
*
* @return int
*/
public function getEndOffsetX()
{
return $this->endOffsetX;
}
/**
* Set offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
*
* @param int $endOffsetY
*/
public function setEndOffsetY($endOffsetY): void
{
$this->endOffsetY = $endOffsetY;
}
/**
* Get offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
*
* @return int
*/
public function getEndOffsetY()
{
return $this->endOffsetY;
}
/**
* Get the nesting level of this spContainer. This is the number of spgrContainers between this spContainer and
* the dgContainer. A value of 1 = immediately within first spgrContainer
* Higher nesting level occurs if and only if spContainer is part of a shape group.
*
* @return int Nesting level
*/
public function getNestingLevel()
{
$nestingLevel = 0;
$parent = $this->getParent();
while ($parent instanceof SpgrContainer) {
++$nestingLevel;
$parent = $parent->getParent();
}
return $nestingLevel;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php 0000644 00000002775 15242753725 0023423 0 ustar 00 parent = $parent;
}
/**
* Get the parent Shape Group Container if any.
*/
public function getParent(): ?self
{
return $this->parent;
}
/**
* Add a child. This will be either spgrContainer or spContainer.
*
* @param mixed $child
*/
public function addChild($child): void
{
$this->children[] = $child;
$child->setParent($this);
}
/**
* Get collection of Shape Containers.
*/
public function getChildren(): array
{
return $this->children;
}
/**
* Recursively get all spContainers within this spgrContainer.
*
* @return SpgrContainer\SpContainer[]
*/
public function getAllSpContainers()
{
$allSpContainers = [];
foreach ($this->children as $child) {
if ($child instanceof self) {
$allSpContainers = array_merge($allSpContainers, $child->getAllSpContainers());
} else {
$allSpContainers[] = $child;
}
}
return $allSpContainers;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE/Blip.php 0000644 00000001624 15242753725 0025404 0 ustar 00 data;
}
/**
* Set the raw image data.
*
* @param string $data
*/
public function setData($data): void
{
$this->data = $data;
}
/**
* Set parent BSE.
*/
public function setParent(BSE $parent): void
{
$this->parent = $parent;
}
/**
* Get parent BSE.
*/
public function getParent(): BSE
{
return $this->parent;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer/BSE.php 0000644 00000003211 15242753725 0024510 0 ustar 00 parent = $parent;
}
/**
* Get the BLIP.
*
* @return ?BSE\Blip
*/
public function getBlip()
{
return $this->blip;
}
/**
* Set the BLIP.
*/
public function setBlip(BSE\Blip $blip): void
{
$this->blip = $blip;
$blip->setParent($this);
}
/**
* Get the BLIP type.
*
* @return int
*/
public function getBlipType()
{
return $this->blipType;
}
/**
* Set the BLIP type.
*
* @param int $blipType
*/
public function setBlipType($blipType): void
{
$this->blipType = $blipType;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer/BstoreContainer.php 0000644 00000001224 15242753725 0024101 0 ustar 00 BSECollection[] = $BSE;
$BSE->setParent($this);
}
/**
* Get the collection of BLIP Store Entries.
*
* @return BstoreContainer\BSE[]
*/
public function getBSECollection()
{
return $this->BSECollection;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DggContainer.php 0000644 00000006444 15242753725 0021011 0 ustar 00 spIdMax;
}
/**
* Set maximum shape index of all shapes in all drawings (plus one).
*
* @param int $value
*/
public function setSpIdMax($value): void
{
$this->spIdMax = $value;
}
/**
* Get total number of drawings saved.
*
* @return int
*/
public function getCDgSaved()
{
return $this->cDgSaved;
}
/**
* Set total number of drawings saved.
*
* @param int $value
*/
public function setCDgSaved($value): void
{
$this->cDgSaved = $value;
}
/**
* Get total number of shapes saved (including group shapes).
*
* @return int
*/
public function getCSpSaved()
{
return $this->cSpSaved;
}
/**
* Set total number of shapes saved (including group shapes).
*
* @param int $value
*/
public function setCSpSaved($value): void
{
$this->cSpSaved = $value;
}
/**
* Get BLIP Store Container.
*
* @return ?DggContainer\BstoreContainer
*/
public function getBstoreContainer()
{
return $this->bstoreContainer;
}
/**
* Set BLIP Store Container.
*
* @param DggContainer\BstoreContainer $bstoreContainer
*/
public function setBstoreContainer($bstoreContainer): void
{
$this->bstoreContainer = $bstoreContainer;
}
/**
* Set an option for the drawing group.
*
* @param int $property The number specifies the option
* @param mixed $value
*/
public function setOPT($property, $value): void
{
$this->OPT[$property] = $value;
}
/**
* Get an option for the drawing group.
*
* @param int $property The number specifies the option
*
* @return mixed
*/
public function getOPT($property)
{
if (isset($this->OPT[$property])) {
return $this->OPT[$property];
}
return null;
}
/**
* Get identifier clusters.
*
* @return array
*/
public function getIDCLs()
{
return $this->IDCLs;
}
/**
* Set identifier clusters. [ => , ...].
*
* @param array $IDCLs
*/
public function setIDCLs($IDCLs): void
{
$this->IDCLs = $IDCLs;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer.php 0000644 00000002500 15242753725 0020627 0 ustar 00 dgId;
}
public function setDgId(int $value): void
{
$this->dgId = $value;
}
public function getLastSpId(): ?int
{
return $this->lastSpId;
}
public function setLastSpId(int $value): void
{
$this->lastSpId = $value;
}
public function getSpgrContainer(): ?DgContainer\SpgrContainer
{
return $this->spgrContainer;
}
public function getSpgrContainerOrThrow(): DgContainer\SpgrContainer
{
if ($this->spgrContainer !== null) {
return $this->spgrContainer;
}
throw new SpreadsheetException('spgrContainer is unexpectedly null');
}
/** @param DgContainer\SpgrContainer $spgrContainer */
public function setSpgrContainer($spgrContainer): DgContainer\SpgrContainer
{
return $this->spgrContainer = $spgrContainer;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/File.php 0000644 00000014003 15242753725 0016101 0 ustar 00 open($zipFile);
if ($res === true) {
$returnValue = ($zip->getFromName($archiveFile) !== false);
$zip->close();
return $returnValue;
}
}
return false;
}
return file_exists($filename);
}
/**
* Returns canonicalized absolute pathname, also for ZIP archives.
*/
public static function realpath(string $filename): string
{
// Returnvalue
$returnValue = '';
// Try using realpath()
if (file_exists($filename)) {
$returnValue = realpath($filename) ?: '';
}
// Found something?
if ($returnValue === '') {
$pathArray = explode('/', $filename);
while (in_array('..', $pathArray) && $pathArray[0] != '..') {
$iMax = count($pathArray);
for ($i = 0; $i < $iMax; ++$i) {
if ($pathArray[$i] == '..' && $i > 0) {
unset($pathArray[$i], $pathArray[$i - 1]);
break;
}
}
}
$returnValue = implode('/', $pathArray);
}
// Return
return $returnValue;
}
/**
* Get the systems temporary directory.
*/
public static function sysGetTempDir(): string
{
$path = sys_get_temp_dir();
if (self::$useUploadTempDirectory) {
// use upload-directory when defined to allow running on environments having very restricted
// open_basedir configs
if (ini_get('upload_tmp_dir') !== false) {
if ($temp = ini_get('upload_tmp_dir')) {
if (file_exists($temp)) {
$path = $temp;
}
}
}
}
return realpath($path) ?: '';
}
public static function temporaryFilename(): string
{
$filename = tempnam(self::sysGetTempDir(), 'phpspreadsheet');
if ($filename === false) {
throw new Exception('Could not create temporary file');
}
return $filename;
}
/**
* Assert that given path is an existing file and is readable, otherwise throw exception.
*/
public static function assertFile(string $filename, string $zipMember = ''): void
{
if (!is_file($filename)) {
throw new ReaderException('File "' . $filename . '" does not exist.');
}
if (!is_readable($filename)) {
throw new ReaderException('Could not open "' . $filename . '" for reading.');
}
if ($zipMember !== '') {
$zipfile = "zip://$filename#$zipMember";
if (!self::fileExists($zipfile)) {
// Has the file been saved with Windoze directory separators rather than unix?
$zipfile = "zip://$filename#" . str_replace('/', '\\', $zipMember);
if (!self::fileExists($zipfile)) {
throw new ReaderException("Could not find zip member $zipfile");
}
}
}
}
/**
* Same as assertFile, except return true/false and don't throw Exception.
*/
public static function testFileNoThrow(string $filename, ?string $zipMember = null): bool
{
if (!is_file($filename)) {
return false;
}
if (!is_readable($filename)) {
return false;
}
if ($zipMember === null) {
return true;
}
// validate zip, but don't check specific member
if ($zipMember === '') {
return self::validateZipFirst4($filename);
}
$zipfile = "zip://$filename#$zipMember";
if (self::fileExists($zipfile)) {
return true;
}
// Has the file been saved with Windoze directory separators rather than unix?
$zipfile = "zip://$filename#" . str_replace('/', '\\', $zipMember);
return self::fileExists($zipfile);
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Escher.php 0000644 00000002213 15242753725 0016433 0 ustar 00 dggContainer;
}
/**
* Set Drawing Group Container.
*
* @param Escher\DggContainer $dggContainer
*
* @return Escher\DggContainer
*/
public function setDggContainer($dggContainer)
{
return $this->dggContainer = $dggContainer;
}
/**
* Get Drawing Container.
*
* @return ?Escher\DgContainer
*/
public function getDgContainer()
{
return $this->dgContainer;
}
/**
* Set Drawing Container.
*
* @param Escher\DgContainer $dgContainer
*
* @return Escher\DgContainer
*/
public function setDgContainer($dgContainer)
{
return $this->dgContainer = $dgContainer;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php 0000644 00000004223 15242753725 0016757 0 ustar 00 setTimeZone(new DateTimeZone($timezoneName));
return $dtobj->getOffset();
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/CodePage.php 0000644 00000011115 15242753725 0016672 0 ustar 00 'CP1252', // CodePage is not always correctly set when the xls file was saved by Apple's Numbers program
367 => 'ASCII', // ASCII
437 => 'CP437', // OEM US
//720 => 'notsupported', // OEM Arabic
737 => 'CP737', // OEM Greek
775 => 'CP775', // OEM Baltic
850 => 'CP850', // OEM Latin I
852 => 'CP852', // OEM Latin II (Central European)
855 => 'CP855', // OEM Cyrillic
857 => 'CP857', // OEM Turkish
858 => 'CP858', // OEM Multilingual Latin I with Euro
860 => 'CP860', // OEM Portugese
861 => 'CP861', // OEM Icelandic
862 => 'CP862', // OEM Hebrew
863 => 'CP863', // OEM Canadian (French)
864 => 'CP864', // OEM Arabic
865 => 'CP865', // OEM Nordic
866 => 'CP866', // OEM Cyrillic (Russian)
869 => 'CP869', // OEM Greek (Modern)
874 => 'CP874', // ANSI Thai
932 => 'CP932', // ANSI Japanese Shift-JIS
936 => 'CP936', // ANSI Chinese Simplified GBK
949 => 'CP949', // ANSI Korean (Wansung)
950 => 'CP950', // ANSI Chinese Traditional BIG5
1200 => 'UTF-16LE', // UTF-16 (BIFF8)
1250 => 'CP1250', // ANSI Latin II (Central European)
1251 => 'CP1251', // ANSI Cyrillic
1252 => 'CP1252', // ANSI Latin I (BIFF4-BIFF7)
1253 => 'CP1253', // ANSI Greek
1254 => 'CP1254', // ANSI Turkish
1255 => 'CP1255', // ANSI Hebrew
1256 => 'CP1256', // ANSI Arabic
1257 => 'CP1257', // ANSI Baltic
1258 => 'CP1258', // ANSI Vietnamese
1361 => 'CP1361', // ANSI Korean (Johab)
10000 => 'MAC', // Apple Roman
10001 => 'CP932', // Macintosh Japanese
10002 => 'CP950', // Macintosh Chinese Traditional
10003 => 'CP1361', // Macintosh Korean
10004 => 'MACARABIC', // Apple Arabic
10005 => 'MACHEBREW', // Apple Hebrew
10006 => 'MACGREEK', // Macintosh Greek
10007 => 'MACCYRILLIC', // Macintosh Cyrillic
10008 => 'CP936', // Macintosh - Simplified Chinese (GB 2312)
10010 => 'MACROMANIA', // Macintosh Romania
10017 => 'MACUKRAINE', // Macintosh Ukraine
10021 => 'MACTHAI', // Macintosh Thai
10029 => ['MACCENTRALEUROPE', 'MAC-CENTRALEUROPE'], // Macintosh Central Europe
10079 => 'MACICELAND', // Macintosh Icelandic
10081 => 'MACTURKISH', // Macintosh Turkish
10082 => 'MACCROATIAN', // Macintosh Croatian
21010 => 'UTF-16LE', // UTF-16 (BIFF8) This isn't correct, but some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE
32768 => 'MAC', // Apple Roman
//32769 => 'unsupported', // ANSI Latin I (BIFF2-BIFF3)
65000 => 'UTF-7', // Unicode (UTF-7)
65001 => 'UTF-8', // Unicode (UTF-8)
99999 => ['unsupported'], // Unicode (UTF-8)
];
public static function validate(string $codePage): bool
{
return in_array($codePage, self::$pageArray, true);
}
/**
* Convert Microsoft Code Page Identifier to Code Page Name which iconv
* and mbstring understands.
*
* @param int $codePage Microsoft Code Page Indentifier
*
* @return string Code Page Name
*/
public static function numberToName(int $codePage): string
{
if (array_key_exists($codePage, self::$pageArray)) {
$value = self::$pageArray[$codePage];
if (is_array($value)) {
foreach ($value as $encoding) {
if (@iconv('UTF-8', $encoding, ' ') !== false) {
self::$pageArray[$codePage] = $encoding;
return $encoding;
}
}
throw new PhpSpreadsheetException("Code page $codePage not implemented on this system.");
} else {
return $value;
}
}
if ($codePage == 720 || $codePage == 32769) {
throw new PhpSpreadsheetException("Code page $codePage not supported."); // OEM Arabic
}
throw new PhpSpreadsheetException('Unknown codepage: ' . $codePage);
}
public static function getEncodings(): array
{
return self::$pageArray;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Drawing.php 0000644 00000011572 15242753725 0016625 0 ustar 00 getName();
$size = $defaultFont->getSize();
if (isset(Font::$defaultColumnWidths[$name][$size])) {
// Exact width can be determined
return $pixelValue * Font::$defaultColumnWidths[$name][$size]['width']
/ Font::$defaultColumnWidths[$name][$size]['px'];
}
// We don't have data for this particular font and size, use approximation by
// extrapolating from Calibri 11
return $pixelValue * 11 * Font::$defaultColumnWidths['Calibri'][11]['width']
/ Font::$defaultColumnWidths['Calibri'][11]['px'] / $size;
}
/**
* Convert column width from (intrinsic) Excel units to pixels.
*
* @param float $cellWidth Value in cell dimension
* @param \PhpOffice\PhpSpreadsheet\Style\Font $defaultFont Default font of the workbook
*
* @return int Value in pixels
*/
public static function cellDimensionToPixels($cellWidth, \PhpOffice\PhpSpreadsheet\Style\Font $defaultFont)
{
// Font name and size
$name = $defaultFont->getName();
$size = $defaultFont->getSize();
if (isset(Font::$defaultColumnWidths[$name][$size])) {
// Exact width can be determined
$colWidth = $cellWidth * Font::$defaultColumnWidths[$name][$size]['px']
/ Font::$defaultColumnWidths[$name][$size]['width'];
} else {
// We don't have data for this particular font and size, use approximation by
// extrapolating from Calibri 11
$colWidth = $cellWidth * $size * Font::$defaultColumnWidths['Calibri'][11]['px']
/ Font::$defaultColumnWidths['Calibri'][11]['width'] / 11;
}
// Round pixels to closest integer
$colWidth = (int) round($colWidth);
return $colWidth;
}
/**
* Convert pixels to points.
*
* @param int $pixelValue Value in pixels
*
* @return float Value in points
*/
public static function pixelsToPoints($pixelValue)
{
return $pixelValue * 0.75;
}
/**
* Convert points to pixels.
*
* @param int $pointValue Value in points
*
* @return int Value in pixels
*/
public static function pointsToPixels($pointValue)
{
if ($pointValue != 0) {
return (int) ceil($pointValue / 0.75);
}
return 0;
}
/**
* Convert degrees to angle.
*
* @param int $degrees Degrees
*
* @return int Angle
*/
public static function degreesToAngle($degrees)
{
return (int) round($degrees * 60000);
}
/**
* Convert angle to degrees.
*
* @param int|SimpleXMLElement $angle Angle
*
* @return int Degrees
*/
public static function angleToDegrees($angle)
{
$angle = (int) $angle;
if ($angle != 0) {
return (int) round($angle / 60000);
}
return 0;
}
/**
* Create a new image from file. By alexander at alexauto dot nl.
*
* @see http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
*
* @param string $bmpFilename Path to Windows DIB (BMP) image
*
* @return GdImage|resource
*
* @deprecated 1.26 use Php function imagecreatefrombmp instead
*
* @codeCoverageIgnore
*/
public static function imagecreatefrombmp($bmpFilename)
{
$retVal = @imagecreatefrombmp($bmpFilename);
if ($retVal === false) {
throw new ReaderException("Unable to create image from $bmpFilename");
}
return $retVal;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php 0000644 00000045512 15242753725 0016110 0 ustar 00 'January',
'Feb' => 'February',
'Mar' => 'March',
'Apr' => 'April',
'May' => 'May',
'Jun' => 'June',
'Jul' => 'July',
'Aug' => 'August',
'Sep' => 'September',
'Oct' => 'October',
'Nov' => 'November',
'Dec' => 'December',
];
/**
* @var string[]
*/
public static $numberSuffixes = [
'st',
'nd',
'rd',
'th',
];
/**
* Base calendar year to use for calculations
* Value is either CALENDAR_WINDOWS_1900 (1900) or CALENDAR_MAC_1904 (1904).
*
* @var int
*/
protected static $excelCalendar = self::CALENDAR_WINDOWS_1900;
/**
* Default timezone to use for DateTime objects.
*
* @var null|DateTimeZone
*/
protected static $defaultTimeZone;
/**
* Set the Excel calendar (Windows 1900 or Mac 1904).
*
* @param int $baseYear Excel base date (1900 or 1904)
*
* @return bool Success or failure
*/
public static function setExcelCalendar($baseYear)
{
if (
($baseYear == self::CALENDAR_WINDOWS_1900) ||
($baseYear == self::CALENDAR_MAC_1904)
) {
self::$excelCalendar = $baseYear;
return true;
}
return false;
}
/**
* Return the Excel calendar (Windows 1900 or Mac 1904).
*
* @return int Excel base date (1900 or 1904)
*/
public static function getExcelCalendar()
{
return self::$excelCalendar;
}
/**
* Set the Default timezone to use for dates.
*
* @param null|DateTimeZone|string $timeZone The timezone to set for all Excel datetimestamp to PHP DateTime Object conversions
*
* @return bool Success or failure
*/
public static function setDefaultTimezone($timeZone)
{
try {
$timeZone = self::validateTimeZone($timeZone);
self::$defaultTimeZone = $timeZone;
$retval = true;
} catch (PhpSpreadsheetException $e) {
$retval = false;
}
return $retval;
}
/**
* Return the Default timezone, or UTC if default not set.
*/
public static function getDefaultTimezone(): DateTimeZone
{
return self::$defaultTimeZone ?? new DateTimeZone('UTC');
}
/**
* Return the Default timezone, or local timezone if default is not set.
*/
public static function getDefaultOrLocalTimezone(): DateTimeZone
{
return self::$defaultTimeZone ?? new DateTimeZone(date_default_timezone_get());
}
/**
* Return the Default timezone even if null.
*/
public static function getDefaultTimezoneOrNull(): ?DateTimeZone
{
return self::$defaultTimeZone;
}
/**
* Validate a timezone.
*
* @param null|DateTimeZone|string $timeZone The timezone to validate, either as a timezone string or object
*
* @return ?DateTimeZone The timezone as a timezone object
*/
private static function validateTimeZone($timeZone)
{
if ($timeZone instanceof DateTimeZone || $timeZone === null) {
return $timeZone;
}
if (in_array($timeZone, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC))) {
return new DateTimeZone($timeZone);
}
throw new PhpSpreadsheetException('Invalid timezone');
}
/**
* @param mixed $value Converts a date/time in ISO-8601 standard format date string to an Excel
* serialized timestamp.
* See https://en.wikipedia.org/wiki/ISO_8601 for details of the ISO-8601 standard format.
*
* @return float|int
*/
public static function convertIsoDate($value)
{
if (!is_string($value)) {
throw new Exception('Non-string value supplied for Iso Date conversion');
}
$date = new DateTime($value);
$dateErrors = DateTime::getLastErrors();
if (is_array($dateErrors) && ($dateErrors['warning_count'] > 0 || $dateErrors['error_count'] > 0)) {
throw new Exception("Invalid string $value supplied for datatype Date");
}
$newValue = SharedDate::PHPToExcel($date);
if ($newValue === false) {
throw new Exception("Invalid string $value supplied for datatype Date");
}
if (preg_match('/^\\s*\\d?\\d:\\d\\d(:\\d\\d([.]\\d+)?)?\\s*(am|pm)?\\s*$/i', $value) == 1) {
$newValue = fmod($newValue, 1.0);
}
return $newValue;
}
/**
* Convert a MS serialized datetime value from Excel to a PHP Date/Time object.
*
* @param float|int $excelTimestamp MS Excel serialized date/time value
* @param null|DateTimeZone|string $timeZone The timezone to assume for the Excel timestamp,
* if you don't want to treat it as a UTC value
* Use the default (UTC) unless you absolutely need a conversion
*
* @return DateTime PHP date/time object
*/
public static function excelToDateTimeObject($excelTimestamp, $timeZone = null)
{
$timeZone = ($timeZone === null) ? self::getDefaultTimezone() : self::validateTimeZone($timeZone);
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
if ($excelTimestamp < 1 && self::$excelCalendar === self::CALENDAR_WINDOWS_1900) {
// Unix timestamp base date
$baseDate = new DateTime('1970-01-01', $timeZone);
} else {
// MS Excel calendar base dates
if (self::$excelCalendar == self::CALENDAR_WINDOWS_1900) {
// Allow adjustment for 1900 Leap Year in MS Excel
$baseDate = ($excelTimestamp < 60) ? new DateTime('1899-12-31', $timeZone) : new DateTime('1899-12-30', $timeZone);
} else {
$baseDate = new DateTime('1904-01-01', $timeZone);
}
}
} else {
$baseDate = new DateTime('1899-12-30', $timeZone);
}
$days = floor($excelTimestamp);
$partDay = $excelTimestamp - $days;
$hours = floor($partDay * 24);
$partDay = $partDay * 24 - $hours;
$minutes = floor($partDay * 60);
$partDay = $partDay * 60 - $minutes;
$seconds = round($partDay * 60);
if ($days >= 0) {
$days = '+' . $days;
}
$interval = $days . ' days';
return $baseDate->modify($interval)
->setTime((int) $hours, (int) $minutes, (int) $seconds);
}
/**
* Convert a MS serialized datetime value from Excel to a unix timestamp.
* The use of Unix timestamps, and therefore this function, is discouraged.
* They are not Y2038-safe on a 32-bit system, and have no timezone info.
*
* @param float|int $excelTimestamp MS Excel serialized date/time value
* @param null|DateTimeZone|string $timeZone The timezone to assume for the Excel timestamp,
* if you don't want to treat it as a UTC value
* Use the default (UTC) unless you absolutely need a conversion
*
* @return int Unix timetamp for this date/time
*/
public static function excelToTimestamp($excelTimestamp, $timeZone = null)
{
return (int) self::excelToDateTimeObject($excelTimestamp, $timeZone)
->format('U');
}
/**
* Convert a date from PHP to an MS Excel serialized date/time value.
*
* @param mixed $dateValue PHP DateTime object or a string - Unix timestamp is also permitted, but discouraged;
* not Y2038-safe on a 32-bit system, and no timezone info
*
* @return false|float Excel date/time value
* or boolean FALSE on failure
*/
public static function PHPToExcel($dateValue)
{
if ((is_object($dateValue)) && ($dateValue instanceof DateTimeInterface)) {
return self::dateTimeToExcel($dateValue);
} elseif (is_numeric($dateValue)) {
return self::timestampToExcel($dateValue);
} elseif (is_string($dateValue)) {
return self::stringToExcel($dateValue);
}
return false;
}
/**
* Convert a PHP DateTime object to an MS Excel serialized date/time value.
*
* @param DateTimeInterface $dateValue PHP DateTime object
*
* @return float MS Excel serialized date/time value
*/
public static function dateTimeToExcel(DateTimeInterface $dateValue)
{
return self::formattedPHPToExcel(
(int) $dateValue->format('Y'),
(int) $dateValue->format('m'),
(int) $dateValue->format('d'),
(int) $dateValue->format('H'),
(int) $dateValue->format('i'),
(int) $dateValue->format('s')
);
}
/**
* Convert a Unix timestamp to an MS Excel serialized date/time value.
* The use of Unix timestamps, and therefore this function, is discouraged.
* They are not Y2038-safe on a 32-bit system, and have no timezone info.
*
* @param float|int|string $unixTimestamp Unix Timestamp
*
* @return false|float MS Excel serialized date/time value
*/
public static function timestampToExcel($unixTimestamp)
{
if (!is_numeric($unixTimestamp)) {
return false;
}
return self::dateTimeToExcel(new DateTime('@' . $unixTimestamp));
}
/**
* formattedPHPToExcel.
*
* @param int $year
* @param int $month
* @param int $day
* @param int $hours
* @param int $minutes
* @param int $seconds
*
* @return float Excel date/time value
*/
public static function formattedPHPToExcel($year, $month, $day, $hours = 0, $minutes = 0, $seconds = 0)
{
if (self::$excelCalendar == self::CALENDAR_WINDOWS_1900) {
//
// Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel
// This affects every date following 28th February 1900
//
$excel1900isLeapYear = true;
if (($year == 1900) && ($month <= 2)) {
$excel1900isLeapYear = false;
}
$myexcelBaseDate = 2415020;
} else {
$myexcelBaseDate = 2416481;
$excel1900isLeapYear = false;
}
// Julian base date Adjustment
if ($month > 2) {
$month -= 3;
} else {
$month += 9;
--$year;
}
// Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
$century = (int) substr((string) $year, 0, 2);
$decade = (int) substr((string) $year, 2, 2);
$excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myexcelBaseDate + $excel1900isLeapYear;
$excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
return (float) $excelDate + $excelTime;
}
/**
* Is a given cell a date/time?
*
* @param mixed $value
*
* @return bool
*/
public static function isDateTime(Cell $cell, $value = null, bool $dateWithoutTimeOkay = true)
{
$result = false;
$worksheet = $cell->getWorksheetOrNull();
$spreadsheet = ($worksheet === null) ? null : $worksheet->getParent();
if ($worksheet !== null && $spreadsheet !== null) {
$index = $spreadsheet->getActiveSheetIndex();
$selected = $worksheet->getSelectedCells();
try {
$result = is_numeric($value ?? $cell->getCalculatedValue()) &&
self::isDateTimeFormat(
$worksheet->getStyle(
$cell->getCoordinate()
)->getNumberFormat(),
$dateWithoutTimeOkay
);
} catch (Exception $e) {
// Result is already false, so no need to actually do anything here
}
$worksheet->setSelectedCells($selected);
$spreadsheet->setActiveSheetIndex($index);
}
return $result;
}
/**
* Is a given NumberFormat code a date/time format code?
*
* @return bool
*/
public static function isDateTimeFormat(NumberFormat $excelFormatCode, bool $dateWithoutTimeOkay = true)
{
return self::isDateTimeFormatCode((string) $excelFormatCode->getFormatCode(), $dateWithoutTimeOkay);
}
private const POSSIBLE_DATETIME_FORMAT_CHARACTERS = 'eymdHs';
private const POSSIBLE_TIME_FORMAT_CHARACTERS = 'Hs'; // note - no 'm' due to ambiguity
/**
* Is a given number format code a date/time?
*
* @param string $excelFormatCode
*
* @return bool
*/
public static function isDateTimeFormatCode($excelFormatCode, bool $dateWithoutTimeOkay = true)
{
if (strtolower($excelFormatCode) === strtolower(NumberFormat::FORMAT_GENERAL)) {
// "General" contains an epoch letter 'e', so we trap for it explicitly here (case-insensitive check)
return false;
}
if (preg_match('/[0#]E[+-]0/i', $excelFormatCode)) {
// Scientific format
return false;
}
// Switch on formatcode
if (in_array($excelFormatCode, NumberFormat::DATE_TIME_OR_DATETIME_ARRAY, true)) {
return $dateWithoutTimeOkay || in_array($excelFormatCode, NumberFormat::TIME_OR_DATETIME_ARRAY);
}
// Typically number, currency or accounting (or occasionally fraction) formats
if ((substr($excelFormatCode, 0, 1) == '_') || (substr($excelFormatCode, 0, 2) == '0 ')) {
return false;
}
// Some "special formats" provided in German Excel versions were detected as date time value,
// so filter them out here - "\C\H\-00000" (Switzerland) and "\D-00000" (Germany).
if (\strpos($excelFormatCode, '-00000') !== false) {
return false;
}
$possibleFormatCharacters = $dateWithoutTimeOkay ? self::POSSIBLE_DATETIME_FORMAT_CHARACTERS : self::POSSIBLE_TIME_FORMAT_CHARACTERS;
// Try checking for any of the date formatting characters that don't appear within square braces
if (preg_match('/(^|\])[^\[]*[' . $possibleFormatCharacters . ']/i', $excelFormatCode)) {
// We might also have a format mask containing quoted strings...
// we don't want to test for any of our characters within the quoted blocks
if (strpos($excelFormatCode, '"') !== false) {
$segMatcher = false;
foreach (explode('"', $excelFormatCode) as $subVal) {
// Only test in alternate array entries (the non-quoted blocks)
$segMatcher = $segMatcher === false;
if (
$segMatcher &&
(preg_match('/(^|\])[^\[]*[' . $possibleFormatCharacters . ']/i', $subVal))
) {
return true;
}
}
return false;
}
return true;
}
// No date...
return false;
}
/**
* Convert a date/time string to Excel time.
*
* @param string $dateValue Examples: '2009-12-31', '2009-12-31 15:59', '2009-12-31 15:59:10'
*
* @return false|float Excel date/time serial value
*/
public static function stringToExcel($dateValue)
{
if (strlen($dateValue) < 2) {
return false;
}
if (!preg_match('/^(\d{1,4}[ \.\/\-][A-Z]{3,9}([ \.\/\-]\d{1,4})?|[A-Z]{3,9}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?|\d{1,4}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?)( \d{1,2}:\d{1,2}(:\d{1,2})?)?$/iu', $dateValue)) {
return false;
}
$dateValueNew = DateTimeExcel\DateValue::fromString($dateValue);
if (!is_float($dateValueNew)) {
return false;
}
if (strpos($dateValue, ':') !== false) {
$timeValue = DateTimeExcel\TimeValue::fromString($dateValue);
if (!is_float($timeValue)) {
return false;
}
$dateValueNew += $timeValue;
}
return $dateValueNew;
}
/**
* Converts a month name (either a long or a short name) to a month number.
*
* @param string $monthName Month name or abbreviation
*
* @return int|string Month number (1 - 12), or the original string argument if it isn't a valid month name
*/
public static function monthStringToNumber($monthName)
{
$monthIndex = 1;
foreach (self::$monthNames as $shortMonthName => $longMonthName) {
if (($monthName === $longMonthName) || ($monthName === $shortMonthName)) {
return $monthIndex;
}
++$monthIndex;
}
return $monthName;
}
/**
* Strips an ordinal from a numeric value.
*
* @param string $day Day number with an ordinal
*
* @return int|string The integer value with any ordinal stripped, or the original string argument if it isn't a valid numeric
*/
public static function dayStringToNumber($day)
{
$strippedDayValue = (str_replace(self::$numberSuffixes, '', $day));
if (is_numeric($strippedDayValue)) {
return (int) $strippedDayValue;
}
return $day;
}
public static function dateTimeFromTimestamp(string $date, ?DateTimeZone $timeZone = null): DateTime
{
$dtobj = DateTime::createFromFormat('U', $date) ?: new DateTime();
$dtobj->setTimeZone($timeZone ?? self::getDefaultOrLocalTimezone());
return $dtobj;
}
public static function formattedDateTimeFromTimestamp(string $date, string $format, ?DateTimeZone $timeZone = null): string
{
$dtobj = self::dateTimeFromTimestamp($date, $timeZone);
return $dtobj->format($format);
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php 0000644 00000026773 15242753725 0016011 0 ustar 00 getParentOrThrow()->getDefaultStyle()->getFont();
$columnDimensions = $worksheet->getColumnDimensions();
// first find the true column width in pixels (uncollapsed and unhidden)
if (isset($columnDimensions[$col]) && $columnDimensions[$col]->getWidth() != -1) {
// then we have column dimension with explicit width
$columnDimension = $columnDimensions[$col];
$width = $columnDimension->getWidth();
$pixelWidth = Drawing::cellDimensionToPixels($width, $font);
} elseif ($worksheet->getDefaultColumnDimension()->getWidth() != -1) {
// then we have default column dimension with explicit width
$defaultColumnDimension = $worksheet->getDefaultColumnDimension();
$width = $defaultColumnDimension->getWidth();
$pixelWidth = Drawing::cellDimensionToPixels($width, $font);
} else {
// we don't even have any default column dimension. Width depends on default font
$pixelWidth = Font::getDefaultColumnWidthByFont($font, true);
}
// now find the effective column width in pixels
if (isset($columnDimensions[$col]) && !$columnDimensions[$col]->getVisible()) {
$effectivePixelWidth = 0;
} else {
$effectivePixelWidth = $pixelWidth;
}
return $effectivePixelWidth;
}
/**
* Convert the height of a cell from user's units to pixels. By interpolation
* the relationship is: y = 4/3x. If the height hasn't been set by the user we
* use the default value. If the row is hidden we use a value of zero.
*
* @param Worksheet $worksheet The sheet
* @param int $row The row index (1-based)
*
* @return int The width in pixels
*/
public static function sizeRow(Worksheet $worksheet, $row = 1)
{
// default font of the workbook
$font = $worksheet->getParentOrThrow()->getDefaultStyle()->getFont();
$rowDimensions = $worksheet->getRowDimensions();
// first find the true row height in pixels (uncollapsed and unhidden)
if (isset($rowDimensions[$row]) && $rowDimensions[$row]->getRowHeight() != -1) {
// then we have a row dimension
$rowDimension = $rowDimensions[$row];
$rowHeight = $rowDimension->getRowHeight();
$pixelRowHeight = (int) ceil(4 * $rowHeight / 3); // here we assume Arial 10
} elseif ($worksheet->getDefaultRowDimension()->getRowHeight() != -1) {
// then we have a default row dimension with explicit height
$defaultRowDimension = $worksheet->getDefaultRowDimension();
$pixelRowHeight = $defaultRowDimension->getRowHeight(Dimension::UOM_PIXELS);
} else {
// we don't even have any default row dimension. Height depends on default font
$pointRowHeight = Font::getDefaultRowHeightByFont($font);
$pixelRowHeight = Font::fontSizeToPixels((int) $pointRowHeight);
}
// now find the effective row height in pixels
if (isset($rowDimensions[$row]) && !$rowDimensions[$row]->getVisible()) {
$effectivePixelRowHeight = 0;
} else {
$effectivePixelRowHeight = $pixelRowHeight;
}
return (int) $effectivePixelRowHeight;
}
/**
* Get the horizontal distance in pixels between two anchors
* The distanceX is found as sum of all the spanning columns widths minus correction for the two offsets.
*
* @param string $startColumn
* @param int $startOffsetX Offset within start cell measured in 1/1024 of the cell width
* @param string $endColumn
* @param int $endOffsetX Offset within end cell measured in 1/1024 of the cell width
*
* @return int Horizontal measured in pixels
*/
public static function getDistanceX(Worksheet $worksheet, $startColumn = 'A', $startOffsetX = 0, $endColumn = 'A', $endOffsetX = 0)
{
$distanceX = 0;
// add the widths of the spanning columns
$startColumnIndex = Coordinate::columnIndexFromString($startColumn);
$endColumnIndex = Coordinate::columnIndexFromString($endColumn);
for ($i = $startColumnIndex; $i <= $endColumnIndex; ++$i) {
$distanceX += self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($i));
}
// correct for offsetX in startcell
$distanceX -= (int) floor(self::sizeCol($worksheet, $startColumn) * $startOffsetX / 1024);
// correct for offsetX in endcell
$distanceX -= (int) floor(self::sizeCol($worksheet, $endColumn) * (1 - $endOffsetX / 1024));
return $distanceX;
}
/**
* Get the vertical distance in pixels between two anchors
* The distanceY is found as sum of all the spanning rows minus two offsets.
*
* @param int $startRow (1-based)
* @param int $startOffsetY Offset within start cell measured in 1/256 of the cell height
* @param int $endRow (1-based)
* @param int $endOffsetY Offset within end cell measured in 1/256 of the cell height
*
* @return int Vertical distance measured in pixels
*/
public static function getDistanceY(Worksheet $worksheet, $startRow = 1, $startOffsetY = 0, $endRow = 1, $endOffsetY = 0)
{
$distanceY = 0;
// add the widths of the spanning rows
for ($row = $startRow; $row <= $endRow; ++$row) {
$distanceY += self::sizeRow($worksheet, $row);
}
// correct for offsetX in startcell
$distanceY -= (int) floor(self::sizeRow($worksheet, $startRow) * $startOffsetY / 256);
// correct for offsetX in endcell
$distanceY -= (int) floor(self::sizeRow($worksheet, $endRow) * (1 - $endOffsetY / 256));
return $distanceY;
}
/**
* Convert 1-cell anchor coordinates to 2-cell anchor coordinates
* This function is ported from PEAR Spreadsheet_Writer_Excel with small modifications.
*
* Calculate the vertices that define the position of the image as required by
* the OBJ record.
*
* +------------+------------+
* | A | B |
* +-----+------------+------------+
* | |(x1,y1) | |
* | 1 |(A1)._______|______ |
* | | | | |
* | | | | |
* +-----+----| BITMAP |-----+
* | | | | |
* | 2 | |______________. |
* | | | (B2)|
* | | | (x2,y2)|
* +---- +------------+------------+
*
* Example of a bitmap that covers some of the area from cell A1 to cell B2.
*
* Based on the width and height of the bitmap we need to calculate 8 vars:
* $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
* The width and height of the cells are also variable and have to be taken into
* account.
* The values of $col_start and $row_start are passed in from the calling
* function. The values of $col_end and $row_end are calculated by subtracting
* the width and height of the bitmap from the width and height of the
* underlying cells.
* The vertices are expressed as a percentage of the underlying cell width as
* follows (rhs values are in pixels):
*
* x1 = X / W *1024
* y1 = Y / H *256
* x2 = (X-1) / W *1024
* y2 = (Y-1) / H *256
*
* Where: X is distance from the left side of the underlying cell
* Y is distance from the top of the underlying cell
* W is the width of the cell
* H is the height of the cell
*
* @param string $coordinates E.g. 'A1'
* @param int $offsetX Horizontal offset in pixels
* @param int $offsetY Vertical offset in pixels
* @param int $width Width in pixels
* @param int $height Height in pixels
*
* @return null|array
*/
public static function oneAnchor2twoAnchor(Worksheet $worksheet, $coordinates, $offsetX, $offsetY, $width, $height)
{
[$col_start, $row] = Coordinate::indexesFromString($coordinates);
$row_start = $row - 1;
$x1 = $offsetX;
$y1 = $offsetY;
// Initialise end cell to the same as the start cell
$col_end = $col_start; // Col containing lower right corner of object
$row_end = $row_start; // Row containing bottom right corner of object
// Zero the specified offset if greater than the cell dimensions
if ($x1 >= self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_start))) {
$x1 = 0;
}
if ($y1 >= self::sizeRow($worksheet, $row_start + 1)) {
$y1 = 0;
}
$width = $width + $x1 - 1;
$height = $height + $y1 - 1;
// Subtract the underlying cell widths to find the end cell of the image
while ($width >= self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end))) {
$width -= self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end));
++$col_end;
}
// Subtract the underlying cell heights to find the end cell of the image
while ($height >= self::sizeRow($worksheet, $row_end + 1)) {
$height -= self::sizeRow($worksheet, $row_end + 1);
++$row_end;
}
// Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
// with zero height or width.
if (self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_start)) == 0) {
return null;
}
if (self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end)) == 0) {
return null;
}
if (self::sizeRow($worksheet, $row_start + 1) == 0) {
return null;
}
if (self::sizeRow($worksheet, $row_end + 1) == 0) {
return null;
}
// Convert the pixel values to the percentage value expected by Excel
$x1 = $x1 / self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_start)) * 1024;
$y1 = $y1 / self::sizeRow($worksheet, $row_start + 1) * 256;
$x2 = ($width + 1) / self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end)) * 1024; // Distance to right side of object
$y2 = ($height + 1) / self::sizeRow($worksheet, $row_end + 1) * 256; // Distance to bottom of object
$startCoordinates = Coordinate::stringFromColumnIndex($col_start) . ($row_start + 1);
$endCoordinates = Coordinate::stringFromColumnIndex($col_end) . ($row_end + 1);
return [
'startCoordinates' => $startCoordinates,
'startOffsetX' => $x1,
'startOffsetY' => $y1,
'endCoordinates' => $endCoordinates,
'endOffsetX' => $x2,
'endOffsetY' => $y2,
];
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/PasswordHasher.php 0000644 00000007654 15242753725 0020175 0 ustar 00 'md2',
Protection::ALGORITHM_MD4 => 'md4',
Protection::ALGORITHM_MD5 => 'md5',
Protection::ALGORITHM_SHA_1 => 'sha1',
Protection::ALGORITHM_SHA_256 => 'sha256',
Protection::ALGORITHM_SHA_384 => 'sha384',
Protection::ALGORITHM_SHA_512 => 'sha512',
Protection::ALGORITHM_RIPEMD_128 => 'ripemd128',
Protection::ALGORITHM_RIPEMD_160 => 'ripemd160',
Protection::ALGORITHM_WHIRLPOOL => 'whirlpool',
];
if (array_key_exists($algorithmName, $mapping)) {
return $mapping[$algorithmName];
}
throw new SpException('Unsupported password algorithm: ' . $algorithmName);
}
/**
* Create a password hash from a given string.
*
* This method is based on the spec at:
* https://interoperability.blob.core.windows.net/files/MS-OFFCRYPTO/[MS-OFFCRYPTO].pdf
* 2.3.7.1 Binary Document Password Verifier Derivation Method 1
*
* It replaces a method based on the algorithm provided by
* Daniel Rentz of OpenOffice and the PEAR package
* Spreadsheet_Excel_Writer by Xavier Noguer .
*
* Scrutinizer will squawk at the use of bitwise operations here,
* but it should ultimately pass.
*
* @param string $password Password to hash
*/
private static function defaultHashPassword(string $password): string
{
$verifier = 0;
$pwlen = strlen($password);
$passwordArray = pack('c', $pwlen) . $password;
for ($i = $pwlen; $i >= 0; --$i) {
$intermediate1 = (($verifier & 0x4000) === 0) ? 0 : 1;
$intermediate2 = 2 * $verifier;
$intermediate2 = $intermediate2 & 0x7fff;
$intermediate3 = $intermediate1 | $intermediate2;
$verifier = $intermediate3 ^ ord($passwordArray[$i]);
}
$verifier ^= 0xCE4B;
return strtoupper(dechex($verifier));
}
/**
* Create a password hash from a given string by a specific algorithm.
*
* 2.4.2.4 ISO Write Protection Method
*
* @see https://docs.microsoft.com/en-us/openspecs/office_file_formats/ms-offcrypto/1357ea58-646e-4483-92ef-95d718079d6f
*
* @param string $password Password to hash
* @param string $algorithm Hash algorithm used to compute the password hash value
* @param string $salt Pseudorandom string
* @param int $spinCount Number of times to iterate on a hash of a password
*
* @return string Hashed password
*/
public static function hashPassword(string $password, string $algorithm = '', string $salt = '', int $spinCount = 10000): string
{
if (strlen($password) > self::MAX_PASSWORD_LENGTH) {
throw new SpException('Password exceeds ' . self::MAX_PASSWORD_LENGTH . ' characters');
}
$phpAlgorithm = self::getAlgorithm($algorithm);
if (!$phpAlgorithm) {
return self::defaultHashPassword($password);
}
$saltValue = base64_decode($salt);
$encodedPassword = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8');
$hashValue = hash($phpAlgorithm, $saltValue . /** @scrutinizer ignore-type */ $encodedPassword, true);
for ($i = 0; $i < $spinCount; ++$i) {
$hashValue = hash($phpAlgorithm, $hashValue . pack('L', $i), true);
}
return base64_encode($hashValue);
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php 0000644 00000056157 15242753725 0017650 0 ustar 00 chr(0),
"\x1B 1" => chr(1),
"\x1B 2" => chr(2),
"\x1B 3" => chr(3),
"\x1B 4" => chr(4),
"\x1B 5" => chr(5),
"\x1B 6" => chr(6),
"\x1B 7" => chr(7),
"\x1B 8" => chr(8),
"\x1B 9" => chr(9),
"\x1B :" => chr(10),
"\x1B ;" => chr(11),
"\x1B <" => chr(12),
"\x1B =" => chr(13),
"\x1B >" => chr(14),
"\x1B ?" => chr(15),
"\x1B!0" => chr(16),
"\x1B!1" => chr(17),
"\x1B!2" => chr(18),
"\x1B!3" => chr(19),
"\x1B!4" => chr(20),
"\x1B!5" => chr(21),
"\x1B!6" => chr(22),
"\x1B!7" => chr(23),
"\x1B!8" => chr(24),
"\x1B!9" => chr(25),
"\x1B!:" => chr(26),
"\x1B!;" => chr(27),
"\x1B!<" => chr(28),
"\x1B!=" => chr(29),
"\x1B!>" => chr(30),
"\x1B!?" => chr(31),
"\x1B'?" => chr(127),
"\x1B(0" => '€', // 128 in CP1252
"\x1B(2" => '‚', // 130 in CP1252
"\x1B(3" => 'ƒ', // 131 in CP1252
"\x1B(4" => '„', // 132 in CP1252
"\x1B(5" => '…', // 133 in CP1252
"\x1B(6" => '†', // 134 in CP1252
"\x1B(7" => '‡', // 135 in CP1252
"\x1B(8" => 'ˆ', // 136 in CP1252
"\x1B(9" => '‰', // 137 in CP1252
"\x1B(:" => 'Š', // 138 in CP1252
"\x1B(;" => '‹', // 139 in CP1252
"\x1BNj" => 'Œ', // 140 in CP1252
"\x1B(>" => 'Ž', // 142 in CP1252
"\x1B)1" => '‘', // 145 in CP1252
"\x1B)2" => '’', // 146 in CP1252
"\x1B)3" => '“', // 147 in CP1252
"\x1B)4" => '”', // 148 in CP1252
"\x1B)5" => '•', // 149 in CP1252
"\x1B)6" => '–', // 150 in CP1252
"\x1B)7" => '—', // 151 in CP1252
"\x1B)8" => '˜', // 152 in CP1252
"\x1B)9" => '™', // 153 in CP1252
"\x1B):" => 'š', // 154 in CP1252
"\x1B);" => '›', // 155 in CP1252
"\x1BNz" => 'œ', // 156 in CP1252
"\x1B)>" => 'ž', // 158 in CP1252
"\x1B)?" => 'Ÿ', // 159 in CP1252
"\x1B*0" => ' ', // 160 in CP1252
"\x1BN!" => '¡', // 161 in CP1252
"\x1BN\"" => '¢', // 162 in CP1252
"\x1BN#" => '£', // 163 in CP1252
"\x1BN(" => '¤', // 164 in CP1252
"\x1BN%" => '¥', // 165 in CP1252
"\x1B*6" => '¦', // 166 in CP1252
"\x1BN'" => '§', // 167 in CP1252
"\x1BNH " => '¨', // 168 in CP1252
"\x1BNS" => '©', // 169 in CP1252
"\x1BNc" => 'ª', // 170 in CP1252
"\x1BN+" => '«', // 171 in CP1252
"\x1B*<" => '¬', // 172 in CP1252
"\x1B*=" => '', // 173 in CP1252
"\x1BNR" => '®', // 174 in CP1252
"\x1B*?" => '¯', // 175 in CP1252
"\x1BN0" => '°', // 176 in CP1252
"\x1BN1" => '±', // 177 in CP1252
"\x1BN2" => '²', // 178 in CP1252
"\x1BN3" => '³', // 179 in CP1252
"\x1BNB " => '´', // 180 in CP1252
"\x1BN5" => 'µ', // 181 in CP1252
"\x1BN6" => '¶', // 182 in CP1252
"\x1BN7" => '·', // 183 in CP1252
"\x1B+8" => '¸', // 184 in CP1252
"\x1BNQ" => '¹', // 185 in CP1252
"\x1BNk" => 'º', // 186 in CP1252
"\x1BN;" => '»', // 187 in CP1252
"\x1BN<" => '¼', // 188 in CP1252
"\x1BN=" => '½', // 189 in CP1252
"\x1BN>" => '¾', // 190 in CP1252
"\x1BN?" => '¿', // 191 in CP1252
"\x1BNAA" => 'À', // 192 in CP1252
"\x1BNBA" => 'Á', // 193 in CP1252
"\x1BNCA" => 'Â', // 194 in CP1252
"\x1BNDA" => 'Ã', // 195 in CP1252
"\x1BNHA" => 'Ä', // 196 in CP1252
"\x1BNJA" => 'Å', // 197 in CP1252
"\x1BNa" => 'Æ', // 198 in CP1252
"\x1BNKC" => 'Ç', // 199 in CP1252
"\x1BNAE" => 'È', // 200 in CP1252
"\x1BNBE" => 'É', // 201 in CP1252
"\x1BNCE" => 'Ê', // 202 in CP1252
"\x1BNHE" => 'Ë', // 203 in CP1252
"\x1BNAI" => 'Ì', // 204 in CP1252
"\x1BNBI" => 'Í', // 205 in CP1252
"\x1BNCI" => 'Î', // 206 in CP1252
"\x1BNHI" => 'Ï', // 207 in CP1252
"\x1BNb" => 'Ð', // 208 in CP1252
"\x1BNDN" => 'Ñ', // 209 in CP1252
"\x1BNAO" => 'Ò', // 210 in CP1252
"\x1BNBO" => 'Ó', // 211 in CP1252
"\x1BNCO" => 'Ô', // 212 in CP1252
"\x1BNDO" => 'Õ', // 213 in CP1252
"\x1BNHO" => 'Ö', // 214 in CP1252
"\x1B-7" => '×', // 215 in CP1252
"\x1BNi" => 'Ø', // 216 in CP1252
"\x1BNAU" => 'Ù', // 217 in CP1252
"\x1BNBU" => 'Ú', // 218 in CP1252
"\x1BNCU" => 'Û', // 219 in CP1252
"\x1BNHU" => 'Ü', // 220 in CP1252
"\x1B-=" => 'Ý', // 221 in CP1252
"\x1BNl" => 'Þ', // 222 in CP1252
"\x1BN{" => 'ß', // 223 in CP1252
"\x1BNAa" => 'à', // 224 in CP1252
"\x1BNBa" => 'á', // 225 in CP1252
"\x1BNCa" => 'â', // 226 in CP1252
"\x1BNDa" => 'ã', // 227 in CP1252
"\x1BNHa" => 'ä', // 228 in CP1252
"\x1BNJa" => 'å', // 229 in CP1252
"\x1BNq" => 'æ', // 230 in CP1252
"\x1BNKc" => 'ç', // 231 in CP1252
"\x1BNAe" => 'è', // 232 in CP1252
"\x1BNBe" => 'é', // 233 in CP1252
"\x1BNCe" => 'ê', // 234 in CP1252
"\x1BNHe" => 'ë', // 235 in CP1252
"\x1BNAi" => 'ì', // 236 in CP1252
"\x1BNBi" => 'í', // 237 in CP1252
"\x1BNCi" => 'î', // 238 in CP1252
"\x1BNHi" => 'ï', // 239 in CP1252
"\x1BNs" => 'ð', // 240 in CP1252
"\x1BNDn" => 'ñ', // 241 in CP1252
"\x1BNAo" => 'ò', // 242 in CP1252
"\x1BNBo" => 'ó', // 243 in CP1252
"\x1BNCo" => 'ô', // 244 in CP1252
"\x1BNDo" => 'õ', // 245 in CP1252
"\x1BNHo" => 'ö', // 246 in CP1252
"\x1B/7" => '÷', // 247 in CP1252
"\x1BNy" => 'ø', // 248 in CP1252
"\x1BNAu" => 'ù', // 249 in CP1252
"\x1BNBu" => 'ú', // 250 in CP1252
"\x1BNCu" => 'û', // 251 in CP1252
"\x1BNHu" => 'ü', // 252 in CP1252
"\x1B/=" => 'ý', // 253 in CP1252
"\x1BN|" => 'þ', // 254 in CP1252
"\x1BNHy" => 'ÿ', // 255 in CP1252
];
}
/**
* Get whether iconv extension is available.
*
* @return bool
*/
public static function getIsIconvEnabled()
{
if (isset(self::$isIconvEnabled)) {
return self::$isIconvEnabled;
}
// Assume no problems with iconv
self::$isIconvEnabled = true;
// Fail if iconv doesn't exist
if (!function_exists('iconv')) {
self::$isIconvEnabled = false;
} elseif (!@iconv('UTF-8', 'UTF-16LE', 'x')) {
// Sometimes iconv is not working, and e.g. iconv('UTF-8', 'UTF-16LE', 'x') just returns false,
self::$isIconvEnabled = false;
} elseif (defined('PHP_OS') && @stristr(PHP_OS, 'AIX') && defined('ICONV_IMPL') && (@strcasecmp(ICONV_IMPL, 'unknown') == 0) && defined('ICONV_VERSION') && (@strcasecmp(ICONV_VERSION, 'unknown') == 0)) {
// CUSTOM: IBM AIX iconv() does not work
self::$isIconvEnabled = false;
}
// Deactivate iconv default options if they fail (as seen on IMB i)
if (self::$isIconvEnabled && !@iconv('UTF-8', 'UTF-16LE' . self::$iconvOptions, 'x')) {
self::$iconvOptions = '';
}
return self::$isIconvEnabled;
}
private static function buildCharacterSets(): void
{
if (empty(self::$controlCharacters)) {
self::buildControlCharacters();
}
if (empty(self::$SYLKCharacters)) {
self::buildSYLKCharacters();
}
}
/**
* Convert from OpenXML escaped control character to PHP control character.
*
* Excel 2007 team:
* ----------------
* That's correct, control characters are stored directly in the shared-strings table.
* We do encode characters that cannot be represented in XML using the following escape sequence:
* _xHHHH_ where H represents a hexadecimal character in the character's value...
* So you could end up with something like _x0008_ in a string (either in a cell value ()
* element or in the shared string element.
*
* @param string $textValue Value to unescape
*
* @return string
*/
public static function controlCharacterOOXML2PHP($textValue)
{
self::buildCharacterSets();
return str_replace(array_keys(self::$controlCharacters), array_values(self::$controlCharacters), $textValue);
}
/**
* Convert from PHP control character to OpenXML escaped control character.
*
* Excel 2007 team:
* ----------------
* That's correct, control characters are stored directly in the shared-strings table.
* We do encode characters that cannot be represented in XML using the following escape sequence:
* _xHHHH_ where H represents a hexadecimal character in the character's value...
* So you could end up with something like _x0008_ in a string (either in a cell value ()
* element or in the shared string element.
*
* @param string $textValue Value to escape
*
* @return string
*/
public static function controlCharacterPHP2OOXML($textValue)
{
self::buildCharacterSets();
return str_replace(array_values(self::$controlCharacters), array_keys(self::$controlCharacters), $textValue);
}
/**
* Try to sanitize UTF8, replacing invalid sequences with Unicode substitution characters.
*/
public static function sanitizeUTF8(string $textValue): string
{
$textValue = str_replace(["\xef\xbf\xbe", "\xef\xbf\xbf"], "\xef\xbf\xbd", $textValue);
$subst = mb_substitute_character(); // default is question mark
mb_substitute_character(65533); // Unicode substitution character
// Phpstan does not think this can return false.
$returnValue = mb_convert_encoding($textValue, 'UTF-8', 'UTF-8');
mb_substitute_character(/** @scrutinizer ignore-type */ $subst);
return self::returnString($returnValue);
}
/**
* Strictly to satisfy Scrutinizer.
*
* @param mixed $value
*/
private static function returnString($value): string
{
return is_string($value) ? $value : '';
}
/**
* Check if a string contains UTF8 data.
*/
public static function isUTF8(string $textValue): bool
{
return $textValue === self::sanitizeUTF8($textValue);
}
/**
* Formats a numeric value as a string for output in various output writers forcing
* point as decimal separator in case locale is other than English.
*
* @param float|int|string $numericValue
*/
public static function formatNumber($numericValue): string
{
if (is_float($numericValue)) {
return str_replace(',', '.', (string) $numericValue);
}
return (string) $numericValue;
}
/**
* Converts a UTF-8 string into BIFF8 Unicode string data (8-bit string length)
* Writes the string using uncompressed notation, no rich text, no Asian phonetics
* If mbstring extension is not available, ASCII is assumed, and compressed notation is used
* although this will give wrong results for non-ASCII strings
* see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
*
* @param string $textValue UTF-8 encoded string
* @param mixed[] $arrcRuns Details of rich text runs in $value
*/
public static function UTF8toBIFF8UnicodeShort(string $textValue, array $arrcRuns = []): string
{
// character count
$ln = self::countCharacters($textValue, 'UTF-8');
// option flags
if (empty($arrcRuns)) {
$data = pack('CC', $ln, 0x0001);
// characters
$data .= self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
} else {
$data = pack('vC', $ln, 0x09);
$data .= pack('v', count($arrcRuns));
// characters
$data .= self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
foreach ($arrcRuns as $cRun) {
$data .= pack('v', $cRun['strlen']);
$data .= pack('v', $cRun['fontidx']);
}
}
return $data;
}
/**
* Converts a UTF-8 string into BIFF8 Unicode string data (16-bit string length)
* Writes the string using uncompressed notation, no rich text, no Asian phonetics
* If mbstring extension is not available, ASCII is assumed, and compressed notation is used
* although this will give wrong results for non-ASCII strings
* see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
*
* @param string $textValue UTF-8 encoded string
*/
public static function UTF8toBIFF8UnicodeLong(string $textValue): string
{
// character count
$ln = self::countCharacters($textValue, 'UTF-8');
// characters
$chars = self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
return pack('vC', $ln, 0x0001) . $chars;
}
/**
* Convert string from one encoding to another.
*
* @param string $to Encoding to convert to, e.g. 'UTF-8'
* @param string $from Encoding to convert from, e.g. 'UTF-16LE'
*/
public static function convertEncoding(string $textValue, string $to, string $from): string
{
if (self::getIsIconvEnabled()) {
$result = iconv($from, $to . self::$iconvOptions, $textValue);
if (false !== $result) {
return $result;
}
}
return self::returnString(mb_convert_encoding($textValue, $to, $from));
}
/**
* Get character count.
*
* @param string $encoding Encoding
*
* @return int Character count
*/
public static function countCharacters(string $textValue, string $encoding = 'UTF-8'): int
{
return mb_strlen($textValue, $encoding);
}
/**
* Get character count using mb_strwidth rather than mb_strlen.
*
* @param string $encoding Encoding
*
* @return int Character count
*/
public static function countCharactersDbcs(string $textValue, string $encoding = 'UTF-8'): int
{
return mb_strwidth($textValue, $encoding);
}
/**
* Get a substring of a UTF-8 encoded string.
*
* @param string $textValue UTF-8 encoded string
* @param int $offset Start offset
* @param ?int $length Maximum number of characters in substring
*/
public static function substring(string $textValue, int $offset, ?int $length = 0): string
{
return mb_substr($textValue, $offset, $length, 'UTF-8');
}
/**
* Convert a UTF-8 encoded string to upper case.
*
* @param string $textValue UTF-8 encoded string
*/
public static function strToUpper(string $textValue): string
{
return mb_convert_case($textValue, MB_CASE_UPPER, 'UTF-8');
}
/**
* Convert a UTF-8 encoded string to lower case.
*
* @param string $textValue UTF-8 encoded string
*/
public static function strToLower(string $textValue): string
{
return mb_convert_case($textValue, MB_CASE_LOWER, 'UTF-8');
}
/**
* Convert a UTF-8 encoded string to title/proper case
* (uppercase every first character in each word, lower case all other characters).
*
* @param string $textValue UTF-8 encoded string
*/
public static function strToTitle(string $textValue): string
{
return mb_convert_case($textValue, MB_CASE_TITLE, 'UTF-8');
}
public static function mbIsUpper(string $character): bool
{
return mb_strtolower($character, 'UTF-8') !== $character;
}
/**
* Splits a UTF-8 string into an array of individual characters.
*/
public static function mbStrSplit(string $string): array
{
// Split at all position not after the start: ^
// and not before the end: $
$split = preg_split('/(? $v) {
$textValue = str_replace($k, $v, $textValue);
}
return $textValue;
}
/**
* Retrieve any leading numeric part of a string, or return the full string if no leading numeric
* (handles basic integer or float, but not exponent or non decimal).
*
* @param string $textValue
*
* @return mixed string or only the leading numeric part of the string
*/
public static function testStringAsNumeric($textValue)
{
if (is_numeric($textValue)) {
return $textValue;
}
$v = (float) $textValue;
return (is_numeric(substr($textValue, 0, strlen((string) $v)))) ? $v : $textValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php 0000644 00000005053 15242753725 0017064 0 ustar 00 openMemory();
} else {
// Create temporary filename
if ($temporaryStorageFolder === null) {
$temporaryStorageFolder = File::sysGetTempDir();
}
$this->tempFileName = (string) @tempnam($temporaryStorageFolder, 'xml');
// Open storage
if (empty($this->tempFileName) || $this->openUri($this->tempFileName) === false) {
// Fallback to memory...
$this->openMemory();
}
}
// Set default values
if (self::$debugEnabled) {
$this->setIndent(true);
}
}
/**
* Destructor.
*/
public function __destruct()
{
// Unlink temporary files
// There is nothing reasonable to do if unlink fails.
if ($this->tempFileName != '') {
/** @scrutinizer ignore-unhandled */
@unlink($this->tempFileName);
}
}
public function __wakeup(): void
{
$this->tempFileName = '';
throw new SpreadsheetException('Unserialize not permitted');
}
/**
* Get written data.
*
* @return string
*/
public function getData()
{
if ($this->tempFileName == '') {
return $this->outputMemory(true);
}
$this->flush();
return file_get_contents($this->tempFileName) ?: '';
}
/**
* Wrapper method for writeRaw.
*
* @param null|string|string[] $rawTextData
*
* @return bool
*/
public function writeRawData($rawTextData)
{
if (is_array($rawTextData)) {
$rawTextData = implode("\n", $rawTextData);
}
return $this->writeRaw(htmlspecialchars($rawTextData ?? ''));
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/Font.php 0000644 00000060066 15242753725 0016142 0 ustar 00 [
'x' => self::ARIAL,
'xb' => self::ARIAL_BOLD,
'xi' => self::ARIAL_ITALIC,
'xbi' => self::ARIAL_BOLD_ITALIC,
],
'Calibri' => [
'x' => self::CALIBRI,
'xb' => self::CALIBRI_BOLD,
'xi' => self::CALIBRI_ITALIC,
'xbi' => self::CALIBRI_BOLD_ITALIC,
],
'Comic Sans MS' => [
'x' => self::COMIC_SANS_MS,
'xb' => self::COMIC_SANS_MS_BOLD,
'xi' => self::COMIC_SANS_MS,
'xbi' => self::COMIC_SANS_MS_BOLD,
],
'Courier New' => [
'x' => self::COURIER_NEW,
'xb' => self::COURIER_NEW_BOLD,
'xi' => self::COURIER_NEW_ITALIC,
'xbi' => self::COURIER_NEW_BOLD_ITALIC,
],
'Georgia' => [
'x' => self::GEORGIA,
'xb' => self::GEORGIA_BOLD,
'xi' => self::GEORGIA_ITALIC,
'xbi' => self::GEORGIA_BOLD_ITALIC,
],
'Impact' => [
'x' => self::IMPACT,
'xb' => self::IMPACT,
'xi' => self::IMPACT,
'xbi' => self::IMPACT,
],
'Liberation Sans' => [
'x' => self::LIBERATION_SANS,
'xb' => self::LIBERATION_SANS_BOLD,
'xi' => self::LIBERATION_SANS_ITALIC,
'xbi' => self::LIBERATION_SANS_BOLD_ITALIC,
],
'Lucida Console' => [
'x' => self::LUCIDA_CONSOLE,
'xb' => self::LUCIDA_CONSOLE,
'xi' => self::LUCIDA_CONSOLE,
'xbi' => self::LUCIDA_CONSOLE,
],
'Lucida Sans Unicode' => [
'x' => self::LUCIDA_SANS_UNICODE,
'xb' => self::LUCIDA_SANS_UNICODE,
'xi' => self::LUCIDA_SANS_UNICODE,
'xbi' => self::LUCIDA_SANS_UNICODE,
],
'Microsoft Sans Serif' => [
'x' => self::MICROSOFT_SANS_SERIF,
'xb' => self::MICROSOFT_SANS_SERIF,
'xi' => self::MICROSOFT_SANS_SERIF,
'xbi' => self::MICROSOFT_SANS_SERIF,
],
'Palatino Linotype' => [
'x' => self::PALATINO_LINOTYPE,
'xb' => self::PALATINO_LINOTYPE_BOLD,
'xi' => self::PALATINO_LINOTYPE_ITALIC,
'xbi' => self::PALATINO_LINOTYPE_BOLD_ITALIC,
],
'Symbol' => [
'x' => self::SYMBOL,
'xb' => self::SYMBOL,
'xi' => self::SYMBOL,
'xbi' => self::SYMBOL,
],
'Tahoma' => [
'x' => self::TAHOMA,
'xb' => self::TAHOMA_BOLD,
'xi' => self::TAHOMA,
'xbi' => self::TAHOMA_BOLD,
],
'Times New Roman' => [
'x' => self::TIMES_NEW_ROMAN,
'xb' => self::TIMES_NEW_ROMAN_BOLD,
'xi' => self::TIMES_NEW_ROMAN_ITALIC,
'xbi' => self::TIMES_NEW_ROMAN_BOLD_ITALIC,
],
'Trebuchet MS' => [
'x' => self::TREBUCHET_MS,
'xb' => self::TREBUCHET_MS_BOLD,
'xi' => self::TREBUCHET_MS_ITALIC,
'xbi' => self::TREBUCHET_MS_BOLD_ITALIC,
],
'Verdana' => [
'x' => self::VERDANA,
'xb' => self::VERDANA_BOLD,
'xi' => self::VERDANA_ITALIC,
'xbi' => self::VERDANA_BOLD_ITALIC,
],
];
/**
* Array that can be used to supplement FONT_FILE_NAMES for calculating exact width.
*
* @var array
*/
private static $extraFontArray = [];
public static function setExtraFontArray(array $extraFontArray): void
{
self::$extraFontArray = $extraFontArray;
}
public static function getExtraFontArray(): array
{
return self::$extraFontArray;
}
/**
* AutoSize method.
*
* @var string
*/
private static $autoSizeMethod = self::AUTOSIZE_METHOD_APPROX;
/**
* Path to folder containing TrueType font .ttf files.
*
* @var string
*/
private static $trueTypeFontPath = '';
/**
* How wide is a default column for a given default font and size?
* Empirical data found by inspecting real Excel files and reading off the pixel width
* in Microsoft Office Excel 2007.
* Added height in points.
*/
public const DEFAULT_COLUMN_WIDTHS = [
'Arial' => [
1 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
2 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
3 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.0],
4 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.75],
5 => ['px' => 40, 'width' => 10.00000000, 'height' => 8.25],
6 => ['px' => 48, 'width' => 9.59765625, 'height' => 8.25],
7 => ['px' => 48, 'width' => 9.59765625, 'height' => 9.0],
8 => ['px' => 56, 'width' => 9.33203125, 'height' => 11.25],
9 => ['px' => 64, 'width' => 9.14062500, 'height' => 12.0],
10 => ['px' => 64, 'width' => 9.14062500, 'height' => 12.75],
],
'Calibri' => [
1 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
2 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
3 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.00],
4 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.75],
5 => ['px' => 40, 'width' => 10.00000000, 'height' => 8.25],
6 => ['px' => 48, 'width' => 9.59765625, 'height' => 8.25],
7 => ['px' => 48, 'width' => 9.59765625, 'height' => 9.0],
8 => ['px' => 56, 'width' => 9.33203125, 'height' => 11.25],
9 => ['px' => 56, 'width' => 9.33203125, 'height' => 12.0],
10 => ['px' => 64, 'width' => 9.14062500, 'height' => 12.75],
11 => ['px' => 64, 'width' => 9.14062500, 'height' => 15.0],
],
'Verdana' => [
1 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
2 => ['px' => 24, 'width' => 12.00000000, 'height' => 5.25],
3 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.0],
4 => ['px' => 32, 'width' => 10.66406250, 'height' => 6.75],
5 => ['px' => 40, 'width' => 10.00000000, 'height' => 8.25],
6 => ['px' => 48, 'width' => 9.59765625, 'height' => 8.25],
7 => ['px' => 48, 'width' => 9.59765625, 'height' => 9.0],
8 => ['px' => 64, 'width' => 9.14062500, 'height' => 10.5],
9 => ['px' => 72, 'width' => 9.00000000, 'height' => 11.25],
10 => ['px' => 72, 'width' => 9.00000000, 'height' => 12.75],
],
];
/**
* List of column widths. Replaced by constant;
* previously it was public and updateable, allowing
* user to make inappropriate alterations.
*
* @deprecated 1.25.0 Use DEFAULT_COLUMN_WIDTHS constant instead.
*
* @var array
*/
public static $defaultColumnWidths = self::DEFAULT_COLUMN_WIDTHS;
/**
* Set autoSize method.
*
* @param string $method see self::AUTOSIZE_METHOD_*
*
* @return bool Success or failure
*/
public static function setAutoSizeMethod($method)
{
if (!in_array($method, self::AUTOSIZE_METHODS)) {
return false;
}
self::$autoSizeMethod = $method;
return true;
}
/**
* Get autoSize method.
*
* @return string
*/
public static function getAutoSizeMethod()
{
return self::$autoSizeMethod;
}
/**
* Set the path to the folder containing .ttf files. There should be a trailing slash.
* Typical locations on variout some platforms:
*
* - C:/Windows/Fonts/
* - /usr/share/fonts/truetype/
* - ~/.fonts/
*
.
*
* @param string $folderPath
*/
public static function setTrueTypeFontPath($folderPath): void
{
self::$trueTypeFontPath = $folderPath;
}
/**
* Get the path to the folder containing .ttf files.
*
* @return string
*/
public static function getTrueTypeFontPath()
{
return self::$trueTypeFontPath;
}
/**
* Calculate an (approximate) OpenXML column width, based on font size and text contained.
*
* @param FontStyle $font Font object
* @param null|RichText|string $cellText Text to calculate width
* @param int $rotation Rotation angle
* @param null|FontStyle $defaultFont Font object
* @param bool $filterAdjustment Add space for Autofilter or Table dropdown
*/
public static function calculateColumnWidth(
FontStyle $font,
$cellText = '',
$rotation = 0,
?FontStyle $defaultFont = null,
bool $filterAdjustment = false,
int $indentAdjustment = 0
): float {
// If it is rich text, use plain text
if ($cellText instanceof RichText) {
$cellText = $cellText->getPlainText();
}
// Special case if there are one or more newline characters ("\n")
$cellText = (string) $cellText;
if (strpos($cellText, "\n") !== false) {
$lineTexts = explode("\n", $cellText);
$lineWidths = [];
foreach ($lineTexts as $lineText) {
$lineWidths[] = self::calculateColumnWidth($font, $lineText, $rotation = 0, $defaultFont, $filterAdjustment);
}
return max($lineWidths); // width of longest line in cell
}
// Try to get the exact text width in pixels
$approximate = self::$autoSizeMethod === self::AUTOSIZE_METHOD_APPROX;
$columnWidth = 0;
if (!$approximate) {
try {
$columnWidthAdjust = ceil(
self::getTextWidthPixelsExact(
str_repeat('n', 1 * (($filterAdjustment ? 3 : 1) + ($indentAdjustment * 2))),
$font,
0
) * 1.07
);
// Width of text in pixels excl. padding
// and addition because Excel adds some padding, just use approx width of 'n' glyph
$columnWidth = self::getTextWidthPixelsExact($cellText, $font, $rotation) + $columnWidthAdjust;
} catch (PhpSpreadsheetException $e) {
$approximate = true;
}
}
if ($approximate) {
$columnWidthAdjust = self::getTextWidthPixelsApprox(
str_repeat('n', 1 * (($filterAdjustment ? 3 : 1) + ($indentAdjustment * 2))),
$font,
0
);
// Width of text in pixels excl. padding, approximation
// and addition because Excel adds some padding, just use approx width of 'n' glyph
$columnWidth = self::getTextWidthPixelsApprox($cellText, $font, $rotation) + $columnWidthAdjust;
}
// Convert from pixel width to column width
$columnWidth = Drawing::pixelsToCellDimension((int) $columnWidth, $defaultFont ?? new FontStyle());
// Return
return round($columnWidth, 4);
}
/**
* Get GD text width in pixels for a string of text in a certain font at a certain rotation angle.
*/
public static function getTextWidthPixelsExact(string $text, FontStyle $font, int $rotation = 0): float
{
// font size should really be supplied in pixels in GD2,
// but since GD2 seems to assume 72dpi, pixels and points are the same
$fontFile = self::getTrueTypeFontFileFromFont($font);
$textBox = imagettfbbox($font->getSize() ?? 10.0, $rotation, $fontFile, $text);
if ($textBox === false) {
// @codeCoverageIgnoreStart
throw new PhpSpreadsheetException('imagettfbbox failed');
// @codeCoverageIgnoreEnd
}
// Get corners positions
$lowerLeftCornerX = $textBox[0];
$lowerRightCornerX = $textBox[2];
$upperRightCornerX = $textBox[4];
$upperLeftCornerX = $textBox[6];
// Consider the rotation when calculating the width
return round(max($lowerRightCornerX - $upperLeftCornerX, $upperRightCornerX - $lowerLeftCornerX), 4);
}
/**
* Get approximate width in pixels for a string of text in a certain font at a certain rotation angle.
*
* @param string $columnText
* @param int $rotation
*
* @return int Text width in pixels (no padding added)
*/
public static function getTextWidthPixelsApprox($columnText, FontStyle $font, $rotation = 0)
{
$fontName = $font->getName();
$fontSize = $font->getSize();
// Calculate column width in pixels.
// We assume fixed glyph width, but count double for "fullwidth" characters.
// Result varies with font name and size.
switch ($fontName) {
case 'Arial':
// value 8 was set because of experience in different exports at Arial 10 font.
$columnWidth = (int) (8 * StringHelper::countCharactersDbcs($columnText));
$columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
break;
case 'Verdana':
// value 8 was found via interpolation by inspecting real Excel files with Verdana 10 font.
$columnWidth = (int) (8 * StringHelper::countCharactersDbcs($columnText));
$columnWidth = $columnWidth * $fontSize / 10; // extrapolate from font size
break;
default:
// just assume Calibri
// value 8.26 was found via interpolation by inspecting real Excel files with Calibri 11 font.
$columnWidth = (int) (8.26 * StringHelper::countCharactersDbcs($columnText));
$columnWidth = $columnWidth * $fontSize / 11; // extrapolate from font size
break;
}
// Calculate approximate rotated column width
if ($rotation !== 0) {
if ($rotation == Alignment::TEXTROTATION_STACK_PHPSPREADSHEET) {
// stacked text
$columnWidth = 4; // approximation
} else {
// rotated text
$columnWidth = $columnWidth * cos(deg2rad($rotation))
+ $fontSize * abs(sin(deg2rad($rotation))) / 5; // approximation
}
}
// pixel width is an integer
return (int) $columnWidth;
}
/**
* Calculate an (approximate) pixel size, based on a font points size.
*
* @param int $fontSizeInPoints Font size (in points)
*
* @return int Font size (in pixels)
*/
public static function fontSizeToPixels($fontSizeInPoints)
{
return (int) ((4 / 3) * $fontSizeInPoints);
}
/**
* Calculate an (approximate) pixel size, based on inch size.
*
* @param int $sizeInInch Font size (in inch)
*
* @return int Size (in pixels)
*/
public static function inchSizeToPixels($sizeInInch)
{
return $sizeInInch * 96;
}
/**
* Calculate an (approximate) pixel size, based on centimeter size.
*
* @param int $sizeInCm Font size (in centimeters)
*
* @return float Size (in pixels)
*/
public static function centimeterSizeToPixels($sizeInCm)
{
return $sizeInCm * 37.795275591;
}
/**
* Returns the font path given the font.
*
* @return string Path to TrueType font file
*/
public static function getTrueTypeFontFileFromFont(FontStyle $font, bool $checkPath = true)
{
if ($checkPath && (!file_exists(self::$trueTypeFontPath) || !is_dir(self::$trueTypeFontPath))) {
throw new PhpSpreadsheetException('Valid directory to TrueType Font files not specified');
}
$name = $font->getName();
$fontArray = array_merge(self::FONT_FILE_NAMES, self::$extraFontArray);
if (!isset($fontArray[$name])) {
throw new PhpSpreadsheetException('Unknown font name "' . $name . '". Cannot map to TrueType font file');
}
$bold = $font->getBold();
$italic = $font->getItalic();
$index = 'x';
if ($bold) {
$index .= 'b';
}
if ($italic) {
$index .= 'i';
}
$fontFile = $fontArray[$name][$index];
$separator = '';
if (mb_strlen(self::$trueTypeFontPath) > 1 && mb_substr(self::$trueTypeFontPath, -1) !== '/' && mb_substr(self::$trueTypeFontPath, -1) !== '\\') {
$separator = DIRECTORY_SEPARATOR;
}
$fontFileAbsolute = preg_match('~^([A-Za-z]:)?[/\\\\]~', $fontFile) === 1;
if (!$fontFileAbsolute) {
$fontFile = self::$trueTypeFontPath . $separator . $fontFile;
}
// Check if file actually exists
if ($checkPath && !file_exists($fontFile) && !$fontFileAbsolute) {
$alternateName = $name;
if ($index !== 'x' && $fontArray[$name][$index] !== $fontArray[$name]['x']) {
// Bold but no italic:
// Comic Sans
// Tahoma
// Neither bold nor italic:
// Impact
// Lucida Console
// Lucida Sans Unicode
// Microsoft Sans Serif
// Symbol
if ($index === 'xb') {
$alternateName .= ' Bold';
} elseif ($index === 'xi') {
$alternateName .= ' Italic';
} elseif ($fontArray[$name]['xb'] === $fontArray[$name]['xbi']) {
$alternateName .= ' Bold';
} else {
$alternateName .= ' Bold Italic';
}
}
$fontFile = self::$trueTypeFontPath . $separator . $alternateName . '.ttf';
if (!file_exists($fontFile)) {
throw new PhpSpreadsheetException('TrueType Font file not found');
}
}
return $fontFile;
}
public const CHARSET_FROM_FONT_NAME = [
'EucrosiaUPC' => self::CHARSET_ANSI_THAI,
'Wingdings' => self::CHARSET_SYMBOL,
'Wingdings 2' => self::CHARSET_SYMBOL,
'Wingdings 3' => self::CHARSET_SYMBOL,
];
/**
* Returns the associated charset for the font name.
*
* @param string $fontName Font name
*
* @return int Character set code
*/
public static function getCharsetFromFontName($fontName)
{
return self::CHARSET_FROM_FONT_NAME[$fontName] ?? self::CHARSET_ANSI_LATIN;
}
/**
* Get the effective column width for columns without a column dimension or column with width -1
* For example, for Calibri 11 this is 9.140625 (64 px).
*
* @param FontStyle $font The workbooks default font
* @param bool $returnAsPixels true = return column width in pixels, false = return in OOXML units
*
* @return mixed Column width
*/
public static function getDefaultColumnWidthByFont(FontStyle $font, $returnAsPixels = false)
{
if (isset(self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()])) {
// Exact width can be determined
$columnWidth = $returnAsPixels ?
self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()]['px']
: self::DEFAULT_COLUMN_WIDTHS[$font->getName()][$font->getSize()]['width'];
} else {
// We don't have data for this particular font and size, use approximation by
// extrapolating from Calibri 11
$columnWidth = $returnAsPixels ?
self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['px']
: self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['width'];
$columnWidth = $columnWidth * $font->getSize() / 11;
// Round pixels to closest integer
if ($returnAsPixels) {
$columnWidth = (int) round($columnWidth);
}
}
return $columnWidth;
}
/**
* Get the effective row height for rows without a row dimension or rows with height -1
* For example, for Calibri 11 this is 15 points.
*
* @param FontStyle $font The workbooks default font
*
* @return float Row height in points
*/
public static function getDefaultRowHeightByFont(FontStyle $font)
{
$name = $font->getName();
$size = $font->getSize();
if (isset(self::DEFAULT_COLUMN_WIDTHS[$name][$size])) {
$rowHeight = self::DEFAULT_COLUMN_WIDTHS[$name][$size]['height'];
} elseif ($name === 'Arial' || $name === 'Verdana') {
$rowHeight = self::DEFAULT_COLUMN_WIDTHS[$name][10]['height'] * $size / 10.0;
} else {
$rowHeight = self::DEFAULT_COLUMN_WIDTHS['Calibri'][11]['height'] * $size / 11.0;
}
return $rowHeight;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php 0000644 00000042743 15242753725 0015655 0 ustar 00 |
// | Based on OLE::Storage_Lite by Kawai, Takanori |
// +----------------------------------------------------------------------+
//
use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Shared\OLE\ChainedBlockStream;
use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\Root;
/*
* Array for storing OLE instances that are accessed from
* OLE_ChainedBlockStream::stream_open().
*
* @var array
*/
$GLOBALS['_OLE_INSTANCES'] = [];
/**
* OLE package base class.
*
* @author Xavier Noguer
* @author Christian Schmidt
*/
class OLE
{
const OLE_PPS_TYPE_ROOT = 5;
const OLE_PPS_TYPE_DIR = 1;
const OLE_PPS_TYPE_FILE = 2;
const OLE_DATA_SIZE_SMALL = 0x1000;
const OLE_LONG_INT_SIZE = 4;
const OLE_PPS_SIZE = 0x80;
/**
* The file handle for reading an OLE container.
*
* @var resource
*/
public $_file_handle;
/**
* Array of PPS's found on the OLE container.
*
* @var array
*/
public $_list = [];
/**
* Root directory of OLE container.
*
* @var Root
*/
public $root;
/**
* Big Block Allocation Table.
*
* @var array (blockId => nextBlockId)
*/
public $bbat;
/**
* Short Block Allocation Table.
*
* @var array (blockId => nextBlockId)
*/
public $sbat;
/**
* Size of big blocks. This is usually 512.
*
* @var int number of octets per block
*/
public $bigBlockSize;
/**
* Size of small blocks. This is usually 64.
*
* @var int number of octets per block
*/
public $smallBlockSize;
/**
* Threshold for big blocks.
*
* @var int
*/
public $bigBlockThreshold;
/**
* Reads an OLE container from the contents of the file given.
*
* @acces public
*
* @param string $filename
*
* @return bool true on success, PEAR_Error on failure
*/
public function read($filename)
{
$fh = @fopen($filename, 'rb');
if ($fh === false) {
throw new ReaderException("Can't open file $filename");
}
$this->_file_handle = $fh;
$signature = fread($fh, 8);
if ("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" != $signature) {
throw new ReaderException("File doesn't seem to be an OLE container.");
}
fseek($fh, 28);
if (fread($fh, 2) != "\xFE\xFF") {
// This shouldn't be a problem in practice
throw new ReaderException('Only Little-Endian encoding is supported.');
}
// Size of blocks and short blocks in bytes
$this->bigBlockSize = 2 ** self::readInt2($fh);
$this->smallBlockSize = 2 ** self::readInt2($fh);
// Skip UID, revision number and version number
fseek($fh, 44);
// Number of blocks in Big Block Allocation Table
$bbatBlockCount = self::readInt4($fh);
// Root chain 1st block
$directoryFirstBlockId = self::readInt4($fh);
// Skip unused bytes
fseek($fh, 56);
// Streams shorter than this are stored using small blocks
$this->bigBlockThreshold = self::readInt4($fh);
// Block id of first sector in Short Block Allocation Table
$sbatFirstBlockId = self::readInt4($fh);
// Number of blocks in Short Block Allocation Table
$sbbatBlockCount = self::readInt4($fh);
// Block id of first sector in Master Block Allocation Table
$mbatFirstBlockId = self::readInt4($fh);
// Number of blocks in Master Block Allocation Table
$mbbatBlockCount = self::readInt4($fh);
$this->bbat = [];
// Remaining 4 * 109 bytes of current block is beginning of Master
// Block Allocation Table
$mbatBlocks = [];
for ($i = 0; $i < 109; ++$i) {
$mbatBlocks[] = self::readInt4($fh);
}
// Read rest of Master Block Allocation Table (if any is left)
$pos = $this->getBlockOffset($mbatFirstBlockId);
for ($i = 0; $i < $mbbatBlockCount; ++$i) {
fseek($fh, $pos);
for ($j = 0; $j < $this->bigBlockSize / 4 - 1; ++$j) {
$mbatBlocks[] = self::readInt4($fh);
}
// Last block id in each block points to next block
$pos = $this->getBlockOffset(self::readInt4($fh));
}
// Read Big Block Allocation Table according to chain specified by $mbatBlocks
for ($i = 0; $i < $bbatBlockCount; ++$i) {
$pos = $this->getBlockOffset($mbatBlocks[$i]);
fseek($fh, $pos);
for ($j = 0; $j < $this->bigBlockSize / 4; ++$j) {
$this->bbat[] = self::readInt4($fh);
}
}
// Read short block allocation table (SBAT)
$this->sbat = [];
$shortBlockCount = $sbbatBlockCount * $this->bigBlockSize / 4;
$sbatFh = $this->getStream($sbatFirstBlockId);
for ($blockId = 0; $blockId < $shortBlockCount; ++$blockId) {
$this->sbat[$blockId] = self::readInt4($sbatFh);
}
fclose($sbatFh);
$this->readPpsWks($directoryFirstBlockId);
return true;
}
/**
* @param int $blockId byte offset from beginning of file
*
* @return int
*/
public function getBlockOffset($blockId)
{
return 512 + $blockId * $this->bigBlockSize;
}
/**
* Returns a stream for use with fread() etc. External callers should
* use \PhpOffice\PhpSpreadsheet\Shared\OLE\PPS\File::getStream().
*
* @param int|OLE\PPS $blockIdOrPps block id or PPS
*
* @return resource read-only stream
*/
public function getStream($blockIdOrPps)
{
static $isRegistered = false;
if (!$isRegistered) {
stream_wrapper_register('ole-chainedblockstream', ChainedBlockStream::class);
$isRegistered = true;
}
// Store current instance in global array, so that it can be accessed
// in OLE_ChainedBlockStream::stream_open().
// Object is removed from self::$instances in OLE_Stream::close().
$GLOBALS['_OLE_INSTANCES'][] = $this;
$keys = array_keys($GLOBALS['_OLE_INSTANCES']);
$instanceId = end($keys);
$path = 'ole-chainedblockstream://oleInstanceId=' . $instanceId;
if ($blockIdOrPps instanceof OLE\PPS) {
$path .= '&blockId=' . $blockIdOrPps->startBlock;
$path .= '&size=' . $blockIdOrPps->Size;
} else {
$path .= '&blockId=' . $blockIdOrPps;
}
$resource = fopen($path, 'rb');
if ($resource === false) {
throw new Exception("Unable to open stream $path");
}
return $resource;
}
/**
* Reads a signed char.
*
* @param resource $fileHandle file handle
*
* @return int
*/
private static function readInt1($fileHandle)
{
[, $tmp] = unpack('c', fread($fileHandle, 1) ?: '') ?: [0, 0];
return $tmp;
}
/**
* Reads an unsigned short (2 octets).
*
* @param resource $fileHandle file handle
*
* @return int
*/
private static function readInt2($fileHandle)
{
[, $tmp] = unpack('v', fread($fileHandle, 2) ?: '') ?: [0, 0];
return $tmp;
}
private const SIGNED_4OCTET_LIMIT = 2147483648;
private const SIGNED_4OCTET_SUBTRACT = 2 * self::SIGNED_4OCTET_LIMIT;
/**
* Reads long (4 octets), interpreted as if signed on 32-bit system.
*
* @param resource $fileHandle file handle
*
* @return int
*/
private static function readInt4($fileHandle)
{
[, $tmp] = unpack('V', fread($fileHandle, 4) ?: '') ?: [0, 0];
if ($tmp >= self::SIGNED_4OCTET_LIMIT) {
$tmp -= self::SIGNED_4OCTET_SUBTRACT;
}
return $tmp;
}
/**
* Gets information about all PPS's on the OLE container from the PPS WK's
* creates an OLE_PPS object for each one.
*
* @param int $blockId the block id of the first block
*
* @return bool true on success, PEAR_Error on failure
*/
public function readPpsWks($blockId)
{
$fh = $this->getStream($blockId);
for ($pos = 0; true; $pos += 128) {
fseek($fh, $pos, SEEK_SET);
$nameUtf16 = (string) fread($fh, 64);
$nameLength = self::readInt2($fh);
$nameUtf16 = substr($nameUtf16, 0, $nameLength - 2);
// Simple conversion from UTF-16LE to ISO-8859-1
$name = str_replace("\x00", '', $nameUtf16);
$type = self::readInt1($fh);
switch ($type) {
case self::OLE_PPS_TYPE_ROOT:
$pps = new OLE\PPS\Root(null, null, []);
$this->root = $pps;
break;
case self::OLE_PPS_TYPE_DIR:
$pps = new OLE\PPS(null, null, null, null, null, null, null, null, null, []);
break;
case self::OLE_PPS_TYPE_FILE:
$pps = new OLE\PPS\File($name);
break;
default:
throw new Exception('Unsupported PPS type');
}
fseek($fh, 1, SEEK_CUR);
$pps->Type = $type;
$pps->Name = $name;
$pps->PrevPps = self::readInt4($fh);
$pps->NextPps = self::readInt4($fh);
$pps->DirPps = self::readInt4($fh);
fseek($fh, 20, SEEK_CUR);
$pps->Time1st = self::OLE2LocalDate((string) fread($fh, 8));
$pps->Time2nd = self::OLE2LocalDate((string) fread($fh, 8));
$pps->startBlock = self::readInt4($fh);
$pps->Size = self::readInt4($fh);
$pps->No = count($this->_list);
$this->_list[] = $pps;
// check if the PPS tree (starting from root) is complete
if (isset($this->root) && $this->ppsTreeComplete($this->root->No)) { //* @phpstan-ignore-line
break;
}
}
fclose($fh);
// Initialize $pps->children on directories
foreach ($this->_list as $pps) {
if ($pps->Type == self::OLE_PPS_TYPE_DIR || $pps->Type == self::OLE_PPS_TYPE_ROOT) {
$nos = [$pps->DirPps];
$pps->children = [];
while (!empty($nos)) {
$no = array_pop($nos);
if ($no != -1) {
$childPps = $this->_list[$no];
$nos[] = $childPps->PrevPps;
$nos[] = $childPps->NextPps;
$pps->children[] = $childPps;
}
}
}
}
return true;
}
/**
* It checks whether the PPS tree is complete (all PPS's read)
* starting with the given PPS (not necessarily root).
*
* @param int $index The index of the PPS from which we are checking
*
* @return bool Whether the PPS tree for the given PPS is complete
*/
private function ppsTreeComplete($index)
{
return isset($this->_list[$index]) &&
($pps = $this->_list[$index]) &&
($pps->PrevPps == -1 ||
$this->ppsTreeComplete($pps->PrevPps)) &&
($pps->NextPps == -1 ||
$this->ppsTreeComplete($pps->NextPps)) &&
($pps->DirPps == -1 ||
$this->ppsTreeComplete($pps->DirPps));
}
/**
* Checks whether a PPS is a File PPS or not.
* If there is no PPS for the index given, it will return false.
*
* @param int $index The index for the PPS
*
* @return bool true if it's a File PPS, false otherwise
*/
public function isFile($index)
{
if (isset($this->_list[$index])) {
return $this->_list[$index]->Type == self::OLE_PPS_TYPE_FILE;
}
return false;
}
/**
* Checks whether a PPS is a Root PPS or not.
* If there is no PPS for the index given, it will return false.
*
* @param int $index the index for the PPS
*
* @return bool true if it's a Root PPS, false otherwise
*/
public function isRoot($index)
{
if (isset($this->_list[$index])) {
return $this->_list[$index]->Type == self::OLE_PPS_TYPE_ROOT;
}
return false;
}
/**
* Gives the total number of PPS's found in the OLE container.
*
* @return int The total number of PPS's found in the OLE container
*/
public function ppsTotal()
{
return count($this->_list);
}
/**
* Gets data from a PPS
* If there is no PPS for the index given, it will return an empty string.
*
* @param int $index The index for the PPS
* @param int $position The position from which to start reading
* (relative to the PPS)
* @param int $length The amount of bytes to read (at most)
*
* @return string The binary string containing the data requested
*
* @see OLE_PPS_File::getStream()
*/
public function getData($index, $position, $length)
{
// if position is not valid return empty string
if (!isset($this->_list[$index]) || ($position >= $this->_list[$index]->Size) || ($position < 0)) {
return '';
}
$fh = $this->getStream($this->_list[$index]);
$data = (string) stream_get_contents($fh, $length, $position);
fclose($fh);
return $data;
}
/**
* Gets the data length from a PPS
* If there is no PPS for the index given, it will return 0.
*
* @param int $index The index for the PPS
*
* @return int The amount of bytes in data the PPS has
*/
public function getDataLength($index)
{
if (isset($this->_list[$index])) {
return $this->_list[$index]->Size;
}
return 0;
}
/**
* Utility function to transform ASCII text to Unicode.
*
* @param string $ascii The ASCII string to transform
*
* @return string The string in Unicode
*/
public static function ascToUcs($ascii)
{
$rawname = '';
$iMax = strlen($ascii);
for ($i = 0; $i < $iMax; ++$i) {
$rawname .= $ascii[$i]
. "\x00";
}
return $rawname;
}
/**
* Utility function
* Returns a string for the OLE container with the date given.
*
* @param float|int $date A timestamp
*
* @return string The string for the OLE container
*/
public static function localDateToOLE($date)
{
if (!$date) {
return "\x00\x00\x00\x00\x00\x00\x00\x00";
}
$dateTime = Date::dateTimeFromTimestamp("$date");
// days from 1-1-1601 until the beggining of UNIX era
$days = 134774;
// calculate seconds
$big_date = $days * 24 * 3600 + (float) $dateTime->format('U');
// multiply just to make MS happy
$big_date *= 10000000;
// Make HEX string
$res = '';
$factor = 2 ** 56;
while ($factor >= 1) {
$hex = (int) floor($big_date / $factor);
$res = pack('c', $hex) . $res;
$big_date = fmod($big_date, $factor);
$factor /= 256;
}
return $res;
}
/**
* Returns a timestamp from an OLE container's date.
*
* @param string $oleTimestamp A binary string with the encoded date
*
* @return float|int The Unix timestamp corresponding to the string
*/
public static function OLE2LocalDate($oleTimestamp)
{
if (strlen($oleTimestamp) != 8) {
throw new ReaderException('Expecting 8 byte string');
}
// convert to units of 100 ns since 1601:
$unpackedTimestamp = unpack('v4', $oleTimestamp) ?: [];
$timestampHigh = (float) $unpackedTimestamp[4] * 65536 + (float) $unpackedTimestamp[3];
$timestampLow = (float) $unpackedTimestamp[2] * 65536 + (float) $unpackedTimestamp[1];
// translate to seconds since 1601:
$timestampHigh /= 10000000;
$timestampLow /= 10000000;
// days from 1601 to 1970:
$days = 134774;
// translate to seconds since 1970:
$unixTimestamp = floor(65536.0 * 65536.0 * $timestampHigh + $timestampLow - $days * 24 * 3600 + 0.5);
return IntOrFloat::evaluate($unixTimestamp);
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/OLERead.php 0000644 00000024323 15242753725 0016443 0 ustar 00 data = (string) file_get_contents($filename, false, null, 0, 8);
// Check OLE identifier
$identifierOle = pack('CCCCCCCC', 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1);
if ($this->data != $identifierOle) {
throw new ReaderException('The filename ' . $filename . ' is not recognised as an OLE file');
}
// Get the file data
$this->data = (string) file_get_contents($filename);
// Total number of sectors used for the SAT
$this->numBigBlockDepotBlocks = self::getInt4d($this->data, self::NUM_BIG_BLOCK_DEPOT_BLOCKS_POS);
// SecID of the first sector of the directory stream
$this->rootStartBlock = self::getInt4d($this->data, self::ROOT_START_BLOCK_POS);
// SecID of the first sector of the SSAT (or -2 if not extant)
$this->sbdStartBlock = self::getInt4d($this->data, self::SMALL_BLOCK_DEPOT_BLOCK_POS);
// SecID of the first sector of the MSAT (or -2 if no additional sectors are used)
$this->extensionBlock = self::getInt4d($this->data, self::EXTENSION_BLOCK_POS);
// Total number of sectors used by MSAT
$this->numExtensionBlocks = self::getInt4d($this->data, self::NUM_EXTENSION_BLOCK_POS);
$bigBlockDepotBlocks = [];
$pos = self::BIG_BLOCK_DEPOT_BLOCKS_POS;
$bbdBlocks = $this->numBigBlockDepotBlocks;
if ($this->numExtensionBlocks !== 0) {
$bbdBlocks = (self::BIG_BLOCK_SIZE - self::BIG_BLOCK_DEPOT_BLOCKS_POS) / 4;
}
for ($i = 0; $i < $bbdBlocks; ++$i) {
$bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
$pos += 4;
}
for ($j = 0; $j < $this->numExtensionBlocks; ++$j) {
$pos = ($this->extensionBlock + 1) * self::BIG_BLOCK_SIZE;
$blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, self::BIG_BLOCK_SIZE / 4 - 1);
for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; ++$i) {
$bigBlockDepotBlocks[$i] = self::getInt4d($this->data, $pos);
$pos += 4;
}
$bbdBlocks += $blocksToRead;
if ($bbdBlocks < $this->numBigBlockDepotBlocks) {
$this->extensionBlock = self::getInt4d($this->data, $pos);
}
}
$pos = 0;
$this->bigBlockChain = '';
$bbs = self::BIG_BLOCK_SIZE / 4;
for ($i = 0; $i < $this->numBigBlockDepotBlocks; ++$i) {
$pos = ($bigBlockDepotBlocks[$i] + 1) * self::BIG_BLOCK_SIZE;
$this->bigBlockChain .= substr($this->data, $pos, 4 * $bbs);
$pos += 4 * $bbs;
}
$sbdBlock = $this->sbdStartBlock;
$this->smallBlockChain = '';
while ($sbdBlock != -2) {
$pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
$this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
$pos += 4 * $bbs;
$sbdBlock = self::getInt4d($this->bigBlockChain, $sbdBlock * 4);
}
// read the directory stream
$block = $this->rootStartBlock;
$this->entry = $this->readData($block);
$this->readPropertySets();
}
/**
* Extract binary stream data.
*
* @param ?int $stream
*
* @return null|string
*/
public function getStream($stream)
{
if ($stream === null) {
return null;
}
$streamData = '';
if ($this->props[$stream]['size'] < self::SMALL_BLOCK_THRESHOLD) {
$rootdata = $this->readData($this->props[$this->rootentry]['startBlock']);
$block = $this->props[$stream]['startBlock'];
while ($block != -2) {
$pos = $block * self::SMALL_BLOCK_SIZE;
$streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
$block = self::getInt4d($this->smallBlockChain, $block * 4);
}
return $streamData;
}
$numBlocks = $this->props[$stream]['size'] / self::BIG_BLOCK_SIZE;
if ($this->props[$stream]['size'] % self::BIG_BLOCK_SIZE != 0) {
++$numBlocks;
}
if ($numBlocks == 0) {
return '';
}
$block = $this->props[$stream]['startBlock'];
while ($block != -2) {
$pos = ($block + 1) * self::BIG_BLOCK_SIZE;
$streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
$block = self::getInt4d($this->bigBlockChain, $block * 4);
}
return $streamData;
}
/**
* Read a standard stream (by joining sectors using information from SAT).
*
* @param int $block Sector ID where the stream starts
*
* @return string Data for standard stream
*/
private function readData($block)
{
$data = '';
while ($block != -2) {
$pos = ($block + 1) * self::BIG_BLOCK_SIZE;
$data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
$block = self::getInt4d($this->bigBlockChain, $block * 4);
}
return $data;
}
/**
* Read entries in the directory stream.
*/
private function readPropertySets(): void
{
$offset = 0;
// loop through entires, each entry is 128 bytes
$entryLen = strlen($this->entry);
while ($offset < $entryLen) {
// entry data (128 bytes)
$d = substr($this->entry, $offset, self::PROPERTY_STORAGE_BLOCK_SIZE);
// size in bytes of name
$nameSize = ord($d[self::SIZE_OF_NAME_POS]) | (ord($d[self::SIZE_OF_NAME_POS + 1]) << 8);
// type of entry
$type = ord($d[self::TYPE_POS]);
// sectorID of first sector or short sector, if this entry refers to a stream (the case with workbook)
// sectorID of first sector of the short-stream container stream, if this entry is root entry
$startBlock = self::getInt4d($d, self::START_BLOCK_POS);
$size = self::getInt4d($d, self::SIZE_POS);
$name = str_replace("\x00", '', substr($d, 0, $nameSize));
$this->props[] = [
'name' => $name,
'type' => $type,
'startBlock' => $startBlock,
'size' => $size,
];
// tmp helper to simplify checks
$upName = strtoupper($name);
// Workbook directory entry (BIFF5 uses Book, BIFF8 uses Workbook)
if (($upName === 'WORKBOOK') || ($upName === 'BOOK')) {
$this->wrkbook = count($this->props) - 1;
} elseif ($upName === 'ROOT ENTRY' || $upName === 'R') {
// Root entry
$this->rootentry = count($this->props) - 1;
}
// Summary information
if ($name == chr(5) . 'SummaryInformation') {
$this->summaryInformation = count($this->props) - 1;
}
// Additional Document Summary information
if ($name == chr(5) . 'DocumentSummaryInformation') {
$this->documentSummaryInformation = count($this->props) - 1;
}
$offset += self::PROPERTY_STORAGE_BLOCK_SIZE;
}
}
/**
* Read 4 bytes of data at specified position.
*
* @param string $data
* @param int $pos
*
* @return int
*/
private static function getInt4d($data, $pos)
{
if ($pos < 0) {
// Invalid position
throw new ReaderException('Parameter pos=' . $pos . ' is invalid.');
}
$len = strlen($data);
if ($len < $pos + 4) {
$data .= str_repeat("\0", $pos + 4 - $len);
}
// FIX: represent numbers correctly on 64-bit system
// http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
// Changed by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
$_or_24 = ord($data[$pos + 3]);
if ($_or_24 >= 128) {
// negative number
$_ord_24 = -abs((256 - $_or_24) << 24);
} else {
$_ord_24 = ($_or_24 & 127) << 24;
}
return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24;
}
}
phpspreadsheet/src/PhpSpreadsheet/Shared/IntOrFloat.php 0000644 00000000673 15242753725 0017253 0 ustar 00 lastModifiedBy = $this->creator;
$this->created = self::intOrFloatTimestamp(null);
$this->modified = $this->created;
}
/**
* Get Creator.
*/
public function getCreator(): string
{
return $this->creator;
}
/**
* Set Creator.
*
* @return $this
*/
public function setCreator(string $creator): self
{
$this->creator = $creator;
return $this;
}
/**
* Get Last Modified By.
*/
public function getLastModifiedBy(): string
{
return $this->lastModifiedBy;
}
/**
* Set Last Modified By.
*
* @return $this
*/
public function setLastModifiedBy(string $modifiedBy): self
{
$this->lastModifiedBy = $modifiedBy;
return $this;
}
/**
* @param null|float|int|string $timestamp
*
* @return float|int
*/
private static function intOrFloatTimestamp($timestamp)
{
if ($timestamp === null) {
$timestamp = (float) (new DateTime())->format('U');
} elseif (is_string($timestamp)) {
if (is_numeric($timestamp)) {
$timestamp = (float) $timestamp;
} else {
$timestamp = (string) preg_replace('/[.][0-9]*$/', '', $timestamp);
$timestamp = (string) preg_replace('/^(\\d{4})- (\\d)/', '$1-0$2', $timestamp);
$timestamp = (string) preg_replace('/^(\\d{4}-\\d{2})- (\\d)/', '$1-0$2', $timestamp);
$timestamp = (float) (new DateTime($timestamp))->format('U');
}
}
return IntOrFloat::evaluate($timestamp);
}
/**
* Get Created.
*
* @return float|int
*/
public function getCreated()
{
return $this->created;
}
/**
* Set Created.
*
* @param null|float|int|string $timestamp
*
* @return $this
*/
public function setCreated($timestamp): self
{
$this->created = self::intOrFloatTimestamp($timestamp);
return $this;
}
/**
* Get Modified.
*
* @return float|int
*/
public function getModified()
{
return $this->modified;
}
/**
* Set Modified.
*
* @param null|float|int|string $timestamp
*
* @return $this
*/
public function setModified($timestamp): self
{
$this->modified = self::intOrFloatTimestamp($timestamp);
return $this;
}
/**
* Get Title.
*/
public function getTitle(): string
{
return $this->title;
}
/**
* Set Title.
*
* @return $this
*/
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
/**
* Get Description.
*/
public function getDescription(): string
{
return $this->description;
}
/**
* Set Description.
*
* @return $this
*/
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
/**
* Get Subject.
*/
public function getSubject(): string
{
return $this->subject;
}
/**
* Set Subject.
*
* @return $this
*/
public function setSubject(string $subject): self
{
$this->subject = $subject;
return $this;
}
/**
* Get Keywords.
*/
public function getKeywords(): string
{
return $this->keywords;
}
/**
* Set Keywords.
*
* @return $this
*/
public function setKeywords(string $keywords): self
{
$this->keywords = $keywords;
return $this;
}
/**
* Get Category.
*/
public function getCategory(): string
{
return $this->category;
}
/**
* Set Category.
*
* @return $this
*/
public function setCategory(string $category): self
{
$this->category = $category;
return $this;
}
/**
* Get Company.
*/
public function getCompany(): string
{
return $this->company;
}
/**
* Set Company.
*
* @return $this
*/
public function setCompany(string $company): self
{
$this->company = $company;
return $this;
}
/**
* Get Manager.
*/
public function getManager(): string
{
return $this->manager;
}
/**
* Set Manager.
*
* @return $this
*/
public function setManager(string $manager): self
{
$this->manager = $manager;
return $this;
}
/**
* Get a List of Custom Property Names.
*
* @return string[]
*/
public function getCustomProperties(): array
{
return array_keys($this->customProperties);
}
/**
* Check if a Custom Property is defined.
*/
public function isCustomPropertySet(string $propertyName): bool
{
return array_key_exists($propertyName, $this->customProperties);
}
/**
* Get a Custom Property Value.
*
* @return mixed
*/
public function getCustomPropertyValue(string $propertyName)
{
if (isset($this->customProperties[$propertyName])) {
return $this->customProperties[$propertyName]['value'];
}
return null;
}
/**
* Get a Custom Property Type.
*
* @return null|string
*/
public function getCustomPropertyType(string $propertyName)
{
return $this->customProperties[$propertyName]['type'] ?? null;
}
/**
* @param mixed $propertyValue
*/
private function identifyPropertyType($propertyValue): string
{
if (is_float($propertyValue)) {
return self::PROPERTY_TYPE_FLOAT;
}
if (is_int($propertyValue)) {
return self::PROPERTY_TYPE_INTEGER;
}
if (is_bool($propertyValue)) {
return self::PROPERTY_TYPE_BOOLEAN;
}
return self::PROPERTY_TYPE_STRING;
}
/**
* Set a Custom Property.
*
* @param mixed $propertyValue
* @param string $propertyType
* 'i' : Integer
* 'f' : Floating Point
* 's' : String
* 'd' : Date/Time
* 'b' : Boolean
*
* @return $this
*/
public function setCustomProperty(string $propertyName, $propertyValue = '', $propertyType = null): self
{
if (($propertyType === null) || (!in_array($propertyType, self::VALID_PROPERTY_TYPE_LIST))) {
$propertyType = $this->identifyPropertyType($propertyValue);
}
if (!is_object($propertyValue)) {
$this->customProperties[$propertyName] = [
'value' => self::convertProperty($propertyValue, $propertyType),
'type' => $propertyType,
];
}
return $this;
}
private const PROPERTY_TYPE_ARRAY = [
'i' => self::PROPERTY_TYPE_INTEGER, // Integer
'i1' => self::PROPERTY_TYPE_INTEGER, // 1-Byte Signed Integer
'i2' => self::PROPERTY_TYPE_INTEGER, // 2-Byte Signed Integer
'i4' => self::PROPERTY_TYPE_INTEGER, // 4-Byte Signed Integer
'i8' => self::PROPERTY_TYPE_INTEGER, // 8-Byte Signed Integer
'int' => self::PROPERTY_TYPE_INTEGER, // Integer
'ui1' => self::PROPERTY_TYPE_INTEGER, // 1-Byte Unsigned Integer
'ui2' => self::PROPERTY_TYPE_INTEGER, // 2-Byte Unsigned Integer
'ui4' => self::PROPERTY_TYPE_INTEGER, // 4-Byte Unsigned Integer
'ui8' => self::PROPERTY_TYPE_INTEGER, // 8-Byte Unsigned Integer
'uint' => self::PROPERTY_TYPE_INTEGER, // Unsigned Integer
'f' => self::PROPERTY_TYPE_FLOAT, // Real Number
'r4' => self::PROPERTY_TYPE_FLOAT, // 4-Byte Real Number
'r8' => self::PROPERTY_TYPE_FLOAT, // 8-Byte Real Number
'decimal' => self::PROPERTY_TYPE_FLOAT, // Decimal
's' => self::PROPERTY_TYPE_STRING, // String
'empty' => self::PROPERTY_TYPE_STRING, // Empty
'null' => self::PROPERTY_TYPE_STRING, // Null
'lpstr' => self::PROPERTY_TYPE_STRING, // LPSTR
'lpwstr' => self::PROPERTY_TYPE_STRING, // LPWSTR
'bstr' => self::PROPERTY_TYPE_STRING, // Basic String
'd' => self::PROPERTY_TYPE_DATE, // Date and Time
'date' => self::PROPERTY_TYPE_DATE, // Date and Time
'filetime' => self::PROPERTY_TYPE_DATE, // File Time
'b' => self::PROPERTY_TYPE_BOOLEAN, // Boolean
'bool' => self::PROPERTY_TYPE_BOOLEAN, // Boolean
];
private const SPECIAL_TYPES = [
'empty' => '',
'null' => null,
];
/**
* Convert property to form desired by Excel.
*
* @param mixed $propertyValue
*
* @return mixed
*/
public static function convertProperty($propertyValue, string $propertyType)
{
return self::SPECIAL_TYPES[$propertyType] ?? self::convertProperty2($propertyValue, $propertyType);
}
/**
* Convert property to form desired by Excel.
*
* @param mixed $propertyValue
*
* @return mixed
*/
private static function convertProperty2($propertyValue, string $type)
{
$propertyType = self::convertPropertyType($type);
switch ($propertyType) {
case self::PROPERTY_TYPE_INTEGER:
$intValue = (int) $propertyValue;
return ($type[0] === 'u') ? abs($intValue) : $intValue;
case self::PROPERTY_TYPE_FLOAT:
return (float) $propertyValue;
case self::PROPERTY_TYPE_DATE:
return self::intOrFloatTimestamp($propertyValue);
case self::PROPERTY_TYPE_BOOLEAN:
return is_bool($propertyValue) ? $propertyValue : ($propertyValue === 'true');
default: // includes string
return $propertyValue;
}
}
public static function convertPropertyType(string $propertyType): string
{
return self::PROPERTY_TYPE_ARRAY[$propertyType] ?? self::PROPERTY_TYPE_UNKNOWN;
}
public function getHyperlinkBase(): string
{
return $this->hyperlinkBase;
}
public function setHyperlinkBase(string $hyperlinkBase): self
{
$this->hyperlinkBase = $hyperlinkBase;
return $this;
}
}
phpspreadsheet/src/PhpSpreadsheet/Document/Security.php 0000644 00000006004 15242753725 0017403 0 ustar 00 lockRevision ||
$this->lockStructure ||
$this->lockWindows;
}
public function getLockRevision(): bool
{
return $this->lockRevision;
}
public function setLockRevision(?bool $locked): self
{
if ($locked !== null) {
$this->lockRevision = $locked;
}
return $this;
}
public function getLockStructure(): bool
{
return $this->lockStructure;
}
public function setLockStructure(?bool $locked): self
{
if ($locked !== null) {
$this->lockStructure = $locked;
}
return $this;
}
public function getLockWindows(): bool
{
return $this->lockWindows;
}
public function setLockWindows(?bool $locked): self
{
if ($locked !== null) {
$this->lockWindows = $locked;
}
return $this;
}
public function getRevisionsPassword(): string
{
return $this->revisionsPassword;
}
/**
* Set RevisionsPassword.
*
* @param string $password
* @param bool $alreadyHashed If the password has already been hashed, set this to true
*
* @return $this
*/
public function setRevisionsPassword(?string $password, bool $alreadyHashed = false)
{
if ($password !== null) {
if (!$alreadyHashed) {
$password = PasswordHasher::hashPassword($password);
}
$this->revisionsPassword = $password;
}
return $this;
}
public function getWorkbookPassword(): string
{
return $this->workbookPassword;
}
/**
* Set WorkbookPassword.
*
* @param string $password
* @param bool $alreadyHashed If the password has already been hashed, set this to true
*
* @return $this
*/
public function setWorkbookPassword(?string $password, bool $alreadyHashed = false)
{
if ($password !== null) {
if (!$alreadyHashed) {
$password = PasswordHasher::hashPassword($password);
}
$this->workbookPassword = $password;
}
return $this;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php 0000644 00000016343 15242753725 0022320 0 ustar 00 getMessage();
}
$startDate = (float) floor($startDate);
$endDays = (int) floor($endDays);
// If endDays is 0, we always return startDate
if ($endDays == 0) {
return $startDate;
}
if ($endDays < 0) {
return self::decrementing($startDate, $endDays, $holidayArray);
}
return self::incrementing($startDate, $endDays, $holidayArray);
}
/**
* Use incrementing logic to determine Workday.
*
* @return mixed
*/
private static function incrementing(float $startDate, int $endDays, array $holidayArray)
{
// Adjust the start date if it falls over a weekend
$startDoW = self::getWeekDay($startDate, 3);
if ($startDoW >= 5) {
$startDate += 7 - $startDoW;
--$endDays;
}
// Add endDays
$endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
$endDays = $endDays % 5;
while ($endDays > 0) {
++$endDate;
// Adjust the calculated end date if it falls over a weekend
$endDow = self::getWeekDay($endDate, 3);
if ($endDow >= 5) {
$endDate += 7 - $endDow;
}
--$endDays;
}
// Test any extra holiday parameters
if (!empty($holidayArray)) {
$endDate = self::incrementingArray($startDate, $endDate, $holidayArray);
}
return Helpers::returnIn3FormatsFloat($endDate);
}
private static function incrementingArray(float $startDate, float $endDate, array $holidayArray): float
{
$holidayCountedArray = $holidayDates = [];
foreach ($holidayArray as $holidayDate) {
if (self::getWeekDay($holidayDate, 3) < 5) {
$holidayDates[] = $holidayDate;
}
}
sort($holidayDates, SORT_NUMERIC);
foreach ($holidayDates as $holidayDate) {
if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
if (!in_array($holidayDate, $holidayCountedArray)) {
++$endDate;
$holidayCountedArray[] = $holidayDate;
}
}
// Adjust the calculated end date if it falls over a weekend
$endDoW = self::getWeekDay($endDate, 3);
if ($endDoW >= 5) {
$endDate += 7 - $endDoW;
}
}
return $endDate;
}
/**
* Use decrementing logic to determine Workday.
*
* @return mixed
*/
private static function decrementing(float $startDate, int $endDays, array $holidayArray)
{
// Adjust the start date if it falls over a weekend
$startDoW = self::getWeekDay($startDate, 3);
if ($startDoW >= 5) {
$startDate += -$startDoW + 4;
++$endDays;
}
// Add endDays
$endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
$endDays = $endDays % 5;
while ($endDays < 0) {
--$endDate;
// Adjust the calculated end date if it falls over a weekend
$endDow = self::getWeekDay($endDate, 3);
if ($endDow >= 5) {
$endDate += 4 - $endDow;
}
++$endDays;
}
// Test any extra holiday parameters
if (!empty($holidayArray)) {
$endDate = self::decrementingArray($startDate, $endDate, $holidayArray);
}
return Helpers::returnIn3FormatsFloat($endDate);
}
private static function decrementingArray(float $startDate, float $endDate, array $holidayArray): float
{
$holidayCountedArray = $holidayDates = [];
foreach ($holidayArray as $holidayDate) {
if (self::getWeekDay($holidayDate, 3) < 5) {
$holidayDates[] = $holidayDate;
}
}
rsort($holidayDates, SORT_NUMERIC);
foreach ($holidayDates as $holidayDate) {
if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
if (!in_array($holidayDate, $holidayCountedArray)) {
--$endDate;
$holidayCountedArray[] = $holidayDate;
}
}
// Adjust the calculated end date if it falls over a weekend
$endDoW = self::getWeekDay($endDate, 3);
/** int $endDoW */
if ($endDoW >= 5) {
$endDate += -$endDoW + 4;
}
}
return $endDate;
}
private static function getWeekDay(float $date, int $wd): int
{
$result = Functions::scalar(Week::day($date, $wd));
return is_int($result) ? $result : -1;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateValue.php 0000644 00000015262 15242753725 0022611 0 ustar 00 31)) {
if ($yearFound) {
return ExcelError::VALUE();
}
if ($t < 100) {
$t += 1900;
}
$yearFound = true;
}
}
if (count($t1) === 1) {
// We've been fed a time value without any date
return ((strpos((string) $t, ':') === false)) ? ExcelError::Value() : 0.0;
}
unset($t);
$dateValue = self::t1ToString($t1, $dti, $yearFound);
$PHPDateArray = self::setUpArray($dateValue, $dti);
return self::finalResults($PHPDateArray, $dti, $baseYear);
}
private static function t1ToString(array $t1, DateTimeImmutable $dti, bool $yearFound): string
{
if (count($t1) == 2) {
// We only have two parts of the date: either day/month or month/year
if ($yearFound) {
array_unshift($t1, 1);
} else {
if (is_numeric($t1[1]) && $t1[1] > 29) {
$t1[1] += 1900;
array_unshift($t1, 1);
} else {
$t1[] = $dti->format('Y');
}
}
}
$dateValue = implode(' ', $t1);
return $dateValue;
}
/**
* Parse date.
*/
private static function setUpArray(string $dateValue, DateTimeImmutable $dti): array
{
$PHPDateArray = Helpers::dateParse($dateValue);
if (!Helpers::dateParseSucceeded($PHPDateArray)) {
// If original count was 1, we've already returned.
// If it was 2, we added another.
// Therefore, neither of the first 2 stroks below can fail.
$testVal1 = strtok($dateValue, '- ');
$testVal2 = strtok('- ');
$testVal3 = strtok('- ') ?: $dti->format('Y');
Helpers::adjustYear((string) $testVal1, (string) $testVal2, $testVal3);
$PHPDateArray = Helpers::dateParse($testVal1 . '-' . $testVal2 . '-' . $testVal3);
if (!Helpers::dateParseSucceeded($PHPDateArray)) {
$PHPDateArray = Helpers::dateParse($testVal2 . '-' . $testVal1 . '-' . $testVal3);
}
}
return $PHPDateArray;
}
/**
* Final results.
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
private static function finalResults(array $PHPDateArray, DateTimeImmutable $dti, int $baseYear)
{
$retValue = ExcelError::Value();
if (Helpers::dateParseSucceeded($PHPDateArray)) {
// Execute function
Helpers::replaceIfEmpty($PHPDateArray['year'], $dti->format('Y'));
if ($PHPDateArray['year'] < $baseYear) {
return ExcelError::VALUE();
}
Helpers::replaceIfEmpty($PHPDateArray['month'], $dti->format('m'));
Helpers::replaceIfEmpty($PHPDateArray['day'], $dti->format('d'));
$PHPDateArray['hour'] = 0;
$PHPDateArray['minute'] = 0;
$PHPDateArray['second'] = 0;
$month = (int) $PHPDateArray['month'];
$day = (int) $PHPDateArray['day'];
$year = (int) $PHPDateArray['year'];
if (!checkdate($month, $day, $year)) {
return ($year === 1900 && $month === 2 && $day === 29) ? Helpers::returnIn3FormatsFloat(60.0) : ExcelError::VALUE();
}
$retValue = Helpers::returnIn3FormatsArray($PHPDateArray, true);
}
return $retValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Constants.php 0000644 00000002117 15242753725 0022706 0 ustar 00 self::DOW_SUNDAY,
self::DOW_MONDAY,
self::STARTWEEK_MONDAY_ALT => self::DOW_MONDAY,
self::DOW_TUESDAY,
self::DOW_WEDNESDAY,
self::DOW_THURSDAY,
self::DOW_FRIDAY,
self::DOW_SATURDAY,
self::DOW_SUNDAY,
self::STARTWEEK_MONDAY_ISO => self::STARTWEEK_MONDAY_ISO,
];
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php 0000644 00000012013 15242753725 0021624 0 ustar 00 getMessage();
}
self::adjustSecond($second, $minute);
self::adjustMinute($minute, $hour);
if ($hour > 23) {
$hour = $hour % 24;
} elseif ($hour < 0) {
return ExcelError::NAN();
}
// Execute function
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
$calendar = SharedDateHelper::getExcelCalendar();
$date = (int) ($calendar !== SharedDateHelper::CALENDAR_WINDOWS_1900);
return (float) SharedDateHelper::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
}
if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
return (int) SharedDateHelper::excelToTimestamp(SharedDateHelper::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
}
// RETURNDATE_PHP_DATETIME_OBJECT
// Hour has already been normalized (0-23) above
$phpDateObject = new DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
return $phpDateObject;
}
private static function adjustSecond(int &$second, int &$minute): void
{
if ($second < 0) {
$minute += floor($second / 60);
$second = 60 - abs($second % 60);
if ($second == 60) {
$second = 0;
}
} elseif ($second >= 60) {
$minute += floor($second / 60);
$second = $second % 60;
}
}
private static function adjustMinute(int &$minute, int &$hour): void
{
if ($minute < 0) {
$hour += floor($minute / 60);
$minute = 60 - abs($minute % 60);
if ($minute == 60) {
$minute = 0;
}
} elseif ($minute >= 60) {
$hour += floor($minute / 60);
$minute = $minute % 60;
}
}
/**
* @param mixed $value expect int
*/
private static function toIntWithNullBool($value): int
{
$value = $value ?? 0;
if (is_bool($value)) {
$value = (int) $value;
}
if (!is_numeric($value)) {
throw new Exception(ExcelError::VALUE());
}
return (int) $value;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php 0000644 00000012245 15242753725 0022066 0 ustar 00 getMessage();
}
if (!is_bool($method)) {
return ExcelError::VALUE();
}
// Execute function
$PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
$startDay = $PHPStartDateObject->format('j');
$startMonth = $PHPStartDateObject->format('n');
$startYear = $PHPStartDateObject->format('Y');
$PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
$endDay = $PHPEndDateObject->format('j');
$endMonth = $PHPEndDateObject->format('n');
$endYear = $PHPEndDateObject->format('Y');
return self::dateDiff360((int) $startDay, (int) $startMonth, (int) $startYear, (int) $endDay, (int) $endMonth, (int) $endYear, !$method);
}
/**
* Return the number of days between two dates based on a 360 day calendar.
*/
private static function dateDiff360(int $startDay, int $startMonth, int $startYear, int $endDay, int $endMonth, int $endYear, bool $methodUS): int
{
$startDay = self::getStartDay($startDay, $startMonth, $startYear, $methodUS);
$endDay = self::getEndDay($endDay, $endMonth, $endYear, $startDay, $methodUS);
return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
}
private static function getStartDay(int $startDay, int $startMonth, int $startYear, bool $methodUS): int
{
if ($startDay == 31) {
--$startDay;
} elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !Helpers::isLeapYear($startYear))))) {
$startDay = 30;
}
return $startDay;
}
private static function getEndDay(int $endDay, int &$endMonth, int &$endYear, int $startDay, bool $methodUS): int
{
if ($endDay == 31) {
if ($methodUS && $startDay != 30) {
$endDay = 1;
if ($endMonth == 12) {
++$endYear;
$endMonth = 1;
} else {
++$endMonth;
}
} else {
$endDay = 30;
}
}
return $endDay;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php 0000644 00000022115 15242753725 0022334 0 ustar 00 format('m');
$oYear = (int) $PHPDateObject->format('Y');
$adjustmentMonthsString = (string) $adjustmentMonths;
if ($adjustmentMonths > 0) {
$adjustmentMonthsString = '+' . $adjustmentMonths;
}
if ($adjustmentMonths != 0) {
$PHPDateObject->modify($adjustmentMonthsString . ' months');
}
$nMonth = (int) $PHPDateObject->format('m');
$nYear = (int) $PHPDateObject->format('Y');
$monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
if ($monthDiff != $adjustmentMonths) {
$adjustDays = (int) $PHPDateObject->format('d');
$adjustDaysString = '-' . $adjustDays . ' days';
$PHPDateObject->modify($adjustDaysString);
}
return $PHPDateObject;
}
/**
* Help reduce perceived complexity of some tests.
*
* @param mixed $value
* @param mixed $altValue
*/
public static function replaceIfEmpty(&$value, $altValue): void
{
$value = $value ?: $altValue;
}
/**
* Adjust year in ambiguous situations.
*/
public static function adjustYear(string $testVal1, string $testVal2, string &$testVal3): void
{
if (!is_numeric($testVal1) || $testVal1 < 31) {
if (!is_numeric($testVal2) || $testVal2 < 12) {
if (is_numeric($testVal3) && $testVal3 < 12) {
$testVal3 += 2000;
}
}
}
}
/**
* Return result in one of three formats.
*
* @return mixed
*/
public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false)
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
return new DateTime(
$dateArray['year']
. '-' . $dateArray['month']
. '-' . $dateArray['day']
. ' ' . $dateArray['hour']
. ':' . $dateArray['minute']
. ':' . $dateArray['second']
);
}
$excelDateValue =
SharedDateHelper::formattedPHPToExcel(
$dateArray['year'],
$dateArray['month'],
$dateArray['day'],
$dateArray['hour'],
$dateArray['minute'],
$dateArray['second']
);
if ($retType === Functions::RETURNDATE_EXCEL) {
return $noFrac ? floor($excelDateValue) : (float) $excelDateValue;
}
// RETURNDATE_UNIX_TIMESTAMP)
return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
}
/**
* Return result in one of three formats.
*
* @return mixed
*/
public static function returnIn3FormatsFloat(float $excelDateValue)
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
return $excelDateValue;
}
if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
}
// RETURNDATE_PHP_DATETIME_OBJECT
return SharedDateHelper::excelToDateTimeObject($excelDateValue);
}
/**
* Return result in one of three formats.
*
* @return mixed
*/
public static function returnIn3FormatsObject(DateTime $PHPDateObject)
{
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
return $PHPDateObject;
}
if ($retType === Functions::RETURNDATE_EXCEL) {
return (float) SharedDateHelper::PHPToExcel($PHPDateObject);
}
// RETURNDATE_UNIX_TIMESTAMP
$stamp = SharedDateHelper::PHPToExcel($PHPDateObject);
$stamp = is_bool($stamp) ? ((int) $stamp) : $stamp;
return (int) SharedDateHelper::excelToTimestamp($stamp);
}
private static function baseDate(): int
{
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
return 0;
}
if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904) {
return 0;
}
return 1;
}
/**
* Many functions accept null/false/true argument treated as 0/0/1.
*
* @param mixed $number
*/
public static function nullFalseTrueToNumber(&$number, bool $allowBool = true): void
{
$number = Functions::flattenSingleValue($number);
$nullVal = self::baseDate();
if ($number === null) {
$number = $nullVal;
} elseif ($allowBool && is_bool($number)) {
$number = $nullVal + (int) $number;
}
}
/**
* Many functions accept null argument treated as 0.
*
* @param mixed $number
*
* @return float|int
*/
public static function validateNumericNull($number)
{
$number = Functions::flattenSingleValue($number);
if ($number === null) {
return 0;
}
if (is_int($number)) {
return $number;
}
if (is_numeric($number)) {
return (float) $number;
}
throw new Exception(ExcelError::VALUE());
}
/**
* Many functions accept null/false/true argument treated as 0/0/1.
*
* @param mixed $number
*
* @return float
*/
public static function validateNotNegative($number)
{
if (!is_numeric($number)) {
throw new Exception(ExcelError::VALUE());
}
if ($number >= 0) {
return (float) $number;
}
throw new Exception(ExcelError::NAN());
}
public static function silly1900(DateTime $PHPDateObject, string $mod = '-1 day'): void
{
$isoDate = $PHPDateObject->format('c');
if ($isoDate < '1900-03-01') {
$PHPDateObject->modify($mod);
}
}
public static function dateParse(string $string): array
{
return self::forceArray(date_parse($string));
}
public static function dateParseSucceeded(array $dateArray): bool
{
return $dateArray['error_count'] === 0;
}
/**
* Despite documentation, date_parse probably never returns false.
* Just in case, this routine helps guarantee it.
*
* @param array|false $dateArray
*/
private static function forceArray($dateArray): array
{
return is_array($dateArray) ? $dateArray : ['error_count' => 1];
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Date.php 0000644 00000016470 15242753725 0021616 0 ustar 00 getMessage();
}
// Execute function
$excelDateValue = SharedDateHelper::formattedPHPToExcel(/** @scrutinizer ignore-type */ $year, $month, $day);
return Helpers::returnIn3FormatsFloat($excelDateValue);
}
/**
* Convert year from multiple formats to int.
*
* @param mixed $year
*/
private static function getYear($year, int $baseYear): int
{
$year = ($year !== null) ? StringHelper::testStringAsNumeric((string) $year) : 0;
if (!is_numeric($year)) {
throw new Exception(ExcelError::VALUE());
}
$year = (int) $year;
if ($year < ($baseYear - 1900)) {
throw new Exception(ExcelError::NAN());
}
if ((($baseYear - 1900) !== 0) && ($year < $baseYear) && ($year >= 1900)) {
throw new Exception(ExcelError::NAN());
}
if (($year < $baseYear) && ($year >= ($baseYear - 1900))) {
$year += 1900;
}
return (int) $year;
}
/**
* Convert month from multiple formats to int.
*
* @param mixed $month
*/
private static function getMonth($month): int
{
if (($month !== null) && (!is_numeric($month))) {
$month = SharedDateHelper::monthStringToNumber($month);
}
$month = ($month !== null) ? StringHelper::testStringAsNumeric((string) $month) : 0;
if (!is_numeric($month)) {
throw new Exception(ExcelError::VALUE());
}
return (int) $month;
}
/**
* Convert day from multiple formats to int.
*
* @param mixed $day
*/
private static function getDay($day): int
{
if (($day !== null) && (!is_numeric($day))) {
$day = SharedDateHelper::dayStringToNumber($day);
}
$day = ($day !== null) ? StringHelper::testStringAsNumeric((string) $day) : 0;
if (!is_numeric($day)) {
throw new Exception(ExcelError::VALUE());
}
return (int) $day;
}
private static function adjustYearMonth(int &$year, int &$month, int $baseYear): void
{
if ($month < 1) {
// Handle year/month adjustment if month < 1
--$month;
$year += ceil($month / 12) - 1;
$month = 13 - abs($month % 12);
} elseif ($month > 12) {
// Handle year/month adjustment if month > 12
$year += floor($month / 12);
$month = ($month % 12);
}
// Re-validate the year parameter after adjustments
if (($year < $baseYear) || ($year >= 10000)) {
throw new Exception(ExcelError::NAN());
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Current.php 0000644 00000004335 15242753725 0022360 0 ustar 00 format('c'));
return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray, true) : ExcelError::VALUE();
}
/**
* DATETIMENOW.
*
* Returns the current date and time.
* The NOW function is useful when you need to display the current date and time on a worksheet or
* calculate a value based on the current date and time, and have that value updated each time you
* open the worksheet.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
* and time format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* NOW()
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
*/
public static function now()
{
$dti = new DateTimeImmutable();
$dateArray = Helpers::dateParse($dti->format('c'));
return Helpers::dateParseSucceeded($dateArray) ? Helpers::returnIn3FormatsArray($dateArray) : ExcelError::VALUE();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php 0000644 00000024076 15242753725 0021635 0 ustar 00 getMessage();
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
if ($method == Constants::STARTWEEK_MONDAY_ISO) {
Helpers::silly1900($PHPDateObject);
return (int) $PHPDateObject->format('W');
}
if (self::buggyWeekNum1904($method, $origDateValueNull, $PHPDateObject)) {
return 0;
}
Helpers::silly1900($PHPDateObject, '+ 5 years'); // 1905 calendar matches
$dayOfYear = (int) $PHPDateObject->format('z');
$PHPDateObject->modify('-' . $dayOfYear . ' days');
$firstDayOfFirstWeek = (int) $PHPDateObject->format('w');
$daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
$daysInFirstWeek += 7 * !$daysInFirstWeek;
$endFirstWeek = $daysInFirstWeek - 1;
$weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
return (int) $weekOfYear;
}
/**
* ISOWEEKNUM.
*
* Returns the ISO 8601 week number of the year for a specified date.
*
* Excel Function:
* ISOWEEKNUM(dateValue)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of date values
*
* @return array|int|string Week Number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function isoWeekNumber($dateValue)
{
if (is_array($dateValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
}
if (self::apparentBug($dateValue)) {
return 52;
}
try {
$dateValue = Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
Helpers::silly1900($PHPDateObject);
return (int) $PHPDateObject->format('W');
}
/**
* WEEKDAY.
*
* Returns the day of the week for a specified date. The day is given as an integer
* ranging from 0 to 7 (dependent on the requested style).
*
* Excel Function:
* WEEKDAY(dateValue[,style])
*
* @param null|array|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of date values
* @param mixed $style A number that determines the type of return value
* 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
* 2 Numbers 1 (Monday) through 7 (Sunday).
* 3 Numbers 0 (Monday) through 6 (Sunday).
* Or can be an array of styles
*
* @return array|int|string Day of the week value
* If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function day($dateValue, $style = 1)
{
if (is_array($dateValue) || is_array($style)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $style);
}
try {
$dateValue = Helpers::getDateValue($dateValue);
$style = self::validateStyle($style);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
Helpers::silly1900($PHPDateObject);
$DoW = (int) $PHPDateObject->format('w');
switch ($style) {
case 1:
++$DoW;
break;
case 2:
$DoW = self::dow0Becomes7($DoW);
break;
case 3:
$DoW = self::dow0Becomes7($DoW) - 1;
break;
}
return $DoW;
}
/**
* @param mixed $style expect int
*/
private static function validateStyle($style): int
{
if (!is_numeric($style)) {
throw new Exception(ExcelError::VALUE());
}
$style = (int) $style;
if (($style < 1) || ($style > 3)) {
throw new Exception(ExcelError::NAN());
}
return $style;
}
private static function dow0Becomes7(int $DoW): int
{
return ($DoW === 0) ? 7 : $DoW;
}
/**
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*/
private static function apparentBug($dateValue): bool
{
if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
if (is_bool($dateValue)) {
return true;
}
if (is_numeric($dateValue) && !((int) $dateValue)) {
return true;
}
}
return false;
}
/**
* Validate dateValue parameter.
*
* @param mixed $dateValue
*/
private static function validateDateValue($dateValue): float
{
if (is_bool($dateValue)) {
throw new Exception(ExcelError::VALUE());
}
return Helpers::getDateValue($dateValue);
}
/**
* Validate method parameter.
*
* @param mixed $method
*/
private static function validateMethod($method): int
{
if ($method === null) {
$method = Constants::STARTWEEK_SUNDAY;
}
if (!is_numeric($method)) {
throw new Exception(ExcelError::VALUE());
}
$method = (int) $method;
if (!array_key_exists($method, Constants::METHODARR)) {
throw new Exception(ExcelError::NAN());
}
$method = Constants::METHODARR[$method];
return $method;
}
private static function buggyWeekNum1900(int $method): bool
{
return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900;
}
private static function buggyWeekNum1904(int $method, bool $origNull, DateTime $dateObject): bool
{
// This appears to be another Excel bug.
return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 &&
!$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php 0000644 00000006764 15242753725 0022641 0 ustar 00 24) {
$arraySplit[0] = ($arraySplit[0] % 24);
$timeValue = implode(':', $arraySplit);
}
$PHPDateArray = Helpers::dateParse($timeValue);
$retValue = ExcelError::VALUE();
if (Helpers::dateParseSucceeded($PHPDateArray)) {
/** @var int */
$hour = $PHPDateArray['hour'];
/** @var int */
$minute = $PHPDateArray['minute'];
/** @var int */
$second = $PHPDateArray['second'];
// OpenOffice-specific code removed - it works just like Excel
$excelDateValue = SharedDateHelper::formattedPHPToExcel(1900, 1, 1, $hour, $minute, $second) - 1;
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
$retValue = (float) $excelDateValue;
} elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
$retValue = (int) SharedDateHelper::excelToTimestamp($excelDateValue + 25569) - 3600;
} else {
$retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
}
}
return $retValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/NetworkDays.php 0000644 00000010301 15242753725 0023176 0 ustar 00 getMessage();
}
// Execute function
$startDow = self::calcStartDow($startDate);
$endDow = self::calcEndDow($endDate);
$wholeWeekDays = (int) floor(($endDate - $startDate) / 7) * 5;
$partWeekDays = self::calcPartWeekDays($startDow, $endDow);
// Test any extra holiday parameters
$holidayCountedArray = [];
foreach ($holidayArray as $holidayDate) {
if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
if ((Week::day($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
--$partWeekDays;
$holidayCountedArray[] = $holidayDate;
}
}
}
return self::applySign($wholeWeekDays + $partWeekDays, $sDate, $eDate);
}
private static function calcStartDow(float $startDate): int
{
$startDow = 6 - (int) Week::day($startDate, 2);
if ($startDow < 0) {
$startDow = 5;
}
return $startDow;
}
private static function calcEndDow(float $endDate): int
{
$endDow = (int) Week::day($endDate, 2);
if ($endDow >= 6) {
$endDow = 0;
}
return $endDow;
}
private static function calcPartWeekDays(int $startDow, int $endDow): int
{
$partWeekDays = $endDow + $startDow;
if ($partWeekDays > 5) {
$partWeekDays -= 5;
}
return $partWeekDays;
}
private static function applySign(int $result, float $sDate, float $eDate): int
{
return ($sDate > $eDate) ? -$result : $result;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Month.php 0000644 00000010763 15242753725 0022025 0 ustar 00 getMessage();
}
$dateValue = floor($dateValue);
$adjustmentMonths = floor($adjustmentMonths);
// Execute function
$PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths);
return Helpers::returnIn3FormatsObject($PHPDateObject);
}
/**
* EOMONTH.
*
* Returns the date value for the last day of the month that is the indicated number of months
* before or after start_date.
* Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
*
* Excel Function:
* EOMONTH(dateValue,adjustmentMonths)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of date values
* @param array|int $adjustmentMonths The number of months before or after start_date.
* A positive value for months yields a future date;
* a negative value yields a past date.
* Or can be an array of adjustment values
*
* @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
* If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function lastDay($dateValue, $adjustmentMonths)
{
if (is_array($dateValue) || is_array($adjustmentMonths)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $adjustmentMonths);
}
try {
$dateValue = Helpers::getDateValue($dateValue, false);
$adjustmentMonths = Helpers::validateNumericNull($adjustmentMonths);
} catch (Exception $e) {
return $e->getMessage();
}
$dateValue = floor($dateValue);
$adjustmentMonths = floor($adjustmentMonths);
// Execute function
$PHPDateObject = Helpers::adjustDateByMonths($dateValue, $adjustmentMonths + 1);
$adjustDays = (int) $PHPDateObject->format('d');
$adjustDaysString = '-' . $adjustDays . ' days';
$PHPDateObject->modify($adjustDaysString);
return Helpers::returnIn3FormatsObject($PHPDateObject);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/DateParts.php 0000644 00000012125 15242753725 0022621 0 ustar 00 = 0) {
return $weirdResult;
}
try {
$dateValue = Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
return (int) $PHPDateObject->format('j');
}
/**
* MONTHOFYEAR.
*
* Returns the month of a date represented by a serial number.
* The month is given as an integer, ranging from 1 (January) to 12 (December).
*
* Excel Function:
* MONTH(dateValue)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of date values
*
* @return array|int|string Month of the year
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function month($dateValue)
{
if (is_array($dateValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
}
try {
$dateValue = Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) {
return 1;
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
return (int) $PHPDateObject->format('n');
}
/**
* YEAR.
*
* Returns the year corresponding to a date.
* The year is returned as an integer in the range 1900-9999.
*
* Excel Function:
* YEAR(dateValue)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of date values
*
* @return array|int|string Year
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function year($dateValue)
{
if (is_array($dateValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
}
try {
$dateValue = Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
if ($dateValue < 1 && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900) {
return 1900;
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
return (int) $PHPDateObject->format('Y');
}
/**
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*/
private static function weirdCondition($dateValue): int
{
// Excel does not treat 0 consistently for DAY vs. (MONTH or YEAR)
if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900 && Functions::getCompatibilityMode() == Functions::COMPATIBILITY_EXCEL) {
if (is_bool($dateValue)) {
return (int) $dateValue;
}
if ($dateValue === null) {
return 0;
}
if (is_numeric($dateValue) && $dateValue < 1 && $dateValue >= 0) {
return 0;
}
}
return -1;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php 0000644 00000013310 15242753725 0022423 0 ustar 00 getMessage();
}
switch ($method) {
case 0:
return Functions::scalar(Days360::between($startDate, $endDate)) / 360;
case 1:
return self::method1($startDate, $endDate);
case 2:
return Functions::scalar(Difference::interval($startDate, $endDate)) / 360;
case 3:
return Functions::scalar(Difference::interval($startDate, $endDate)) / 365;
case 4:
return Functions::scalar(Days360::between($startDate, $endDate, true)) / 360;
}
return ExcelError::NAN();
}
/**
* Excel 1900 calendar treats date argument of null as 1900-01-00. Really.
*
* @param mixed $startDate
* @param mixed $endDate
*/
private static function excelBug(float $sDate, $startDate, $endDate, int $method): float
{
if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
if ($endDate === null && $startDate !== null) {
if (DateParts::month($sDate) == 12 && DateParts::day($sDate) === 31 && $method === 0) {
$sDate += 2;
} else {
++$sDate;
}
}
}
return $sDate;
}
private static function method1(float $startDate, float $endDate): float
{
$days = Functions::scalar(Difference::interval($startDate, $endDate));
$startYear = (int) DateParts::year($startDate);
$endYear = (int) DateParts::year($endDate);
$years = $endYear - $startYear + 1;
$startMonth = (int) DateParts::month($startDate);
$startDay = (int) DateParts::day($startDate);
$endMonth = (int) DateParts::month($endDate);
$endDay = (int) DateParts::day($endDate);
$startMonthDay = 100 * $startMonth + $startDay;
$endMonthDay = 100 * $endMonth + $endDay;
if ($years == 1) {
$tmpCalcAnnualBasis = 365 + (int) Helpers::isLeapYear($endYear);
} elseif ($years == 2 && $startMonthDay >= $endMonthDay) {
if (Helpers::isLeapYear($startYear)) {
$tmpCalcAnnualBasis = 365 + (int) ($startMonthDay <= 229);
} elseif (Helpers::isLeapYear($endYear)) {
$tmpCalcAnnualBasis = 365 + (int) ($endMonthDay >= 229);
} else {
$tmpCalcAnnualBasis = 365;
}
} else {
$tmpCalcAnnualBasis = 0;
for ($year = $startYear; $year <= $endYear; ++$year) {
$tmpCalcAnnualBasis += 365 + (int) Helpers::isLeapYear($year);
}
$tmpCalcAnnualBasis /= $years;
}
return $days / $tmpCalcAnnualBasis;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Difference.php 0000644 00000013727 15242753725 0022775 0 ustar 00 getMessage();
}
// Execute function
$PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
$startDays = (int) $PHPStartDateObject->format('j');
//$startMonths = (int) $PHPStartDateObject->format('n');
$startYears = (int) $PHPStartDateObject->format('Y');
$PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
$endDays = (int) $PHPEndDateObject->format('j');
//$endMonths = (int) $PHPEndDateObject->format('n');
$endYears = (int) $PHPEndDateObject->format('Y');
$PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
$retVal = false;
$retVal = self::replaceRetValue($retVal, $unit, 'D') ?? self::datedifD($difference);
$retVal = self::replaceRetValue($retVal, $unit, 'M') ?? self::datedifM($PHPDiffDateObject);
$retVal = self::replaceRetValue($retVal, $unit, 'MD') ?? self::datedifMD($startDays, $endDays, $PHPEndDateObject, $PHPDiffDateObject);
$retVal = self::replaceRetValue($retVal, $unit, 'Y') ?? self::datedifY($PHPDiffDateObject);
$retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
$retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);
return is_bool($retVal) ? ExcelError::VALUE() : $retVal;
}
private static function initialDiff(float $startDate, float $endDate): float
{
// Validate parameters
if ($startDate > $endDate) {
throw new Exception(ExcelError::NAN());
}
return $endDate - $startDate;
}
/**
* Decide whether it's time to set retVal.
*
* @param bool|int $retVal
*
* @return null|bool|int
*/
private static function replaceRetValue($retVal, string $unit, string $compare)
{
if ($retVal !== false || $unit !== $compare) {
return $retVal;
}
return null;
}
private static function datedifD(float $difference): int
{
return (int) $difference;
}
private static function datedifM(DateInterval $PHPDiffDateObject): int
{
return 12 * (int) $PHPDiffDateObject->format('%y') + (int) $PHPDiffDateObject->format('%m');
}
private static function datedifMD(int $startDays, int $endDays, DateTime $PHPEndDateObject, DateInterval $PHPDiffDateObject): int
{
if ($endDays < $startDays) {
$retVal = $endDays;
$PHPEndDateObject->modify('-' . $endDays . ' days');
$adjustDays = (int) $PHPEndDateObject->format('j');
$retVal += ($adjustDays - $startDays);
} else {
$retVal = (int) $PHPDiffDateObject->format('%d');
}
return $retVal;
}
private static function datedifY(DateInterval $PHPDiffDateObject): int
{
return (int) $PHPDiffDateObject->format('%y');
}
private static function datedifYD(float $difference, int $startYears, int $endYears, DateTime $PHPStartDateObject, DateTime $PHPEndDateObject): int
{
$retVal = (int) $difference;
if ($endYears > $startYears) {
$isLeapStartYear = $PHPStartDateObject->format('L');
$wasLeapEndYear = $PHPEndDateObject->format('L');
// Adjust end year to be as close as possible as start year
while ($PHPEndDateObject >= $PHPStartDateObject) {
$PHPEndDateObject->modify('-1 year');
//$endYears = $PHPEndDateObject->format('Y');
}
$PHPEndDateObject->modify('+1 year');
// Get the result
$retVal = (int) $PHPEndDateObject->diff($PHPStartDateObject)->days;
// Adjust for leap years cases
$isLeapEndYear = $PHPEndDateObject->format('L');
$limit = new DateTime($PHPEndDateObject->format('Y-02-29'));
if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
--$retVal;
}
}
return (int) $retVal;
}
private static function datedifYM(DateInterval $PHPDiffDateObject): int
{
return (int) $PHPDiffDateObject->format('%m');
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php 0000644 00000010555 15242753725 0022647 0 ustar 00 getMessage();
}
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
return (int) $timeValue->format('H');
}
/**
* MINUTE.
*
* Returns the minutes of a time value.
* The minute is given as an integer, ranging from 0 to 59.
*
* Excel Function:
* MINUTE(timeValue)
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
* Or can be an array of date/time values
*
* @return array|int|string Minute
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function minute($timeValue)
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
}
try {
Helpers::nullFalseTrueToNumber($timeValue);
if (!is_numeric($timeValue)) {
$timeValue = Helpers::getTimeValue($timeValue);
}
Helpers::validateNotNegative($timeValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
return (int) $timeValue->format('i');
}
/**
* SECOND.
*
* Returns the seconds of a time value.
* The minute is given as an integer, ranging from 0 to 59.
*
* Excel Function:
* SECOND(timeValue)
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
* Or can be an array of date/time values
*
* @return array|int|string Second
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function second($timeValue)
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
}
try {
Helpers::nullFalseTrueToNumber($timeValue);
if (!is_numeric($timeValue)) {
$timeValue = Helpers::getTimeValue($timeValue);
}
Helpers::validateNotNegative($timeValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
return (int) $timeValue->format('s');
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days.php 0000644 00000004325 15242753725 0021635 0 ustar 00 getMessage();
}
// Execute function
$PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
$PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
$days = ExcelError::VALUE();
$diff = $PHPStartDateObject->diff($PHPEndDateObject);
if ($diff !== false && !is_bool($diff->days)) {
$days = $diff->days;
if ($diff->invert) {
$days = -$days;
}
}
return $days;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Operations.php 0000644 00000015047 15242753725 0021760 0 ustar 00 0;
});
}
/**
* LOGICAL_XOR.
*
* Returns the Exclusive Or logical operation for one or more supplied conditions.
* i.e. the Xor function returns TRUE if an odd number of the supplied conditions evaluate to TRUE,
* and FALSE otherwise.
*
* Excel Function:
* =XOR(logical1[,logical2[, ...]])
*
* The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
* or references that contain logical values.
*
* Boolean arguments are treated as True or False as appropriate
* Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
* If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
* holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
*
* @param mixed $args Data values
*
* @return bool|string the logical XOR of the arguments
*/
public static function logicalXor(...$args)
{
return self::countTrueValues($args, function (int $trueValueCount): bool {
return $trueValueCount % 2 === 1;
});
}
/**
* NOT.
*
* Returns the boolean inverse of the argument.
*
* Excel Function:
* =NOT(logical)
*
* The argument must evaluate to a logical value such as TRUE or FALSE
*
* Boolean arguments are treated as True or False as appropriate
* Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
* If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
* holds the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
*
* @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
* Or can be an array of values
*
* @return array|bool|string the boolean inverse of the argument
* If an array of values is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function NOT($logical = false)
{
if (is_array($logical)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $logical);
}
if (is_string($logical)) {
$logical = mb_strtoupper($logical, 'UTF-8');
if (($logical == 'TRUE') || ($logical == Calculation::getTRUE())) {
return false;
} elseif (($logical == 'FALSE') || ($logical == Calculation::getFALSE())) {
return true;
}
return ExcelError::VALUE();
}
return !$logical;
}
/**
* @return bool|string
*/
private static function countTrueValues(array $args, callable $func)
{
$trueValueCount = 0;
$count = 0;
$aArgs = Functions::flattenArrayIndexed($args);
foreach ($aArgs as $k => $arg) {
++$count;
// Is it a boolean value?
if (is_bool($arg)) {
$trueValueCount += $arg;
} elseif (is_string($arg)) {
$isLiteral = !Functions::isCellValue($k);
$arg = mb_strtoupper($arg, 'UTF-8');
if ($isLiteral && ($arg == 'TRUE' || $arg == Calculation::getTRUE())) {
++$trueValueCount;
} elseif ($isLiteral && ($arg == 'FALSE' || $arg == Calculation::getFALSE())) {
//$trueValueCount += 0;
} else {
--$count;
}
} elseif (is_int($arg) || is_float($arg)) {
$trueValueCount += (int) ($arg != 0);
} else {
--$count;
}
}
return ($count === 0) ? ExcelError::VALUE() : $func($trueValueCount, $count);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical/Boolean.php 0000644 00000001035 15242753725 0021204 0 ustar 00 0) {
$targetValue = Functions::flattenSingleValue($arguments[0]);
$argc = count($arguments) - 1;
$switchCount = floor($argc / 2);
$hasDefaultClause = $argc % 2 !== 0;
$defaultClause = $argc % 2 === 0 ? null : $arguments[$argc];
$switchSatisfied = false;
if ($switchCount > 0) {
for ($index = 0; $index < $switchCount; ++$index) {
if ($targetValue == Functions::flattenSingleValue($arguments[$index * 2 + 1])) {
$result = $arguments[$index * 2 + 2];
$switchSatisfied = true;
break;
}
}
}
if ($switchSatisfied !== true) {
$result = $hasDefaultClause ? $defaultClause : ExcelError::NA();
}
}
return $result;
}
/**
* IFERROR.
*
* Excel Function:
* =IFERROR(testValue,errorpart)
*
* @param mixed $testValue Value to check, is also the value returned when no error
* Or can be an array of values
* @param mixed $errorpart Value to return when testValue is an error condition
* Note that this can be an array value to be returned
*
* @return mixed The value of errorpart or testValue determined by error condition
* If an array of values is passed as the $testValue argument, then the returned result will also be
* an array with the same dimensions
*/
public static function IFERROR($testValue = '', $errorpart = '')
{
if (is_array($testValue)) {
return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $errorpart);
}
$errorpart = $errorpart ?? '';
$testValue = $testValue ?? 0; // this is how Excel handles empty cell
return self::statementIf(ErrorValue::isError($testValue), $errorpart, $testValue);
}
/**
* IFNA.
*
* Excel Function:
* =IFNA(testValue,napart)
*
* @param mixed $testValue Value to check, is also the value returned when not an NA
* Or can be an array of values
* @param mixed $napart Value to return when testValue is an NA condition
* Note that this can be an array value to be returned
*
* @return mixed The value of errorpart or testValue determined by error condition
* If an array of values is passed as the $testValue argument, then the returned result will also be
* an array with the same dimensions
*/
public static function IFNA($testValue = '', $napart = '')
{
if (is_array($testValue)) {
return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $testValue, $napart);
}
$napart = $napart ?? '';
$testValue = $testValue ?? 0; // this is how Excel handles empty cell
return self::statementIf(ErrorValue::isNa($testValue), $napart, $testValue);
}
/**
* IFS.
*
* Excel Function:
* =IFS(testValue1;returnIfTrue1;testValue2;returnIfTrue2;...;testValue_n;returnIfTrue_n)
*
* testValue1 ... testValue_n
* Conditions to Evaluate
* returnIfTrue1 ... returnIfTrue_n
* Value returned if corresponding testValue (nth) was true
*
* @param mixed ...$arguments Statement arguments
* Note that this can be an array value to be returned
*
* @return mixed|string The value of returnIfTrue_n, if testValue_n was true. #N/A if none of testValues was true
*/
public static function IFS(...$arguments)
{
$argumentCount = count($arguments);
if ($argumentCount % 2 != 0) {
return ExcelError::NA();
}
// We use instance of Exception as a falseValue in order to prevent string collision with value in cell
$falseValueException = new Exception();
for ($i = 0; $i < $argumentCount; $i += 2) {
$testValue = ($arguments[$i] === null) ? '' : Functions::flattenSingleValue($arguments[$i]);
$returnIfTrue = ($arguments[$i + 1] === null) ? '' : $arguments[$i + 1];
$result = self::statementIf($testValue, $returnIfTrue, $falseValueException);
if ($result !== $falseValueException) {
return $result;
}
}
return ExcelError::NA();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/Value.php 0000644 00000023606 15242753725 0021624 0 ustar 00 getCoordinate()) {
return false;
}
$cellValue = Functions::trimTrailingRange($value);
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/ui', $cellValue) === 1) {
[$worksheet, $cellValue] = Worksheet::extractSheetTitle($cellValue, true);
if (!empty($worksheet) && $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheet) === null) {
return false;
}
[$column, $row] = Coordinate::indexesFromString($cellValue);
if ($column > 16384 || $row > 1048576) {
return false;
}
return true;
}
$namedRange = $cell->getWorksheet()->getParentOrThrow()->getNamedRange($value);
return $namedRange instanceof NamedRange;
}
/**
* IS_EVEN.
*
* @param mixed $value Value to check
* Or can be an array of values
*
* @return array|bool|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function isEven($value = null)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
if ($value === null) {
return ExcelError::NAME();
} elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
return ExcelError::VALUE();
}
return ((int) fmod($value, 2)) === 0;
}
/**
* IS_ODD.
*
* @param mixed $value Value to check
* Or can be an array of values
*
* @return array|bool|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function isOdd($value = null)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
if ($value === null) {
return ExcelError::NAME();
} elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
return ExcelError::VALUE();
}
return ((int) fmod($value, 2)) !== 0;
}
/**
* IS_NUMBER.
*
* @param mixed $value Value to check
* Or can be an array of values
*
* @return array|bool
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function isNumber($value = null)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
if (is_string($value)) {
return false;
}
return is_numeric($value);
}
/**
* IS_LOGICAL.
*
* @param mixed $value Value to check
* Or can be an array of values
*
* @return array|bool
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function isLogical($value = null)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
return is_bool($value);
}
/**
* IS_TEXT.
*
* @param mixed $value Value to check
* Or can be an array of values
*
* @return array|bool
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function isText($value = null)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
return is_string($value) && !ErrorValue::isError($value);
}
/**
* IS_NONTEXT.
*
* @param mixed $value Value to check
* Or can be an array of values
*
* @return array|bool
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function isNonText($value = null)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
return !self::isText($value);
}
/**
* ISFORMULA.
*
* @param mixed $cellReference The cell to check
* @param ?Cell $cell The current cell (containing this formula)
*
* @return array|bool|string
*/
public static function isFormula($cellReference = '', ?Cell $cell = null)
{
if ($cell === null) {
return ExcelError::REF();
}
$fullCellReference = Functions::expandDefinedName((string) $cellReference, $cell);
if (strpos($cellReference, '!') !== false) {
$cellReference = Functions::trimSheetFromCellReference($cellReference);
$cellReferences = Coordinate::extractAllCellReferencesInRange($cellReference);
if (count($cellReferences) > 1) {
return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $cellReferences, $cell);
}
}
$fullCellReference = Functions::trimTrailingRange($fullCellReference);
preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $fullCellReference, $matches);
$fullCellReference = $matches[6] . $matches[7];
$worksheetName = str_replace("''", "'", trim($matches[2], "'"));
$worksheet = (!empty($worksheetName))
? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
: $cell->getWorksheet();
return ($worksheet !== null) ? $worksheet->getCell($fullCellReference)->isFormula() : ExcelError::REF();
}
/**
* N.
*
* Returns a value converted to a number
*
* @param null|mixed $value The value you want converted
*
* @return number|string N converts values listed in the following table
* If value is or refers to N returns
* A number That number value
* A date The Excel serialized number of that date
* TRUE 1
* FALSE 0
* An error value The error value
* Anything else 0
*/
public static function asNumber($value = null)
{
while (is_array($value)) {
$value = array_shift($value);
}
switch (gettype($value)) {
case 'double':
case 'float':
case 'integer':
return $value;
case 'boolean':
return (int) $value;
case 'string':
// Errors
if ((strlen($value) > 0) && ($value[0] == '#')) {
return $value;
}
break;
}
return 0;
}
/**
* TYPE.
*
* Returns a number that identifies the type of a value
*
* @param null|mixed $value The value you want tested
*
* @return number N converts values listed in the following table
* If value is or refers to N returns
* A number 1
* Text 2
* Logical Value 4
* An error value 16
* Array or Matrix 64
*/
public static function type($value = null)
{
$value = Functions::flattenArrayIndexed($value);
if (is_array($value) && (count($value) > 1)) {
end($value);
$a = key($value);
// Range of cells is an error
if (Functions::isCellValue($a)) {
return 16;
// Test for Matrix
} elseif (Functions::isMatrixValue($a)) {
return 64;
}
} elseif (empty($value)) {
// Empty Cell
return 1;
}
$value = Functions::flattenSingleValue($value);
if (($value === null) || (is_float($value)) || (is_int($value))) {
return 1;
} elseif (is_bool($value)) {
return 4;
} elseif (is_array($value)) {
return 64;
} elseif (is_string($value)) {
// Errors
if ((strlen($value) > 0) && ($value[0] == '#')) {
return 16;
}
return 2;
}
return 0;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ExcelError.php 0000644 00000006756 15242753725 0022631 0 ustar 00
*/
public const ERROR_CODES = [
'null' => '#NULL!', // 1
'divisionbyzero' => '#DIV/0!', // 2
'value' => '#VALUE!', // 3
'reference' => '#REF!', // 4
'name' => '#NAME?', // 5
'num' => '#NUM!', // 6
'na' => '#N/A', // 7
'gettingdata' => '#GETTING_DATA', // 8
'spill' => '#SPILL!', // 9
'connect' => '#CONNECT!', //10
'blocked' => '#BLOCKED!', //11
'unknown' => '#UNKNOWN!', //12
'field' => '#FIELD!', //13
'calculation' => '#CALC!', //14
];
/**
* List of error codes. Replaced by constant;
* previously it was public and updateable, allowing
* user to make inappropriate alterations.
*
* @deprecated 1.25.0 Use ERROR_CODES constant instead.
*
* @var array
*/
public static $errorCodes = self::ERROR_CODES;
/**
* @param mixed $value
*/
public static function throwError($value): string
{
return in_array($value, self::ERROR_CODES, true) ? $value : self::ERROR_CODES['value'];
}
/**
* ERROR_TYPE.
*
* @param mixed $value Value to check
*
* @return array|int|string
*/
public static function type($value = '')
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
$i = 1;
foreach (self::ERROR_CODES as $errorCode) {
if ($value === $errorCode) {
return $i;
}
++$i;
}
return self::NA();
}
/**
* NULL.
*
* Returns the error value #NULL!
*
* @return string #NULL!
*/
public static function null(): string
{
return self::ERROR_CODES['null'];
}
/**
* NaN.
*
* Returns the error value #NUM!
*
* @return string #NUM!
*/
public static function NAN(): string
{
return self::ERROR_CODES['num'];
}
/**
* REF.
*
* Returns the error value #REF!
*
* @return string #REF!
*/
public static function REF(): string
{
return self::ERROR_CODES['reference'];
}
/**
* NA.
*
* Excel Function:
* =NA()
*
* Returns the error value #N/A
* #N/A is the error value that means "no value is available."
*
* @return string #N/A!
*/
public static function NA(): string
{
return self::ERROR_CODES['na'];
}
/**
* VALUE.
*
* Returns the error value #VALUE!
*
* @return string #VALUE!
*/
public static function VALUE(): string
{
return self::ERROR_CODES['value'];
}
/**
* NAME.
*
* Returns the error value #NAME?
*
* @return string #NAME?
*/
public static function NAME(): string
{
return self::ERROR_CODES['name'];
}
/**
* DIV0.
*
* @return string #DIV/0!
*/
public static function DIV0(): string
{
return self::ERROR_CODES['divisionbyzero'];
}
/**
* CALC.
*
* @return string #CALC!
*/
public static function CALC(): string
{
return self::ERROR_CODES['calculation'];
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Information/ErrorValue.php 0000644 00000003654 15242753725 0022637 0 ustar 00 getMessage();
}
// MATCH() is not case sensitive, so we convert lookup value to be lower cased if it's a string type.
if (is_string($lookupValue)) {
$lookupValue = StringHelper::strToLower($lookupValue);
}
$valueKey = null;
switch ($matchType) {
case self::MATCHTYPE_LARGEST_VALUE:
$valueKey = self::matchLargestValue($lookupArray, $lookupValue, $keySet);
break;
case self::MATCHTYPE_FIRST_VALUE:
$valueKey = self::matchFirstValue($lookupArray, $lookupValue);
break;
case self::MATCHTYPE_SMALLEST_VALUE:
default:
$valueKey = self::matchSmallestValue($lookupArray, $lookupValue);
}
if ($valueKey !== null) {
return ++$valueKey;
}
// Unsuccessful in finding a match, return #N/A error value
return ExcelError::NA();
}
/**
* @param mixed $lookupValue
*
* @return mixed
*/
private static function matchFirstValue(array $lookupArray, $lookupValue)
{
if (is_string($lookupValue)) {
$valueIsString = true;
$wildcard = WildcardMatch::wildcard($lookupValue);
} else {
$valueIsString = false;
$wildcard = '';
}
$valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
foreach ($lookupArray as $i => $lookupArrayValue) {
if (
$valueIsString
&& is_string($lookupArrayValue)
) {
if (WildcardMatch::compare($lookupArrayValue, $wildcard)) {
return $i; // wildcard match
}
} else {
if ($lookupArrayValue === $lookupValue) {
return $i; // exact match
}
if (
$valueIsNumeric
&& (is_float($lookupArrayValue) || is_int($lookupArrayValue))
&& $lookupArrayValue == $lookupValue
) {
return $i; // exact match
}
}
}
return null;
}
/**
* @param mixed $lookupValue
*
* @return mixed
*/
private static function matchLargestValue(array $lookupArray, $lookupValue, array $keySet)
{
if (is_string($lookupValue)) {
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
$wildcard = WildcardMatch::wildcard($lookupValue);
foreach (array_reverse($lookupArray) as $i => $lookupArrayValue) {
if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
return $i;
}
}
} else {
foreach ($lookupArray as $i => $lookupArrayValue) {
if ($lookupArrayValue === $lookupValue) {
return $keySet[$i];
}
}
}
}
$valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
foreach ($lookupArray as $i => $lookupArrayValue) {
if ($valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue))) {
if ($lookupArrayValue <= $lookupValue) {
return array_search($i, $keySet);
}
}
$typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
if ($typeMatch && ($lookupArrayValue <= $lookupValue)) {
return array_search($i, $keySet);
}
}
return null;
}
/**
* @param mixed $lookupValue
*
* @return mixed
*/
private static function matchSmallestValue(array $lookupArray, $lookupValue)
{
$valueKey = null;
if (is_string($lookupValue)) {
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
$wildcard = WildcardMatch::wildcard($lookupValue);
foreach ($lookupArray as $i => $lookupArrayValue) {
if (is_string($lookupArrayValue) && WildcardMatch::compare($lookupArrayValue, $wildcard)) {
return $i;
}
}
}
}
$valueIsNumeric = is_int($lookupValue) || is_float($lookupValue);
// The basic algorithm is:
// Iterate and keep the highest match until the next element is smaller than the searched value.
// Return immediately if perfect match is found
foreach ($lookupArray as $i => $lookupArrayValue) {
$typeMatch = gettype($lookupValue) === gettype($lookupArrayValue);
$bothNumeric = $valueIsNumeric && (is_int($lookupArrayValue) || is_float($lookupArrayValue));
if ($lookupArrayValue === $lookupValue) {
// Another "special" case. If a perfect match is found,
// the algorithm gives up immediately
return $i;
}
if ($bothNumeric && $lookupValue == $lookupArrayValue) {
return $i; // exact match, as above
}
if (($typeMatch || $bothNumeric) && $lookupArrayValue >= $lookupValue) {
$valueKey = $i;
} elseif ($typeMatch && $lookupArrayValue < $lookupValue) {
//Excel algorithm gives up immediately if the first element is smaller than the searched value
break;
}
}
return $valueKey;
}
/**
* @param mixed $lookupValue
*/
private static function validateLookupValue($lookupValue): void
{
// Lookup_value type has to be number, text, or logical values
if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
throw new Exception(ExcelError::NA());
}
}
/**
* @param mixed $matchType
*/
private static function validateMatchType($matchType): int
{
// Match_type is 0, 1 or -1
// However Excel accepts other numeric values,
// including numeric strings and floats.
// It seems to just be interested in the sign.
if (!is_numeric($matchType)) {
throw new Exception(ExcelError::Value());
}
if ($matchType > 0) {
return self::MATCHTYPE_LARGEST_VALUE;
}
if ($matchType < 0) {
return self::MATCHTYPE_SMALLEST_VALUE;
}
return self::MATCHTYPE_FIRST_VALUE;
}
private static function validateLookupArray(array $lookupArray): void
{
// Lookup_array should not be empty
$lookupArraySize = count($lookupArray);
if ($lookupArraySize <= 0) {
throw new Exception(ExcelError::NA());
}
}
/**
* @param mixed $matchType
*/
private static function prepareLookupArray(array $lookupArray, $matchType): array
{
// Lookup_array should contain only number, text, or logical values, or empty (null) cells
foreach ($lookupArray as $i => $value) {
// check the type of the value
if ((!is_numeric($value)) && (!is_string($value)) && (!is_bool($value)) && ($value !== null)) {
throw new Exception(ExcelError::NA());
}
// Convert strings to lowercase for case-insensitive testing
if (is_string($value)) {
$lookupArray[$i] = StringHelper::strToLower($value);
}
if (
($value === null) &&
(($matchType == self::MATCHTYPE_LARGEST_VALUE) || ($matchType == self::MATCHTYPE_SMALLEST_VALUE))
) {
unset($lookupArray[$i]);
}
}
return $lookupArray;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Unique.php 0000644 00000010513 15242753725 0021430 0 ustar 00 count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
// We're looking at a full column check (multiple rows)
$transpose = Matrix::transpose($lookupVector);
$result = self::uniqueByRow($transpose, $exactlyOnce);
return (is_array($result)) ? Matrix::transpose($result) : $result;
}
$result = self::countValuesCaseInsensitive($flattenedLookupVector);
if ($exactlyOnce === true) {
$result = self::exactlyOnceFilter($result);
}
if (count($result) === 0) {
return ExcelError::CALC();
}
$result = array_keys($result);
return $result;
}
private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
{
$caseInsensitiveCounts = array_count_values(
array_map(
function (string $value) {
return StringHelper::strToUpper($value);
},
$caseSensitiveLookupValues
)
);
$caseSensitiveCounts = [];
foreach ($caseInsensitiveCounts as $caseInsensitiveKey => $count) {
if (is_numeric($caseInsensitiveKey)) {
$caseSensitiveCounts[$caseInsensitiveKey] = $count;
} else {
foreach ($caseSensitiveLookupValues as $caseSensitiveValue) {
if ($caseInsensitiveKey === StringHelper::strToUpper($caseSensitiveValue)) {
$caseSensitiveCounts[$caseSensitiveValue] = $count;
break;
}
}
}
}
return $caseSensitiveCounts;
}
private static function exactlyOnceFilter(array $values): array
{
return array_filter(
$values,
function ($value) {
return $value === 1;
}
);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/HLookup.php 0000644 00000010536 15242753725 0021550 0 ustar 00 getMessage();
}
$f = array_keys($lookupArray);
$firstRow = reset($f);
if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray))) {
return ExcelError::REF();
}
$firstkey = $f[0] - 1;
$returnColumn = $firstkey + $indexNumber;
$firstColumn = array_shift($f) ?? 1;
$rowNumber = self::hLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
if ($rowNumber !== null) {
// otherwise return the appropriate value
return $lookupArray[$returnColumn][Coordinate::stringFromColumnIndex($rowNumber)];
}
return ExcelError::NA();
}
/**
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param int|string $column
*/
private static function hLookupSearch($lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
{
$lookupLower = StringHelper::strToLower((string) $lookupValue);
$rowNumber = null;
foreach ($lookupArray[$column] as $rowKey => $rowData) {
// break if we have passed possible keys
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData);
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData);
$cellDataLower = StringHelper::strToLower((string) $rowData);
if (
$notExactMatch &&
(($bothNumeric && $rowData > $lookupValue) || ($bothNotNumeric && $cellDataLower > $lookupLower))
) {
break;
}
$rowNumber = self::checkMatch(
$bothNumeric,
$bothNotNumeric,
$notExactMatch,
Coordinate::columnIndexFromString($rowKey),
$cellDataLower,
$lookupLower,
$rowNumber
);
}
return $rowNumber;
}
private static function convertLiteralArray(array $lookupArray): array
{
if (array_key_exists(0, $lookupArray)) {
$lookupArray2 = [];
$row = 0;
foreach ($lookupArray as $arrayVal) {
++$row;
if (!is_array($arrayVal)) {
$arrayVal = [$arrayVal];
}
$arrayVal2 = [];
foreach ($arrayVal as $key2 => $val2) {
$index = Coordinate::stringFromColumnIndex($key2 + 1);
$arrayVal2[$index] = $val2;
}
$lookupArray2[$row] = $arrayVal2;
}
$lookupArray = $lookupArray2;
}
return $lookupArray;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php 0000644 00000011723 15242753725 0021727 0 ustar 00 getCoordinate());
try {
$a1 = self::a1Format($a1fmt);
$cellAddress = self::validateAddress($cellAddress);
} catch (Exception $e) {
return $e->getMessage();
}
[$cellAddress, $worksheet, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_COLUMNRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
$cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
} elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_ROWRANGE_RELATIVE . '$/miu', $cellAddress, $matches)) {
$cellAddress = self::handleRowColumnRanges($worksheet, ...explode(':', $cellAddress));
}
try {
[$cellAddress1, $cellAddress2, $cellAddress] = Helpers::extractCellAddresses($cellAddress, $a1, $cell->getWorkSheet(), $sheetName, $baseRow, $baseCol);
} catch (Exception $e) {
return ExcelError::REF();
}
if (
(!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress1, $matches)) ||
(($cellAddress2 !== null) && (!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $cellAddress2, $matches)))
) {
return ExcelError::REF();
}
return self::extractRequiredCells($worksheet, $cellAddress);
}
/**
* Extract range values.
*
* @return mixed Array of values in range if range contains more than one element.
* Otherwise, a single value is returned.
*/
private static function extractRequiredCells(?Worksheet $worksheet, string $cellAddress)
{
return Calculation::getInstance($worksheet !== null ? $worksheet->getParent() : null)
->extractCellRange($cellAddress, $worksheet, false);
}
private static function handleRowColumnRanges(?Worksheet $worksheet, string $start, string $end): string
{
// Being lazy, we're only checking a single row/column to get the max
if (ctype_digit($start) && $start <= 1048576) {
// Max 16,384 columns for Excel2007
$endColRef = ($worksheet !== null) ? $worksheet->getHighestDataColumn((int) $start) : AddressRange::MAX_COLUMN;
return "A{$start}:{$endColRef}{$end}";
} elseif (ctype_alpha($start) && strlen($start) <= 3) {
// Max 1,048,576 rows for Excel2007
$endRowRef = ($worksheet !== null) ? $worksheet->getHighestDataRow($start) : AddressRange::MAX_ROW;
return "{$start}1:{$end}{$endRowRef}";
}
return "{$start}:{$end}";
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Sort.php 0000644 00000030173 15242753725 0021115 0 ustar 00 getMessage();
}
// We want a simple, enumrated array of arrays where we can reference column by its index number.
$sortArray = array_values(array_map('array_values', $sortArray));
return ($byColumn === true)
? self::sortByColumn($sortArray, $sortIndex, $sortOrder)
: self::sortByRow($sortArray, $sortIndex, $sortOrder);
}
/**
* SORTBY
* The SORTBY function sorts the contents of a range or array based on the values in a corresponding range or array.
* The returned array is the same shape as the provided array argument.
* Both $sortIndex and $sortOrder can be arrays, to provide multi-level sorting.
*
* @param mixed $sortArray The range of cells being sorted
* @param mixed $args
* At least one additional argument must be provided, The vector or range to sort on
* After that, arguments are passed as pairs:
* sort order: ascending or descending
* Ascending = 1 (self::ORDER_ASCENDING)
* Descending = -1 (self::ORDER_DESCENDING)
* additional arrays or ranges for multi-level sorting
*
* @return mixed The sorted values from the sort range
*/
public static function sortBy($sortArray, ...$args)
{
if (!is_array($sortArray)) {
// Scalars are always returned "as is"
return $sortArray;
}
$sortArray = self::enumerateArrayKeys($sortArray);
$lookupArraySize = count($sortArray);
$argumentCount = count($args);
try {
$sortBy = $sortOrder = [];
for ($i = 0; $i < $argumentCount; $i += 2) {
$sortBy[] = self::validateSortVector($args[$i], $lookupArraySize);
$sortOrder[] = self::validateSortOrder($args[$i + 1] ?? self::ORDER_ASCENDING);
}
} catch (Exception $e) {
return $e->getMessage();
}
return self::processSortBy($sortArray, $sortBy, $sortOrder);
}
private static function enumerateArrayKeys(array $sortArray): array
{
array_walk(
$sortArray,
function (&$columns): void {
if (is_array($columns)) {
$columns = array_values($columns);
}
}
);
return array_values($sortArray);
}
/**
* @param mixed $sortIndex
* @param mixed $sortOrder
*/
private static function validateScalarArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
{
if (is_array($sortIndex) || is_array($sortOrder)) {
throw new Exception(ExcelError::VALUE());
}
$sortIndex = self::validatePositiveInt($sortIndex, false);
if ($sortIndex > $sortArraySize) {
throw new Exception(ExcelError::VALUE());
}
$sortOrder = self::validateSortOrder($sortOrder);
}
/**
* @param mixed $sortVector
*/
private static function validateSortVector($sortVector, int $sortArraySize): array
{
if (!is_array($sortVector)) {
throw new Exception(ExcelError::VALUE());
}
// It doesn't matter if it's a row or a column vectors, it works either way
$sortVector = Functions::flattenArray($sortVector);
if (count($sortVector) !== $sortArraySize) {
throw new Exception(ExcelError::VALUE());
}
return $sortVector;
}
/**
* @param mixed $sortOrder
*/
private static function validateSortOrder($sortOrder): int
{
$sortOrder = self::validateInt($sortOrder);
if (($sortOrder == self::ORDER_ASCENDING || $sortOrder === self::ORDER_DESCENDING) === false) {
throw new Exception(ExcelError::VALUE());
}
return $sortOrder;
}
/**
* @param array $sortIndex
* @param mixed $sortOrder
*/
private static function validateArrayArgumentsForSort(&$sortIndex, &$sortOrder, int $sortArraySize): void
{
// It doesn't matter if they're row or column vectors, it works either way
$sortIndex = Functions::flattenArray($sortIndex);
$sortOrder = Functions::flattenArray($sortOrder);
if (
count($sortOrder) === 0 || count($sortOrder) > $sortArraySize ||
(count($sortOrder) > count($sortIndex))
) {
throw new Exception(ExcelError::VALUE());
}
if (count($sortIndex) > count($sortOrder)) {
// If $sortOrder has fewer elements than $sortIndex, then the last order element is repeated.
$sortOrder = array_merge(
$sortOrder,
array_fill(0, count($sortIndex) - count($sortOrder), array_pop($sortOrder))
);
}
foreach ($sortIndex as $key => &$value) {
self::validateScalarArgumentsForSort($value, $sortOrder[$key], $sortArraySize);
}
}
private static function prepareSortVectorValues(array $sortVector): array
{
// Strings should be sorted case-insensitive; with booleans converted to locale-strings
return array_map(
function ($value) {
if (is_bool($value)) {
return ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
} elseif (is_string($value)) {
return StringHelper::strToLower($value);
}
return $value;
},
$sortVector
);
}
/**
* @param array[] $sortIndex
* @param int[] $sortOrder
*/
private static function processSortBy(array $sortArray, array $sortIndex, $sortOrder): array
{
$sortArguments = [];
$sortData = [];
foreach ($sortIndex as $index => $sortValues) {
$sortData[] = $sortValues;
$sortArguments[] = self::prepareSortVectorValues($sortValues);
$sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
}
$sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
$sortVector = self::executeVectorSortQuery($sortData, $sortArguments);
return self::sortLookupArrayFromVector($sortArray, $sortVector);
}
/**
* @param int[] $sortIndex
* @param int[] $sortOrder
*/
private static function sortByRow(array $sortArray, array $sortIndex, array $sortOrder): array
{
$sortVector = self::buildVectorForSort($sortArray, $sortIndex, $sortOrder);
return self::sortLookupArrayFromVector($sortArray, $sortVector);
}
/**
* @param int[] $sortIndex
* @param int[] $sortOrder
*/
private static function sortByColumn(array $sortArray, array $sortIndex, array $sortOrder): array
{
$sortArray = Matrix::transpose($sortArray);
$result = self::sortByRow($sortArray, $sortIndex, $sortOrder);
return Matrix::transpose($result);
}
/**
* @param int[] $sortIndex
* @param int[] $sortOrder
*/
private static function buildVectorForSort(array $sortArray, array $sortIndex, array $sortOrder): array
{
$sortArguments = [];
$sortData = [];
foreach ($sortIndex as $index => $sortIndexValue) {
$sortValues = array_column($sortArray, $sortIndexValue - 1);
$sortData[] = $sortValues;
$sortArguments[] = self::prepareSortVectorValues($sortValues);
$sortArguments[] = $sortOrder[$index] === self::ORDER_ASCENDING ? SORT_ASC : SORT_DESC;
}
$sortArguments = self::applyPHP7Patch($sortArray, $sortArguments);
$sortData = self::executeVectorSortQuery($sortData, $sortArguments);
return $sortData;
}
private static function executeVectorSortQuery(array $sortData, array $sortArguments): array
{
$sortData = Matrix::transpose($sortData);
// We need to set an index that can be retained, as array_multisort doesn't maintain numeric keys.
$sortDataIndexed = [];
foreach ($sortData as $key => $value) {
$sortDataIndexed[Coordinate::stringFromColumnIndex($key + 1)] = $value;
}
unset($sortData);
$sortArguments[] = &$sortDataIndexed;
array_multisort(...$sortArguments);
// After the sort, we restore the numeric keys that will now be in the correct, sorted order
$sortedData = [];
foreach (array_keys($sortDataIndexed) as $key) {
$sortedData[] = Coordinate::columnIndexFromString($key) - 1;
}
return $sortedData;
}
private static function sortLookupArrayFromVector(array $sortArray, array $sortVector): array
{
// Building a new array in the correct (sorted) order works; but may be memory heavy for larger arrays
$sortedArray = [];
foreach ($sortVector as $index) {
$sortedArray[] = $sortArray[$index];
}
return $sortedArray;
// uksort(
// $lookupArray,
// function (int $a, int $b) use (array $sortVector) {
// return $sortVector[$a] <=> $sortVector[$b];
// }
// );
//
// return $lookupArray;
}
/**
* Hack to handle PHP 7:
* From PHP 8.0.0, If two members compare as equal in a sort, they retain their original order;
* but prior to PHP 8.0.0, their relative order in the sorted array was undefined.
* MS Excel replicates the PHP 8.0.0 behaviour, retaining the original order of matching elements.
* To replicate that behaviour with PHP 7, we add an extra sort based on the row index.
*/
private static function applyPHP7Patch(array $sortArray, array $sortArguments): array
{
if (PHP_VERSION_ID < 80000) {
$sortArguments[] = range(1, count($sortArray));
$sortArguments[] = SORT_ASC;
}
return $sortArguments;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Helpers.php 0000644 00000005367 15242753725 0021577 0 ustar 00 getWorkSheet();
$sheetTitle = ($workSheet === null) ? '' : $workSheet->getTitle();
$value = (string) preg_replace('/^=/', '', $namedRange->getValue());
self::adjustSheetTitle($sheetTitle, $value);
$cellAddress1 = $sheetTitle . $value;
$cellAddress = $cellAddress1;
$a1 = self::CELLADDRESS_USE_A1;
}
if (strpos($cellAddress, ':') !== false) {
[$cellAddress1, $cellAddress2] = explode(':', $cellAddress);
}
$cellAddress = self::convertR1C1($cellAddress1, $cellAddress2, $a1, $baseRow, $baseCol);
return [$cellAddress1, $cellAddress2, $cellAddress];
}
public static function extractWorksheet(string $cellAddress, Cell $cell): array
{
$sheetName = '';
if (strpos($cellAddress, '!') !== false) {
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
$sheetName = trim($sheetName, "'");
}
$worksheet = ($sheetName !== '')
? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($sheetName)
: $cell->getWorksheet();
return [$cellAddress, $worksheet, $sheetName];
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php 0000644 00000010112 15242753725 0021554 0 ustar 00 getMessage();
}
$f = array_keys($lookupArray);
$firstRow = array_pop($f);
if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray[$firstRow]))) {
return ExcelError::REF();
}
$columnKeys = array_keys($lookupArray[$firstRow]);
$returnColumn = $columnKeys[--$indexNumber];
$firstColumn = array_shift($columnKeys) ?? 1;
if (!$notExactMatch) {
/** @var callable */
$callable = [self::class, 'vlookupSort'];
uasort($lookupArray, $callable);
}
$rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
if ($rowNumber !== null) {
// return the appropriate value
return $lookupArray[$rowNumber][$returnColumn];
}
return ExcelError::NA();
}
private static function vlookupSort(array $a, array $b): int
{
reset($a);
$firstColumn = key($a);
$aLower = StringHelper::strToLower((string) $a[$firstColumn]);
$bLower = StringHelper::strToLower((string) $b[$firstColumn]);
if ($aLower == $bLower) {
return 0;
}
return ($aLower < $bLower) ? -1 : 1;
}
/**
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param int|string $column
*/
private static function vLookupSearch($lookupValue, array $lookupArray, $column, bool $notExactMatch): ?int
{
$lookupLower = StringHelper::strToLower((string) $lookupValue);
$rowNumber = null;
foreach ($lookupArray as $rowKey => $rowData) {
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
$cellDataLower = StringHelper::strToLower((string) $rowData[$column]);
// break if we have passed possible keys
if (
$notExactMatch &&
(($bothNumeric && ($rowData[$column] > $lookupValue)) ||
($bothNotNumeric && ($cellDataLower > $lookupLower)))
) {
break;
}
$rowNumber = self::checkMatch(
$bothNumeric,
$bothNotNumeric,
$notExactMatch,
$rowKey,
$cellDataLower,
$lookupLower,
$rowNumber
);
}
return $rowNumber;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Filter.php 0000644 00000004122 15242753725 0021406 0 ustar 00 getWorksheet()->getParentOrThrow()->getSheetByName($worksheetName)
: $cell->getWorksheet();
if (
$worksheet === null ||
!$worksheet->cellExists($cellReference) ||
!$worksheet->getCell($cellReference)->isFormula()
) {
return ExcelError::NA();
}
return $worksheet->getCell($cellReference)->getValue();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupRefValidations.php 0000644 00000001722 15242753725 0024270 0 ustar 00 getColumn()) : 1;
}
/**
* COLUMN.
*
* Returns the column number of the given cell reference
* If the cell reference is a range of cells, COLUMN returns the column numbers of each column
* in the reference as a horizontal array.
* If cell reference is omitted, and the function is being called through the calculation engine,
* then it is assumed to be the reference of the cell in which the COLUMN function appears;
* otherwise this function returns 1.
*
* Excel Function:
* =COLUMN([cellAddress])
*
* @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
*
* @return int|int[]
*/
public static function COLUMN($cellAddress = null, ?Cell $cell = null)
{
if (self::cellAddressNullOrWhitespace($cellAddress)) {
return self::cellColumn($cell);
}
if (is_array($cellAddress)) {
foreach ($cellAddress as $columnKey => $value) {
$columnKey = (string) preg_replace('/[^a-z]/i', '', $columnKey);
return (int) Coordinate::columnIndexFromString($columnKey);
}
return self::cellColumn($cell);
}
$cellAddress = $cellAddress ?? '';
if ($cell != null) {
[,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
[,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
}
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = (string) preg_replace('/[^a-z]/i', '', $startAddress);
$endAddress = (string) preg_replace('/[^a-z]/i', '', $endAddress);
return range(
(int) Coordinate::columnIndexFromString($startAddress),
(int) Coordinate::columnIndexFromString($endAddress)
);
}
$cellAddress = (string) preg_replace('/[^a-z]/i', '', $cellAddress);
return (int) Coordinate::columnIndexFromString($cellAddress);
}
/**
* COLUMNS.
*
* Returns the number of columns in an array or reference.
*
* Excel Function:
* =COLUMNS(cellAddress)
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
* for which you want the number of columns
*
* @return int|string The number of columns in cellAddress, or a string if arguments are invalid
*/
public static function COLUMNS($cellAddress = null)
{
if (self::cellAddressNullOrWhitespace($cellAddress)) {
return 1;
}
if (!is_array($cellAddress)) {
return ExcelError::VALUE();
}
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $rows;
}
return $columns;
}
private static function cellRow(?Cell $cell): int
{
return ($cell !== null) ? $cell->getRow() : 1;
}
/**
* ROW.
*
* Returns the row number of the given cell reference
* If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference
* as a vertical array.
* If cell reference is omitted, and the function is being called through the calculation engine,
* then it is assumed to be the reference of the cell in which the ROW function appears;
* otherwise this function returns 1.
*
* Excel Function:
* =ROW([cellAddress])
*
* @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
*
* @return int|mixed[]|string
*/
public static function ROW($cellAddress = null, ?Cell $cell = null)
{
if (self::cellAddressNullOrWhitespace($cellAddress)) {
return self::cellRow($cell);
}
if (is_array($cellAddress)) {
foreach ($cellAddress as $rowKey => $rowValue) {
foreach ($rowValue as $columnKey => $cellValue) {
return (int) preg_replace('/\D/', '', $rowKey);
}
}
return self::cellRow($cell);
}
$cellAddress = $cellAddress ?? '';
if ($cell !== null) {
[,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
[,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
}
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = (string) preg_replace('/\D/', '', $startAddress);
$endAddress = (string) preg_replace('/\D/', '', $endAddress);
return array_map(
function ($value) {
return [$value];
},
range($startAddress, $endAddress)
);
}
[$cellAddress] = explode(':', $cellAddress);
return (int) preg_replace('/\D/', '', $cellAddress);
}
/**
* ROWS.
*
* Returns the number of rows in an array or reference.
*
* Excel Function:
* =ROWS(cellAddress)
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
* for which you want the number of rows
*
* @return int|string The number of rows in cellAddress, or a string if arguments are invalid
*/
public static function ROWS($cellAddress = null)
{
if (self::cellAddressNullOrWhitespace($cellAddress)) {
return 1;
}
if (!is_array($cellAddress)) {
return ExcelError::VALUE();
}
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $columns;
}
return $rows;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Lookup.php 0000644 00000006755 15242753725 0021450 0 ustar 00 1) || (!$hasResultVector && $lookupRows === 2 && $lookupColumns !== 2)) {
$lookupVector = LookupRef\Matrix::transpose($lookupVector);
$lookupRows = self::rowCount($lookupVector);
$lookupColumns = self::columnCount($lookupVector);
}
$resultVector = self::verifyResultVector($resultVector ?? $lookupVector);
if ($lookupRows === 2 && !$hasResultVector) {
$resultVector = array_pop($lookupVector);
$lookupVector = array_shift($lookupVector);
}
if ($lookupColumns !== 2) {
$lookupVector = self::verifyLookupValues($lookupVector, $resultVector);
}
return VLookup::lookup($lookupValue, $lookupVector, 2);
}
private static function verifyLookupValues(array $lookupVector, array $resultVector): array
{
foreach ($lookupVector as &$value) {
if (is_array($value)) {
$k = array_keys($value);
$key1 = $key2 = array_shift($k);
++$key2;
$dataValue1 = $value[$key1];
} else {
$key1 = 0;
$key2 = 1;
$dataValue1 = $value;
}
$dataValue2 = array_shift($resultVector);
if (is_array($dataValue2)) {
$dataValue2 = array_shift($dataValue2);
}
$value = [$key1 => $dataValue1, $key2 => $dataValue2];
}
unset($value);
return $lookupVector;
}
private static function verifyResultVector(array $resultVector): array
{
$resultRows = self::rowCount($resultVector);
$resultColumns = self::columnCount($resultVector);
// we correctly orient our results
if ($resultRows === 1 && $resultColumns > 1) {
$resultVector = LookupRef\Matrix::transpose($resultVector);
}
return $resultVector;
}
private static function rowCount(array $dataArray): int
{
return count($dataArray);
}
private static function columnCount(array $dataArray): int
{
$rowKeys = array_keys($dataArray);
$row = array_shift($rowKeys);
return count($dataArray[$row]);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/LookupBase.php 0000644 00000004366 15242753725 0022237 0 ustar 00 getParent() : null)
->extractCellRange($cellAddress, $worksheet, false);
}
private static function extractWorksheet(?string $cellAddress, Cell $cell): array
{
$cellAddress = self::assessCellAddress($cellAddress ?? '', $cell);
$sheetName = '';
if (strpos($cellAddress, '!') !== false) {
[$sheetName, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
$sheetName = trim($sheetName, "'");
}
$worksheet = ($sheetName !== '')
? $cell->getWorksheet()->getParentOrThrow()->getSheetByName($sheetName)
: $cell->getWorksheet();
return [$cellAddress, $worksheet];
}
private static function assessCellAddress(string $cellAddress, Cell $cell): string
{
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $cellAddress) !== false) {
$cellAddress = Functions::expandDefinedName($cellAddress, $cell);
}
return $cellAddress;
}
/**
* @param mixed $width
* @param mixed $columns
*/
private static function adjustEndCellColumnForWidth(string $endCellColumn, $width, int $startCellColumn, $columns): int
{
$endCellColumn = Coordinate::columnIndexFromString($endCellColumn) - 1;
if (($width !== null) && (!is_object($width))) {
$endCellColumn = $startCellColumn + (int) $width - 1;
} else {
$endCellColumn += (int) $columns;
}
return $endCellColumn;
}
/**
* @param mixed $height
* @param mixed $rows
* @param mixed $endCellRow
*/
private static function adustEndCellRowForHeight($height, int $startCellRow, $rows, $endCellRow): int
{
if (($height !== null) && (!is_object($height))) {
$endCellRow = $startCellRow + (int) $height - 1;
} else {
$endCellRow += (int) $rows;
}
return $endCellRow;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Hyperlink.php 0000644 00000002527 15242753725 0022135 0 ustar 00 getHyperlink()->setUrl($linkURL);
$cell->getHyperlink()->setTooltip($displayName);
return $displayName;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Address.php 0000644 00000011301 15242753725 0021543 0 ustar 00 '') {
if (strpos($sheetName, ' ') !== false || strpos($sheetName, '[') !== false) {
$sheetName = "'{$sheetName}'";
}
$sheetName .= '!';
}
return $sheetName;
}
private static function formatAsA1(int $row, int $column, int $relativity, string $sheetName): string
{
$rowRelative = $columnRelative = '$';
if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
$columnRelative = '';
}
if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
$rowRelative = '';
}
$column = Coordinate::stringFromColumnIndex($column);
return "{$sheetName}{$columnRelative}{$column}{$rowRelative}{$row}";
}
private static function formatAsR1C1(int $row, int $column, int $relativity, string $sheetName): string
{
if (($relativity == self::ADDRESS_COLUMN_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
$column = "[{$column}]";
}
if (($relativity == self::ADDRESS_ROW_RELATIVE) || ($relativity == self::ADDRESS_RELATIVE)) {
$row = "[{$row}]";
}
[$rowChar, $colChar] = AddressHelper::getRowAndColumnChars();
return "{$sheetName}$rowChar{$row}$colChar{$column}";
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php 0000644 00000010507 15242753725 0021431 0 ustar 00 1 &&
(count($values, COUNT_NORMAL) === 1 || count($values, COUNT_RECURSIVE) === count($values, COUNT_NORMAL));
}
/**
* TRANSPOSE.
*
* @param array|mixed $matrixData A matrix of values
*
* @return array
*/
public static function transpose($matrixData)
{
$returnMatrix = [];
if (!is_array($matrixData)) {
$matrixData = [[$matrixData]];
}
$column = 0;
foreach ($matrixData as $matrixRow) {
$row = 0;
foreach ($matrixRow as $matrixCell) {
$returnMatrix[$row][$column] = $matrixCell;
++$row;
}
++$column;
}
return $returnMatrix;
}
/**
* INDEX.
*
* Uses an index to choose a value from a reference or array
*
* Excel Function:
* =INDEX(range_array, row_num, [column_num], [area_num])
*
* @param mixed $matrix A range of cells or an array constant
* @param mixed $rowNum The row in the array or range from which to return a value.
* If row_num is omitted, column_num is required.
* Or can be an array of values
* @param mixed $columnNum The column in the array or range from which to return a value.
* If column_num is omitted, row_num is required.
* Or can be an array of values
*
* TODO Provide support for area_num, currently not supported
*
* @return mixed the value of a specified cell or array of cells
* If an array of values is passed as the $rowNum and/or $columnNum arguments, then the returned result
* will also be an array with the same dimensions
*/
public static function index($matrix, $rowNum = 0, $columnNum = null)
{
if (is_array($rowNum) || is_array($columnNum)) {
return self::evaluateArrayArgumentsSubsetFrom([self::class, __FUNCTION__], 1, $matrix, $rowNum, $columnNum);
}
$rowNum = $rowNum ?? 0;
$originalColumnNum = $columnNum;
$columnNum = $columnNum ?? 0;
try {
$rowNum = LookupRefValidations::validatePositiveInt($rowNum);
$columnNum = LookupRefValidations::validatePositiveInt($columnNum);
} catch (Exception $e) {
return $e->getMessage();
}
if (!is_array($matrix) || ($rowNum > count($matrix))) {
return ExcelError::REF();
}
$rowKeys = array_keys($matrix);
$columnKeys = @array_keys($matrix[$rowKeys[0]]);
if ($columnNum > count($columnKeys)) {
return ExcelError::REF();
}
if ($originalColumnNum === null && 1 < count($columnKeys)) {
return ExcelError::REF();
}
if ($columnNum === 0) {
return self::extractRowValue($matrix, $rowKeys, $rowNum);
}
$columnNum = $columnKeys[--$columnNum];
if ($rowNum === 0) {
return array_map(
function ($value) {
return [$value];
},
array_column($matrix, $columnNum)
);
}
$rowNum = $rowKeys[--$rowNum];
return $matrix[$rowNum][$columnNum];
}
/** @return mixed */
private static function extractRowValue(array $matrix, array $rowKeys, int $rowNum)
{
if ($rowNum === 0) {
return $matrix;
}
$rowNum = $rowKeys[--$rowNum];
$row = $matrix[$rowNum];
if (is_array($row)) {
return [$rowNum => $row];
}
return $row;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php 0000644 00000002743 15242753725 0022115 0 ustar 00 $entryCount)) {
return ExcelError::VALUE();
}
if (is_array($chooseArgs[$chosenEntry])) {
return Functions::flattenArray($chooseArgs[$chosenEntry]);
}
return $chooseArgs[$chosenEntry];
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php 0000644 00000000307 15242753725 0022072 0 ustar 00 1) {
return ExcelError::NAN();
}
$row = array_pop($columnData);
return array_pop($row);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCount.php 0000644 00000004336 15242753725 0021162 0 ustar 00 = count($fieldNames)) {
return null;
}
return $field;
}
$key = array_search($field, array_values($fieldNames), true);
return ($key !== false) ? (int) $key : null;
}
/**
* filter.
*
* Parses the selection criteria, extracts the database rows that match those criteria, and
* returns that subset of rows.
*
* @param mixed[] $database The range of cells that makes up the list or database.
* A database is a list of related data in which rows of related
* information are records, and columns of data are fields. The
* first row of the list contains labels for each column.
* @param mixed[] $criteria The range of cells that contains the conditions you specify.
* You can use any range for the criteria argument, as long as it
* includes at least one column label and at least one cell below
* the column label in which you specify a condition for the
* column.
*
* @return mixed[]
*/
protected static function filter(array $database, array $criteria): array
{
$fieldNames = array_shift($database);
$criteriaNames = array_shift($criteria);
// Convert the criteria into a set of AND/OR conditions with [:placeholders]
$query = self::buildQuery($criteriaNames, $criteria);
// Loop through each row of the database
return self::executeQuery($database, $query, $criteriaNames, $fieldNames);
}
protected static function getFilteredColumn(array $database, ?int $field, array $criteria): array
{
// reduce the database to a set of rows that match all the criteria
$database = self::filter($database, $criteria);
$defaultReturnColumnValue = ($field === null) ? 1 : null;
// extract an array of values for the requested column
$columnData = [];
foreach ($database as $rowKey => $row) {
$keys = array_keys($row);
$key = $keys[$field] ?? null;
$columnKey = $key ?? 'A';
$columnData[$rowKey][$columnKey] = $row[$key] ?? $defaultReturnColumnValue;
}
return $columnData;
}
private static function buildQuery(array $criteriaNames, array $criteria): string
{
$baseQuery = [];
foreach ($criteria as $key => $criterion) {
foreach ($criterion as $field => $value) {
$criterionName = $criteriaNames[$field];
if ($value !== null) {
$condition = self::buildCondition($value, $criterionName);
$baseQuery[$key][] = $condition;
}
}
}
$rowQuery = array_map(
function ($rowValue) {
return (count($rowValue) > 1) ? 'AND(' . implode(',', $rowValue) . ')' : ($rowValue[0] ?? '');
},
$baseQuery
);
return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : ($rowQuery[0] ?? '');
}
/**
* @param mixed $criterion
*/
private static function buildCondition($criterion, string $criterionName): string
{
$ifCondition = Functions::ifCondition($criterion);
// Check for wildcard characters used in the condition
$result = preg_match('/(?[^"]*)(?".*[*?].*")/ui', $ifCondition, $matches);
if ($result !== 1) {
return "[:{$criterionName}]{$ifCondition}";
}
$trueFalse = ($matches['operator'] !== '<>');
$wildcard = WildcardMatch::wildcard($matches['operand']);
$condition = "WILDCARDMATCH([:{$criterionName}],{$wildcard})";
if ($trueFalse === false) {
$condition = "NOT({$condition})";
}
return $condition;
}
private static function executeQuery(array $database, string $query, array $criteria, array $fields): array
{
foreach ($database as $dataRow => $dataValues) {
// Substitute actual values from the database row for our [:placeholders]
$conditions = $query;
foreach ($criteria as $criterion) {
$conditions = self::processCondition($criterion, $fields, $dataValues, $conditions);
}
// evaluate the criteria against the row data
$result = Calculation::getInstance()->_calculateFormulaValue('=' . $conditions);
// If the row failed to meet the criteria, remove it from the database
if ($result !== true) {
unset($database[$dataRow]);
}
}
return $database;
}
/**
* @return mixed
*/
private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions)
{
$key = array_search($criterion, $fields, true);
$dataValue = 'NULL';
if (is_bool($dataValues[$key])) {
$dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE';
} elseif ($dataValues[$key] !== null) {
$dataValue = $dataValues[$key];
// escape quotes if we have a string containing quotes
if (is_string($dataValue) && strpos($dataValue, '"') !== false) {
$dataValue = str_replace('"', '""', $dataValue);
}
$dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
}
return str_replace('[:' . $criterion . ']', $dataValue, $conditions);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Database/DCountA.php 0000644 00000004240 15242753725 0021255 0 ustar 00 value = $structuredReference;
}
public static function fromParser(string $formula, int $index, array $matches): self
{
$val = $matches[0];
$srCount = substr_count($val, self::OPEN_BRACE)
- substr_count($val, self::CLOSE_BRACE);
while ($srCount > 0) {
$srIndex = strlen($val);
$srStringRemainder = substr($formula, $index + $srIndex);
$closingPos = strpos($srStringRemainder, self::CLOSE_BRACE);
if ($closingPos === false) {
throw new Exception("Formula Error: No closing ']' to match opening '['");
}
$srStringRemainder = substr($srStringRemainder, 0, $closingPos + 1);
--$srCount;
if (strpos($srStringRemainder, self::OPEN_BRACE) !== false) {
++$srCount;
}
$val .= $srStringRemainder;
}
return new self($val);
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
public function parse(Cell $cell): string
{
$this->getTableStructure($cell);
$cellRange = ($this->isRowReference()) ? $this->getRowReference($cell) : $this->getColumnReference();
return $cellRange;
}
private function isRowReference(): bool
{
return strpos($this->value, '[@') !== false
|| strpos($this->value, '[' . self::ITEM_SPECIFIER_THIS_ROW . ']') !== false;
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function getTableStructure(Cell $cell): void
{
preg_match(self::TABLE_REFERENCE, $this->value, $matches);
$this->tableName = $matches[1];
$this->table = ($this->tableName === '')
? $this->getTableForCell($cell)
: $this->getTableByName($cell);
$this->reference = $matches[2];
$tableRange = Coordinate::getRangeBoundaries($this->table->getRange());
$this->headersRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] : null;
$this->firstDataRow = ($this->table->getShowHeaderRow()) ? (int) $tableRange[0][1] + 1 : $tableRange[0][1];
$this->totalsRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] : null;
$this->lastDataRow = ($this->table->getShowTotalsRow()) ? (int) $tableRange[1][1] - 1 : $tableRange[1][1];
$this->columns = $this->getColumns($cell, $tableRange);
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function getTableForCell(Cell $cell): Table
{
$tables = $cell->getWorksheet()->getTableCollection();
foreach ($tables as $table) {
/** @var Table $table */
$range = $table->getRange();
if ($cell->isInRange($range) === true) {
$this->tableName = $table->getName();
return $table;
}
}
throw new Exception('Table for Structured Reference cannot be identified');
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function getTableByName(Cell $cell): Table
{
$table = $cell->getWorksheet()->getTableByName($this->tableName);
if ($table === null) {
throw new Exception("Table {$this->tableName} for Structured Reference cannot be located");
}
return $table;
}
private function getColumns(Cell $cell, array $tableRange): array
{
$worksheet = $cell->getWorksheet();
$cellReference = $cell->getCoordinate();
$columns = [];
$lastColumn = ++$tableRange[1][0];
for ($column = $tableRange[0][0]; $column !== $lastColumn; ++$column) {
$columns[$column] = $worksheet
->getCell($column . ($this->headersRow ?? ($this->firstDataRow - 1)))
->getCalculatedValue();
}
$worksheet->getCell($cellReference);
return $columns;
}
private function getRowReference(Cell $cell): string
{
$reference = str_replace("\u{a0}", ' ', $this->reference);
/** @var string $reference */
$reference = str_replace('[' . self::ITEM_SPECIFIER_THIS_ROW . '],', '', $reference);
foreach ($this->columns as $columnId => $columnName) {
$columnName = str_replace("\u{a0}", ' ', $columnName);
$reference = $this->adjustRowReference($columnName, $reference, $cell, $columnId);
}
/** @var string $reference */
return $this->validateParsedReference(trim($reference, '[]@, '));
}
private function adjustRowReference(string $columnName, string $reference, Cell $cell, string $columnId): string
{
if ($columnName !== '') {
$cellReference = $columnId . $cell->getRow();
$pattern1 = '/\[' . preg_quote($columnName, '/') . '\]/miu';
$pattern2 = '/@' . preg_quote($columnName, '/') . '/miu';
if (preg_match($pattern1, $reference) === 1) {
$reference = preg_replace($pattern1, $cellReference, $reference);
} elseif (preg_match($pattern2, $reference) === 1) {
$reference = preg_replace($pattern2, $cellReference, $reference);
}
/** @var string $reference */
}
return $reference;
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function getColumnReference(): string
{
$reference = str_replace("\u{a0}", ' ', $this->reference);
$startRow = ($this->totalsRow === null) ? $this->lastDataRow : $this->totalsRow;
$endRow = ($this->headersRow === null) ? $this->firstDataRow : $this->headersRow;
[$startRow, $endRow] = $this->getRowsForColumnReference($reference, $startRow, $endRow);
$reference = $this->getColumnsForColumnReference($reference, $startRow, $endRow);
$reference = trim($reference, '[]@, ');
if (substr_count($reference, ':') > 1) {
$cells = explode(':', $reference);
$firstCell = array_shift($cells);
$lastCell = array_pop($cells);
$reference = "{$firstCell}:{$lastCell}";
}
return $this->validateParsedReference($reference);
}
/**
* @throws Exception
* @throws \PhpOffice\PhpSpreadsheet\Exception
*/
private function validateParsedReference(string $reference): string
{
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . ':' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/miu', $reference) !== 1) {
throw new Exception(
"Invalid Structured Reference {$this->reference} {$reference}",
Exception::CALCULATION_ENGINE_PUSH_TO_STACK
);
}
}
return $reference;
}
private function fullData(int $startRow, int $endRow): string
{
$columns = array_keys($this->columns);
$firstColumn = array_shift($columns);
$lastColumn = (empty($columns)) ? $firstColumn : array_pop($columns);
return "{$firstColumn}{$startRow}:{$lastColumn}{$endRow}";
}
private function getMinimumRow(string $reference): int
{
switch ($reference) {
case self::ITEM_SPECIFIER_ALL:
case self::ITEM_SPECIFIER_HEADERS:
return $this->headersRow ?? $this->firstDataRow;
case self::ITEM_SPECIFIER_DATA:
return $this->firstDataRow;
case self::ITEM_SPECIFIER_TOTALS:
return $this->totalsRow ?? $this->lastDataRow;
}
return $this->headersRow ?? $this->firstDataRow;
}
private function getMaximumRow(string $reference): int
{
switch ($reference) {
case self::ITEM_SPECIFIER_HEADERS:
return $this->headersRow ?? $this->firstDataRow;
case self::ITEM_SPECIFIER_DATA:
return $this->lastDataRow;
case self::ITEM_SPECIFIER_ALL:
case self::ITEM_SPECIFIER_TOTALS:
return $this->totalsRow ?? $this->lastDataRow;
}
return $this->totalsRow ?? $this->lastDataRow;
}
public function value(): string
{
return $this->value;
}
/**
* @return array
*/
private function getRowsForColumnReference(string &$reference, int $startRow, int $endRow): array
{
$rowsSelected = false;
foreach (self::ITEM_SPECIFIER_ROWS_SET as $rowReference) {
$pattern = '/\[' . $rowReference . '\]/mui';
/** @var string $reference */
if (preg_match($pattern, $reference) === 1) {
if (($rowReference === self::ITEM_SPECIFIER_HEADERS) && ($this->table->getShowHeaderRow() === false)) {
throw new Exception(
'Table Headers are Hidden, and should not be Referenced',
Exception::CALCULATION_ENGINE_PUSH_TO_STACK
);
}
$rowsSelected = true;
$startRow = min($startRow, $this->getMinimumRow($rowReference));
$endRow = max($endRow, $this->getMaximumRow($rowReference));
$reference = preg_replace($pattern, '', $reference);
}
}
if ($rowsSelected === false) {
// If there isn't any Special Item Identifier specified, then the selection defaults to data rows only.
$startRow = $this->firstDataRow;
$endRow = $this->lastDataRow;
}
return [$startRow, $endRow];
}
private function getColumnsForColumnReference(string $reference, int $startRow, int $endRow): string
{
$columnsSelected = false;
foreach ($this->columns as $columnId => $columnName) {
$columnName = str_replace("\u{a0}", ' ', $columnName);
$cellFrom = "{$columnId}{$startRow}";
$cellTo = "{$columnId}{$endRow}";
$cellReference = ($cellFrom === $cellTo) ? $cellFrom : "{$cellFrom}:{$cellTo}";
$pattern = '/\[' . preg_quote($columnName, '/') . '\]/mui';
if (preg_match($pattern, $reference) === 1) {
$columnsSelected = true;
$reference = preg_replace($pattern, $cellReference, $reference);
}
/** @var string $reference */
}
if ($columnsSelected === false) {
return $this->fullData($startRow, $endRow);
}
return $reference;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentHelper.php 0000644 00000012325 15242753725 0023405 0 ustar 00 indexStart = (int) array_shift($keys);
$this->rows = $this->rows($arguments);
$this->columns = $this->columns($arguments);
$this->argumentCount = count($arguments);
$this->arguments = $this->flattenSingleCellArrays($arguments, $this->rows, $this->columns);
$this->rows = $this->rows($arguments);
$this->columns = $this->columns($arguments);
if ($this->arrayArguments() > 2) {
throw new Exception('Formulae with more than two array arguments are not supported');
}
}
public function arguments(): array
{
return $this->arguments;
}
public function hasArrayArgument(): bool
{
return $this->arrayArguments() > 0;
}
public function getFirstArrayArgumentNumber(): int
{
$rowArrays = $this->filterArray($this->rows);
$columnArrays = $this->filterArray($this->columns);
for ($index = $this->indexStart; $index < $this->argumentCount; ++$index) {
if (isset($rowArrays[$index]) || isset($columnArrays[$index])) {
return ++$index;
}
}
return 0;
}
public function getSingleRowVector(): ?int
{
$rowVectors = $this->getRowVectors();
return count($rowVectors) === 1 ? array_pop($rowVectors) : null;
}
private function getRowVectors(): array
{
$rowVectors = [];
for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
if ($this->rows[$index] === 1 && $this->columns[$index] > 1) {
$rowVectors[] = $index;
}
}
return $rowVectors;
}
public function getSingleColumnVector(): ?int
{
$columnVectors = $this->getColumnVectors();
return count($columnVectors) === 1 ? array_pop($columnVectors) : null;
}
private function getColumnVectors(): array
{
$columnVectors = [];
for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
if ($this->rows[$index] > 1 && $this->columns[$index] === 1) {
$columnVectors[] = $index;
}
}
return $columnVectors;
}
public function getMatrixPair(): array
{
for ($i = $this->indexStart; $i < ($this->indexStart + $this->argumentCount - 1); ++$i) {
for ($j = $i + 1; $j < $this->argumentCount; ++$j) {
if (isset($this->rows[$i], $this->rows[$j])) {
return [$i, $j];
}
}
}
return [];
}
public function isVector(int $argument): bool
{
return $this->rows[$argument] === 1 || $this->columns[$argument] === 1;
}
public function isRowVector(int $argument): bool
{
return $this->rows[$argument] === 1;
}
public function isColumnVector(int $argument): bool
{
return $this->columns[$argument] === 1;
}
public function rowCount(int $argument): int
{
return $this->rows[$argument];
}
public function columnCount(int $argument): int
{
return $this->columns[$argument];
}
private function rows(array $arguments): array
{
return array_map(
function ($argument) {
return is_countable($argument) ? count($argument) : 1;
},
$arguments
);
}
private function columns(array $arguments): array
{
return array_map(
function ($argument) {
return is_array($argument) && is_array($argument[array_keys($argument)[0]])
? count($argument[array_keys($argument)[0]])
: 1;
},
$arguments
);
}
public function arrayArguments(): int
{
$count = 0;
foreach (array_keys($this->arguments) as $argument) {
if ($this->rows[$argument] > 1 || $this->columns[$argument] > 1) {
++$count;
}
}
return $count;
}
private function flattenSingleCellArrays(array $arguments, array $rows, array $columns): array
{
foreach ($arguments as $index => $argument) {
if ($rows[$index] === 1 && $columns[$index] === 1) {
while (is_array($argument)) {
$argument = array_pop($argument);
}
$arguments[$index] = $argument;
}
}
return $arguments;
}
private function filterArray(array $array): array
{
return array_filter(
$array,
function ($value) {
return $value > 1;
}
);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/Logger.php 0000644 00000006537 15242753725 0020713 0 ustar 00 cellStack = $stack;
}
/**
* Enable/Disable Calculation engine logging.
*
* @param bool $writeDebugLog
*/
public function setWriteDebugLog($writeDebugLog): void
{
$this->writeDebugLog = $writeDebugLog;
}
/**
* Return whether calculation engine logging is enabled or disabled.
*
* @return bool
*/
public function getWriteDebugLog()
{
return $this->writeDebugLog;
}
/**
* Enable/Disable echoing of debug log information.
*
* @param bool $echoDebugLog
*/
public function setEchoDebugLog($echoDebugLog): void
{
$this->echoDebugLog = $echoDebugLog;
}
/**
* Return whether echoing of debug log information is enabled or disabled.
*
* @return bool
*/
public function getEchoDebugLog()
{
return $this->echoDebugLog;
}
/**
* Write an entry to the calculation engine debug log.
*
* @param mixed $args
*/
public function writeDebugLog(string $message, ...$args): void
{
// Only write the debug log if logging is enabled
if ($this->writeDebugLog) {
$message = sprintf($message, ...$args);
$cellReference = implode(' -> ', $this->cellStack->showStack());
if ($this->echoDebugLog) {
echo $cellReference,
($this->cellStack->count() > 0 ? ' => ' : ''),
$message,
PHP_EOL;
}
$this->debugLog[] = $cellReference .
($this->cellStack->count() > 0 ? ' => ' : '') .
$message;
}
}
/**
* Write a series of entries to the calculation engine debug log.
*
* @param string[] $args
*/
public function mergeDebugLog(array $args): void
{
if ($this->writeDebugLog) {
foreach ($args as $entry) {
$this->writeDebugLog($entry);
}
}
}
/**
* Clear the calculation engine debug log.
*/
public function clearLog(): void
{
$this->debugLog = [];
}
/**
* Return the calculation engine debug log.
*
* @return string[]
*/
public function getLog()
{
return $this->debugLog;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/FormattedNumber.php 0000644 00000013565 15242753725 0022571 0 ustar 00 [-+])? *\% *(?[-+])? *(?[0-9]+\.?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?[-+])? *(?[0-9]+\.?[0-9]*(?:E[-+]?[0-9]*)?) *\% *))$~i';
// preg_quoted string for major currency symbols, with a %s for locale currency
private const CURRENCY_CONVERSION_LIST = '\$€£¥%s';
private const STRING_CONVERSION_LIST = [
[self::class, 'convertToNumberIfNumeric'],
[self::class, 'convertToNumberIfFraction'],
[self::class, 'convertToNumberIfPercent'],
[self::class, 'convertToNumberIfCurrency'],
];
/**
* Identify whether a string contains a formatted numeric value,
* and convert it to a numeric if it is.
*
* @param string $operand string value to test
*/
public static function convertToNumberIfFormatted(string &$operand): bool
{
foreach (self::STRING_CONVERSION_LIST as $conversionMethod) {
if ($conversionMethod($operand) === true) {
return true;
}
}
return false;
}
/**
* Identify whether a string contains a numeric value,
* and convert it to a numeric if it is.
*
* @param string $operand string value to test
*/
public static function convertToNumberIfNumeric(string &$operand): bool
{
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
$value = preg_replace(['/(\d)' . $thousandsSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1$2', '$1$2'], trim($operand));
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
$value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
if (is_numeric($value)) {
$operand = (float) $value;
return true;
}
return false;
}
/**
* Identify whether a string contains a fractional numeric value,
* and convert it to a numeric if it is.
*
* @param string $operand string value to test
*/
public static function convertToNumberIfFraction(string &$operand): bool
{
if (preg_match(self::STRING_REGEXP_FRACTION, $operand, $match)) {
$sign = ($match[1] === '-') ? '-' : '+';
$wholePart = ($match[3] === '') ? '' : ($sign . $match[3]);
$fractionFormula = '=' . $wholePart . $sign . $match[4];
$operand = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
return true;
}
return false;
}
/**
* Identify whether a string contains a percentage, and if so,
* convert it to a numeric.
*
* @param string $operand string value to test
*/
public static function convertToNumberIfPercent(string &$operand): bool
{
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', trim($operand));
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
$value = preg_replace(['/(\d)' . $decimalSeparator . '(\d)/u', '/([+-])\s+(\d)/u'], ['$1.$2', '$1$2'], $value ?? '');
$match = [];
if ($value !== null && preg_match(self::STRING_REGEXP_PERCENT, $value, $match, PREG_UNMATCHED_AS_NULL)) {
//Calculate the percentage
$sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
$operand = (float) ($sign . ($match['PostfixedValue'] ?? $match['PrefixedValue'])) / 100;
return true;
}
return false;
}
/**
* Identify whether a string contains a currency value, and if so,
* convert it to a numeric.
*
* @param string $operand string value to test
*/
public static function convertToNumberIfCurrency(string &$operand): bool
{
$currencyRegexp = self::currencyMatcherRegexp();
$thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator(), '/');
$value = preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $operand);
$match = [];
if ($value !== null && preg_match($currencyRegexp, $value, $match, PREG_UNMATCHED_AS_NULL)) {
//Determine the sign
$sign = ($match['PrefixedSign'] ?? $match['PrefixedSign2'] ?? $match['PostfixedSign']) ?? '';
$decimalSeparator = StringHelper::getDecimalSeparator();
//Cast to a float
$intermediate = (string) ($match['PostfixedValue'] ?? $match['PrefixedValue']);
$intermediate = str_replace($decimalSeparator, '.', $intermediate);
if (is_numeric($intermediate)) {
$operand = (float) ($sign . str_replace($decimalSeparator, '.', $intermediate));
return true;
}
}
return false;
}
public static function currencyMatcherRegexp(): string
{
$currencyCodes = sprintf(self::CURRENCY_CONVERSION_LIST, preg_quote(StringHelper::getCurrencyCode(), '/'));
$decimalSeparator = preg_quote(StringHelper::getDecimalSeparator(), '/');
return '~^(?:(?: *(?[-+])? *(?[' . $currencyCodes . ']) *(?[-+])? *(?[0-9]+[' . $decimalSeparator . ']?[0-9*]*(?:E[-+]?[0-9]*)?) *)|(?: *(?[-+])? *(?[0-9]+' . $decimalSeparator . '?[0-9]*(?:E[-+]?[0-9]*)?) *(?[' . $currencyCodes . ']) *))$~ui';
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/BranchPruner.php 0000644 00000014252 15242753725 0022056 0 ustar 00 branchPruningEnabled = $branchPruningEnabled;
}
public function clearBranchStore(): void
{
$this->branchStoreKeyCounter = 0;
}
public function initialiseForLoop(): void
{
$this->currentCondition = null;
$this->currentOnlyIf = null;
$this->currentOnlyIfNot = null;
$this->previousStoreKey = null;
$this->pendingStoreKey = empty($this->storeKeysStack) ? null : end($this->storeKeysStack);
if ($this->branchPruningEnabled) {
$this->initialiseCondition();
$this->initialiseThen();
$this->initialiseElse();
}
}
private function initialiseCondition(): void
{
if (isset($this->conditionMap[$this->pendingStoreKey]) && $this->conditionMap[$this->pendingStoreKey]) {
$this->currentCondition = $this->pendingStoreKey;
$stackDepth = count($this->storeKeysStack);
if ($stackDepth > 1) {
// nested if
$this->previousStoreKey = $this->storeKeysStack[$stackDepth - 2];
}
}
}
private function initialiseThen(): void
{
if (isset($this->thenMap[$this->pendingStoreKey]) && $this->thenMap[$this->pendingStoreKey]) {
$this->currentOnlyIf = $this->pendingStoreKey;
} elseif (
isset($this->previousStoreKey, $this->thenMap[$this->previousStoreKey])
&& $this->thenMap[$this->previousStoreKey]
) {
$this->currentOnlyIf = $this->previousStoreKey;
}
}
private function initialiseElse(): void
{
if (isset($this->elseMap[$this->pendingStoreKey]) && $this->elseMap[$this->pendingStoreKey]) {
$this->currentOnlyIfNot = $this->pendingStoreKey;
} elseif (
isset($this->previousStoreKey, $this->elseMap[$this->previousStoreKey])
&& $this->elseMap[$this->previousStoreKey]
) {
$this->currentOnlyIfNot = $this->previousStoreKey;
}
}
public function decrementDepth(): void
{
if (!empty($this->pendingStoreKey)) {
--$this->braceDepthMap[$this->pendingStoreKey];
}
}
public function incrementDepth(): void
{
if (!empty($this->pendingStoreKey)) {
++$this->braceDepthMap[$this->pendingStoreKey];
}
}
public function functionCall(string $functionName): void
{
if ($this->branchPruningEnabled && ($functionName === 'IF(')) {
// we handle a new if
$this->pendingStoreKey = $this->getUnusedBranchStoreKey();
$this->storeKeysStack[] = $this->pendingStoreKey;
$this->conditionMap[$this->pendingStoreKey] = true;
$this->braceDepthMap[$this->pendingStoreKey] = 0;
} elseif (!empty($this->pendingStoreKey) && array_key_exists($this->pendingStoreKey, $this->braceDepthMap)) {
// this is not an if but we go deeper
++$this->braceDepthMap[$this->pendingStoreKey];
}
}
public function argumentSeparator(): void
{
if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === 0) {
// We must go to the IF next argument
if ($this->conditionMap[$this->pendingStoreKey]) {
$this->conditionMap[$this->pendingStoreKey] = false;
$this->thenMap[$this->pendingStoreKey] = true;
} elseif ($this->thenMap[$this->pendingStoreKey]) {
$this->thenMap[$this->pendingStoreKey] = false;
$this->elseMap[$this->pendingStoreKey] = true;
} elseif ($this->elseMap[$this->pendingStoreKey]) {
throw new Exception('Reaching fourth argument of an IF');
}
}
}
/**
* @param mixed $value
*/
public function closingBrace($value): void
{
if (!empty($this->pendingStoreKey) && $this->braceDepthMap[$this->pendingStoreKey] === -1) {
// we are closing an IF(
if ($value !== 'IF(') {
throw new Exception('Parser bug we should be in an "IF("');
}
if ($this->conditionMap[$this->pendingStoreKey]) {
throw new Exception('We should not be expecting a condition');
}
$this->thenMap[$this->pendingStoreKey] = false;
$this->elseMap[$this->pendingStoreKey] = false;
--$this->braceDepthMap[$this->pendingStoreKey];
array_pop($this->storeKeysStack);
$this->pendingStoreKey = null;
}
}
public function currentCondition(): ?string
{
return $this->currentCondition;
}
public function currentOnlyIf(): ?string
{
return $this->currentOnlyIf;
}
public function currentOnlyIfNot(): ?string
{
return $this->currentOnlyIfNot;
}
private function getUnusedBranchStoreKey(): string
{
$storeKeyValue = 'storeKey-' . $this->branchStoreKeyCounter;
++$this->branchStoreKeyCounter;
return $storeKeyValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php 0000644 00000014605 15242753725 0024150 0 ustar 00 hasArrayArgument() === false) {
return [$method(...$arguments)];
}
if (self::$arrayArgumentHelper->arrayArguments() === 1) {
$nthArgument = self::$arrayArgumentHelper->getFirstArrayArgumentNumber();
return self::evaluateNthArgumentAsArray($method, $nthArgument, ...$arguments);
}
$singleRowVectorIndex = self::$arrayArgumentHelper->getSingleRowVector();
$singleColumnVectorIndex = self::$arrayArgumentHelper->getSingleColumnVector();
if ($singleRowVectorIndex !== null && $singleColumnVectorIndex !== null) {
// Basic logic for a single row vector and a single column vector
return self::evaluateVectorPair($method, $singleRowVectorIndex, $singleColumnVectorIndex, ...$arguments);
}
$matrixPair = self::$arrayArgumentHelper->getMatrixPair();
if ($matrixPair !== []) {
if (
(self::$arrayArgumentHelper->isVector($matrixPair[0]) === true &&
self::$arrayArgumentHelper->isVector($matrixPair[1]) === false) ||
(self::$arrayArgumentHelper->isVector($matrixPair[0]) === false &&
self::$arrayArgumentHelper->isVector($matrixPair[1]) === true)
) {
// Logic for a matrix and a vector (row or column)
return self::evaluateVectorMatrixPair($method, $matrixPair, ...$arguments);
}
// Logic for matrix/matrix, column vector/column vector or row vector/row vector
return self::evaluateMatrixPair($method, $matrixPair, ...$arguments);
}
// Still need to work out the logic for more than two array arguments,
// For the moment, we're throwing an Exception when we initialise the ArrayArgumentHelper
return ['#VALUE!'];
}
/**
* @param mixed ...$arguments
*/
private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
{
$matrix2 = array_pop($matrixIndexes);
/** @var array $matrixValues2 */
$matrixValues2 = $arguments[$matrix2];
$matrix1 = array_pop($matrixIndexes);
/** @var array $matrixValues1 */
$matrixValues1 = $arguments[$matrix1];
$rows = min(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
$columns = min(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
if ($rows === 1) {
$rows = max(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
}
if ($columns === 1) {
$columns = max(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
}
$result = [];
for ($rowIndex = 0; $rowIndex < $rows; ++$rowIndex) {
for ($columnIndex = 0; $columnIndex < $columns; ++$columnIndex) {
$rowIndex1 = self::$arrayArgumentHelper->isRowVector($matrix1) ? 0 : $rowIndex;
$columnIndex1 = self::$arrayArgumentHelper->isColumnVector($matrix1) ? 0 : $columnIndex;
$value1 = $matrixValues1[$rowIndex1][$columnIndex1];
$rowIndex2 = self::$arrayArgumentHelper->isRowVector($matrix2) ? 0 : $rowIndex;
$columnIndex2 = self::$arrayArgumentHelper->isColumnVector($matrix2) ? 0 : $columnIndex;
$value2 = $matrixValues2[$rowIndex2][$columnIndex2];
$arguments[$matrix1] = $value1;
$arguments[$matrix2] = $value2;
$result[$rowIndex][$columnIndex] = $method(...$arguments);
}
}
return $result;
}
/**
* @param mixed ...$arguments
*/
private static function evaluateMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
{
$matrix2 = array_pop($matrixIndexes);
/** @var array $matrixValues2 */
$matrixValues2 = $arguments[$matrix2];
$matrix1 = array_pop($matrixIndexes);
/** @var array $matrixValues1 */
$matrixValues1 = $arguments[$matrix1];
$result = [];
foreach ($matrixValues1 as $rowIndex => $row) {
foreach ($row as $columnIndex => $value1) {
if (isset($matrixValues2[$rowIndex][$columnIndex]) === false) {
continue;
}
$value2 = $matrixValues2[$rowIndex][$columnIndex];
$arguments[$matrix1] = $value1;
$arguments[$matrix2] = $value2;
$result[$rowIndex][$columnIndex] = $method(...$arguments);
}
}
return $result;
}
/**
* @param mixed ...$arguments
*/
private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, ...$arguments): array
{
$rowVector = Functions::flattenArray($arguments[$rowIndex]);
$columnVector = Functions::flattenArray($arguments[$columnIndex]);
$result = [];
foreach ($columnVector as $column) {
$rowResults = [];
foreach ($rowVector as $row) {
$arguments[$rowIndex] = $row;
$arguments[$columnIndex] = $column;
$rowResults[] = $method(...$arguments);
}
$result[] = $rowResults;
}
return $result;
}
/**
* Note, offset is from 1 (for the first argument) rather than from 0.
*
* @param mixed ...$arguments
*/
private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, ...$arguments): array
{
$values = array_slice($arguments, $nthArgument - 1, 1);
/** @var array $values */
$values = array_pop($values);
$result = [];
foreach ($values as $value) {
$arguments[$nthArgument - 1] = $value;
$result[] = $method(...$arguments);
}
return $result;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engine/CyclicReferenceStack.php 0000644 00000002350 15242753725 0023474 0 ustar 00 stack);
}
/**
* Push a new entry onto the stack.
*
* @param mixed $value
*/
public function push($value): void
{
$this->stack[$value] = $value;
}
/**
* Pop the last entry from the stack.
*
* @return mixed
*/
public function pop()
{
return array_pop($this->stack);
}
/**
* Test to see if a specified entry exists on the stack.
*
* @param mixed $value The value to test
*
* @return bool
*/
public function onStack($value)
{
return isset($this->stack[$value]);
}
/**
* Clear the stack.
*/
public function clear(): void
{
$this->stack = [];
}
/**
* Return an array of all entries on the stack.
*
* @return mixed[]
*/
public function showStack()
{
return $this->stack;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php 0000644 00000005752 15242753725 0020412 0 ustar 00 branchPruner = $branchPruner;
}
/**
* Return the number of entries on the stack.
*/
public function count(): int
{
return $this->count;
}
/**
* Push a new entry onto the stack.
*
* @param mixed $value
*/
public function push(string $type, $value, ?string $reference = null): void
{
$stackItem = $this->getStackItem($type, $value, $reference);
$this->stack[$this->count++] = $stackItem;
if ($type === 'Function') {
$localeFunction = Calculation::localeFunc($value);
if ($localeFunction != $value) {
$this->stack[($this->count - 1)]['localeValue'] = $localeFunction;
}
}
}
public function pushStackItem(array $stackItem): void
{
$this->stack[$this->count++] = $stackItem;
}
/**
* @param mixed $value
*/
public function getStackItem(string $type, $value, ?string $reference = null): array
{
$stackItem = [
'type' => $type,
'value' => $value,
'reference' => $reference,
];
// will store the result under this alias
$storeKey = $this->branchPruner->currentCondition();
if (isset($storeKey) || $reference === 'NULL') {
$stackItem['storeKey'] = $storeKey;
}
// will only run computation if the matching store key is true
$onlyIf = $this->branchPruner->currentOnlyIf();
if (isset($onlyIf) || $reference === 'NULL') {
$stackItem['onlyIf'] = $onlyIf;
}
// will only run computation if the matching store key is false
$onlyIfNot = $this->branchPruner->currentOnlyIfNot();
if (isset($onlyIfNot) || $reference === 'NULL') {
$stackItem['onlyIfNot'] = $onlyIfNot;
}
return $stackItem;
}
/**
* Pop the last entry from the stack.
*/
public function pop(): ?array
{
if ($this->count > 0) {
return $this->stack[--$this->count];
}
return null;
}
/**
* Return an entry from the stack without removing it.
*/
public function last(int $n = 1): ?array
{
if ($this->count - $n < 0) {
return null;
}
return $this->stack[$this->count - $n];
}
/**
* Clear the stack.
*/
public function clear(): void
{
$this->stack = [];
$this->count = 0;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cotangent.php 0000644 00000006744 15242753725 0022635 0 ustar 00 getMessage();
}
return Helpers::verySmallDenominator(cos($angle), sin($angle));
}
/**
* COTH.
*
* Returns the hyperbolic cotangent of an angle.
*
* @param array|float $angle Number, or can be an array of numbers
*
* @return array|float|string The hyperbolic cotangent of the angle
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function coth($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::verySmallDenominator(1.0, tanh($angle));
}
/**
* ACOT.
*
* Returns the arccotangent of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The arccotangent of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function acot($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return (M_PI / 2) - atan($number);
}
/**
* ACOTH.
*
* Returns the hyperbolic arccotangent of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The hyperbolic arccotangent of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function acoth($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
$result = ($number === 1) ? NAN : (log(($number + 1) / ($number - 1)) / 2);
return Helpers::numberOrNan($result);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php 0000644 00000003505 15242753725 0022120 0 ustar 00 getMessage();
}
return Helpers::verySmallDenominator(1.0, cos($angle));
}
/**
* SECH.
*
* Returns the hyperbolic secant of an angle.
*
* @param array|float $angle Number, or can be an array of numbers
*
* @return array|float|string The hyperbolic secant of the angle
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function sech($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::verySmallDenominator(1.0, cosh($angle));
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php 0000644 00000012667 15242753725 0022314 0 ustar 00 getMessage();
}
return Helpers::verySmallDenominator(sin($angle), cos($angle));
}
/**
* TANH.
*
* Returns the result of builtin function sinh after validating args.
*
* @param mixed $angle Should be numeric, or can be an array of numbers
*
* @return array|float|string hyperbolic tangent
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function tanh($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return tanh($angle);
}
/**
* ATAN.
*
* Returns the arctangent of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The arctangent of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function atan($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(atan($number));
}
/**
* ATANH.
*
* Returns the inverse hyperbolic tangent of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The inverse hyperbolic tangent of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function atanh($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(atanh($number));
}
/**
* ATAN2.
*
* This function calculates the arc tangent of the two variables x and y. It is similar to
* calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
* to determine the quadrant of the result.
* The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
* point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
* -pi and pi, excluding -pi.
*
* Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
* PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
*
* Excel Function:
* ATAN2(xCoordinate,yCoordinate)
*
* @param mixed $xCoordinate should be float, the x-coordinate of the point, or can be an array of numbers
* @param mixed $yCoordinate should be float, the y-coordinate of the point, or can be an array of numbers
*
* @return array|float|string
* The inverse tangent of the specified x- and y-coordinates, or a string containing an error
* If an array of numbers is passed as one of the arguments, then the returned result will also be an array
* with the same dimensions
*/
public static function atan2($xCoordinate, $yCoordinate)
{
if (is_array($xCoordinate) || is_array($yCoordinate)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $xCoordinate, $yCoordinate);
}
try {
$xCoordinate = Helpers::validateNumericNullBool($xCoordinate);
$yCoordinate = Helpers::validateNumericNullBool($yCoordinate);
} catch (Exception $e) {
return $e->getMessage();
}
if (($xCoordinate == 0) && ($yCoordinate == 0)) {
return ExcelError::DIV0();
}
return atan2($yCoordinate, $xCoordinate);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosecant.php 0000644 00000003517 15242753725 0022445 0 ustar 00 getMessage();
}
return Helpers::verySmallDenominator(1.0, sin($angle));
}
/**
* CSCH.
*
* Returns the hyperbolic cosecant of an angle.
*
* @param array|float $angle Number, or can be an array of numbers
*
* @return array|float|string The hyperbolic cosecant of the angle
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function csch($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::verySmallDenominator(1.0, sinh($angle));
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Cosine.php 0000644 00000006606 15242753725 0022130 0 ustar 00 getMessage();
}
return cos($number);
}
/**
* COSH.
*
* Returns the result of builtin function cosh after validating args.
*
* @param mixed $number Should be numeric, or can be an array of numbers
*
* @return array|float|string hyperbolic cosine
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function cosh($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return cosh($number);
}
/**
* ACOS.
*
* Returns the arccosine of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The arccosine of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function acos($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(acos($number));
}
/**
* ACOSH.
*
* Returns the arc inverse hyperbolic cosine of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The inverse hyperbolic cosine of the number, or an error string
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function acosh($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(acosh($number));
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php 0000644 00000006522 15242753725 0021603 0 ustar 00 getMessage();
}
return sin($angle);
}
/**
* SINH.
*
* Returns the result of builtin function sinh after validating args.
*
* @param mixed $angle Should be numeric, or can be an array of numbers
*
* @return array|float|string hyperbolic sine
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function sinh($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return sinh($angle);
}
/**
* ASIN.
*
* Returns the arcsine of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The arcsine of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function asin($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(asin($number));
}
/**
* ASINH.
*
* Returns the inverse hyperbolic sine of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The inverse hyperbolic sine of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function asinh($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(asinh($number));
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Combinations.php 0000644 00000007054 15242753725 0022426 0 ustar 00 getMessage();
}
return round(Factorial::fact($numObjs) / Factorial::fact($numObjs - $numInSet)) / Factorial::fact($numInSet); // @phpstan-ignore-line
}
/**
* COMBINA.
*
* Returns the number of combinations for a given number of items. Use COMBIN to
* determine the total possible number of groups for a given number of items.
*
* Excel Function:
* COMBINA(numObjs,numInSet)
*
* @param mixed $numObjs Number of different objects, or can be an array of numbers
* @param mixed $numInSet Number of objects in each combination, or can be an array of numbers
*
* @return array|float|int|string Number of combinations, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function withRepetition($numObjs, $numInSet)
{
if (is_array($numObjs) || is_array($numInSet)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
}
try {
$numObjs = Helpers::validateNumericNullSubstitution($numObjs, null);
$numInSet = Helpers::validateNumericNullSubstitution($numInSet, null);
Helpers::validateNotNegative($numInSet);
Helpers::validateNotNegative($numObjs);
$numObjs = (int) $numObjs;
$numInSet = (int) $numInSet;
// Microsoft documentation says following is true, but Excel
// does not enforce this restriction.
//Helpers::validateNotNegative($numObjs - $numInSet);
if ($numObjs === 0) {
Helpers::validateNotNegative(-$numInSet);
return 1;
}
} catch (Exception $e) {
return $e->getMessage();
}
return round(
Factorial::fact($numObjs + $numInSet - 1) / Factorial::fact($numObjs - 1) // @phpstan-ignore-line
) / Factorial::fact($numInSet);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Exp.php 0000644 00000001743 15242753725 0020534 0 ustar 00 getMessage();
}
return exp($number);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/IntClass.php 0000644 00000002100 15242753725 0021504 0 ustar 00 getMessage();
}
return (int) floor($number);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php 0000644 00000007642 15242753725 0022114 0 ustar 00 getMessage();
}
return $returnValue;
}
private static function getCount(array $array1, array $array2): int
{
$count = count($array1);
if ($count !== count($array2)) {
throw new Exception(ExcelError::NA());
}
return $count;
}
/**
* These functions accept only numeric arguments, not even strings which are numeric.
*
* @param mixed $item
*/
private static function numericNotString($item): bool
{
return is_numeric($item) && !is_string($item);
}
/**
* SUMX2MY2.
*
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
* @return float|string
*/
public static function sumXSquaredMinusYSquared($matrixData1, $matrixData2)
{
try {
$array1 = Functions::flattenArray($matrixData1);
$array2 = Functions::flattenArray($matrixData2);
$count = self::getCount($array1, $array2);
$result = 0;
for ($i = 0; $i < $count; ++$i) {
if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
$result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
}
}
} catch (Exception $e) {
return $e->getMessage();
}
return $result;
}
/**
* SUMX2PY2.
*
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
* @return float|string
*/
public static function sumXSquaredPlusYSquared($matrixData1, $matrixData2)
{
try {
$array1 = Functions::flattenArray($matrixData1);
$array2 = Functions::flattenArray($matrixData2);
$count = self::getCount($array1, $array2);
$result = 0;
for ($i = 0; $i < $count; ++$i) {
if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
$result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
}
}
} catch (Exception $e) {
return $e->getMessage();
}
return $result;
}
/**
* SUMXMY2.
*
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
* @return float|string
*/
public static function sumXMinusYSquared($matrixData1, $matrixData2)
{
try {
$array1 = Functions::flattenArray($matrixData1);
$array2 = Functions::flattenArray($matrixData2);
$count = self::getCount($array1, $array2);
$result = 0;
for ($i = 0; $i < $count; ++$i) {
if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
$result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
}
}
} catch (Exception $e) {
return $e->getMessage();
}
return $result;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Logarithms.php 0000644 00000006254 15242753725 0022113 0 ustar 00 getMessage();
}
return log($number, $base);
}
/**
* LOG10.
*
* Returns the result of builtin function log after validating args.
*
* @param mixed $number Should be numeric
* Or can be an array of values
*
* @return array|float|string Rounded number
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function base10($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
Helpers::validatePositive($number);
} catch (Exception $e) {
return $e->getMessage();
}
return log10($number);
}
/**
* LN.
*
* Returns the result of builtin function log after validating args.
*
* @param mixed $number Should be numeric
* Or can be an array of values
*
* @return array|float|string Rounded number
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function natural($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
Helpers::validatePositive($number);
} catch (Exception $e) {
return $e->getMessage();
}
return log($number);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php 0000644 00000003533 15242753725 0020730 0 ustar 00 getMessage();
}
return Helpers::numberOrNan(sqrt($number));
}
/**
* SQRTPI.
*
* Returns the square root of (number * pi).
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string Square Root of Number * Pi, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function pi($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullSubstitution($number, 0);
Helpers::validateNotNegative($number);
} catch (Exception $e) {
return $e->getMessage();
}
return sqrt($number * M_PI);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Operations.php 0000644 00000011604 15242753725 0022120 0 ustar 00 getMessage();
}
if (($dividend < 0.0) && ($divisor > 0.0)) {
return $divisor - fmod(abs($dividend), $divisor);
}
if (($dividend > 0.0) && ($divisor < 0.0)) {
return $divisor + fmod($dividend, abs($divisor));
}
return fmod($dividend, $divisor);
}
/**
* POWER.
*
* Computes x raised to the power y.
*
* @param array|float|int $x
* Or can be an array of values
* @param array|float|int $y
* Or can be an array of values
*
* @return array|float|int|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function power($x, $y)
{
if (is_array($x) || is_array($y)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $y);
}
try {
$x = Helpers::validateNumericNullBool($x);
$y = Helpers::validateNumericNullBool($y);
} catch (Exception $e) {
return $e->getMessage();
}
// Validate parameters
if (!$x && !$y) {
return ExcelError::NAN();
}
if (!$x && $y < 0.0) {
return ExcelError::DIV0();
}
// Return
$result = $x ** $y;
return Helpers::numberOrNan($result);
}
/**
* PRODUCT.
*
* PRODUCT returns the product of all the values and cells referenced in the argument list.
*
* Excel Function:
* PRODUCT(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function product(...$args)
{
$args = array_filter(
Functions::flattenArray($args),
function ($value) {
return $value !== null;
}
);
// Return value
$returnValue = (count($args) === 0) ? 0.0 : 1.0;
// Loop through arguments
foreach ($args as $arg) {
// Is it a numeric value?
if (is_numeric($arg)) {
$returnValue *= $arg;
} else {
return ExcelError::throwError($arg);
}
}
return (float) $returnValue;
}
/**
* QUOTIENT.
*
* QUOTIENT function returns the integer portion of a division. Numerator is the divided number
* and denominator is the divisor.
*
* Excel Function:
* QUOTIENT(value1,value2)
*
* @param mixed $numerator Expect float|int
* Or can be an array of values
* @param mixed $denominator Expect float|int
* Or can be an array of values
*
* @return array|int|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function quotient($numerator, $denominator)
{
if (is_array($numerator) || is_array($denominator)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $numerator, $denominator);
}
try {
$numerator = Helpers::validateNumericNullSubstitution($numerator, 0);
$denominator = Helpers::validateNumericNullSubstitution($denominator, 0);
Helpers::validateNotZero($denominator);
} catch (Exception $e) {
return $e->getMessage();
}
return (int) ($numerator / $denominator);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Helpers.php 0000644 00000006206 15242753725 0021401 0 ustar 00 = 0.
*
* @param float|int $number
*/
public static function validateNotNegative($number, ?string $except = null): void
{
if ($number >= 0) {
return;
}
throw new Exception($except ?? ExcelError::NAN());
}
/**
* Confirm number > 0.
*
* @param float|int $number
*/
public static function validatePositive($number, ?string $except = null): void
{
if ($number > 0) {
return;
}
throw new Exception($except ?? ExcelError::NAN());
}
/**
* Confirm number != 0.
*
* @param float|int $number
*/
public static function validateNotZero($number): void
{
if ($number) {
return;
}
throw new Exception(ExcelError::DIV0());
}
public static function returnSign(float $number): int
{
return $number ? (($number > 0) ? 1 : -1) : 0;
}
public static function getEven(float $number): float
{
$significance = 2 * self::returnSign($number);
return $significance ? (ceil($number / $significance) * $significance) : 0;
}
/**
* Return NAN or value depending on argument.
*
* @param float $result Number
*
* @return float|string
*/
public static function numberOrNan($result)
{
return is_nan($result) ? ExcelError::NAN() : $result;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php 0000644 00000017502 15242753725 0021067 0 ustar 00 getMessage();
}
return round($number, (int) $precision);
}
/**
* ROUNDUP.
*
* Rounds a number up to a specified number of decimal places
*
* @param array|float $number Number to round, or can be an array of numbers
* @param array|int $digits Number of digits to which you want to round $number, or can be an array of numbers
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function up($number, $digits)
{
if (is_array($number) || is_array($digits)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
}
try {
$number = Helpers::validateNumericNullBool($number);
$digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
} catch (Exception $e) {
return $e->getMessage();
}
if ($number == 0.0) {
return 0.0;
}
if (PHP_VERSION_ID >= 80400) {
return round(
(float) (string) $number,
$digits,
RoundingMode::AwayFromZero //* @phpstan-ignore-line
);
}
if ($number < 0.0) {
return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
}
return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
}
/**
* ROUNDDOWN.
*
* Rounds a number down to a specified number of decimal places
*
* @param null|array|float|string $number Number to round, or can be an array of numbers
* @param array|float|int|string $digits Number of digits to which you want to round $number, or can be an array of numbers
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function down($number, $digits)
{
if (is_array($number) || is_array($digits)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
}
try {
$number = Helpers::validateNumericNullBool($number);
$digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
} catch (Exception $e) {
return $e->getMessage();
}
if ($number == 0.0) {
return 0.0;
}
if (PHP_VERSION_ID >= 80400) {
return round(
(float) (string) $number,
$digits,
RoundingMode::TowardsZero //* @phpstan-ignore-line
);
}
if ($number < 0.0) {
return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
}
return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
}
/**
* MROUND.
*
* Rounds a number to the nearest multiple of a specified value
*
* @param mixed $number Expect float. Number to round, or can be an array of numbers
* @param mixed $multiple Expect int. Multiple to which you want to round, or can be an array of numbers.
*
* @return array|float|int|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function multiple($number, $multiple)
{
if (is_array($number) || is_array($multiple)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $multiple);
}
try {
$number = Helpers::validateNumericNullSubstitution($number, 0);
$multiple = Helpers::validateNumericNullSubstitution($multiple, null);
} catch (Exception $e) {
return $e->getMessage();
}
if ($number == 0 || $multiple == 0) {
return 0;
}
if ((Helpers::returnSign($number)) == (Helpers::returnSign($multiple))) {
$multiplier = 1 / $multiple;
return round($number * $multiplier) / $multiplier;
}
return ExcelError::NAN();
}
/**
* EVEN.
*
* Returns number rounded up to the nearest even integer.
* You can use this function for processing items that come in twos. For example,
* a packing crate accepts rows of one or two items. The crate is full when
* the number of items, rounded up to the nearest two, matches the crate's
* capacity.
*
* Excel Function:
* EVEN(number)
*
* @param array|float $number Number to round, or can be an array of numbers
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function even($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::getEven($number);
}
/**
* ODD.
*
* Returns number rounded up to the nearest odd integer.
*
* @param array|float $number Number to round, or can be an array of numbers
*
* @return array|float|int|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function odd($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
$significance = Helpers::returnSign($number);
if ($significance == 0) {
return 1;
}
$result = ceil($number / $significance) * $significance;
if ($result == Helpers::getEven($result)) {
$result += $significance;
}
return $result;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Base.php 0000644 00000004362 15242753725 0020652 0 ustar 00 getMessage();
}
return self::calculate($number, $radix, $minLength);
}
/**
* @param mixed $minLength
*/
private static function calculate(float $number, int $radix, $minLength): string
{
if ($minLength === null || is_numeric($minLength)) {
if ($number < 0 || $number >= 2 ** 53 || $radix < 2 || $radix > 36) {
return ExcelError::NAN(); // Numeric range constraints
}
$outcome = strtoupper((string) base_convert("$number", 10, $radix));
if ($minLength !== null) {
$outcome = str_pad($outcome, (int) $minLength, '0', STR_PAD_LEFT); // String padding
}
return $outcome;
}
return ExcelError::VALUE();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Ceiling.php 0000644 00000013725 15242753725 0021355 0 ustar 00 getMessage();
}
return self::argumentsOk((float) $number, (float) $significance);
}
/**
* CEILING.MATH.
*
* Round a number down to the nearest integer or to the nearest multiple of significance.
*
* Excel Function:
* CEILING.MATH(number[,significance[,mode]])
*
* @param mixed $number Number to round
* Or can be an array of values
* @param mixed $significance Significance
* Or can be an array of values
* @param array|int $mode direction to round negative numbers
* Or can be an array of values
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function math($number, $significance = null, $mode = 0)
{
if (is_array($number) || is_array($significance) || is_array($mode)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
}
try {
$number = Helpers::validateNumericNullBool($number);
$significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
$mode = Helpers::validateNumericNullSubstitution($mode, null);
} catch (Exception $e) {
return $e->getMessage();
}
if (empty($significance * $number)) {
return 0.0;
}
if (self::ceilingMathTest((float) $significance, (float) $number, (int) $mode)) {
return floor($number / $significance) * $significance;
}
return ceil($number / $significance) * $significance;
}
/**
* CEILING.PRECISE.
*
* Rounds number up, away from zero, to the nearest multiple of significance.
*
* Excel Function:
* CEILING.PRECISE(number[,significance])
*
* @param mixed $number the number you want to round
* Or can be an array of values
* @param array|float $significance the multiple to which you want to round
* Or can be an array of values
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function precise($number, $significance = 1)
{
if (is_array($number) || is_array($significance)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
}
try {
$number = Helpers::validateNumericNullBool($number);
$significance = Helpers::validateNumericNullSubstitution($significance, null);
} catch (Exception $e) {
return $e->getMessage();
}
if (!$significance) {
return 0.0;
}
$result = $number / abs($significance);
return ceil($result) * $significance * (($significance < 0) ? -1 : 1);
}
/**
* Let CEILINGMATH complexity pass Scrutinizer.
*/
private static function ceilingMathTest(float $significance, float $number, int $mode): bool
{
return ((float) $significance < 0) || ((float) $number < 0 && !empty($mode));
}
/**
* Avoid Scrutinizer problems concerning complexity.
*
* @return float|string
*/
private static function argumentsOk(float $number, float $significance)
{
if (empty($number * $significance)) {
return 0.0;
}
if (Helpers::returnSign($number) == Helpers::returnSign($significance)) {
return ceil($number / $significance) * $significance;
}
return ExcelError::NAN();
}
private static function floorCheck1Arg(): void
{
$compatibility = Functions::getCompatibilityMode();
if ($compatibility === Functions::COMPATIBILITY_EXCEL) {
throw new Exception('Excel requires 2 arguments for CEILING');
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php 0000644 00000003113 15242753725 0021710 0 ustar 00 getMessage();
}
return $returnValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/MatrixFunctions.php 0000644 00000012271 15242753725 0023133 0 ustar 00 getMessage();
}
if ($step === 0) {
return array_chunk(
array_fill(0, $rows * $columns, $start),
max($columns, 1)
);
}
return array_chunk(
range($start, $start + (($rows * $columns - 1) * $step), $step),
max($columns, 1)
);
}
/**
* MDETERM.
*
* Returns the matrix determinant of an array.
*
* Excel Function:
* MDETERM(array)
*
* @param mixed $matrixValues A matrix of values
*
* @return float|string The result, or a string containing an error
*/
public static function determinant($matrixValues)
{
try {
$matrix = self::getMatrix($matrixValues);
return $matrix->determinant();
} catch (MatrixException $ex) {
return ExcelError::VALUE();
} catch (Exception $e) {
return $e->getMessage();
}
}
/**
* MINVERSE.
*
* Returns the inverse matrix for the matrix stored in an array.
*
* Excel Function:
* MINVERSE(array)
*
* @param mixed $matrixValues A matrix of values
*
* @return array|string The result, or a string containing an error
*/
public static function inverse($matrixValues)
{
try {
$matrix = self::getMatrix($matrixValues);
return $matrix->inverse()->toArray();
} catch (MatrixDiv0Exception $e) {
return ExcelError::NAN();
} catch (MatrixException $e) {
return ExcelError::VALUE();
} catch (Exception $e) {
return $e->getMessage();
}
}
/**
* MMULT.
*
* @param mixed $matrixData1 A matrix of values
* @param mixed $matrixData2 A matrix of values
*
* @return array|string The result, or a string containing an error
*/
public static function multiply($matrixData1, $matrixData2)
{
try {
$matrixA = self::getMatrix($matrixData1);
$matrixB = self::getMatrix($matrixData2);
return $matrixA->multiply($matrixB)->toArray();
} catch (MatrixException $ex) {
return ExcelError::VALUE();
} catch (Exception $e) {
return $e->getMessage();
}
}
/**
* MUnit.
*
* @param mixed $dimension Number of rows and columns
*
* @return array|string The result, or a string containing an error
*/
public static function identity($dimension)
{
try {
$dimension = (int) Helpers::validateNumericNullBool($dimension);
Helpers::validatePositive($dimension, ExcelError::VALUE());
$matrix = Builder::createIdentityMatrix($dimension, 0)->toArray();
return $matrix;
} catch (Exception $e) {
return $e->getMessage();
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php 0000644 00000011213 15242753725 0021566 0 ustar 00 getWorksheet()->getRowDimension($row)->getVisible();
},
ARRAY_FILTER_USE_KEY
);
}
/**
* @param mixed $cellReference
* @param mixed $args
*/
protected static function filterFormulaArgs($cellReference, $args): array
{
return array_filter(
$args,
function ($index) use ($cellReference) {
$explodeArray = explode('.', $index);
$row = $explodeArray[1] ?? '';
$column = $explodeArray[2] ?? '';
$retVal = true;
if ($cellReference->getWorksheet()->cellExists($column . $row)) {
//take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
$isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
$cellFormula = !preg_match(
'/^=.*\b(SUBTOTAL|AGGREGATE)\s*\(/i',
$cellReference->getWorksheet()->getCell($column . $row)->getValue() ?? ''
);
$retVal = !$isFormula || $cellFormula;
}
return $retVal;
},
ARRAY_FILTER_USE_KEY
);
}
private const CALL_FUNCTIONS = [
1 => [Statistical\Averages::class, 'average'], // 1 and 101
[Statistical\Counts::class, 'COUNT'], // 2 and 102
[Statistical\Counts::class, 'COUNTA'], // 3 and 103
[Statistical\Maximum::class, 'max'], // 4 and 104
[Statistical\Minimum::class, 'min'], // 5 and 105
[Operations::class, 'product'], // 6 and 106
[Statistical\StandardDeviations::class, 'STDEV'], // 7 and 107
[Statistical\StandardDeviations::class, 'STDEVP'], // 8 and 108
[Sum::class, 'sumIgnoringStrings'], // 9 and 109
[Statistical\Variances::class, 'VAR'], // 10 and 110
[Statistical\Variances::class, 'VARP'], // 111 and 111
];
/**
* SUBTOTAL.
*
* Returns a subtotal in a list or database.
*
* @param mixed $functionType
* A number 1 to 11 that specifies which function to
* use in calculating subtotals within a range
* list
* Numbers 101 to 111 shadow the functions of 1 to 11
* but ignore any values in the range that are
* in hidden rows
* @param mixed[] $args A mixed data series of values
*
* @return float|string
*/
public static function evaluate($functionType, ...$args)
{
$cellReference = array_pop($args);
$bArgs = Functions::flattenArrayIndexed($args);
$aArgs = [];
// int keys must come before string keys for PHP 8.0+
// Otherwise, PHP thinks positional args follow keyword
// in the subsequent call to call_user_func_array.
// Fortunately, order of args is unimportant to Subtotal.
foreach ($bArgs as $key => $value) {
if (is_int($key)) {
$aArgs[$key] = $value;
}
}
foreach ($bArgs as $key => $value) {
if (!is_int($key)) {
$aArgs[$key] = $value;
}
}
try {
$subtotal = (int) Helpers::validateNumericNullBool($functionType);
} catch (Exception $e) {
return $e->getMessage();
}
// Calculate
if ($subtotal > 100) {
$aArgs = self::filterHiddenArgs($cellReference, $aArgs);
$subtotal -= 100;
}
$aArgs = self::filterFormulaArgs($cellReference, $aArgs);
if (array_key_exists($subtotal, self::CALL_FUNCTIONS)) {
/** @var callable */
$call = self::CALL_FUNCTIONS[$subtotal];
return call_user_func_array($call, $aArgs);
}
return ExcelError::VALUE();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php 0000644 00000064117 15242753725 0021060 0 ustar 00 ['VL'],
46 => ['VLI'],
47 => ['VLII'],
48 => ['VLIII'],
49 => ['VLIV', 'IL'],
95 => ['VC'],
96 => ['VCI'],
97 => ['VCII'],
98 => ['VCIII'],
99 => ['VCIV', 'IC'],
145 => ['CVL'],
146 => ['CVLI'],
147 => ['CVLII'],
148 => ['CVLIII'],
149 => ['CVLIV', 'CIL'],
195 => ['CVC'],
196 => ['CVCI'],
197 => ['CVCII'],
198 => ['CVCIII'],
199 => ['CVCIV', 'CIC'],
245 => ['CCVL'],
246 => ['CCVLI'],
247 => ['CCVLII'],
248 => ['CCVLIII'],
249 => ['CCVLIV', 'CCIL'],
295 => ['CCVC'],
296 => ['CCVCI'],
297 => ['CCVCII'],
298 => ['CCVCIII'],
299 => ['CCVCIV', 'CCIC'],
345 => ['CCCVL'],
346 => ['CCCVLI'],
347 => ['CCCVLII'],
348 => ['CCCVLIII'],
349 => ['CCCVLIV', 'CCCIL'],
395 => ['CCCVC'],
396 => ['CCCVCI'],
397 => ['CCCVCII'],
398 => ['CCCVCIII'],
399 => ['CCCVCIV', 'CCCIC'],
445 => ['CDVL'],
446 => ['CDVLI'],
447 => ['CDVLII'],
448 => ['CDVLIII'],
449 => ['CDVLIV', 'CDIL'],
450 => ['LD'],
451 => ['LDI'],
452 => ['LDII'],
453 => ['LDIII'],
454 => ['LDIV'],
455 => ['LDV'],
456 => ['LDVI'],
457 => ['LDVII'],
458 => ['LDVIII'],
459 => ['LDIX'],
460 => ['LDX'],
461 => ['LDXI'],
462 => ['LDXII'],
463 => ['LDXIII'],
464 => ['LDXIV'],
465 => ['LDXV'],
466 => ['LDXVI'],
467 => ['LDXVII'],
468 => ['LDXVIII'],
469 => ['LDXIX'],
470 => ['LDXX'],
471 => ['LDXXI'],
472 => ['LDXXII'],
473 => ['LDXXIII'],
474 => ['LDXXIV'],
475 => ['LDXXV'],
476 => ['LDXXVI'],
477 => ['LDXXVII'],
478 => ['LDXXVIII'],
479 => ['LDXXIX'],
480 => ['LDXXX'],
481 => ['LDXXXI'],
482 => ['LDXXXII'],
483 => ['LDXXXIII'],
484 => ['LDXXXIV'],
485 => ['LDXXXV'],
486 => ['LDXXXVI'],
487 => ['LDXXXVII'],
488 => ['LDXXXVIII'],
489 => ['LDXXXIX'],
490 => ['LDXL', 'XD'],
491 => ['LDXLI', 'XDI'],
492 => ['LDXLII', 'XDII'],
493 => ['LDXLIII', 'XDIII'],
494 => ['LDXLIV', 'XDIV'],
495 => ['LDVL', 'XDV', 'VD'],
496 => ['LDVLI', 'XDVI', 'VDI'],
497 => ['LDVLII', 'XDVII', 'VDII'],
498 => ['LDVLIII', 'XDVIII', 'VDIII'],
499 => ['LDVLIV', 'XDIX', 'VDIV', 'ID'],
545 => ['DVL'],
546 => ['DVLI'],
547 => ['DVLII'],
548 => ['DVLIII'],
549 => ['DVLIV', 'DIL'],
595 => ['DVC'],
596 => ['DVCI'],
597 => ['DVCII'],
598 => ['DVCIII'],
599 => ['DVCIV', 'DIC'],
645 => ['DCVL'],
646 => ['DCVLI'],
647 => ['DCVLII'],
648 => ['DCVLIII'],
649 => ['DCVLIV', 'DCIL'],
695 => ['DCVC'],
696 => ['DCVCI'],
697 => ['DCVCII'],
698 => ['DCVCIII'],
699 => ['DCVCIV', 'DCIC'],
745 => ['DCCVL'],
746 => ['DCCVLI'],
747 => ['DCCVLII'],
748 => ['DCCVLIII'],
749 => ['DCCVLIV', 'DCCIL'],
795 => ['DCCVC'],
796 => ['DCCVCI'],
797 => ['DCCVCII'],
798 => ['DCCVCIII'],
799 => ['DCCVCIV', 'DCCIC'],
845 => ['DCCCVL'],
846 => ['DCCCVLI'],
847 => ['DCCCVLII'],
848 => ['DCCCVLIII'],
849 => ['DCCCVLIV', 'DCCCIL'],
895 => ['DCCCVC'],
896 => ['DCCCVCI'],
897 => ['DCCCVCII'],
898 => ['DCCCVCIII'],
899 => ['DCCCVCIV', 'DCCCIC'],
945 => ['CMVL'],
946 => ['CMVLI'],
947 => ['CMVLII'],
948 => ['CMVLIII'],
949 => ['CMVLIV', 'CMIL'],
950 => ['LM'],
951 => ['LMI'],
952 => ['LMII'],
953 => ['LMIII'],
954 => ['LMIV'],
955 => ['LMV'],
956 => ['LMVI'],
957 => ['LMVII'],
958 => ['LMVIII'],
959 => ['LMIX'],
960 => ['LMX'],
961 => ['LMXI'],
962 => ['LMXII'],
963 => ['LMXIII'],
964 => ['LMXIV'],
965 => ['LMXV'],
966 => ['LMXVI'],
967 => ['LMXVII'],
968 => ['LMXVIII'],
969 => ['LMXIX'],
970 => ['LMXX'],
971 => ['LMXXI'],
972 => ['LMXXII'],
973 => ['LMXXIII'],
974 => ['LMXXIV'],
975 => ['LMXXV'],
976 => ['LMXXVI'],
977 => ['LMXXVII'],
978 => ['LMXXVIII'],
979 => ['LMXXIX'],
980 => ['LMXXX'],
981 => ['LMXXXI'],
982 => ['LMXXXII'],
983 => ['LMXXXIII'],
984 => ['LMXXXIV'],
985 => ['LMXXXV'],
986 => ['LMXXXVI'],
987 => ['LMXXXVII'],
988 => ['LMXXXVIII'],
989 => ['LMXXXIX'],
990 => ['LMXL', 'XM'],
991 => ['LMXLI', 'XMI'],
992 => ['LMXLII', 'XMII'],
993 => ['LMXLIII', 'XMIII'],
994 => ['LMXLIV', 'XMIV'],
995 => ['LMVL', 'XMV', 'VM'],
996 => ['LMVLI', 'XMVI', 'VMI'],
997 => ['LMVLII', 'XMVII', 'VMII'],
998 => ['LMVLIII', 'XMVIII', 'VMIII'],
999 => ['LMVLIV', 'XMIX', 'VMIV', 'IM'],
1045 => ['MVL'],
1046 => ['MVLI'],
1047 => ['MVLII'],
1048 => ['MVLIII'],
1049 => ['MVLIV', 'MIL'],
1095 => ['MVC'],
1096 => ['MVCI'],
1097 => ['MVCII'],
1098 => ['MVCIII'],
1099 => ['MVCIV', 'MIC'],
1145 => ['MCVL'],
1146 => ['MCVLI'],
1147 => ['MCVLII'],
1148 => ['MCVLIII'],
1149 => ['MCVLIV', 'MCIL'],
1195 => ['MCVC'],
1196 => ['MCVCI'],
1197 => ['MCVCII'],
1198 => ['MCVCIII'],
1199 => ['MCVCIV', 'MCIC'],
1245 => ['MCCVL'],
1246 => ['MCCVLI'],
1247 => ['MCCVLII'],
1248 => ['MCCVLIII'],
1249 => ['MCCVLIV', 'MCCIL'],
1295 => ['MCCVC'],
1296 => ['MCCVCI'],
1297 => ['MCCVCII'],
1298 => ['MCCVCIII'],
1299 => ['MCCVCIV', 'MCCIC'],
1345 => ['MCCCVL'],
1346 => ['MCCCVLI'],
1347 => ['MCCCVLII'],
1348 => ['MCCCVLIII'],
1349 => ['MCCCVLIV', 'MCCCIL'],
1395 => ['MCCCVC'],
1396 => ['MCCCVCI'],
1397 => ['MCCCVCII'],
1398 => ['MCCCVCIII'],
1399 => ['MCCCVCIV', 'MCCCIC'],
1445 => ['MCDVL'],
1446 => ['MCDVLI'],
1447 => ['MCDVLII'],
1448 => ['MCDVLIII'],
1449 => ['MCDVLIV', 'MCDIL'],
1450 => ['MLD'],
1451 => ['MLDI'],
1452 => ['MLDII'],
1453 => ['MLDIII'],
1454 => ['MLDIV'],
1455 => ['MLDV'],
1456 => ['MLDVI'],
1457 => ['MLDVII'],
1458 => ['MLDVIII'],
1459 => ['MLDIX'],
1460 => ['MLDX'],
1461 => ['MLDXI'],
1462 => ['MLDXII'],
1463 => ['MLDXIII'],
1464 => ['MLDXIV'],
1465 => ['MLDXV'],
1466 => ['MLDXVI'],
1467 => ['MLDXVII'],
1468 => ['MLDXVIII'],
1469 => ['MLDXIX'],
1470 => ['MLDXX'],
1471 => ['MLDXXI'],
1472 => ['MLDXXII'],
1473 => ['MLDXXIII'],
1474 => ['MLDXXIV'],
1475 => ['MLDXXV'],
1476 => ['MLDXXVI'],
1477 => ['MLDXXVII'],
1478 => ['MLDXXVIII'],
1479 => ['MLDXXIX'],
1480 => ['MLDXXX'],
1481 => ['MLDXXXI'],
1482 => ['MLDXXXII'],
1483 => ['MLDXXXIII'],
1484 => ['MLDXXXIV'],
1485 => ['MLDXXXV'],
1486 => ['MLDXXXVI'],
1487 => ['MLDXXXVII'],
1488 => ['MLDXXXVIII'],
1489 => ['MLDXXXIX'],
1490 => ['MLDXL', 'MXD'],
1491 => ['MLDXLI', 'MXDI'],
1492 => ['MLDXLII', 'MXDII'],
1493 => ['MLDXLIII', 'MXDIII'],
1494 => ['MLDXLIV', 'MXDIV'],
1495 => ['MLDVL', 'MXDV', 'MVD'],
1496 => ['MLDVLI', 'MXDVI', 'MVDI'],
1497 => ['MLDVLII', 'MXDVII', 'MVDII'],
1498 => ['MLDVLIII', 'MXDVIII', 'MVDIII'],
1499 => ['MLDVLIV', 'MXDIX', 'MVDIV', 'MID'],
1545 => ['MDVL'],
1546 => ['MDVLI'],
1547 => ['MDVLII'],
1548 => ['MDVLIII'],
1549 => ['MDVLIV', 'MDIL'],
1595 => ['MDVC'],
1596 => ['MDVCI'],
1597 => ['MDVCII'],
1598 => ['MDVCIII'],
1599 => ['MDVCIV', 'MDIC'],
1645 => ['MDCVL'],
1646 => ['MDCVLI'],
1647 => ['MDCVLII'],
1648 => ['MDCVLIII'],
1649 => ['MDCVLIV', 'MDCIL'],
1695 => ['MDCVC'],
1696 => ['MDCVCI'],
1697 => ['MDCVCII'],
1698 => ['MDCVCIII'],
1699 => ['MDCVCIV', 'MDCIC'],
1745 => ['MDCCVL'],
1746 => ['MDCCVLI'],
1747 => ['MDCCVLII'],
1748 => ['MDCCVLIII'],
1749 => ['MDCCVLIV', 'MDCCIL'],
1795 => ['MDCCVC'],
1796 => ['MDCCVCI'],
1797 => ['MDCCVCII'],
1798 => ['MDCCVCIII'],
1799 => ['MDCCVCIV', 'MDCCIC'],
1845 => ['MDCCCVL'],
1846 => ['MDCCCVLI'],
1847 => ['MDCCCVLII'],
1848 => ['MDCCCVLIII'],
1849 => ['MDCCCVLIV', 'MDCCCIL'],
1895 => ['MDCCCVC'],
1896 => ['MDCCCVCI'],
1897 => ['MDCCCVCII'],
1898 => ['MDCCCVCIII'],
1899 => ['MDCCCVCIV', 'MDCCCIC'],
1945 => ['MCMVL'],
1946 => ['MCMVLI'],
1947 => ['MCMVLII'],
1948 => ['MCMVLIII'],
1949 => ['MCMVLIV', 'MCMIL'],
1950 => ['MLM'],
1951 => ['MLMI'],
1952 => ['MLMII'],
1953 => ['MLMIII'],
1954 => ['MLMIV'],
1955 => ['MLMV'],
1956 => ['MLMVI'],
1957 => ['MLMVII'],
1958 => ['MLMVIII'],
1959 => ['MLMIX'],
1960 => ['MLMX'],
1961 => ['MLMXI'],
1962 => ['MLMXII'],
1963 => ['MLMXIII'],
1964 => ['MLMXIV'],
1965 => ['MLMXV'],
1966 => ['MLMXVI'],
1967 => ['MLMXVII'],
1968 => ['MLMXVIII'],
1969 => ['MLMXIX'],
1970 => ['MLMXX'],
1971 => ['MLMXXI'],
1972 => ['MLMXXII'],
1973 => ['MLMXXIII'],
1974 => ['MLMXXIV'],
1975 => ['MLMXXV'],
1976 => ['MLMXXVI'],
1977 => ['MLMXXVII'],
1978 => ['MLMXXVIII'],
1979 => ['MLMXXIX'],
1980 => ['MLMXXX'],
1981 => ['MLMXXXI'],
1982 => ['MLMXXXII'],
1983 => ['MLMXXXIII'],
1984 => ['MLMXXXIV'],
1985 => ['MLMXXXV'],
1986 => ['MLMXXXVI'],
1987 => ['MLMXXXVII'],
1988 => ['MLMXXXVIII'],
1989 => ['MLMXXXIX'],
1990 => ['MLMXL', 'MXM'],
1991 => ['MLMXLI', 'MXMI'],
1992 => ['MLMXLII', 'MXMII'],
1993 => ['MLMXLIII', 'MXMIII'],
1994 => ['MLMXLIV', 'MXMIV'],
1995 => ['MLMVL', 'MXMV', 'MVM'],
1996 => ['MLMVLI', 'MXMVI', 'MVMI'],
1997 => ['MLMVLII', 'MXMVII', 'MVMII'],
1998 => ['MLMVLIII', 'MXMVIII', 'MVMIII'],
1999 => ['MLMVLIV', 'MXMIX', 'MVMIV', 'MIM'],
2045 => ['MMVL'],
2046 => ['MMVLI'],
2047 => ['MMVLII'],
2048 => ['MMVLIII'],
2049 => ['MMVLIV', 'MMIL'],
2095 => ['MMVC'],
2096 => ['MMVCI'],
2097 => ['MMVCII'],
2098 => ['MMVCIII'],
2099 => ['MMVCIV', 'MMIC'],
2145 => ['MMCVL'],
2146 => ['MMCVLI'],
2147 => ['MMCVLII'],
2148 => ['MMCVLIII'],
2149 => ['MMCVLIV', 'MMCIL'],
2195 => ['MMCVC'],
2196 => ['MMCVCI'],
2197 => ['MMCVCII'],
2198 => ['MMCVCIII'],
2199 => ['MMCVCIV', 'MMCIC'],
2245 => ['MMCCVL'],
2246 => ['MMCCVLI'],
2247 => ['MMCCVLII'],
2248 => ['MMCCVLIII'],
2249 => ['MMCCVLIV', 'MMCCIL'],
2295 => ['MMCCVC'],
2296 => ['MMCCVCI'],
2297 => ['MMCCVCII'],
2298 => ['MMCCVCIII'],
2299 => ['MMCCVCIV', 'MMCCIC'],
2345 => ['MMCCCVL'],
2346 => ['MMCCCVLI'],
2347 => ['MMCCCVLII'],
2348 => ['MMCCCVLIII'],
2349 => ['MMCCCVLIV', 'MMCCCIL'],
2395 => ['MMCCCVC'],
2396 => ['MMCCCVCI'],
2397 => ['MMCCCVCII'],
2398 => ['MMCCCVCIII'],
2399 => ['MMCCCVCIV', 'MMCCCIC'],
2445 => ['MMCDVL'],
2446 => ['MMCDVLI'],
2447 => ['MMCDVLII'],
2448 => ['MMCDVLIII'],
2449 => ['MMCDVLIV', 'MMCDIL'],
2450 => ['MMLD'],
2451 => ['MMLDI'],
2452 => ['MMLDII'],
2453 => ['MMLDIII'],
2454 => ['MMLDIV'],
2455 => ['MMLDV'],
2456 => ['MMLDVI'],
2457 => ['MMLDVII'],
2458 => ['MMLDVIII'],
2459 => ['MMLDIX'],
2460 => ['MMLDX'],
2461 => ['MMLDXI'],
2462 => ['MMLDXII'],
2463 => ['MMLDXIII'],
2464 => ['MMLDXIV'],
2465 => ['MMLDXV'],
2466 => ['MMLDXVI'],
2467 => ['MMLDXVII'],
2468 => ['MMLDXVIII'],
2469 => ['MMLDXIX'],
2470 => ['MMLDXX'],
2471 => ['MMLDXXI'],
2472 => ['MMLDXXII'],
2473 => ['MMLDXXIII'],
2474 => ['MMLDXXIV'],
2475 => ['MMLDXXV'],
2476 => ['MMLDXXVI'],
2477 => ['MMLDXXVII'],
2478 => ['MMLDXXVIII'],
2479 => ['MMLDXXIX'],
2480 => ['MMLDXXX'],
2481 => ['MMLDXXXI'],
2482 => ['MMLDXXXII'],
2483 => ['MMLDXXXIII'],
2484 => ['MMLDXXXIV'],
2485 => ['MMLDXXXV'],
2486 => ['MMLDXXXVI'],
2487 => ['MMLDXXXVII'],
2488 => ['MMLDXXXVIII'],
2489 => ['MMLDXXXIX'],
2490 => ['MMLDXL', 'MMXD'],
2491 => ['MMLDXLI', 'MMXDI'],
2492 => ['MMLDXLII', 'MMXDII'],
2493 => ['MMLDXLIII', 'MMXDIII'],
2494 => ['MMLDXLIV', 'MMXDIV'],
2495 => ['MMLDVL', 'MMXDV', 'MMVD'],
2496 => ['MMLDVLI', 'MMXDVI', 'MMVDI'],
2497 => ['MMLDVLII', 'MMXDVII', 'MMVDII'],
2498 => ['MMLDVLIII', 'MMXDVIII', 'MMVDIII'],
2499 => ['MMLDVLIV', 'MMXDIX', 'MMVDIV', 'MMID'],
2545 => ['MMDVL'],
2546 => ['MMDVLI'],
2547 => ['MMDVLII'],
2548 => ['MMDVLIII'],
2549 => ['MMDVLIV', 'MMDIL'],
2595 => ['MMDVC'],
2596 => ['MMDVCI'],
2597 => ['MMDVCII'],
2598 => ['MMDVCIII'],
2599 => ['MMDVCIV', 'MMDIC'],
2645 => ['MMDCVL'],
2646 => ['MMDCVLI'],
2647 => ['MMDCVLII'],
2648 => ['MMDCVLIII'],
2649 => ['MMDCVLIV', 'MMDCIL'],
2695 => ['MMDCVC'],
2696 => ['MMDCVCI'],
2697 => ['MMDCVCII'],
2698 => ['MMDCVCIII'],
2699 => ['MMDCVCIV', 'MMDCIC'],
2745 => ['MMDCCVL'],
2746 => ['MMDCCVLI'],
2747 => ['MMDCCVLII'],
2748 => ['MMDCCVLIII'],
2749 => ['MMDCCVLIV', 'MMDCCIL'],
2795 => ['MMDCCVC'],
2796 => ['MMDCCVCI'],
2797 => ['MMDCCVCII'],
2798 => ['MMDCCVCIII'],
2799 => ['MMDCCVCIV', 'MMDCCIC'],
2845 => ['MMDCCCVL'],
2846 => ['MMDCCCVLI'],
2847 => ['MMDCCCVLII'],
2848 => ['MMDCCCVLIII'],
2849 => ['MMDCCCVLIV', 'MMDCCCIL'],
2895 => ['MMDCCCVC'],
2896 => ['MMDCCCVCI'],
2897 => ['MMDCCCVCII'],
2898 => ['MMDCCCVCIII'],
2899 => ['MMDCCCVCIV', 'MMDCCCIC'],
2945 => ['MMCMVL'],
2946 => ['MMCMVLI'],
2947 => ['MMCMVLII'],
2948 => ['MMCMVLIII'],
2949 => ['MMCMVLIV', 'MMCMIL'],
2950 => ['MMLM'],
2951 => ['MMLMI'],
2952 => ['MMLMII'],
2953 => ['MMLMIII'],
2954 => ['MMLMIV'],
2955 => ['MMLMV'],
2956 => ['MMLMVI'],
2957 => ['MMLMVII'],
2958 => ['MMLMVIII'],
2959 => ['MMLMIX'],
2960 => ['MMLMX'],
2961 => ['MMLMXI'],
2962 => ['MMLMXII'],
2963 => ['MMLMXIII'],
2964 => ['MMLMXIV'],
2965 => ['MMLMXV'],
2966 => ['MMLMXVI'],
2967 => ['MMLMXVII'],
2968 => ['MMLMXVIII'],
2969 => ['MMLMXIX'],
2970 => ['MMLMXX'],
2971 => ['MMLMXXI'],
2972 => ['MMLMXXII'],
2973 => ['MMLMXXIII'],
2974 => ['MMLMXXIV'],
2975 => ['MMLMXXV'],
2976 => ['MMLMXXVI'],
2977 => ['MMLMXXVII'],
2978 => ['MMLMXXVIII'],
2979 => ['MMLMXXIX'],
2980 => ['MMLMXXX'],
2981 => ['MMLMXXXI'],
2982 => ['MMLMXXXII'],
2983 => ['MMLMXXXIII'],
2984 => ['MMLMXXXIV'],
2985 => ['MMLMXXXV'],
2986 => ['MMLMXXXVI'],
2987 => ['MMLMXXXVII'],
2988 => ['MMLMXXXVIII'],
2989 => ['MMLMXXXIX'],
2990 => ['MMLMXL', 'MMXM'],
2991 => ['MMLMXLI', 'MMXMI'],
2992 => ['MMLMXLII', 'MMXMII'],
2993 => ['MMLMXLIII', 'MMXMIII'],
2994 => ['MMLMXLIV', 'MMXMIV'],
2995 => ['MMLMVL', 'MMXMV', 'MMVM'],
2996 => ['MMLMVLI', 'MMXMVI', 'MMVMI'],
2997 => ['MMLMVLII', 'MMXMVII', 'MMVMII'],
2998 => ['MMLMVLIII', 'MMXMVIII', 'MMVMIII'],
2999 => ['MMLMVLIV', 'MMXMIX', 'MMVMIV', 'MMIM'],
3045 => ['MMMVL'],
3046 => ['MMMVLI'],
3047 => ['MMMVLII'],
3048 => ['MMMVLIII'],
3049 => ['MMMVLIV', 'MMMIL'],
3095 => ['MMMVC'],
3096 => ['MMMVCI'],
3097 => ['MMMVCII'],
3098 => ['MMMVCIII'],
3099 => ['MMMVCIV', 'MMMIC'],
3145 => ['MMMCVL'],
3146 => ['MMMCVLI'],
3147 => ['MMMCVLII'],
3148 => ['MMMCVLIII'],
3149 => ['MMMCVLIV', 'MMMCIL'],
3195 => ['MMMCVC'],
3196 => ['MMMCVCI'],
3197 => ['MMMCVCII'],
3198 => ['MMMCVCIII'],
3199 => ['MMMCVCIV', 'MMMCIC'],
3245 => ['MMMCCVL'],
3246 => ['MMMCCVLI'],
3247 => ['MMMCCVLII'],
3248 => ['MMMCCVLIII'],
3249 => ['MMMCCVLIV', 'MMMCCIL'],
3295 => ['MMMCCVC'],
3296 => ['MMMCCVCI'],
3297 => ['MMMCCVCII'],
3298 => ['MMMCCVCIII'],
3299 => ['MMMCCVCIV', 'MMMCCIC'],
3345 => ['MMMCCCVL'],
3346 => ['MMMCCCVLI'],
3347 => ['MMMCCCVLII'],
3348 => ['MMMCCCVLIII'],
3349 => ['MMMCCCVLIV', 'MMMCCCIL'],
3395 => ['MMMCCCVC'],
3396 => ['MMMCCCVCI'],
3397 => ['MMMCCCVCII'],
3398 => ['MMMCCCVCIII'],
3399 => ['MMMCCCVCIV', 'MMMCCCIC'],
3445 => ['MMMCDVL'],
3446 => ['MMMCDVLI'],
3447 => ['MMMCDVLII'],
3448 => ['MMMCDVLIII'],
3449 => ['MMMCDVLIV', 'MMMCDIL'],
3450 => ['MMMLD'],
3451 => ['MMMLDI'],
3452 => ['MMMLDII'],
3453 => ['MMMLDIII'],
3454 => ['MMMLDIV'],
3455 => ['MMMLDV'],
3456 => ['MMMLDVI'],
3457 => ['MMMLDVII'],
3458 => ['MMMLDVIII'],
3459 => ['MMMLDIX'],
3460 => ['MMMLDX'],
3461 => ['MMMLDXI'],
3462 => ['MMMLDXII'],
3463 => ['MMMLDXIII'],
3464 => ['MMMLDXIV'],
3465 => ['MMMLDXV'],
3466 => ['MMMLDXVI'],
3467 => ['MMMLDXVII'],
3468 => ['MMMLDXVIII'],
3469 => ['MMMLDXIX'],
3470 => ['MMMLDXX'],
3471 => ['MMMLDXXI'],
3472 => ['MMMLDXXII'],
3473 => ['MMMLDXXIII'],
3474 => ['MMMLDXXIV'],
3475 => ['MMMLDXXV'],
3476 => ['MMMLDXXVI'],
3477 => ['MMMLDXXVII'],
3478 => ['MMMLDXXVIII'],
3479 => ['MMMLDXXIX'],
3480 => ['MMMLDXXX'],
3481 => ['MMMLDXXXI'],
3482 => ['MMMLDXXXII'],
3483 => ['MMMLDXXXIII'],
3484 => ['MMMLDXXXIV'],
3485 => ['MMMLDXXXV'],
3486 => ['MMMLDXXXVI'],
3487 => ['MMMLDXXXVII'],
3488 => ['MMMLDXXXVIII'],
3489 => ['MMMLDXXXIX'],
3490 => ['MMMLDXL', 'MMMXD'],
3491 => ['MMMLDXLI', 'MMMXDI'],
3492 => ['MMMLDXLII', 'MMMXDII'],
3493 => ['MMMLDXLIII', 'MMMXDIII'],
3494 => ['MMMLDXLIV', 'MMMXDIV'],
3495 => ['MMMLDVL', 'MMMXDV', 'MMMVD'],
3496 => ['MMMLDVLI', 'MMMXDVI', 'MMMVDI'],
3497 => ['MMMLDVLII', 'MMMXDVII', 'MMMVDII'],
3498 => ['MMMLDVLIII', 'MMMXDVIII', 'MMMVDIII'],
3499 => ['MMMLDVLIV', 'MMMXDIX', 'MMMVDIV', 'MMMID'],
3545 => ['MMMDVL'],
3546 => ['MMMDVLI'],
3547 => ['MMMDVLII'],
3548 => ['MMMDVLIII'],
3549 => ['MMMDVLIV', 'MMMDIL'],
3595 => ['MMMDVC'],
3596 => ['MMMDVCI'],
3597 => ['MMMDVCII'],
3598 => ['MMMDVCIII'],
3599 => ['MMMDVCIV', 'MMMDIC'],
3645 => ['MMMDCVL'],
3646 => ['MMMDCVLI'],
3647 => ['MMMDCVLII'],
3648 => ['MMMDCVLIII'],
3649 => ['MMMDCVLIV', 'MMMDCIL'],
3695 => ['MMMDCVC'],
3696 => ['MMMDCVCI'],
3697 => ['MMMDCVCII'],
3698 => ['MMMDCVCIII'],
3699 => ['MMMDCVCIV', 'MMMDCIC'],
3745 => ['MMMDCCVL'],
3746 => ['MMMDCCVLI'],
3747 => ['MMMDCCVLII'],
3748 => ['MMMDCCVLIII'],
3749 => ['MMMDCCVLIV', 'MMMDCCIL'],
3795 => ['MMMDCCVC'],
3796 => ['MMMDCCVCI'],
3797 => ['MMMDCCVCII'],
3798 => ['MMMDCCVCIII'],
3799 => ['MMMDCCVCIV', 'MMMDCCIC'],
3845 => ['MMMDCCCVL'],
3846 => ['MMMDCCCVLI'],
3847 => ['MMMDCCCVLII'],
3848 => ['MMMDCCCVLIII'],
3849 => ['MMMDCCCVLIV', 'MMMDCCCIL'],
3895 => ['MMMDCCCVC'],
3896 => ['MMMDCCCVCI'],
3897 => ['MMMDCCCVCII'],
3898 => ['MMMDCCCVCIII'],
3899 => ['MMMDCCCVCIV', 'MMMDCCCIC'],
3945 => ['MMMCMVL'],
3946 => ['MMMCMVLI'],
3947 => ['MMMCMVLII'],
3948 => ['MMMCMVLIII'],
3949 => ['MMMCMVLIV', 'MMMCMIL'],
3950 => ['MMMLM'],
3951 => ['MMMLMI'],
3952 => ['MMMLMII'],
3953 => ['MMMLMIII'],
3954 => ['MMMLMIV'],
3955 => ['MMMLMV'],
3956 => ['MMMLMVI'],
3957 => ['MMMLMVII'],
3958 => ['MMMLMVIII'],
3959 => ['MMMLMIX'],
3960 => ['MMMLMX'],
3961 => ['MMMLMXI'],
3962 => ['MMMLMXII'],
3963 => ['MMMLMXIII'],
3964 => ['MMMLMXIV'],
3965 => ['MMMLMXV'],
3966 => ['MMMLMXVI'],
3967 => ['MMMLMXVII'],
3968 => ['MMMLMXVIII'],
3969 => ['MMMLMXIX'],
3970 => ['MMMLMXX'],
3971 => ['MMMLMXXI'],
3972 => ['MMMLMXXII'],
3973 => ['MMMLMXXIII'],
3974 => ['MMMLMXXIV'],
3975 => ['MMMLMXXV'],
3976 => ['MMMLMXXVI'],
3977 => ['MMMLMXXVII'],
3978 => ['MMMLMXXVIII'],
3979 => ['MMMLMXXIX'],
3980 => ['MMMLMXXX'],
3981 => ['MMMLMXXXI'],
3982 => ['MMMLMXXXII'],
3983 => ['MMMLMXXXIII'],
3984 => ['MMMLMXXXIV'],
3985 => ['MMMLMXXXV'],
3986 => ['MMMLMXXXVI'],
3987 => ['MMMLMXXXVII'],
3988 => ['MMMLMXXXVIII'],
3989 => ['MMMLMXXXIX'],
3990 => ['MMMLMXL', 'MMMXM'],
3991 => ['MMMLMXLI', 'MMMXMI'],
3992 => ['MMMLMXLII', 'MMMXMII'],
3993 => ['MMMLMXLIII', 'MMMXMIII'],
3994 => ['MMMLMXLIV', 'MMMXMIV'],
3995 => ['MMMLMVL', 'MMMXMV', 'MMMVM'],
3996 => ['MMMLMVLI', 'MMMXMVI', 'MMMVMI'],
3997 => ['MMMLMVLII', 'MMMXMVII', 'MMMVMII'],
3998 => ['MMMLMVLIII', 'MMMXMVIII', 'MMMVMIII'],
3999 => ['MMMLMVLIV', 'MMMXMIX', 'MMMVMIV', 'MMMIM'],
];
private const THOUSANDS = ['', 'M', 'MM', 'MMM'];
private const HUNDREDS = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
private const TENS = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
private const ONES = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
const MAX_ROMAN_VALUE = 3999;
const MAX_ROMAN_STYLE = 4;
private static function valueOk(int $aValue, int $style): string
{
$origValue = $aValue;
$m = \intdiv($aValue, 1000);
$aValue %= 1000;
$c = \intdiv($aValue, 100);
$aValue %= 100;
$t = \intdiv($aValue, 10);
$aValue %= 10;
$result = self::THOUSANDS[$m] . self::HUNDREDS[$c] . self::TENS[$t] . self::ONES[$aValue];
if ($style > 0) {
if (array_key_exists($origValue, self::VALUES)) {
$arr = self::VALUES[$origValue];
$idx = min($style, count($arr)) - 1;
$result = $arr[$idx];
}
}
return $result;
}
private static function styleOk(int $aValue, int $style): string
{
return ($aValue < 0 || $aValue > self::MAX_ROMAN_VALUE) ? ExcelError::VALUE() : self::valueOk($aValue, $style);
}
public static function calculateRoman(int $aValue, int $style): string
{
return ($style < 0 || $style > self::MAX_ROMAN_STYLE) ? ExcelError::VALUE() : self::styleOk($aValue, $style);
}
/**
* ROMAN.
*
* Converts a number to Roman numeral
*
* @param mixed $aValue Number to convert
* Or can be an array of numbers
* @param mixed $style Number indicating one of five possible forms
* Or can be an array of styles
*
* @return array|string Roman numeral, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function evaluate($aValue, $style = 0)
{
if (is_array($aValue) || is_array($style)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $aValue, $style);
}
try {
$aValue = Helpers::validateNumericNullBool($aValue);
if (is_bool($style)) {
$style = $style ? 0 : 4;
}
$style = Helpers::validateNumericNullSubstitution($style, null);
} catch (Exception $e) {
return $e->getMessage();
}
return self::calculateRoman((int) $aValue, (int) $style);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Absolute.php 0000644 00000001754 15242753725 0021560 0 ustar 00 getMessage();
}
return abs($number);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php 0000644 00000002145 15242753725 0020675 0 ustar 00 getMessage();
}
return Helpers::returnSign($number);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Floor.php 0000644 00000015050 15242753725 0021055 0 ustar 00 getMessage();
}
return self::argumentsOk((float) $number, (float) $significance);
}
/**
* FLOOR.MATH.
*
* Round a number down to the nearest integer or to the nearest multiple of significance.
*
* Excel Function:
* FLOOR.MATH(number[,significance[,mode]])
*
* @param mixed $number Number to round
* Or can be an array of values
* @param mixed $significance Significance
* Or can be an array of values
* @param mixed $mode direction to round negative numbers
* Or can be an array of values
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function math($number, $significance = null, $mode = 0)
{
if (is_array($number) || is_array($significance) || is_array($mode)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance, $mode);
}
try {
$number = Helpers::validateNumericNullBool($number);
$significance = Helpers::validateNumericNullSubstitution($significance, ($number < 0) ? -1 : 1);
$mode = Helpers::validateNumericNullSubstitution($mode, null);
} catch (Exception $e) {
return $e->getMessage();
}
return self::argsOk((float) $number, (float) $significance, (int) $mode);
}
/**
* FLOOR.PRECISE.
*
* Rounds number down, toward zero, to the nearest multiple of significance.
*
* Excel Function:
* FLOOR.PRECISE(number[,significance])
*
* @param array|float $number Number to round
* Or can be an array of values
* @param array|float $significance Significance
* Or can be an array of values
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function precise($number, $significance = 1)
{
if (is_array($number) || is_array($significance)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $significance);
}
try {
$number = Helpers::validateNumericNullBool($number);
$significance = Helpers::validateNumericNullSubstitution($significance, null);
} catch (Exception $e) {
return $e->getMessage();
}
return self::argumentsOkPrecise((float) $number, (float) $significance);
}
/**
* Avoid Scrutinizer problems concerning complexity.
*
* @return float|string
*/
private static function argumentsOkPrecise(float $number, float $significance)
{
if ($significance == 0.0) {
return ExcelError::DIV0();
}
if ($number == 0.0) {
return 0.0;
}
return floor($number / abs($significance)) * abs($significance);
}
/**
* Avoid Scrutinizer complexity problems.
*
* @return float|string Rounded Number, or a string containing an error
*/
private static function argsOk(float $number, float $significance, int $mode)
{
if (!$significance) {
return ExcelError::DIV0();
}
if (!$number) {
return 0.0;
}
if (self::floorMathTest($number, $significance, $mode)) {
return ceil($number / $significance) * $significance;
}
return floor($number / $significance) * $significance;
}
/**
* Let FLOORMATH complexity pass Scrutinizer.
*/
private static function floorMathTest(float $number, float $significance, int $mode): bool
{
return Helpers::returnSign($significance) == -1 || (Helpers::returnSign($number) == -1 && !empty($mode));
}
/**
* Avoid Scrutinizer problems concerning complexity.
*
* @return float|string
*/
private static function argumentsOk(float $number, float $significance)
{
if ($significance == 0.0) {
return ExcelError::DIV0();
}
if ($number == 0.0) {
return 0.0;
}
if (Helpers::returnSign($significance) == 1) {
return floor($number / $significance) * $significance;
}
if (Helpers::returnSign($number) == -1 && Helpers::returnSign($significance) == -1) {
return floor($number / $significance) * $significance;
}
return ExcelError::NAN();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Factorial.php 0000644 00000007351 15242753725 0021705 0 ustar 00 getMessage();
}
$factLoop = floor($factVal);
if ($factVal > $factLoop) {
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
return Statistical\Distributions\Gamma::gammaValue($factVal + 1);
}
}
$factorial = 1;
while ($factLoop > 1) {
$factorial *= $factLoop--;
}
return $factorial;
}
/**
* FACTDOUBLE.
*
* Returns the double factorial of a number.
*
* Excel Function:
* FACTDOUBLE(factVal)
*
* @param array|float $factVal Factorial Value, or can be an array of numbers
*
* @return array|float|int|string Double Factorial, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function factDouble($factVal)
{
if (is_array($factVal)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $factVal);
}
try {
$factVal = Helpers::validateNumericNullSubstitution($factVal, 0);
Helpers::validateNotNegative($factVal);
} catch (Exception $e) {
return $e->getMessage();
}
$factLoop = floor($factVal);
$factorial = 1;
while ($factLoop > 1) {
$factorial *= $factLoop;
$factLoop -= 2;
}
return $factorial;
}
/**
* MULTINOMIAL.
*
* Returns the ratio of the factorial of a sum of values to the product of factorials.
*
* @param mixed[] $args An array of mixed values for the Data Series
*
* @return float|string The result, or a string containing an error
*/
public static function multinomial(...$args)
{
$summer = 0;
$divisor = 1;
try {
// Loop through arguments
foreach (Functions::flattenArray($args) as $argx) {
$arg = Helpers::validateNumericNullSubstitution($argx, null);
Helpers::validateNotNegative($arg);
$arg = (int) $arg;
$summer += $arg;
$divisor *= self::fact($arg);
}
} catch (Exception $e) {
return $e->getMessage();
}
$summer = self::fact($summer);
return is_numeric($summer) ? ($summer / $divisor) : ExcelError::VALUE();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Lcm.php 0000644 00000007122 15242753725 0020510 0 ustar 00 1; --$i) {
if (($value % $i) == 0) {
$factorArray = array_merge($factorArray, self::factors($value / $i));
$factorArray = array_merge($factorArray, self::factors($i));
if ($i <= sqrt($value)) {
break;
}
}
}
if (!empty($factorArray)) {
rsort($factorArray);
return $factorArray;
}
return [(int) $value];
}
/**
* LCM.
*
* Returns the lowest common multiplier of a series of numbers
* The least common multiple is the smallest positive integer that is a multiple
* of all integer arguments number1, number2, and so on. Use LCM to add fractions
* with different denominators.
*
* Excel Function:
* LCM(number1[,number2[, ...]])
*
* @param mixed ...$args Data values
*
* @return int|string Lowest Common Multiplier, or a string containing an error
*/
public static function evaluate(...$args)
{
try {
$arrayArgs = [];
$anyZeros = 0;
$anyNonNulls = 0;
foreach (Functions::flattenArray($args) as $value1) {
$anyNonNulls += (int) ($value1 !== null);
$value = Helpers::validateNumericNullSubstitution($value1, 1);
Helpers::validateNotNegative($value);
$arrayArgs[] = (int) $value;
$anyZeros += (int) !((bool) $value);
}
self::testNonNulls($anyNonNulls);
if ($anyZeros) {
return 0;
}
} catch (Exception $e) {
return $e->getMessage();
}
$returnValue = 1;
$allPoweredFactors = [];
// Loop through arguments
foreach ($arrayArgs as $value) {
$myFactors = self::factors(floor($value));
$myCountedFactors = array_count_values($myFactors);
$myPoweredFactors = [];
foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
$myPoweredFactors[$myCountedFactor] = $myCountedFactor ** $myCountedPower;
}
self::processPoweredFactors($allPoweredFactors, $myPoweredFactors);
}
foreach ($allPoweredFactors as $allPoweredFactor) {
$returnValue *= (int) $allPoweredFactor;
}
return $returnValue;
}
private static function processPoweredFactors(array &$allPoweredFactors, array &$myPoweredFactors): void
{
foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
if (isset($allPoweredFactors[$myPoweredValue])) {
if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
$allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
}
} else {
$allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
}
}
}
private static function testNonNulls(int $anyNonNulls): void
{
if (!$anyNonNulls) {
throw new Exception(ExcelError::VALUE());
}
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Gcd.php 0000644 00000003754 15242753725 0020501 0 ustar 00 getMessage();
}
if (count($arrayArgs) <= 0) {
return ExcelError::VALUE();
}
$gcd = (int) array_pop($arrayArgs);
do {
$gcd = self::evaluateGCD($gcd, (int) array_pop($arrayArgs));
} while (!empty($arrayArgs));
return $gcd;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php 0000644 00000002323 15242753725 0021066 0 ustar 00 getMessage();
}
return mt_rand($min, $max);
}
/**
* RANDARRAY.
*
* Generates a list of sequential numbers in an array.
*
* Excel Function:
* RANDARRAY([rows],[columns],[start],[step])
*
* @param mixed $rows the number of rows to return, defaults to 1
* @param mixed $columns the number of columns to return, defaults to 1
* @param mixed $min the minimum number to be returned, defaults to 0
* @param mixed $max the maximum number to be returned, defaults to 1
* @param bool $wholeNumber the type of numbers to return:
* False - Decimal numbers to 15 decimal places. (default)
* True - Whole (integer) numbers
*
* @return array|string The resulting array, or a string containing an error
*/
public static function randArray($rows = 1, $columns = 1, $min = 0, $max = 1, $wholeNumber = false)
{
try {
$rows = (int) Helpers::validateNumericNullSubstitution($rows, 1);
Helpers::validatePositive($rows);
$columns = (int) Helpers::validateNumericNullSubstitution($columns, 1);
Helpers::validatePositive($columns);
$min = Helpers::validateNumericNullSubstitution($min, 1);
$max = Helpers::validateNumericNullSubstitution($max, 1);
if ($max <= $min) {
return ExcelError::VALUE();
}
} catch (Exception $e) {
return $e->getMessage();
}
return array_chunk(
array_map(
function () use ($min, $max, $wholeNumber) {
return $wholeNumber
? mt_rand((int) $min, (int) $max)
: (mt_rand() / mt_getrandmax()) * ($max - $min) + $min;
},
array_fill(0, $rows * $columns, $min)
),
max($columns, 1)
);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php 0000644 00000005641 15242753725 0021162 0 ustar 00 1000,
'D' => 500,
'C' => 100,
'L' => 50,
'X' => 10,
'V' => 5,
'I' => 1,
];
/**
* Recursively calculate the arabic value of a roman numeral.
*
* @param int $sum
* @param int $subtract
*
* @return int
*/
private static function calculateArabic(array $roman, &$sum = 0, $subtract = 0)
{
$numeral = array_shift($roman);
if (!isset(self::ROMAN_LOOKUP[$numeral])) {
throw new Exception('Invalid character detected');
}
$arabic = self::ROMAN_LOOKUP[$numeral];
if (count($roman) > 0 && isset(self::ROMAN_LOOKUP[$roman[0]]) && $arabic < self::ROMAN_LOOKUP[$roman[0]]) {
$subtract += $arabic;
} else {
$sum += ($arabic - $subtract);
$subtract = 0;
}
if (count($roman) > 0) {
self::calculateArabic($roman, $sum, $subtract);
}
return $sum;
}
/**
* @param mixed $value
*/
private static function mollifyScrutinizer($value): array
{
return is_array($value) ? $value : [];
}
private static function strSplit(string $roman): array
{
$rslt = str_split($roman);
return self::mollifyScrutinizer($rslt);
}
/**
* ARABIC.
*
* Converts a Roman numeral to an Arabic numeral.
*
* Excel Function:
* ARABIC(text)
*
* @param mixed $roman Should be a string, or can be an array of strings
*
* @return array|int|string the arabic numberal contrived from the roman numeral
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function evaluate($roman)
{
if (is_array($roman)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $roman);
}
// An empty string should return 0
$roman = substr(trim(strtoupper((string) $roman)), 0, 255);
if ($roman === '') {
return 0;
}
// Convert the roman numeral to an arabic number
$negativeNumber = $roman[0] === '-';
if ($negativeNumber) {
$roman = substr($roman, 1);
}
try {
$arabic = self::calculateArabic(self::strSplit($roman));
} catch (Exception $e) {
return ExcelError::VALUE(); // Invalid character detected
}
if ($negativeNumber) {
$arabic *= -1; // The number should be negative
}
return $arabic;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Angle.php 0000644 00000003421 15242753725 0021021 0 ustar 00 getMessage();
}
return rad2deg($number);
}
/**
* RADIANS.
*
* Returns the result of builtin function deg2rad after validating args.
*
* @param mixed $number Should be numeric, or can be an array of numbers
*
* @return array|float|string Rounded number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toRadians($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return deg2rad($number);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php 0000644 00000006616 15242753725 0020550 0 ustar 00 $arg) {
// Is it a numeric value?
if (is_numeric($arg) || empty($arg)) {
if (is_string($arg)) {
$arg = (int) $arg;
}
$returnValue += $arg;
} elseif (is_bool($arg)) {
$returnValue += (int) $arg;
} elseif (ErrorValue::isError($arg)) {
return $arg;
} elseif ($arg !== null && !Functions::isCellValue($k)) {
// ignore non-numerics from cell, but fail as literals (except null)
return ExcelError::VALUE();
}
}
return $returnValue;
}
/**
* SUMPRODUCT.
*
* Excel Function:
* SUMPRODUCT(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function product(...$args)
{
$arrayList = $args;
$wrkArray = Functions::flattenArray(array_shift($arrayList));
$wrkCellCount = count($wrkArray);
for ($i = 0; $i < $wrkCellCount; ++$i) {
if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
$wrkArray[$i] = 0;
}
}
foreach ($arrayList as $matrixData) {
$array2 = Functions::flattenArray($matrixData);
$count = count($array2);
if ($wrkCellCount != $count) {
return ExcelError::VALUE();
}
foreach ($array2 as $i => $val) {
if ((!is_numeric($val)) || (is_string($val))) {
$val = 0;
}
$wrkArray[$i] *= $val;
}
}
return array_sum($wrkArray);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php 0000644 00000005051 15242753725 0023230 0 ustar 00 255) {
return ExcelError::VALUE();
}
$result = iconv('UCS-4LE', 'UTF-8', pack('V', $character));
return ($result === false) ? '' : $result;
}
/**
* CODE.
*
* @param mixed $characters String character to convert to its ASCII value
* Or can be an array of values
*
* @return array|int|string A string if arguments are invalid
* If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function code($characters)
{
if (is_array($characters)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $characters);
}
$characters = Helpers::extractString($characters);
if ($characters === '') {
return ExcelError::VALUE();
}
$character = $characters;
if (mb_strlen($characters, 'UTF-8') > 1) {
$character = mb_substr($characters, 0, 1, 'UTF-8');
}
return self::unicodeToOrd($character);
}
private static function unicodeToOrd(string $character): int
{
$retVal = 0;
$iconv = iconv('UTF-8', 'UCS-4LE', $character);
if ($iconv !== false) {
$result = unpack('V', $iconv);
if (is_array($result) && isset($result[1])) {
$retVal = $result[1];
}
}
return $retVal;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Helpers.php 0000644 00000005031 15242753725 0021373 0 ustar 00 DataType::MAX_STRING_LENGTH) {
$returnValue = ExcelError::CALC();
break;
}
}
return $returnValue;
}
/**
* TEXTJOIN.
*
* @param mixed $delimiter The delimter to use between the joined arguments
* Or can be an array of values
* @param mixed $ignoreEmpty true/false Flag indicating whether empty arguments should be skipped
* Or can be an array of values
* @param mixed $args The values to join
*
* @return array|string The joined string
* If an array of values is passed for the $delimiter or $ignoreEmpty arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function TEXTJOIN($delimiter = '', $ignoreEmpty = true, ...$args)
{
if (is_array($delimiter) || is_array($ignoreEmpty)) {
return self::evaluateArrayArgumentsSubset(
[self::class, __FUNCTION__],
2,
$delimiter,
$ignoreEmpty,
...$args
);
}
$delimiter ??= '';
$ignoreEmpty ??= true;
$aArgs = Functions::flattenArray($args);
$returnValue = self::evaluateTextJoinArray($ignoreEmpty, $aArgs);
$returnValue ??= implode($delimiter, $aArgs);
if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
$returnValue = ExcelError::CALC();
}
return $returnValue;
}
private static function evaluateTextJoinArray(bool $ignoreEmpty, array &$aArgs): ?string
{
foreach ($aArgs as $key => &$arg) {
$value = Helpers::extractString($arg);
if (ErrorValue::isError($value)) {
return $value;
}
if ($ignoreEmpty === true && ((is_string($arg) && trim($arg) === '') || $arg === null)) {
unset($aArgs[$key]);
} elseif (is_bool($arg)) {
$arg = Helpers::convertBooleanValue($arg);
}
}
return null;
}
/**
* REPT.
*
* Returns the result of builtin function round after validating args.
*
* @param mixed $stringValue The value to repeat
* Or can be an array of values
* @param mixed $repeatCount The number of times the string value should be repeated
* Or can be an array of values
*
* @return array|string The repeated string
* If an array of values is passed for the $stringValue or $repeatCount arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function builtinREPT($stringValue, $repeatCount)
{
if (is_array($stringValue) || is_array($repeatCount)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $stringValue, $repeatCount);
}
$stringValue = Helpers::extractString($stringValue);
if (!is_numeric($repeatCount) || $repeatCount < 0) {
$returnValue = ExcelError::VALUE();
} elseif (ErrorValue::isError($stringValue)) {
$returnValue = $stringValue;
} else {
$returnValue = str_repeat($stringValue, (int) $repeatCount);
if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
$returnValue = ExcelError::VALUE(); // note VALUE not CALC
}
}
return $returnValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php 0000644 00000011463 15242753725 0021352 0 ustar 00 getMessage();
}
$returnValue = $left . $newText . $right;
if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
$returnValue = ExcelError::VALUE();
}
return $returnValue;
}
/**
* SUBSTITUTE.
*
* @param mixed $text The text string value to modify
* Or can be an array of values
* @param mixed $fromText The string value that we want to replace in $text
* Or can be an array of values
* @param mixed $toText The string value that we want to replace with in $text
* Or can be an array of values
* @param mixed $instance Integer instance Number for the occurrence of frmText to change
* Or can be an array of values
*
* @return array|string
* If an array of values is passed for either of the arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function substitute($text = '', $fromText = '', $toText = '', $instance = null)
{
if (is_array($text) || is_array($fromText) || is_array($toText) || is_array($instance)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $text, $fromText, $toText, $instance);
}
try {
$text = Helpers::extractString($text, true);
$fromText = Helpers::extractString($fromText, true);
$toText = Helpers::extractString($toText, true);
if ($instance === null) {
$returnValue = str_replace($fromText, $toText, $text);
} else {
if (is_bool($instance)) {
if ($instance === false || Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
return ExcelError::Value();
}
$instance = 1;
}
$instance = Helpers::extractInt($instance, 1, 0, true);
$returnValue = self::executeSubstitution($text, $fromText, $toText, $instance);
}
} catch (CalcExp $e) {
return $e->getMessage();
}
if (StringHelper::countCharacters($returnValue) > DataType::MAX_STRING_LENGTH) {
$returnValue = ExcelError::VALUE();
}
return $returnValue;
}
private static function executeSubstitution(string $text, string $fromText, string $toText, int $instance): string
{
$pos = -1;
while ($instance > 0) {
$pos = mb_strpos($text, $fromText, $pos + 1, 'UTF-8');
if ($pos === false) {
return $text;
}
--$instance;
}
return Functions::scalar(self::REPLACE($text, ++$pos, StringHelper::countCharacters($fromText), $toText));
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php 0000644 00000003036 15242753725 0020707 0 ustar 00 1) {
$quotedDelimiters = array_map(
function ($delimiter) {
return preg_quote($delimiter ?? '', '/');
},
$valueSet
);
$delimiters = implode('|', $quotedDelimiters);
return '(' . $delimiters . ')';
}
return '(' . preg_quote(/** @scrutinizer ignore-type */ Functions::flattenSingleValue($delimiter), '/') . ')';
}
private static function matchFlags(bool $matchMode): string
{
return ($matchMode === true) ? 'miu' : 'mu';
}
public static function fromArray(array $array, int $format = 0): string
{
$result = [];
foreach ($array as $row) {
$cells = [];
foreach ($row as $cellValue) {
$value = ($format === 1) ? self::formatValueMode1($cellValue) : self::formatValueMode0($cellValue);
$cells[] = $value;
}
$result[] = implode(($format === 1) ? ',' : ', ', $cells);
}
$result = implode(($format === 1) ? ';' : ', ', $result);
return ($format === 1) ? '{' . $result . '}' : $result;
}
/**
* @param mixed $cellValue
*/
private static function formatValueMode0($cellValue): string
{
if (is_bool($cellValue)) {
return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE');
}
return (string) $cellValue;
}
/**
* @param mixed $cellValue
*/
private static function formatValueMode1($cellValue): string
{
if (is_string($cellValue) && ErrorValue::isError($cellValue) === false) {
return Calculation::FORMULA_STRING_QUOTE . $cellValue . Calculation::FORMULA_STRING_QUOTE;
} elseif (is_bool($cellValue)) {
return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE');
}
return (string) $cellValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php 0000644 00000007055 15242753725 0021206 0 ustar 00 getMessage();
}
if (StringHelper::countCharacters($haystack) >= $offset) {
if (StringHelper::countCharacters($needle) === 0) {
return $offset;
}
$pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8');
if ($pos !== false) {
return ++$pos;
}
}
return ExcelError::VALUE();
}
/**
* SEARCH (case insensitive search).
*
* @param mixed $needle The string to look for
* Or can be an array of values
* @param mixed $haystack The string in which to look
* Or can be an array of values
* @param mixed $offset Integer offset within $haystack to start searching from
* Or can be an array of values
*
* @return array|int|string The offset where the first occurrence of needle was found in the haystack
* If an array of values is passed for the $value or $chars arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function insensitive($needle, $haystack, $offset = 1)
{
if (is_array($needle) || is_array($haystack) || is_array($offset)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $needle, $haystack, $offset);
}
try {
$needle = Helpers::extractString($needle);
$haystack = Helpers::extractString($haystack);
$offset = Helpers::extractInt($offset, 1, 0, true);
} catch (CalcExp $e) {
return $e->getMessage();
}
if (StringHelper::countCharacters($haystack) >= $offset) {
if (StringHelper::countCharacters($needle) === 0) {
return $offset;
}
$pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8');
if ($pos !== false) {
return ++$pos;
}
}
return ExcelError::VALUE();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php 0000644 00000004740 15242753725 0022213 0 ustar 00 getMessage();
}
return mb_substr($value, 0, $chars, 'UTF-8');
}
/**
* MID.
*
* @param mixed $value String value from which to extract characters
* Or can be an array of values
* @param mixed $start Integer offset of the first character that we want to extract
* Or can be an array of values
* @param mixed $chars The number of characters to extract (as an integer)
* Or can be an array of values
*
* @return array|string The joined string
* If an array of values is passed for the $value, $start or $chars arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function mid($value, $start, $chars)
{
if (is_array($value) || is_array($start) || is_array($chars)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $start, $chars);
}
try {
$value = Helpers::extractString($value);
$start = Helpers::extractInt($start, 1);
$chars = Helpers::extractInt($chars, 0);
} catch (CalcExp $e) {
return $e->getMessage();
}
return mb_substr($value, --$start, $chars, 'UTF-8');
}
/**
* RIGHT.
*
* @param mixed $value String value from which to extract characters
* Or can be an array of values
* @param mixed $chars The number of characters to extract (as an integer)
* Or can be an array of values
*
* @return array|string The joined string
* If an array of values is passed for the $value or $chars arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function right($value, $chars = 1)
{
if (is_array($value) || is_array($chars)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $chars);
}
try {
$value = Helpers::extractString($value);
$chars = Helpers::extractInt($chars, 0, 1);
} catch (CalcExp $e) {
return $e->getMessage();
}
return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
}
/**
* TEXTBEFORE.
*
* @param mixed $text the text that you're searching
* Or can be an array of values
* @param null|array|string $delimiter the text that marks the point before which you want to extract
* Multiple delimiters can be passed as an array of string values
* @param mixed $instance The instance of the delimiter after which you want to extract the text.
* By default, this is the first instance (1).
* A negative value means start searching from the end of the text string.
* Or can be an array of values
* @param mixed $matchMode Determines whether the match is case-sensitive or not.
* 0 - Case-sensitive
* 1 - Case-insensitive
* Or can be an array of values
* @param mixed $matchEnd Treats the end of text as a delimiter.
* 0 - Don't match the delimiter against the end of the text.
* 1 - Match the delimiter against the end of the text.
* Or can be an array of values
* @param mixed $ifNotFound value to return if no match is found
* The default is a #N/A Error
* Or can be an array of values
*
* @return mixed|mixed[] the string extracted from text before the delimiter; or the $ifNotFound value
* If an array of values is passed for any of the arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function before($text, $delimiter, $instance = 1, $matchMode = 0, $matchEnd = 0, $ifNotFound = '#N/A')
{
if (is_array($text) || is_array($instance) || is_array($matchMode) || is_array($matchEnd) || is_array($ifNotFound)) {
return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
}
$text = Helpers::extractString($text ?? '');
$instance = (int) $instance;
$matchMode = (int) $matchMode;
$matchEnd = (int) $matchEnd;
$split = self::validateTextBeforeAfter($text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
if (is_string($split)) {
return $split;
}
if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
return ($instance > 0) ? '' : $text;
}
// Adjustment for a match as the first element of the split
$flags = self::matchFlags($matchMode);
$delimiter = self::buildDelimiter($delimiter);
$adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]);
$oddReverseAdjustment = count($split) % 2;
$split = ($instance < 0)
? array_slice($split, 0, max(count($split) - (abs($instance) * 2 - 1) - $adjust - $oddReverseAdjustment, 0))
: array_slice($split, 0, $instance * 2 - 1 - $adjust);
return implode('', $split);
}
/**
* TEXTAFTER.
*
* @param mixed $text the text that you're searching
* @param null|array|string $delimiter the text that marks the point before which you want to extract
* Multiple delimiters can be passed as an array of string values
* @param mixed $instance The instance of the delimiter after which you want to extract the text.
* By default, this is the first instance (1).
* A negative value means start searching from the end of the text string.
* Or can be an array of values
* @param mixed $matchMode Determines whether the match is case-sensitive or not.
* 0 - Case-sensitive
* 1 - Case-insensitive
* Or can be an array of values
* @param mixed $matchEnd Treats the end of text as a delimiter.
* 0 - Don't match the delimiter against the end of the text.
* 1 - Match the delimiter against the end of the text.
* Or can be an array of values
* @param mixed $ifNotFound value to return if no match is found
* The default is a #N/A Error
* Or can be an array of values
*
* @return mixed|mixed[] the string extracted from text before the delimiter; or the $ifNotFound value
* If an array of values is passed for any of the arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function after($text, $delimiter, $instance = 1, $matchMode = 0, $matchEnd = 0, $ifNotFound = '#N/A')
{
if (is_array($text) || is_array($instance) || is_array($matchMode) || is_array($matchEnd) || is_array($ifNotFound)) {
return self::evaluateArrayArgumentsIgnore([self::class, __FUNCTION__], 1, $text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
}
$text = Helpers::extractString($text ?? '');
$instance = (int) $instance;
$matchMode = (int) $matchMode;
$matchEnd = (int) $matchEnd;
$split = self::validateTextBeforeAfter($text, $delimiter, $instance, $matchMode, $matchEnd, $ifNotFound);
if (is_string($split)) {
return $split;
}
if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
return ($instance < 0) ? '' : $text;
}
// Adjustment for a match as the first element of the split
$flags = self::matchFlags($matchMode);
$delimiter = self::buildDelimiter($delimiter);
$adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]);
$oddReverseAdjustment = count($split) % 2;
$split = ($instance < 0)
? array_slice($split, count($split) - ((int) abs($instance + 1) * 2) - $adjust - $oddReverseAdjustment)
: array_slice($split, $instance * 2 - $adjust);
return implode('', $split);
}
/**
* @param null|array|string $delimiter
* @param int $matchMode
* @param int $matchEnd
* @param mixed $ifNotFound
*
* @return array|string
*/
private static function validateTextBeforeAfter(string $text, $delimiter, int $instance, $matchMode, $matchEnd, $ifNotFound)
{
$flags = self::matchFlags($matchMode);
$delimiter = self::buildDelimiter($delimiter);
if (preg_match('/' . $delimiter . "/{$flags}", $text) === 0 && $matchEnd === 0) {
return $ifNotFound;
}
$split = preg_split('/' . $delimiter . "/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
if ($split === false) {
return ExcelError::NA();
}
if ($instance === 0 || abs($instance) > StringHelper::countCharacters($text)) {
return ExcelError::VALUE();
}
if ($matchEnd === 0 && (abs($instance) > floor(count($split) / 2))) {
return ExcelError::NA();
} elseif ($matchEnd !== 0 && (abs($instance) - 1 > ceil(count($split) / 2))) {
return ExcelError::NA();
}
return $split;
}
/**
* @param null|array|string $delimiter the text that marks the point before which you want to extract
* Multiple delimiters can be passed as an array of string values
*/
private static function buildDelimiter($delimiter): string
{
if (is_array($delimiter)) {
$delimiter = Functions::flattenArray($delimiter);
$quotedDelimiters = array_map(
function ($delimiter) {
return preg_quote($delimiter ?? '', '/');
},
$delimiter
);
$delimiters = implode('|', $quotedDelimiters);
return '(' . $delimiters . ')';
}
return '(' . preg_quote($delimiter ?? '', '/') . ')';
}
private static function matchFlags(int $matchMode): string
{
return ($matchMode === 0) ? 'mu' : 'miu';
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Format.php 0000644 00000026770 15242753725 0021236 0 ustar 00 getMessage();
}
$mask = '$#,##0';
if ($decimals > 0) {
$mask .= '.' . str_repeat('0', $decimals);
} else {
$round = 10 ** abs($decimals);
if ($value < 0) {
$round = 0 - $round;
}
$value = MathTrig\Round::multiple($value, $round);
}
$mask = "{$mask};-{$mask}";
return NumberFormat::toFormattedString($value, $mask);
}
/**
* FIXED.
*
* @param mixed $value The value to format
* Or can be an array of values
* @param mixed $decimals Integer value for the number of decimal places that should be formatted
* Or can be an array of values
* @param mixed $noCommas Boolean value indicating whether the value should have thousands separators or not
* Or can be an array of values
*
* @return array|string
* If an array of values is passed for either of the arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function FIXEDFORMAT($value, $decimals = 2, $noCommas = false)
{
if (is_array($value) || is_array($decimals) || is_array($noCommas)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimals, $noCommas);
}
try {
$value = Helpers::extractFloat($value);
$decimals = Helpers::extractInt($decimals, -100, 0, true);
} catch (CalcExp $e) {
return $e->getMessage();
}
$valueResult = round($value, $decimals);
if ($decimals < 0) {
$decimals = 0;
}
if ($noCommas === false) {
$valueResult = number_format(
$valueResult,
$decimals,
StringHelper::getDecimalSeparator(),
StringHelper::getThousandsSeparator()
);
}
return (string) $valueResult;
}
/**
* TEXT.
*
* @param mixed $value The value to format
* Or can be an array of values
* @param mixed $format A string with the Format mask that should be used
* Or can be an array of values
*
* @return array|string
* If an array of values is passed for either of the arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function TEXTFORMAT($value, $format)
{
if (is_array($value) || is_array($format)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
}
$value = Helpers::extractString($value);
$format = Helpers::extractString($format);
if (!is_numeric($value) && Date::isDateTimeFormatCode($format)) {
$value = DateTimeExcel\DateValue::fromString($value) + DateTimeExcel\TimeValue::fromString($value);
}
return (string) NumberFormat::toFormattedString($value, $format);
}
/**
* @param mixed $value Value to check
*
* @return mixed
*/
private static function convertValue($value, bool $spacesMeanZero = false)
{
$value = $value ?? 0;
if (is_bool($value)) {
if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
$value = (int) $value;
} else {
throw new CalcExp(ExcelError::VALUE());
}
}
if (is_string($value)) {
$value = trim($value);
if ($spacesMeanZero && $value === '') {
$value = 0;
}
}
return $value;
}
/**
* VALUE.
*
* @param mixed $value Value to check
* Or can be an array of values
*
* @return array|DateTimeInterface|float|int|string A string if arguments are invalid
* If an array of values is passed for the argument, then the returned result
* will also be an array with matching dimensions
*/
public static function VALUE($value = '')
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
try {
$value = self::convertValue($value);
} catch (CalcExp $e) {
return $e->getMessage();
}
if (!is_numeric($value)) {
$numberValue = str_replace(
StringHelper::getThousandsSeparator(),
'',
trim($value, " \t\n\r\0\x0B" . StringHelper::getCurrencyCode())
);
if ($numberValue === '') {
return ExcelError::VALUE();
}
if (is_numeric($numberValue)) {
return (float) $numberValue;
}
$dateSetting = Functions::getReturnDateType();
Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
if (strpos($value, ':') !== false) {
$timeValue = Functions::scalar(DateTimeExcel\TimeValue::fromString($value));
if ($timeValue !== ExcelError::VALUE()) {
Functions::setReturnDateType($dateSetting);
return $timeValue;
}
}
$dateValue = Functions::scalar(DateTimeExcel\DateValue::fromString($value));
if ($dateValue !== ExcelError::VALUE()) {
Functions::setReturnDateType($dateSetting);
return $dateValue;
}
Functions::setReturnDateType($dateSetting);
return ExcelError::VALUE();
}
return (float) $value;
}
/**
* TEXT.
*
* @param mixed $value The value to format
* Or can be an array of values
* @param mixed $format
*
* @return array|string
* If an array of values is passed for either of the arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function valueToText($value, $format = false)
{
if (is_array($value) || is_array($format)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $format);
}
$format = (bool) $format;
if (is_object($value) && $value instanceof RichText) {
$value = $value->getPlainText();
}
if (is_string($value)) {
$value = ($format === true) ? Calculation::wrapResult($value) : $value;
$value = str_replace("\n", '', $value);
} elseif (is_bool($value)) {
$value = Calculation::getLocaleBoolean($value ? 'TRUE' : 'FALSE');
}
return (string) $value;
}
/**
* @param mixed $decimalSeparator
*/
private static function getDecimalSeparator($decimalSeparator): string
{
return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : (string) $decimalSeparator;
}
/**
* @param mixed $groupSeparator
*/
private static function getGroupSeparator($groupSeparator): string
{
return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : (string) $groupSeparator;
}
/**
* NUMBERVALUE.
*
* @param mixed $value The value to format
* Or can be an array of values
* @param mixed $decimalSeparator A string with the decimal separator to use, defaults to locale defined value
* Or can be an array of values
* @param mixed $groupSeparator A string with the group/thousands separator to use, defaults to locale defined value
* Or can be an array of values
*
* @return array|float|string
*/
public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
{
if (is_array($value) || is_array($decimalSeparator) || is_array($groupSeparator)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $decimalSeparator, $groupSeparator);
}
try {
$value = self::convertValue($value, true);
$decimalSeparator = self::getDecimalSeparator($decimalSeparator);
$groupSeparator = self::getGroupSeparator($groupSeparator);
} catch (CalcExp $e) {
return $e->getMessage();
}
if (!is_numeric($value)) {
$decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator, '/') . '/', $value, $matches, PREG_OFFSET_CAPTURE);
if ($decimalPositions > 1) {
return ExcelError::VALUE();
}
$decimalOffset = array_pop($matches[0])[1] ?? null;
if ($decimalOffset === null || strpos($value, $groupSeparator, $decimalOffset) !== false) {
return ExcelError::VALUE();
}
$value = str_replace([$groupSeparator, $decimalSeparator], ['', '.'], $value);
// Handle the special case of trailing % signs
$percentageString = rtrim($value, '%');
if (!is_numeric($percentageString)) {
return ExcelError::VALUE();
}
$percentageAdjustment = strlen($value) - strlen($percentageString);
if ($percentageAdjustment) {
$value = (float) $percentageString;
$value /= 10 ** ($percentageAdjustment * 2);
}
}
return is_array($value) ? ExcelError::VALUE() : (float) $value;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Constants.php 0000644 00000000247 15242753725 0022465 0 ustar 00 getMessage();
}
if (($ord < 0) || ($x <= 0.0)) {
return ExcelError::NAN();
}
$fBy = self::calculate($x, $ord);
return (is_nan($fBy)) ? ExcelError::NAN() : $fBy;
}
private static function calculate(float $x, int $ord): float
{
// special cases
switch ($ord) {
case 0:
return self::besselY0($x);
case 1:
return self::besselY1($x);
}
return self::besselY2($x, $ord);
}
/**
* Mollify Phpstan.
*
* @codeCoverageIgnore
*/
private static function callBesselJ(float $x, int $ord): float
{
$rslt = BesselJ::BESSELJ($x, $ord);
if (!is_float($rslt)) {
throw new Exception('Unexpected array or string');
}
return $rslt;
}
private static function besselY0(float $x): float
{
if ($x < 8.0) {
$y = ($x * $x);
$ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y *
(-86327.92757 + $y * 228.4622733))));
$ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y *
(47447.26470 + $y * (226.1030244 + $y))));
return $ans1 / $ans2 + 0.636619772 * self::callBesselJ($x, 0) * log($x);
}
$z = 8.0 / $x;
$y = ($z * $z);
$xx = $x - 0.785398164;
$ans1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
$ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y *
(-0.934945152e-7))));
return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
}
private static function besselY1(float $x): float
{
if ($x < 8.0) {
$y = ($x * $x);
$ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y *
(0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
$ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
(0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
return ($ans1 / $ans2) + 0.636619772 * (self::callBesselJ($x, 1) * log($x) - 1 / $x);
}
$z = 8.0 / $x;
$y = $z * $z;
$xx = $x - 2.356194491;
$ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
(-0.88228987e-6 + $y * 0.105787412e-6)));
return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
}
private static function besselY2(float $x, int $ord): float
{
$fTox = 2.0 / $x;
$fBym = self::besselY0($x);
$fBy = self::besselY1($x);
for ($n = 1; $n < $ord; ++$n) {
$fByp = $n * $fTox * $fBy - $fBym;
$fBym = $fBy;
$fBy = $fByp;
}
return $fBy;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php 0000644 00000105716 15242753725 0022521 0 ustar 00 ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Gram', 'AllowPrefix' => true],
'sg' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Slug', 'AllowPrefix' => false],
'lbm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
'u' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
'ozm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
'grain' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Grain', 'AllowPrefix' => false],
'cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
'shweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
'uk_cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
'lcwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
'hweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
'stone' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Stone', 'AllowPrefix' => false],
'ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ton', 'AllowPrefix' => false],
'uk_ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
'LTON' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
'brton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
// Distance
'm' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Meter', 'AllowPrefix' => true],
'mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
'Nmi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
'in' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Inch', 'AllowPrefix' => false],
'ft' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Foot', 'AllowPrefix' => false],
'yd' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Yard', 'AllowPrefix' => false],
'ang' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
'ell' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Ell', 'AllowPrefix' => false],
'ly' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Light Year', 'AllowPrefix' => false],
'parsec' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
'pc' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
'Pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
'Picapt' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
'pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/6 in)', 'AllowPrefix' => false],
'survey_mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'U.S survey mile (statute mile)', 'AllowPrefix' => false],
// Time
'yr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Year', 'AllowPrefix' => false],
'day' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
'd' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
'hr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Hour', 'AllowPrefix' => false],
'mn' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
'min' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
'sec' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
's' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
// Pressure
'Pa' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
'p' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
'atm' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
'at' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
'mmHg' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
'psi' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'PSI', 'AllowPrefix' => true],
'Torr' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Torr', 'AllowPrefix' => true],
// Force
'N' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Newton', 'AllowPrefix' => true],
'dyn' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
'dy' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
'lbf' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
'pond' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pond', 'AllowPrefix' => true],
// Energy
'J' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Joule', 'AllowPrefix' => true],
'e' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Erg', 'AllowPrefix' => true],
'c' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
'cal' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
'eV' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
'ev' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
'HPh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
'hh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
'Wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
'wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
'flb' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
'BTU' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
'btu' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
// Power
'HP' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
'h' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
'W' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
'w' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
'PS' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Pferdestärke', 'AllowPrefix' => false],
// Magnetism
'T' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
'ga' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
// Temperature
'C' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
'cel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
'F' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
'fah' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
'K' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
'kel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
'Rank' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Rankine', 'AllowPrefix' => false],
'Reau' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Réaumur', 'AllowPrefix' => false],
// Volume
'l' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
'L' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
'lt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
'tsp' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
'tspm' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Modern Teaspoon', 'AllowPrefix' => false],
'tbs' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
'oz' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
'cup' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cup', 'AllowPrefix' => false],
'pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
'us_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
'uk_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
'qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Quart', 'AllowPrefix' => false],
'uk_qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Quart (UK)', 'AllowPrefix' => false],
'gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
'uk_gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Gallon (UK)', 'AllowPrefix' => false],
'ang3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
'ang^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
'barrel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Oil Barrel', 'AllowPrefix' => false],
'bushel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Bushel', 'AllowPrefix' => false],
'in3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
'in^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
'ft3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
'ft^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
'ly3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
'ly^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
'm3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
'm^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
'mi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
'mi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
'yd3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
'yd^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
'Nmi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
'Nmi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
'Pica3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
'Pica^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
'Picapt3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
'Picapt^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
'GRT' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
'regton' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
'MTON' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Measurement Ton (Freight Ton)', 'AllowPrefix' => false],
// Area
'ha' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Hectare', 'AllowPrefix' => true],
'uk_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'International Acre', 'AllowPrefix' => false],
'us_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'US Survey/Statute Acre', 'AllowPrefix' => false],
'ang2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
'ang^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
'ar' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Are', 'AllowPrefix' => true],
'ft2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
'ft^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
'in2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
'in^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
'ly2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
'ly^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
'm2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
'm^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
'Morgen' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Morgen', 'AllowPrefix' => false],
'mi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
'mi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
'Nmi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
'Nmi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
'Pica2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
'Pica^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
'Picapt2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
'Picapt^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
'yd2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
'yd^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
// Information
'byte' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Byte', 'AllowPrefix' => true],
'bit' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Bit', 'AllowPrefix' => true],
// Speed
'm/s' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
'm/sec' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
'm/h' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
'm/hr' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
'mph' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Miles per hour', 'AllowPrefix' => false],
'admkn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Admiralty Knot', 'AllowPrefix' => false],
'kn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Knot', 'AllowPrefix' => false],
];
/**
* Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
*
* @var mixed[]
*/
private static $conversionMultipliers = [
'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
'E' => ['multiplier' => 1E18, 'name' => 'exa'],
'P' => ['multiplier' => 1E15, 'name' => 'peta'],
'T' => ['multiplier' => 1E12, 'name' => 'tera'],
'G' => ['multiplier' => 1E9, 'name' => 'giga'],
'M' => ['multiplier' => 1E6, 'name' => 'mega'],
'k' => ['multiplier' => 1E3, 'name' => 'kilo'],
'h' => ['multiplier' => 1E2, 'name' => 'hecto'],
'e' => ['multiplier' => 1E1, 'name' => 'dekao'],
'da' => ['multiplier' => 1E1, 'name' => 'dekao'],
'd' => ['multiplier' => 1E-1, 'name' => 'deci'],
'c' => ['multiplier' => 1E-2, 'name' => 'centi'],
'm' => ['multiplier' => 1E-3, 'name' => 'milli'],
'u' => ['multiplier' => 1E-6, 'name' => 'micro'],
'n' => ['multiplier' => 1E-9, 'name' => 'nano'],
'p' => ['multiplier' => 1E-12, 'name' => 'pico'],
'f' => ['multiplier' => 1E-15, 'name' => 'femto'],
'a' => ['multiplier' => 1E-18, 'name' => 'atto'],
'z' => ['multiplier' => 1E-21, 'name' => 'zepto'],
'y' => ['multiplier' => 1E-24, 'name' => 'yocto'],
];
/**
* Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
*
* @var mixed[]
*/
private static $binaryConversionMultipliers = [
'Yi' => ['multiplier' => 2 ** 80, 'name' => 'yobi'],
'Zi' => ['multiplier' => 2 ** 70, 'name' => 'zebi'],
'Ei' => ['multiplier' => 2 ** 60, 'name' => 'exbi'],
'Pi' => ['multiplier' => 2 ** 50, 'name' => 'pebi'],
'Ti' => ['multiplier' => 2 ** 40, 'name' => 'tebi'],
'Gi' => ['multiplier' => 2 ** 30, 'name' => 'gibi'],
'Mi' => ['multiplier' => 2 ** 20, 'name' => 'mebi'],
'ki' => ['multiplier' => 2 ** 10, 'name' => 'kibi'],
];
/**
* Details of the Units of measure conversion factors, organised by group.
*
* @var mixed[]
*/
private static $unitConversions = [
// Conversion uses gram (g) as an intermediate unit
self::CATEGORY_WEIGHT_AND_MASS => [
'g' => 1.0,
'sg' => 6.85217658567918E-05,
'lbm' => 2.20462262184878E-03,
'u' => 6.02214179421676E+23,
'ozm' => 3.52739619495804E-02,
'grain' => 1.54323583529414E+01,
'cwt' => 2.20462262184878E-05,
'shweight' => 2.20462262184878E-05,
'uk_cwt' => 1.96841305522212E-05,
'lcwt' => 1.96841305522212E-05,
'hweight' => 1.96841305522212E-05,
'stone' => 1.57473044417770E-04,
'ton' => 1.10231131092439E-06,
'uk_ton' => 9.84206527611061E-07,
'LTON' => 9.84206527611061E-07,
'brton' => 9.84206527611061E-07,
],
// Conversion uses meter (m) as an intermediate unit
self::CATEGORY_DISTANCE => [
'm' => 1.0,
'mi' => 6.21371192237334E-04,
'Nmi' => 5.39956803455724E-04,
'in' => 3.93700787401575E+01,
'ft' => 3.28083989501312E+00,
'yd' => 1.09361329833771E+00,
'ang' => 1.0E+10,
'ell' => 8.74890638670166E-01,
'ly' => 1.05700083402462E-16,
'parsec' => 3.24077928966473E-17,
'pc' => 3.24077928966473E-17,
'Pica' => 2.83464566929134E+03,
'Picapt' => 2.83464566929134E+03,
'pica' => 2.36220472440945E+02,
'survey_mi' => 6.21369949494950E-04,
],
// Conversion uses second (s) as an intermediate unit
self::CATEGORY_TIME => [
'yr' => 3.16880878140289E-08,
'day' => 1.15740740740741E-05,
'd' => 1.15740740740741E-05,
'hr' => 2.77777777777778E-04,
'mn' => 1.66666666666667E-02,
'min' => 1.66666666666667E-02,
'sec' => 1.0,
's' => 1.0,
],
// Conversion uses Pascal (Pa) as an intermediate unit
self::CATEGORY_PRESSURE => [
'Pa' => 1.0,
'p' => 1.0,
'atm' => 9.86923266716013E-06,
'at' => 9.86923266716013E-06,
'mmHg' => 7.50063755419211E-03,
'psi' => 1.45037737730209E-04,
'Torr' => 7.50061682704170E-03,
],
// Conversion uses Newton (N) as an intermediate unit
self::CATEGORY_FORCE => [
'N' => 1.0,
'dyn' => 1.0E+5,
'dy' => 1.0E+5,
'lbf' => 2.24808923655339E-01,
'pond' => 1.01971621297793E+02,
],
// Conversion uses Joule (J) as an intermediate unit
self::CATEGORY_ENERGY => [
'J' => 1.0,
'e' => 9.99999519343231E+06,
'c' => 2.39006249473467E-01,
'cal' => 2.38846190642017E-01,
'eV' => 6.24145700000000E+18,
'ev' => 6.24145700000000E+18,
'HPh' => 3.72506430801000E-07,
'hh' => 3.72506430801000E-07,
'Wh' => 2.77777916238711E-04,
'wh' => 2.77777916238711E-04,
'flb' => 2.37304222192651E+01,
'BTU' => 9.47815067349015E-04,
'btu' => 9.47815067349015E-04,
],
// Conversion uses Horsepower (HP) as an intermediate unit
self::CATEGORY_POWER => [
'HP' => 1.0,
'h' => 1.0,
'W' => 7.45699871582270E+02,
'w' => 7.45699871582270E+02,
'PS' => 1.01386966542400E+00,
],
// Conversion uses Tesla (T) as an intermediate unit
self::CATEGORY_MAGNETISM => [
'T' => 1.0,
'ga' => 10000.0,
],
// Conversion uses litre (l) as an intermediate unit
self::CATEGORY_VOLUME => [
'l' => 1.0,
'L' => 1.0,
'lt' => 1.0,
'tsp' => 2.02884136211058E+02,
'tspm' => 2.0E+02,
'tbs' => 6.76280454036860E+01,
'oz' => 3.38140227018430E+01,
'cup' => 4.22675283773038E+00,
'pt' => 2.11337641886519E+00,
'us_pt' => 2.11337641886519E+00,
'uk_pt' => 1.75975398639270E+00,
'qt' => 1.05668820943259E+00,
'uk_qt' => 8.79876993196351E-01,
'gal' => 2.64172052358148E-01,
'uk_gal' => 2.19969248299088E-01,
'ang3' => 1.0E+27,
'ang^3' => 1.0E+27,
'barrel' => 6.28981077043211E-03,
'bushel' => 2.83775932584017E-02,
'in3' => 6.10237440947323E+01,
'in^3' => 6.10237440947323E+01,
'ft3' => 3.53146667214886E-02,
'ft^3' => 3.53146667214886E-02,
'ly3' => 1.18093498844171E-51,
'ly^3' => 1.18093498844171E-51,
'm3' => 1.0E-03,
'm^3' => 1.0E-03,
'mi3' => 2.39912758578928E-13,
'mi^3' => 2.39912758578928E-13,
'yd3' => 1.30795061931439E-03,
'yd^3' => 1.30795061931439E-03,
'Nmi3' => 1.57426214685811E-13,
'Nmi^3' => 1.57426214685811E-13,
'Pica3' => 2.27769904358706E+07,
'Pica^3' => 2.27769904358706E+07,
'Picapt3' => 2.27769904358706E+07,
'Picapt^3' => 2.27769904358706E+07,
'GRT' => 3.53146667214886E-04,
'regton' => 3.53146667214886E-04,
'MTON' => 8.82866668037215E-04,
],
// Conversion uses hectare (ha) as an intermediate unit
self::CATEGORY_AREA => [
'ha' => 1.0,
'uk_acre' => 2.47105381467165E+00,
'us_acre' => 2.47104393046628E+00,
'ang2' => 1.0E+24,
'ang^2' => 1.0E+24,
'ar' => 1.0E+02,
'ft2' => 1.07639104167097E+05,
'ft^2' => 1.07639104167097E+05,
'in2' => 1.55000310000620E+07,
'in^2' => 1.55000310000620E+07,
'ly2' => 1.11725076312873E-28,
'ly^2' => 1.11725076312873E-28,
'm2' => 1.0E+04,
'm^2' => 1.0E+04,
'Morgen' => 4.0E+00,
'mi2' => 3.86102158542446E-03,
'mi^2' => 3.86102158542446E-03,
'Nmi2' => 2.91553349598123E-03,
'Nmi^2' => 2.91553349598123E-03,
'Pica2' => 8.03521607043214E+10,
'Pica^2' => 8.03521607043214E+10,
'Picapt2' => 8.03521607043214E+10,
'Picapt^2' => 8.03521607043214E+10,
'yd2' => 1.19599004630108E+04,
'yd^2' => 1.19599004630108E+04,
],
// Conversion uses bit (bit) as an intermediate unit
self::CATEGORY_INFORMATION => [
'bit' => 1.0,
'byte' => 0.125,
],
// Conversion uses Meters per Second (m/s) as an intermediate unit
self::CATEGORY_SPEED => [
'm/s' => 1.0,
'm/sec' => 1.0,
'm/h' => 3.60E+03,
'm/hr' => 3.60E+03,
'mph' => 2.23693629205440E+00,
'admkn' => 1.94260256941567E+00,
'kn' => 1.94384449244060E+00,
],
];
/**
* getConversionGroups
* Returns a list of the different conversion groups for UOM conversions.
*
* @return array
*/
public static function getConversionCategories()
{
$conversionGroups = [];
foreach (self::$conversionUnits as $conversionUnit) {
$conversionGroups[] = $conversionUnit['Group'];
}
return array_merge(array_unique($conversionGroups));
}
/**
* getConversionGroupUnits
* Returns an array of units of measure, for a specified conversion group, or for all groups.
*
* @param string $category The group whose units of measure you want to retrieve
*
* @return array
*/
public static function getConversionCategoryUnits($category = null)
{
$conversionGroups = [];
foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
if (($category === null) || ($conversionGroup['Group'] == $category)) {
$conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
}
}
return $conversionGroups;
}
/**
* getConversionGroupUnitDetails.
*
* @param string $category The group whose units of measure you want to retrieve
*
* @return array
*/
public static function getConversionCategoryUnitDetails($category = null)
{
$conversionGroups = [];
foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
if (($category === null) || ($conversionGroup['Group'] == $category)) {
$conversionGroups[$conversionGroup['Group']][] = [
'unit' => $conversionUnit,
'description' => $conversionGroup['Unit Name'],
];
}
}
return $conversionGroups;
}
/**
* getConversionMultipliers
* Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
*
* @return mixed[]
*/
public static function getConversionMultipliers()
{
return self::$conversionMultipliers;
}
/**
* getBinaryConversionMultipliers
* Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM().
*
* @return mixed[]
*/
public static function getBinaryConversionMultipliers()
{
return self::$binaryConversionMultipliers;
}
/**
* CONVERT.
*
* Converts a number from one measurement system to another.
* For example, CONVERT can translate a table of distances in miles to a table of distances
* in kilometers.
*
* Excel Function:
* CONVERT(value,fromUOM,toUOM)
*
* @param array|float|int|string $value the value in fromUOM to convert
* Or can be an array of values
* @param array|string $fromUOM the units for value
* Or can be an array of values
* @param array|string $toUOM the units for the result
* Or can be an array of values
*
* @return array|float|string Result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function CONVERT($value, $fromUOM, $toUOM)
{
if (is_array($value) || is_array($fromUOM) || is_array($toUOM)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $fromUOM, $toUOM);
}
if (!is_numeric($value)) {
return ExcelError::VALUE();
}
try {
[$fromUOM, $fromCategory, $fromMultiplier] = self::getUOMDetails($fromUOM);
[$toUOM, $toCategory, $toMultiplier] = self::getUOMDetails($toUOM);
} catch (Exception $e) {
return ExcelError::NA();
}
if ($fromCategory !== $toCategory) {
return ExcelError::NA();
}
// @var float $value
$value *= $fromMultiplier;
if (($fromUOM === $toUOM) && ($fromMultiplier === $toMultiplier)) {
// We've already factored $fromMultiplier into the value, so we need
// to reverse it again
return $value / $fromMultiplier;
} elseif ($fromUOM === $toUOM) {
return $value / $toMultiplier;
} elseif ($fromCategory === self::CATEGORY_TEMPERATURE) {
return self::convertTemperature($fromUOM, $toUOM, /** @scrutinizer ignore-type */ $value);
}
$baseValue = $value * (1.0 / self::$unitConversions[$fromCategory][$fromUOM]);
return ($baseValue * self::$unitConversions[$fromCategory][$toUOM]) / $toMultiplier;
}
private static function getUOMDetails(string $uom): array
{
if (isset(self::$conversionUnits[$uom])) {
$unitCategory = self::$conversionUnits[$uom]['Group'];
return [$uom, $unitCategory, 1.0];
}
// Check 1-character standard metric multiplier prefixes
$multiplierType = substr($uom, 0, 1);
$uom = substr($uom, 1);
if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) {
if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
throw new Exception('Prefix not allowed for UoM');
}
$unitCategory = self::$conversionUnits[$uom]['Group'];
return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']];
}
$multiplierType .= substr($uom, 0, 1);
$uom = substr($uom, 1);
// Check 2-character standard metric multiplier prefixes
if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) {
if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
throw new Exception('Prefix not allowed for UoM');
}
$unitCategory = self::$conversionUnits[$uom]['Group'];
return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']];
}
// Check 2-character binary multiplier prefixes
if (isset(self::$conversionUnits[$uom], self::$binaryConversionMultipliers[$multiplierType])) {
if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
throw new Exception('Prefix not allowed for UoM');
}
$unitCategory = self::$conversionUnits[$uom]['Group'];
if ($unitCategory !== 'Information') {
throw new Exception('Binary Prefix is only allowed for Information UoM');
}
return [$uom, $unitCategory, self::$binaryConversionMultipliers[$multiplierType]['multiplier']];
}
throw new Exception('UoM Not Found');
}
/**
* @param float|int $value
*
* @return float|int
*/
protected static function convertTemperature(string $fromUOM, string $toUOM, $value)
{
$fromUOM = self::resolveTemperatureSynonyms($fromUOM);
$toUOM = self::resolveTemperatureSynonyms($toUOM);
if ($fromUOM === $toUOM) {
return $value;
}
// Convert to Kelvin
switch ($fromUOM) {
case 'F':
$value = ($value - 32) / 1.8 + 273.15;
break;
case 'C':
$value += 273.15;
break;
case 'Rank':
$value /= 1.8;
break;
case 'Reau':
$value = $value * 1.25 + 273.15;
break;
}
// Convert from Kelvin
switch ($toUOM) {
case 'F':
$value = ($value - 273.15) * 1.8 + 32.00;
break;
case 'C':
$value -= 273.15;
break;
case 'Rank':
$value *= 1.8;
break;
case 'Reau':
$value = ($value - 273.15) * 0.80000;
break;
}
return $value;
}
private static function resolveTemperatureSynonyms(string $uom): string
{
switch ($uom) {
case 'fah':
return 'F';
case 'cel':
return 'C';
case 'kel':
return 'K';
}
return $uom;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php 0000644 00000003666 15242753725 0022734 0 ustar 00 10) {
throw new Exception(ExcelError::NAN());
}
return (int) $places;
}
throw new Exception(ExcelError::VALUE());
}
/**
* Formats a number base string value with leading zeroes.
*
* @param string $value The "number" to pad
* @param ?int $places The length that we want to pad this value
*
* @return string The padded "number"
*/
protected static function nbrConversionFormat(string $value, ?int $places): string
{
if ($places !== null) {
if (strlen($value) <= $places) {
return substr(str_pad($value, $places, '0', STR_PAD_LEFT), -10);
}
return ExcelError::NAN();
}
return substr($value, -10);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php 0000644 00000046176 15242753725 0024024 0 ustar 00 abs();
}
/**
* IMARGUMENT.
*
* Returns the argument theta of a complex number, i.e. the angle in radians from the real
* axis to the representation of the number in polar coordinates.
*
* Excel Function:
* IMARGUMENT(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the argument theta
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMARGUMENT($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
return ExcelError::DIV0();
}
return $complex->argument();
}
/**
* IMCONJUGATE.
*
* Returns the complex conjugate of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMCONJUGATE(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the conjugate
* Or can be an array of values
*
* @return array|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMCONJUGATE($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->conjugate();
}
/**
* IMCOS.
*
* Returns the cosine of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMCOS(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the cosine
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMCOS($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->cos();
}
/**
* IMCOSH.
*
* Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMCOSH(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMCOSH($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->cosh();
}
/**
* IMCOT.
*
* Returns the cotangent of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMCOT(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the cotangent
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMCOT($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->cot();
}
/**
* IMCSC.
*
* Returns the cosecant of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMCSC(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the cosecant
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMCSC($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->csc();
}
/**
* IMCSCH.
*
* Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMCSCH(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMCSCH($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->csch();
}
/**
* IMSIN.
*
* Returns the sine of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMSIN(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the sine
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMSIN($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->sin();
}
/**
* IMSINH.
*
* Returns the hyperbolic sine of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMSINH(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the hyperbolic sine
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMSINH($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->sinh();
}
/**
* IMSEC.
*
* Returns the secant of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMSEC(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the secant
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMSEC($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->sec();
}
/**
* IMSECH.
*
* Returns the hyperbolic secant of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMSECH(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the hyperbolic secant
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMSECH($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->sech();
}
/**
* IMTAN.
*
* Returns the tangent of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMTAN(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the tangent
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMTAN($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->tan();
}
/**
* IMSQRT.
*
* Returns the square root of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMSQRT(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the square root
* Or can be an array of values
*
* @return array|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMSQRT($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
$theta = self::IMARGUMENT($complexNumber);
if ($theta === ExcelError::DIV0()) {
return '0';
}
return (string) $complex->sqrt();
}
/**
* IMLN.
*
* Returns the natural logarithm of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMLN(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the natural logarithm
* Or can be an array of values
*
* @return array|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMLN($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
return ExcelError::NAN();
}
return (string) $complex->ln();
}
/**
* IMLOG10.
*
* Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMLOG10(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the common logarithm
* Or can be an array of values
*
* @return array|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMLOG10($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
return ExcelError::NAN();
}
return (string) $complex->log10();
}
/**
* IMLOG2.
*
* Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMLOG2(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the base-2 logarithm
* Or can be an array of values
*
* @return array|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMLOG2($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
return ExcelError::NAN();
}
return (string) $complex->log2();
}
/**
* IMEXP.
*
* Returns the exponential of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMEXP(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the exponential
* Or can be an array of values
*
* @return array|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMEXP($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $complex->exp();
}
/**
* IMPOWER.
*
* Returns a complex number in x + yi or x + yj text format raised to a power.
*
* Excel Function:
* IMPOWER(complexNumber,realNumber)
*
* @param array|string $complexNumber the complex number you want to raise to a power
* Or can be an array of values
* @param array|float|int|string $realNumber the power to which you want to raise the complex number
* Or can be an array of values
*
* @return array|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMPOWER($complexNumber, $realNumber)
{
if (is_array($complexNumber) || is_array($realNumber)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber, $realNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
if (!is_numeric($realNumber)) {
return ExcelError::VALUE();
}
return (string) $complex->pow((float) $realNumber);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php 0000644 00000016615 15242753725 0023122 0 ustar 00 getMessage();
}
return ConvertDecimal::toBinary(self::toDecimal($value), $places);
}
/**
* toDecimal.
*
* Return an octal value as decimal.
*
* Excel Function:
* OCT2DEC(x)
*
* @param array|string $value The octal number you want to convert. Number may not contain
* more than 10 octal characters (30 bits). The most significant
* bit of number is the sign bit. The remaining 29 bits are
* magnitude bits. Negative numbers are represented using
* two's-complement notation.
* If number is not a valid octal number, OCT2DEC returns the
* #NUM! error value.
* Or can be an array of values
*
* @return array|string Result, or an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toDecimal($value)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
try {
$value = self::validateValue($value);
$value = self::validateOctal($value);
} catch (Exception $e) {
return $e->getMessage();
}
$binX = '';
foreach (str_split($value) as $char) {
$binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
}
if (strlen($binX) == 30 && $binX[0] == '1') {
for ($i = 0; $i < 30; ++$i) {
$binX[$i] = ($binX[$i] == '1' ? '0' : '1');
}
return (string) ((bindec($binX) + 1) * -1);
}
return (string) bindec($binX);
}
/**
* toHex.
*
* Return an octal value as hex.
*
* Excel Function:
* OCT2HEX(x[,places])
*
* @param array|string $value The octal number you want to convert. Number may not contain
* more than 10 octal characters (30 bits). The most significant
* bit of number is the sign bit. The remaining 29 bits are
* magnitude bits. Negative numbers are represented using
* two's-complement notation.
* If number is negative, OCT2HEX ignores places and returns a
* 10-character hexadecimal number.
* If number is not a valid octal number, OCT2HEX returns the
* #NUM! error value.
* If OCT2HEX requires more than places characters, it returns
* the #NUM! error value.
* Or can be an array of values
* @param array|int $places The number of characters to use. If places is omitted, OCT2HEX
* uses the minimum number of characters necessary. Places is useful
* for padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
* If places is negative, OCT2HEX returns the #NUM! error value.
* Or can be an array of values
*
* @return array|string Result, or an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toHex($value, $places = null)
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
}
try {
$value = self::validateValue($value);
$value = self::validateOctal($value);
$places = self::validatePlaces($places);
} catch (Exception $e) {
return $e->getMessage();
}
$hexVal = strtoupper(dechex((int) self::toDecimal($value)));
$hexVal = (PHP_INT_SIZE === 4 && strlen($value) === 10 && $value[0] >= '4') ? "FF{$hexVal}" : $hexVal;
return self::nbrConversionFormat($hexVal, $places);
}
protected static function validateOctal(string $value): string
{
$numDigits = (int) preg_match_all('/[01234567]/', $value);
if (strlen($value) > $numDigits || $numDigits > 10) {
throw new Exception(ExcelError::NAN());
}
return $value;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php 0000644 00000010370 15242753725 0022037 0 ustar 00 getMessage();
}
if (($ord < 0) || ($x <= 0.0)) {
return ExcelError::NAN();
}
$fBk = self::calculate($x, $ord);
return (is_nan($fBk)) ? ExcelError::NAN() : $fBk;
}
private static function calculate(float $x, int $ord): float
{
// special cases
switch ($ord) {
case 0:
return self::besselK0($x);
case 1:
return self::besselK1($x);
}
return self::besselK2($x, $ord);
}
/**
* Mollify Phpstan.
*
* @codeCoverageIgnore
*/
private static function callBesselI(float $x, int $ord): float
{
$rslt = BesselI::BESSELI($x, $ord);
if (!is_float($rslt)) {
throw new Exception('Unexpected array or string');
}
return $rslt;
}
private static function besselK0(float $x): float
{
if ($x <= 2) {
$fNum2 = $x * 0.5;
$y = ($fNum2 * $fNum2);
return -log($fNum2) * self::callBesselI($x, 0) +
(-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
(0.10750e-3 + $y * 0.74e-5))))));
}
$y = 2 / $x;
return exp(-$x) / sqrt($x) *
(1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
(0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
}
private static function besselK1(float $x): float
{
if ($x <= 2) {
$fNum2 = $x * 0.5;
$y = ($fNum2 * $fNum2);
return log($fNum2) * self::callBesselI($x, 1) +
(1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
(-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
}
$y = 2 / $x;
return exp(-$x) / sqrt($x) *
(1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
(0.325614e-2 + $y * (-0.68245e-3)))))));
}
private static function besselK2(float $x, int $ord): float
{
$fTox = 2 / $x;
$fBkm = self::besselK0($x);
$fBk = self::besselK1($x);
for ($n = 1; $n < $ord; ++$n) {
$fBkp = $fBkm + $n * $fTox * $fBk;
$fBkm = $fBk;
$fBk = $fBkp;
}
return $fBk;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php 0000644 00000011243 15242753725 0022035 0 ustar 00 getMessage();
}
if ($ord < 0) {
return ExcelError::NAN();
}
$fResult = self::calculate($x, $ord);
return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
}
private static function calculate(float $x, int $ord): float
{
// special cases
switch ($ord) {
case 0:
return self::besselI0($x);
case 1:
return self::besselI1($x);
}
return self::besselI2($x, $ord);
}
private static function besselI0(float $x): float
{
$ax = abs($x);
if ($ax < 3.75) {
$y = $x / 3.75;
$y = $y * $y;
return 1.0 + $y * (3.5156229 + $y * (3.0899424 + $y * (1.2067492
+ $y * (0.2659732 + $y * (0.360768e-1 + $y * 0.45813e-2)))));
}
$y = 3.75 / $ax;
return (exp($ax) / sqrt($ax)) * (0.39894228 + $y * (0.1328592e-1 + $y * (0.225319e-2 + $y * (-0.157565e-2
+ $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1 +
$y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
}
private static function besselI1(float $x): float
{
$ax = abs($x);
if ($ax < 3.75) {
$y = $x / 3.75;
$y = $y * $y;
$ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1 +
$y * (0.301532e-2 + $y * 0.32411e-3))))));
return ($x < 0.0) ? -$ans : $ans;
}
$y = 3.75 / $ax;
$ans = 0.2282967e-1 + $y * (-0.2895312e-1 + $y * (0.1787654e-1 - $y * 0.420059e-2));
$ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2 +
$y * (-0.1031555e-1 + $y * $ans))));
$ans *= exp($ax) / sqrt($ax);
return ($x < 0.0) ? -$ans : $ans;
}
/**
* Sop to Scrutinizer.
*
* @var float
*/
private static $zeroPointZero = 0.0;
private static function besselI2(float $x, int $ord): float
{
if ($x === self::$zeroPointZero) {
return 0.0;
}
$tox = 2.0 / abs($x);
$bip = 0;
$ans = 0.0;
$bi = 1.0;
for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
$bim = $bip + $j * $tox * $bi;
$bip = $bi;
$bi = $bim;
if (abs($bi) > 1.0e+12) {
$ans *= 1.0e-12;
$bi *= 1.0e-12;
$bip *= 1.0e-12;
}
if ($j === $ord) {
$ans = $bip;
}
}
$ans *= self::besselI0($x) / $bi;
return ($x < 0.0 && (($ord % 2) === 1)) ? -$ans : $ans;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php 0000644 00000020134 15242753725 0022054 0 ustar 00 getMessage();
}
$split1 = self::splitNumber($number1);
$split2 = self::splitNumber($number2);
return self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
}
/**
* BITOR.
*
* Returns the bitwise OR of two integer values.
*
* Excel Function:
* BITOR(number1, number2)
*
* @param array|int $number1
* Or can be an array of values
* @param array|int $number2
* Or can be an array of values
*
* @return array|int|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function BITOR($number1, $number2)
{
if (is_array($number1) || is_array($number2)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
}
try {
$number1 = self::validateBitwiseArgument($number1);
$number2 = self::validateBitwiseArgument($number2);
} catch (Exception $e) {
return $e->getMessage();
}
$split1 = self::splitNumber($number1);
$split2 = self::splitNumber($number2);
return self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
}
/**
* BITXOR.
*
* Returns the bitwise XOR of two integer values.
*
* Excel Function:
* BITXOR(number1, number2)
*
* @param array|int $number1
* Or can be an array of values
* @param array|int $number2
* Or can be an array of values
*
* @return array|int|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function BITXOR($number1, $number2)
{
if (is_array($number1) || is_array($number2)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
}
try {
$number1 = self::validateBitwiseArgument($number1);
$number2 = self::validateBitwiseArgument($number2);
} catch (Exception $e) {
return $e->getMessage();
}
$split1 = self::splitNumber($number1);
$split2 = self::splitNumber($number2);
return self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
}
/**
* BITLSHIFT.
*
* Returns the number value shifted left by shift_amount bits.
*
* Excel Function:
* BITLSHIFT(number, shift_amount)
*
* @param array|int $number
* Or can be an array of values
* @param array|int $shiftAmount
* Or can be an array of values
*
* @return array|float|int|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function BITLSHIFT($number, $shiftAmount)
{
if (is_array($number) || is_array($shiftAmount)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
}
try {
$number = self::validateBitwiseArgument($number);
$shiftAmount = self::validateShiftAmount($shiftAmount);
} catch (Exception $e) {
return $e->getMessage();
}
$result = floor($number * (2 ** $shiftAmount));
if ($result > 2 ** 48 - 1) {
return ExcelError::NAN();
}
return $result;
}
/**
* BITRSHIFT.
*
* Returns the number value shifted right by shift_amount bits.
*
* Excel Function:
* BITRSHIFT(number, shift_amount)
*
* @param array|int $number
* Or can be an array of values
* @param array|int $shiftAmount
* Or can be an array of values
*
* @return array|float|int|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function BITRSHIFT($number, $shiftAmount)
{
if (is_array($number) || is_array($shiftAmount)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
}
try {
$number = self::validateBitwiseArgument($number);
$shiftAmount = self::validateShiftAmount($shiftAmount);
} catch (Exception $e) {
return $e->getMessage();
}
$result = floor($number / (2 ** $shiftAmount));
if ($result > 2 ** 48 - 1) { // possible because shiftAmount can be negative
return ExcelError::NAN();
}
return $result;
}
/**
* Validate arguments passed to the bitwise functions.
*
* @param mixed $value
*
* @return float
*/
private static function validateBitwiseArgument($value)
{
$value = self::nullFalseTrueToNumber($value);
if (is_numeric($value)) {
$value = (float) $value;
if ($value == floor($value)) {
if (($value > 2 ** 48 - 1) || ($value < 0)) {
throw new Exception(ExcelError::NAN());
}
return floor($value);
}
throw new Exception(ExcelError::NAN());
}
throw new Exception(ExcelError::VALUE());
}
/**
* Validate arguments passed to the bitwise functions.
*
* @param mixed $value
*
* @return int
*/
private static function validateShiftAmount($value)
{
$value = self::nullFalseTrueToNumber($value);
if (is_numeric($value)) {
if (abs($value) > 53) {
throw new Exception(ExcelError::NAN());
}
return (int) $value;
}
throw new Exception(ExcelError::VALUE());
}
/**
* Many functions accept null/false/true argument treated as 0/0/1.
*
* @param mixed $number
*
* @return mixed
*/
private static function nullFalseTrueToNumber(&$number)
{
if ($number === null) {
$number = 0;
} elseif (is_bool($number)) {
$number = (int) $number;
}
return $number;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Erf.php 0000644 00000006723 15242753725 0021232 0 ustar 00 2.2) {
return 1 - ErfC::ERFC($value);
}
$sum = $term = $value;
$xsqr = ($value * $value);
$j = 1;
do {
$term *= $xsqr / $j;
$sum -= $term / (2 * $j + 1);
++$j;
$term *= $xsqr / $j;
$sum += $term / (2 * $j + 1);
++$j;
if ($sum == 0.0) {
break;
}
} while (abs($term / $sum) > Functions::PRECISION);
return self::TWO_SQRT_PI * $sum;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php 0000644 00000022401 15242753725 0023404 0 ustar 00 511, DEC2BIN returns the #NUM! error
* value.
* If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
* If DEC2BIN requires more than places characters, it returns the #NUM!
* error value.
* Or can be an array of values
* @param array|int $places The number of characters to use. If places is omitted, DEC2BIN uses
* the minimum number of characters necessary. Places is useful for
* padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
* If places is zero or negative, DEC2BIN returns the #NUM! error value.
* Or can be an array of values
*
* @return array|string Result, or an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toBinary($value, $places = null)
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
}
try {
$value = self::validateValue($value);
$value = self::validateDecimal($value);
$places = self::validatePlaces($places);
} catch (Exception $e) {
return $e->getMessage();
}
$value = (int) floor((float) $value);
if ($value > self::LARGEST_BINARY_IN_DECIMAL || $value < self::SMALLEST_BINARY_IN_DECIMAL) {
return ExcelError::NAN();
}
$r = decbin($value);
// Two's Complement
$r = substr($r, -10);
return self::nbrConversionFormat($r, $places);
}
/**
* toHex.
*
* Return a decimal value as hex.
*
* Excel Function:
* DEC2HEX(x[,places])
*
* @param array|string $value The decimal integer you want to convert. If number is negative,
* places is ignored and DEC2HEX returns a 10-character (40-bit)
* hexadecimal number in which the most significant bit is the sign
* bit. The remaining 39 bits are magnitude bits. Negative numbers
* are represented using two's-complement notation.
* If number < -549,755,813,888 or if number > 549,755,813,887,
* DEC2HEX returns the #NUM! error value.
* If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
* If DEC2HEX requires more than places characters, it returns the
* #NUM! error value.
* Or can be an array of values
* @param array|int $places The number of characters to use. If places is omitted, DEC2HEX uses
* the minimum number of characters necessary. Places is useful for
* padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
* If places is zero or negative, DEC2HEX returns the #NUM! error value.
* Or can be an array of values
*
* @return array|string Result, or an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toHex($value, $places = null)
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
}
try {
$value = self::validateValue($value);
$value = self::validateDecimal($value);
$places = self::validatePlaces($places);
} catch (Exception $e) {
return $e->getMessage();
}
$value = floor((float) $value);
if ($value > self::LARGEST_HEX_IN_DECIMAL || $value < self::SMALLEST_HEX_IN_DECIMAL) {
return ExcelError::NAN();
}
$r = strtoupper(dechex((int) $value));
$r = self::hex32bit($value, $r);
return self::nbrConversionFormat($r, $places);
}
public static function hex32bit(float $value, string $hexstr, bool $force = false): string
{
if (PHP_INT_SIZE === 4 || $force) {
if ($value >= 2 ** 32) {
$quotient = (int) ($value / (2 ** 32));
return strtoupper(substr('0' . dechex($quotient), -2) . $hexstr);
}
if ($value < -(2 ** 32)) {
$quotient = 256 - (int) ceil((-$value) / (2 ** 32));
return strtoupper(substr('0' . dechex($quotient), -2) . substr("00000000$hexstr", -8));
}
if ($value < 0) {
return "FF$hexstr";
}
}
return $hexstr;
}
/**
* toOctal.
*
* Return an decimal value as octal.
*
* Excel Function:
* DEC2OCT(x[,places])
*
* @param array|string $value The decimal integer you want to convert. If number is negative,
* places is ignored and DEC2OCT returns a 10-character (30-bit)
* octal number in which the most significant bit is the sign bit.
* The remaining 29 bits are magnitude bits. Negative numbers are
* represented using two's-complement notation.
* If number < -536,870,912 or if number > 536,870,911, DEC2OCT
* returns the #NUM! error value.
* If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
* If DEC2OCT requires more than places characters, it returns the
* #NUM! error value.
* Or can be an array of values
* @param array|int $places The number of characters to use. If places is omitted, DEC2OCT uses
* the minimum number of characters necessary. Places is useful for
* padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
* If places is zero or negative, DEC2OCT returns the #NUM! error value.
* Or can be an array of values
*
* @return array|string Result, or an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toOctal($value, $places = null)
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
}
try {
$value = self::validateValue($value);
$value = self::validateDecimal($value);
$places = self::validatePlaces($places);
} catch (Exception $e) {
return $e->getMessage();
}
$value = (int) floor((float) $value);
if ($value > self::LARGEST_OCTAL_IN_DECIMAL || $value < self::SMALLEST_OCTAL_IN_DECIMAL) {
return ExcelError::NAN();
}
$r = decoct($value);
$r = substr($r, -10);
return self::nbrConversionFormat($r, $places);
}
protected static function validateDecimal(string $value): string
{
if (strlen($value) > preg_match_all('/[-0123456789.]/', $value)) {
throw new Exception(ExcelError::VALUE());
}
return $value;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php 0000644 00000016134 15242753725 0023300 0 ustar 00 getMessage();
}
if (strlen($value) == 10 && $value[0] === '1') {
// Two's Complement
$value = substr($value, -9);
return '-' . (512 - bindec($value));
}
return (string) bindec($value);
}
/**
* toHex.
*
* Return a binary value as hex.
*
* Excel Function:
* BIN2HEX(x[,places])
*
* @param array|string $value The binary number (as a string) that you want to convert. The number
* cannot contain more than 10 characters (10 bits). The most significant
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
* Negative numbers are represented using two's-complement notation.
* If number is not a valid binary number, or if number contains more than
* 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
* Or can be an array of values
* @param array|int $places The number of characters to use. If places is omitted, BIN2HEX uses the
* minimum number of characters necessary. Places is useful for padding the
* return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
* If places is negative, BIN2HEX returns the #NUM! error value.
* Or can be an array of values
*
* @return array|string Result, or an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toHex($value, $places = null)
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
}
try {
$value = self::validateValue($value);
$value = self::validateBinary($value);
$places = self::validatePlaces($places);
} catch (Exception $e) {
return $e->getMessage();
}
if (strlen($value) == 10 && $value[0] === '1') {
$high2 = substr($value, 0, 2);
$low8 = substr($value, 2);
$xarr = ['00' => '00000000', '01' => '00000001', '10' => 'FFFFFFFE', '11' => 'FFFFFFFF'];
return $xarr[$high2] . strtoupper(substr('0' . dechex((int) bindec($low8)), -2));
}
$hexVal = (string) strtoupper(dechex((int) bindec($value)));
return self::nbrConversionFormat($hexVal, $places);
}
/**
* toOctal.
*
* Return a binary value as octal.
*
* Excel Function:
* BIN2OCT(x[,places])
*
* @param array|string $value The binary number (as a string) that you want to convert. The number
* cannot contain more than 10 characters (10 bits). The most significant
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
* Negative numbers are represented using two's-complement notation.
* If number is not a valid binary number, or if number contains more than
* 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
* Or can be an array of values
* @param array|int $places The number of characters to use. If places is omitted, BIN2OCT uses the
* minimum number of characters necessary. Places is useful for padding the
* return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
* If places is negative, BIN2OCT returns the #NUM! error value.
* Or can be an array of values
*
* @return array|string Result, or an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toOctal($value, $places = null)
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
}
try {
$value = self::validateValue($value);
$value = self::validateBinary($value);
$places = self::validatePlaces($places);
} catch (Exception $e) {
return $e->getMessage();
}
if (strlen($value) == 10 && $value[0] === '1') { // Two's Complement
return str_repeat('7', 6) . strtoupper(decoct((int) bindec("11$value")));
}
$octVal = (string) decoct((int) bindec($value));
return self::nbrConversionFormat($octVal, $places);
}
protected static function validateBinary(string $value): string
{
if ((strlen($value) > preg_match_all('/[01]/', $value)) || (strlen($value) > 10)) {
throw new Exception(ExcelError::NAN());
}
return $value;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php 0000644 00000010511 15242753725 0024157 0 ustar 00 divideby(new ComplexObject($complexDivisor));
} catch (ComplexException $e) {
return ExcelError::NAN();
}
}
/**
* IMSUB.
*
* Returns the difference of two complex numbers in x + yi or x + yj text format.
*
* Excel Function:
* IMSUB(complexNumber1,complexNumber2)
*
* @param array|string $complexNumber1 the complex number from which to subtract complexNumber2
* Or can be an array of values
* @param array|string $complexNumber2 the complex number to subtract from complexNumber1
* Or can be an array of values
*
* @return array|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMSUB($complexNumber1, $complexNumber2)
{
if (is_array($complexNumber1) || is_array($complexNumber2)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber1, $complexNumber2);
}
try {
return (string) (new ComplexObject($complexNumber1))->subtract(new ComplexObject($complexNumber2));
} catch (ComplexException $e) {
return ExcelError::NAN();
}
}
/**
* IMSUM.
*
* Returns the sum of two or more complex numbers in x + yi or x + yj text format.
*
* Excel Function:
* IMSUM(complexNumber[,complexNumber[,...]])
*
* @param string ...$complexNumbers Series of complex numbers to add
*
* @return string
*/
public static function IMSUM(...$complexNumbers)
{
// Return value
$returnValue = new ComplexObject(0.0);
$aArgs = Functions::flattenArray($complexNumbers);
try {
// Loop through the arguments
foreach ($aArgs as $complex) {
$returnValue = $returnValue->add(new ComplexObject($complex));
}
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $returnValue;
}
/**
* IMPRODUCT.
*
* Returns the product of two or more complex numbers in x + yi or x + yj text format.
*
* Excel Function:
* IMPRODUCT(complexNumber[,complexNumber[,...]])
*
* @param string ...$complexNumbers Series of complex numbers to multiply
*
* @return string
*/
public static function IMPRODUCT(...$complexNumbers)
{
// Return value
$returnValue = new ComplexObject(1.0);
$aArgs = Functions::flattenArray($complexNumbers);
try {
// Loop through the arguments
foreach ($aArgs as $complex) {
$returnValue = $returnValue->multiply(new ComplexObject($complex));
}
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return (string) $returnValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/ErfC.php 0000644 00000004623 15242753725 0021332 0 ustar 00 Functions::PRECISION);
return self::ONE_SQRT_PI * exp(-$value * $value) * $q2;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/EngineeringValidations.php 0000644 00000001311 15242753725 0025132 0 ustar 00 getMessage();
}
$dec = self::toDecimal($value);
return ConvertDecimal::toBinary($dec, $places);
}
/**
* toDecimal.
*
* Return a hex value as decimal.
*
* Excel Function:
* HEX2DEC(x)
*
* @param array|string $value The hexadecimal number you want to convert. This number cannot
* contain more than 10 characters (40 bits). The most significant
* bit of number is the sign bit. The remaining 39 bits are magnitude
* bits. Negative numbers are represented using two's-complement
* notation.
* If number is not a valid hexadecimal number, HEX2DEC returns the
* #NUM! error value.
* Or can be an array of values
*
* @return array|string Result, or an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toDecimal($value)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
try {
$value = self::validateValue($value);
$value = self::validateHex($value);
} catch (Exception $e) {
return $e->getMessage();
}
if (strlen($value) > 10) {
return ExcelError::NAN();
}
$binX = '';
foreach (str_split($value) as $char) {
$binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
}
if (strlen($binX) == 40 && $binX[0] == '1') {
for ($i = 0; $i < 40; ++$i) {
$binX[$i] = ($binX[$i] == '1' ? '0' : '1');
}
return (string) ((bindec($binX) + 1) * -1);
}
return (string) bindec($binX);
}
/**
* toOctal.
*
* Return a hex value as octal.
*
* Excel Function:
* HEX2OCT(x[,places])
*
* @param array|string $value The hexadecimal number you want to convert. Number cannot
* contain more than 10 characters. The most significant bit of
* number is the sign bit. The remaining 39 bits are magnitude
* bits. Negative numbers are represented using two's-complement
* notation.
* If number is negative, HEX2OCT ignores places and returns a
* 10-character octal number.
* If number is negative, it cannot be less than FFE0000000, and
* if number is positive, it cannot be greater than 1FFFFFFF.
* If number is not a valid hexadecimal number, HEX2OCT returns
* the #NUM! error value.
* If HEX2OCT requires more than places characters, it returns
* the #NUM! error value.
* Or can be an array of values
* @param array|int $places The number of characters to use. If places is omitted, HEX2OCT
* uses the minimum number of characters necessary. Places is
* useful for padding the return value with leading 0s (zeros).
* If places is not an integer, it is truncated.
* If places is nonnumeric, HEX2OCT returns the #VALUE! error
* value.
* If places is negative, HEX2OCT returns the #NUM! error value.
* Or can be an array of values
*
* @return array|string Result, or an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function toOctal($value, $places = null)
{
if (is_array($value) || is_array($places)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
}
try {
$value = self::validateValue($value);
$value = self::validateHex($value);
$places = self::validatePlaces($places);
} catch (Exception $e) {
return $e->getMessage();
}
$decimal = self::toDecimal($value);
return ConvertDecimal::toOctal($decimal, $places);
}
protected static function validateHex(string $value): string
{
if (strlen($value) > preg_match_all('/[0123456789ABCDEF]/', $value)) {
throw new Exception(ExcelError::NAN());
}
return $value;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Complex.php 0000644 00000010224 15242753725 0022114 0 ustar 00 getMessage();
}
if (($suffix === 'i') || ($suffix === 'j') || ($suffix === '')) {
$complex = new ComplexObject($realNumber, $imaginary, $suffix);
return (string) $complex;
}
return ExcelError::VALUE();
}
/**
* IMAGINARY.
*
* Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMAGINARY(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the imaginary
* coefficient
* Or can be an array of values
*
* @return array|float|string (string if an error)
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMAGINARY($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return $complex->getImaginary();
}
/**
* IMREAL.
*
* Returns the real coefficient of a complex number in x + yi or x + yj text format.
*
* Excel Function:
* IMREAL(complexNumber)
*
* @param array|string $complexNumber the complex number for which you want the real coefficient
* Or can be an array of values
*
* @return array|float|string (string if an error)
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function IMREAL($complexNumber)
{
if (is_array($complexNumber)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
}
try {
$complex = new ComplexObject($complexNumber);
} catch (ComplexException $e) {
return ExcelError::NAN();
}
return $complex->getReal();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/Compare.php 0000644 00000005620 15242753725 0022077 0 ustar 00 getMessage();
}
return (int) (abs($a - $b) < 1.0e-15);
}
/**
* GESTEP.
*
* Excel Function:
* GESTEP(number[,step])
*
* Returns 1 if number >= step; returns 0 (zero) otherwise
* Use this function to filter a set of values. For example, by summing several GESTEP
* functions you calculate the count of values that exceed a threshold.
*
* @param array|float $number the value to test against step
* Or can be an array of values
* @param null|array|float $step The threshold value. If you omit a value for step, GESTEP uses zero.
* Or can be an array of values
*
* @return array|int|string (string in the event of an error)
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function GESTEP($number, $step = 0.0)
{
if (is_array($number) || is_array($step)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $step);
}
try {
$number = EngineeringValidations::validateFloat($number);
$step = EngineeringValidations::validateFloat($step ?? 0.0);
} catch (Exception $e) {
return $e->getMessage();
}
return (int) ($number >= $step);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php 0000644 00000013442 15242753725 0022041 0 ustar 00 8. This code provides a more accurate calculation
*
* @param mixed $x A float value at which to evaluate the function.
* If x is nonnumeric, BESSELJ returns the #VALUE! error value.
* Or can be an array of values
* @param mixed $ord The integer order of the Bessel function.
* If ord is not an integer, it is truncated.
* If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
* If $ord < 0, BESSELJ returns the #NUM! error value.
* Or can be an array of values
*
* @return array|float|string Result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function BESSELJ($x, $ord)
{
if (is_array($x) || is_array($ord)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
}
try {
$x = EngineeringValidations::validateFloat($x);
$ord = EngineeringValidations::validateInt($ord);
} catch (Exception $e) {
return $e->getMessage();
}
if ($ord < 0) {
return ExcelError::NAN();
}
$fResult = self::calculate($x, $ord);
return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
}
private static function calculate(float $x, int $ord): float
{
// special cases
switch ($ord) {
case 0:
return self::besselJ0($x);
case 1:
return self::besselJ1($x);
}
return self::besselJ2($x, $ord);
}
private static function besselJ0(float $x): float
{
$ax = abs($x);
if ($ax < 8.0) {
$y = $x * $x;
$ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y *
(77392.33017 + $y * (-184.9052456)))));
$ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y *
(267.8532712 + $y * 1.0))));
return $ans1 / $ans2;
}
$z = 8.0 / $ax;
$y = $z * $z;
$xx = $ax - 0.785398164;
$ans1 = 1.0 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
$ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y *
(0.7621095161e-6 - $y * 0.934935152e-7)));
return sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
}
private static function besselJ1(float $x): float
{
$ax = abs($x);
if ($ax < 8.0) {
$y = $x * $x;
$ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y *
(-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
$ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y *
(376.9991397 + $y * 1.0))));
return $ans1 / $ans2;
}
$z = 8.0 / $ax;
$y = $z * $z;
$xx = $ax - 2.356194491;
$ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y *
(-0.88228987e-6 + $y * 0.105787412e-6)));
$ans = sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
return ($x < 0.0) ? -$ans : $ans;
}
private static function besselJ2(float $x, int $ord): float
{
$ax = abs($x);
if ($ax === 0.0) {
return 0.0;
}
if ($ax > $ord) {
return self::besselj2a($ax, $ord, $x);
}
return self::besselj2b($ax, $ord, $x);
}
private static function besselj2a(float $ax, int $ord, float $x): float
{
$tox = 2.0 / $ax;
$bjm = self::besselJ0($ax);
$bj = self::besselJ1($ax);
for ($j = 1; $j < $ord; ++$j) {
$bjp = $j * $tox * $bj - $bjm;
$bjm = $bj;
$bj = $bjp;
}
$ans = $bj;
return ($x < 0.0 && ($ord % 2) == 1) ? -$ans : $ans;
}
private static function besselj2b(float $ax, int $ord, float $x): float
{
$tox = 2.0 / $ax;
$jsum = false;
$bjp = $ans = $sum = 0.0;
$bj = 1.0;
for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
$bjm = $j * $tox * $bj - $bjp;
$bjp = $bj;
$bj = $bjm;
if (abs($bj) > 1.0e+10) {
$bj *= 1.0e-10;
$bjp *= 1.0e-10;
$ans *= 1.0e-10;
$sum *= 1.0e-10;
}
if ($jsum === true) {
$sum += $bj;
}
$jsum = $jsum === false;
if ($j === $ord) {
$ans = $bjp;
}
}
$sum = 2.0 * $sum - $bj;
$ans /= $sum;
return ($x < 0.0 && ($ord % 2) === 1) ? -$ans : $ans;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php 0000644 00000003661 15242753725 0020377 0 ustar 00 2048) {
return ExcelError::VALUE(); // Invalid URL length
}
if (!preg_match('/^http[s]?:\/\//', $url)) {
return ExcelError::VALUE(); // Invalid protocol
}
// Get results from the the webservice
$client = Settings::getHttpClient();
$requestFactory = Settings::getRequestFactory();
$request = $requestFactory->createRequest('GET', $url);
try {
$response = $client->sendRequest($request);
} catch (ClientExceptionInterface $e) {
return ExcelError::VALUE(); // cURL error
}
if ($response->getStatusCode() != 200) {
return ExcelError::VALUE(); // cURL error
}
$output = $response->getBody()->getContents();
if (strlen($output) > 32767) {
return ExcelError::VALUE(); // Output not a string or too long
}
return $output;
}
/**
* URLENCODE.
*
* Returns data from a web service on the Internet or Intranet.
*
* Excel Function:
* urlEncode(text)
*
* @param mixed $text
*
* @return string the url encoded output
*/
public static function urlEncode($text)
{
if (!is_string($text)) {
return ExcelError::VALUE();
}
return str_replace('+', '%20', urlencode($text));
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Averages/Mean.php 0000644 00000007107 15242753725 0023162 0 ustar 00 0)) {
$aCount = Counts::COUNT($aArgs);
if (Minimum::min($aArgs) > 0) {
return $aMean ** (1 / $aCount);
}
}
return ExcelError::NAN();
}
/**
* HARMEAN.
*
* Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
* arithmetic mean of reciprocals.
*
* Excel Function:
* HARMEAN(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function harmonic(...$args)
{
// Loop through arguments
$aArgs = Functions::flattenArray($args);
if (Minimum::min($aArgs) < 0) {
return ExcelError::NAN();
}
$returnValue = 0;
$aCount = 0;
foreach ($aArgs as $arg) {
// Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) {
if ($arg <= 0) {
return ExcelError::NAN();
}
$returnValue += (1 / $arg);
++$aCount;
}
}
// Return
if ($aCount > 0) {
return 1 / ($returnValue / $aCount);
}
return ExcelError::NA();
}
/**
* TRIMMEAN.
*
* Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
* taken by excluding a percentage of data points from the top and bottom tails
* of a data set.
*
* Excel Function:
* TRIMEAN(value1[,value2[, ...]], $discard)
*
* @param mixed $args Data values
*
* @return float|string
*/
public static function trim(...$args)
{
$aArgs = Functions::flattenArray($args);
// Calculate
$percent = array_pop($aArgs);
if ((is_numeric($percent)) && (!is_string($percent))) {
if (($percent < 0) || ($percent > 1)) {
return ExcelError::NAN();
}
$mArgs = [];
foreach ($aArgs as $arg) {
// Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) {
$mArgs[] = $arg;
}
}
$discard = floor(Counts::COUNT($mArgs) * $percent / 2);
sort($mArgs);
for ($i = 0; $i < $discard; ++$i) {
array_pop($mArgs);
array_shift($mArgs);
}
return Averages::average($mArgs);
}
return ExcelError::VALUE();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/LogNormal.php 0000644 00000012662 15242753725 0025303 0 ustar 00 getMessage();
}
if (($value <= 0) || ($stdDev <= 0)) {
return ExcelError::NAN();
}
return StandardNormal::cumulative((log($value) - $mean) / $stdDev);
}
/**
* LOGNORM.DIST.
*
* Returns the lognormal distribution of x, where ln(x) is normally distributed
* with parameters mean and standard_dev.
*
* @param mixed $value Float value for which we want the probability
* Or can be an array of values
* @param mixed $mean Mean value as a float
* Or can be an array of values
* @param mixed $stdDev Standard Deviation as a float
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function distribution($value, $mean, $stdDev, $cumulative = false)
{
if (is_array($value) || is_array($mean) || is_array($stdDev) || is_array($cumulative)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev, $cumulative);
}
try {
$value = DistributionValidations::validateFloat($value);
$mean = DistributionValidations::validateFloat($mean);
$stdDev = DistributionValidations::validateFloat($stdDev);
$cumulative = DistributionValidations::validateBool($cumulative);
} catch (Exception $e) {
return $e->getMessage();
}
if (($value <= 0) || ($stdDev <= 0)) {
return ExcelError::NAN();
}
if ($cumulative === true) {
return StandardNormal::distribution((log($value) - $mean) / $stdDev, true);
}
return (1 / (sqrt(2 * M_PI) * $stdDev * $value)) *
exp(0 - ((log($value) - $mean) ** 2 / (2 * $stdDev ** 2)));
}
/**
* LOGINV.
*
* Returns the inverse of the lognormal cumulative distribution
*
* @param mixed $probability Float probability for which we want the value
* Or can be an array of values
* @param mixed $mean Mean Value as a float
* Or can be an array of values
* @param mixed $stdDev Standard Deviation as a float
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*
* @TODO Try implementing P J Acklam's refinement algorithm for greater
* accuracy if I can get my head round the mathematics
* (as described at) http://home.online.no/~pjacklam/notes/invnorm/
*/
public static function inverse($probability, $mean, $stdDev)
{
if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
}
try {
$probability = DistributionValidations::validateProbability($probability);
$mean = DistributionValidations::validateFloat($mean);
$stdDev = DistributionValidations::validateFloat($stdDev);
} catch (Exception $e) {
return $e->getMessage();
}
if ($stdDev <= 0) {
return ExcelError::NAN();
}
/** @var float */
$inverse = StandardNormal::inverse($probability);
return exp($mean + $stdDev * $inverse);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/NewtonRaphson.php 0000644 00000003262 15242753725 0026212 0 ustar 00 callback = $callback;
}
/** @return float|string */
public function execute(float $probability)
{
$xLo = 100;
$xHi = 0;
$dx = 1;
$x = $xNew = 1;
$i = 0;
while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
// Apply Newton-Raphson step
$result = call_user_func($this->callback, $x);
$error = $result - $probability;
if ($error == 0.0) {
$dx = 0;
} elseif ($error < 0.0) {
$xLo = $x;
} else {
$xHi = $x;
}
// Avoid division by zero
if ($result != 0.0) {
$dx = $error / $result;
$xNew = $x - $dx;
}
// If the NR fails to converge (which for example may be the
// case if the initial guess is too rough) we apply a bisection
// step to determine a more narrow interval around the root.
if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
$xNew = ($xLo + $xHi) / 2;
$dx = $xNew - $x;
}
$x = $xNew;
}
if ($i == self::MAX_ITERATIONS) {
return ExcelError::NA();
}
return $x;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Exponential.php 0000644 00000004035 15242753725 0025672 0 ustar 00 getMessage();
}
if (($value < 0) || ($lambda < 0)) {
return ExcelError::NAN();
}
if ($cumulative === true) {
return 1 - exp(0 - $value * $lambda);
}
return $lambda * exp(0 - $value * $lambda);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php 0000644 00000004636 15242753725 0024633 0 ustar 00 getMessage();
}
if (($value <= -1) || ($value >= 1)) {
return ExcelError::NAN();
}
return 0.5 * log((1 + $value) / (1 - $value));
}
/**
* FISHERINV.
*
* Returns the inverse of the Fisher transformation. Use this transformation when
* analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
* FISHERINV(y) = x.
*
* @param mixed $probability Float probability at which you want to evaluate the distribution
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverse($probability)
{
if (is_array($probability)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $probability);
}
try {
DistributionValidations::validateFloat($probability);
} catch (Exception $e) {
return $e->getMessage();
}
return (exp(2 * $probability) - 1) / (exp(2 * $probability) + 1);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php 0000644 00000013754 15242753725 0026325 0 ustar 00 Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
// Apply Newton-Raphson step
$result = self::calculateDistribution($x, $alpha, $beta, true);
if (!is_float($result)) {
return ExcelError::NA();
}
$error = $result - $probability;
if ($error == 0.0) {
$dx = 0;
} elseif ($error < 0.0) {
$xLo = $x;
} else {
$xHi = $x;
}
$pdf = self::calculateDistribution($x, $alpha, $beta, false);
// Avoid division by zero
if (!is_float($pdf)) {
return ExcelError::NA();
}
if ($pdf !== 0.0) {
$dx = $error / $pdf;
$xNew = $x - $dx;
}
// If the NR fails to converge (which for example may be the
// case if the initial guess is too rough) we apply a bisection
// step to determine a more narrow interval around the root.
if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) {
$xNew = ($xLo + $xHi) / 2;
$dx = $xNew - $x;
}
$x = $xNew;
}
if ($i === self::MAX_ITERATIONS) {
return ExcelError::NA();
}
return $x;
}
//
// Implementation of the incomplete Gamma function
//
public static function incompleteGamma(float $a, float $x): float
{
static $max = 32;
$summer = 0;
for ($n = 0; $n <= $max; ++$n) {
$divisor = $a;
for ($i = 1; $i <= $n; ++$i) {
$divisor *= ($a + $i);
}
$summer += ($x ** $n / $divisor);
}
return $x ** $a * exp(0 - $x) * $summer;
}
//
// Implementation of the Gamma function
//
public static function gammaValue(float $value): float
{
if ($value == 0.0) {
return 0;
}
static $p0 = 1.000000000190015;
static $p = [
1 => 76.18009172947146,
2 => -86.50532032941677,
3 => 24.01409824083091,
4 => -1.231739572450155,
5 => 1.208650973866179e-3,
6 => -5.395239384953e-6,
];
$y = $x = $value;
$tmp = $x + 5.5;
$tmp -= ($x + 0.5) * log($tmp);
$summer = $p0;
for ($j = 1; $j <= 6; ++$j) {
$summer += ($p[$j] / ++$y);
}
return exp(0 - $tmp + log(self::SQRT2PI * $summer / $x));
}
private const LG_D1 = -0.5772156649015328605195174;
private const LG_D2 = 0.4227843350984671393993777;
private const LG_D4 = 1.791759469228055000094023;
private const LG_P1 = [
4.945235359296727046734888,
201.8112620856775083915565,
2290.838373831346393026739,
11319.67205903380828685045,
28557.24635671635335736389,
38484.96228443793359990269,
26377.48787624195437963534,
7225.813979700288197698961,
];
private const LG_P2 = [
4.974607845568932035012064,
542.4138599891070494101986,
15506.93864978364947665077,
184793.2904445632425417223,
1088204.76946882876749847,
3338152.967987029735917223,
5106661.678927352456275255,
3074109.054850539556250927,
];
private const LG_P4 = [
14745.02166059939948905062,
2426813.369486704502836312,
121475557.4045093227939592,
2663432449.630976949898078,
29403789566.34553899906876,
170266573776.5398868392998,
492612579337.743088758812,
560625185622.3951465078242,
];
private const LG_Q1 = [
67.48212550303777196073036,
1113.332393857199323513008,
7738.757056935398733233834,
27639.87074403340708898585,
54993.10206226157329794414,
61611.22180066002127833352,
36351.27591501940507276287,
8785.536302431013170870835,
];
private const LG_Q2 = [
183.0328399370592604055942,
7765.049321445005871323047,
133190.3827966074194402448,
1136705.821321969608938755,
5267964.117437946917577538,
13467014.54311101692290052,
17827365.30353274213975932,
9533095.591844353613395747,
];
private const LG_Q4 = [
2690.530175870899333379843,
639388.5654300092398984238,
41355999.30241388052042842,
1120872109.61614794137657,
14886137286.78813811542398,
101680358627.2438228077304,
341747634550.7377132798597,
446315818741.9713286462081,
];
private const LG_C = [
-0.001910444077728,
8.4171387781295e-4,
-5.952379913043012e-4,
7.93650793500350248e-4,
-0.002777777777777681622553,
0.08333333333333333331554247,
0.0057083835261,
];
// Rough estimate of the fourth root of logGamma_xBig
private const LG_FRTBIG = 2.25e76;
private const PNT68 = 0.6796875;
// Function cache for logGamma
/** @var float */
private static $logGammaCacheResult = 0.0;
/** @var float */
private static $logGammaCacheX = 0.0;
/**
* logGamma function.
*
* Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
*
* The natural logarithm of the gamma function.
* Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
* Applied Mathematics Division
* Argonne National Laboratory
* Argonne, IL 60439
*
* References:
*
* - W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
* Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.
* - K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.
* - Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.
*
*
*
* From the original documentation:
*
*
* This routine calculates the LOG(GAMMA) function for a positive real argument X.
* Computation is based on an algorithm outlined in references 1 and 2.
* The program uses rational functions that theoretically approximate LOG(GAMMA)
* to at least 18 significant decimal digits. The approximation for X > 12 is from
* reference 3, while approximations for X < 12.0 are similar to those in reference
* 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
* the compiler, the intrinsic functions, and proper selection of the
* machine-dependent constants.
*
*
* Error returns:
* The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
* The computation is believed to be free of underflow and overflow.
*
*
* @version 1.1
*
* @author Jaco van Kooten
*
* @return float MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
*/
public static function logGamma(float $x): float
{
if ($x == self::$logGammaCacheX) {
return self::$logGammaCacheResult;
}
$y = $x;
if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) {
if ($y <= self::EPS) {
$res = -log($y);
} elseif ($y <= 1.5) {
$res = self::logGamma1($y);
} elseif ($y <= 4.0) {
$res = self::logGamma2($y);
} elseif ($y <= 12.0) {
$res = self::logGamma3($y);
} else {
$res = self::logGamma4($y);
}
} else {
// --------------------------
// Return for bad arguments
// --------------------------
$res = self::MAX_VALUE;
}
// ------------------------------
// Final adjustments and return
// ------------------------------
self::$logGammaCacheX = $x;
self::$logGammaCacheResult = $res;
return $res;
}
private static function logGamma1(float $y): float
{
// ---------------------
// EPS .LT. X .LE. 1.5
// ---------------------
if ($y < self::PNT68) {
$corr = -log($y);
$xm1 = $y;
} else {
$corr = 0.0;
$xm1 = $y - 1.0;
}
$xden = 1.0;
$xnum = 0.0;
if ($y <= 0.5 || $y >= self::PNT68) {
for ($i = 0; $i < 8; ++$i) {
$xnum = $xnum * $xm1 + self::LG_P1[$i];
$xden = $xden * $xm1 + self::LG_Q1[$i];
}
return $corr + $xm1 * (self::LG_D1 + $xm1 * ($xnum / $xden));
}
$xm2 = $y - 1.0;
for ($i = 0; $i < 8; ++$i) {
$xnum = $xnum * $xm2 + self::LG_P2[$i];
$xden = $xden * $xm2 + self::LG_Q2[$i];
}
return $corr + $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
}
private static function logGamma2(float $y): float
{
// ---------------------
// 1.5 .LT. X .LE. 4.0
// ---------------------
$xm2 = $y - 2.0;
$xden = 1.0;
$xnum = 0.0;
for ($i = 0; $i < 8; ++$i) {
$xnum = $xnum * $xm2 + self::LG_P2[$i];
$xden = $xden * $xm2 + self::LG_Q2[$i];
}
return $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden));
}
protected static function logGamma3(float $y): float
{
// ----------------------
// 4.0 .LT. X .LE. 12.0
// ----------------------
$xm4 = $y - 4.0;
$xden = -1.0;
$xnum = 0.0;
for ($i = 0; $i < 8; ++$i) {
$xnum = $xnum * $xm4 + self::LG_P4[$i];
$xden = $xden * $xm4 + self::LG_Q4[$i];
}
return self::LG_D4 + $xm4 * ($xnum / $xden);
}
protected static function logGamma4(float $y): float
{
// ---------------------------------
// Evaluate for argument .GE. 12.0
// ---------------------------------
$res = 0.0;
if ($y <= self::LG_FRTBIG) {
$res = self::LG_C[6];
$ysq = $y * $y;
for ($i = 0; $i < 6; ++$i) {
$res = $res / $ysq + self::LG_C[$i];
}
$res /= $y;
$corr = log($y);
$res = $res + log(self::SQRT2PI) - 0.5 * $corr;
$res += $y * ($corr - 1.0);
}
return $res;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Beta.php 0000644 00000022717 15242753725 0024266 0 ustar 00 getMessage();
}
if ($rMin > $rMax) {
$tmp = $rMin;
$rMin = $rMax;
$rMax = $tmp;
}
if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
return ExcelError::NAN();
}
$value -= $rMin;
$value /= ($rMax - $rMin);
return self::incompleteBeta($value, $alpha, $beta);
}
/**
* BETAINV.
*
* Returns the inverse of the Beta distribution.
*
* @param mixed $probability Float probability at which you want to evaluate the distribution
* Or can be an array of values
* @param mixed $alpha Parameter to the distribution as a float
* Or can be an array of values
* @param mixed $beta Parameter to the distribution as a float
* Or can be an array of values
* @param mixed $rMin Minimum value as a float
* Or can be an array of values
* @param mixed $rMax Maximum value as a float
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverse($probability, $alpha, $beta, $rMin = 0.0, $rMax = 1.0)
{
if (is_array($probability) || is_array($alpha) || is_array($beta) || is_array($rMin) || is_array($rMax)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $alpha, $beta, $rMin, $rMax);
}
$rMin = $rMin ?? 0.0;
$rMax = $rMax ?? 1.0;
try {
$probability = DistributionValidations::validateProbability($probability);
$alpha = DistributionValidations::validateFloat($alpha);
$beta = DistributionValidations::validateFloat($beta);
$rMax = DistributionValidations::validateFloat($rMax);
$rMin = DistributionValidations::validateFloat($rMin);
} catch (Exception $e) {
return $e->getMessage();
}
if ($rMin > $rMax) {
$tmp = $rMin;
$rMin = $rMax;
$rMax = $tmp;
}
if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0.0)) {
return ExcelError::NAN();
}
return self::calculateInverse($probability, $alpha, $beta, $rMin, $rMax);
}
/**
* @return float|string
*/
private static function calculateInverse(float $probability, float $alpha, float $beta, float $rMin, float $rMax)
{
$a = 0;
$b = 2;
$guess = ($a + $b) / 2;
$i = 0;
while ((($b - $a) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) {
$guess = ($a + $b) / 2;
$result = self::distribution($guess, $alpha, $beta);
if (($result === $probability) || ($result === 0.0)) {
$b = $a;
} elseif ($result > $probability) {
$b = $guess;
} else {
$a = $guess;
}
}
if ($i === self::MAX_ITERATIONS) {
return ExcelError::NA();
}
return round($rMin + $guess * ($rMax - $rMin), 12);
}
/**
* Incomplete beta function.
*
* @author Jaco van Kooten
* @author Paul Meagher
*
* The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
*
* @param float $x require 0<=x<=1
* @param float $p require p>0
* @param float $q require q>0
*
* @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
*/
public static function incompleteBeta(float $x, float $p, float $q): float
{
if ($x <= 0.0) {
return 0.0;
} elseif ($x >= 1.0) {
return 1.0;
} elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
return 0.0;
}
$beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
return $beta_gam * self::betaFraction($x, $p, $q) / $p;
}
return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
}
// Function cache for logBeta function
/** @var float */
private static $logBetaCacheP = 0.0;
/** @var float */
private static $logBetaCacheQ = 0.0;
/** @var float */
private static $logBetaCacheResult = 0.0;
/**
* The natural logarithm of the beta function.
*
* @param float $p require p>0
* @param float $q require q>0
*
* @return float 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
*
* @author Jaco van Kooten
*/
private static function logBeta(float $p, float $q): float
{
if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
self::$logBetaCacheP = $p;
self::$logBetaCacheQ = $q;
if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
self::$logBetaCacheResult = 0.0;
} else {
self::$logBetaCacheResult = Gamma::logGamma($p) + Gamma::logGamma($q) - Gamma::logGamma($p + $q);
}
}
return self::$logBetaCacheResult;
}
/**
* Evaluates of continued fraction part of incomplete beta function.
* Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
*
* @author Jaco van Kooten
*/
private static function betaFraction(float $x, float $p, float $q): float
{
$c = 1.0;
$sum_pq = $p + $q;
$p_plus = $p + 1.0;
$p_minus = $p - 1.0;
$h = 1.0 - $sum_pq * $x / $p_plus;
if (abs($h) < self::XMININ) {
$h = self::XMININ;
}
$h = 1.0 / $h;
$frac = $h;
$m = 1;
$delta = 0.0;
while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) {
$m2 = 2 * $m;
// even index for d
$d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2));
$h = 1.0 + $d * $h;
if (abs($h) < self::XMININ) {
$h = self::XMININ;
}
$h = 1.0 / $h;
$c = 1.0 + $d / $c;
if (abs($c) < self::XMININ) {
$c = self::XMININ;
}
$frac *= $h * $c;
// odd index for d
$d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
$h = 1.0 + $d * $h;
if (abs($h) < self::XMININ) {
$h = self::XMININ;
}
$h = 1.0 / $h;
$c = 1.0 + $d / $c;
if (abs($c) < self::XMININ) {
$c = self::XMININ;
}
$delta = $h * $c;
$frac *= $delta;
++$m;
}
return $frac;
}
/*
private static function betaValue(float $a, float $b): float
{
return (Gamma::gammaValue($a) * Gamma::gammaValue($b)) /
Gamma::gammaValue($a + $b);
}
private static function regularizedIncompleteBeta(float $value, float $a, float $b): float
{
return self::incompleteBeta($value, $a, $b) / self::betaValue($a, $b);
}
*/
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php 0000644 00000025363 15242753725 0025443 0 ustar 00 getMessage();
}
if ($degrees < 1) {
return ExcelError::NAN();
}
if ($value < 0) {
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
return 1;
}
return ExcelError::NAN();
}
return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2) / Gamma::gammaValue($degrees / 2));
}
/**
* CHIDIST.
*
* Returns the one-tailed probability of the chi-squared distribution.
*
* @param mixed $value Float value for which we want the probability
* Or can be an array of values
* @param mixed $degrees Integer degrees of freedom
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function distributionLeftTail($value, $degrees, $cumulative)
{
if (is_array($value) || is_array($degrees) || is_array($cumulative)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $degrees, $cumulative);
}
try {
$value = DistributionValidations::validateFloat($value);
$degrees = DistributionValidations::validateInt($degrees);
$cumulative = DistributionValidations::validateBool($cumulative);
} catch (Exception $e) {
return $e->getMessage();
}
if ($degrees < 1) {
return ExcelError::NAN();
}
if ($value < 0) {
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
return 1;
}
return ExcelError::NAN();
}
if ($cumulative === true) {
return 1 - self::distributionRightTail($value, $degrees);
}
return ($value ** (($degrees / 2) - 1) * exp(-$value / 2)) /
((2 ** ($degrees / 2)) * Gamma::gammaValue($degrees / 2));
}
/**
* CHIINV.
*
* Returns the inverse of the right-tailed probability of the chi-squared distribution.
*
* @param mixed $probability Float probability at which you want to evaluate the distribution
* Or can be an array of values
* @param mixed $degrees Integer degrees of freedom
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverseRightTail($probability, $degrees)
{
if (is_array($probability) || is_array($degrees)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
}
try {
$probability = DistributionValidations::validateProbability($probability);
$degrees = DistributionValidations::validateInt($degrees);
} catch (Exception $e) {
return $e->getMessage();
}
if ($degrees < 1) {
return ExcelError::NAN();
}
$callback = function ($value) use ($degrees) {
return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2)
/ Gamma::gammaValue($degrees / 2));
};
$newtonRaphson = new NewtonRaphson($callback);
return $newtonRaphson->execute($probability);
}
/**
* CHIINV.
*
* Returns the inverse of the left-tailed probability of the chi-squared distribution.
*
* @param mixed $probability Float probability at which you want to evaluate the distribution
* Or can be an array of values
* @param mixed $degrees Integer degrees of freedom
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverseLeftTail($probability, $degrees)
{
if (is_array($probability) || is_array($degrees)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
}
try {
$probability = DistributionValidations::validateProbability($probability);
$degrees = DistributionValidations::validateInt($degrees);
} catch (Exception $e) {
return $e->getMessage();
}
if ($degrees < 1) {
return ExcelError::NAN();
}
return self::inverseLeftTailCalculation($probability, $degrees);
}
/**
* CHITEST.
*
* Uses the chi-square test to calculate the probability that the differences between two supplied data sets
* (of observed and expected frequencies), are likely to be simply due to sampling error,
* or if they are likely to be real.
*
* @param mixed $actual an array of observed frequencies
* @param mixed $expected an array of expected frequencies
*
* @return float|string
*/
public static function test($actual, $expected)
{
$rows = count($actual);
$actual = Functions::flattenArray($actual);
$expected = Functions::flattenArray($expected);
$columns = intdiv(count($actual), $rows);
$countActuals = count($actual);
$countExpected = count($expected);
if ($countActuals !== $countExpected || $countActuals === 1) {
return ExcelError::NAN();
}
$result = 0.0;
for ($i = 0; $i < $countActuals; ++$i) {
if ($expected[$i] == 0.0) {
return ExcelError::DIV0();
} elseif ($expected[$i] < 0.0) {
return ExcelError::NAN();
}
$result += (($actual[$i] - $expected[$i]) ** 2) / $expected[$i];
}
$degrees = self::degrees($rows, $columns);
$result = Functions::scalar(self::distributionRightTail($result, $degrees));
return $result;
}
protected static function degrees(int $rows, int $columns): int
{
if ($rows === 1) {
return $columns - 1;
} elseif ($columns === 1) {
return $rows - 1;
}
return ($columns - 1) * ($rows - 1);
}
private static function inverseLeftTailCalculation(float $probability, int $degrees): float
{
// bracket the root
$min = 0;
$sd = sqrt(2.0 * $degrees);
$max = 2 * $sd;
$s = -1;
while ($s * self::pchisq($max, $degrees) > $probability * $s) {
$min = $max;
$max += 2 * $sd;
}
// Find root using bisection
$chi2 = 0.5 * ($min + $max);
while (($max - $min) > self::EPS * $chi2) {
if ($s * self::pchisq($chi2, $degrees) > $probability * $s) {
$min = $chi2;
} else {
$max = $chi2;
}
$chi2 = 0.5 * ($min + $max);
}
return $chi2;
}
private static function pchisq(float $chi2, int $degrees): float
{
return self::gammp($degrees, 0.5 * $chi2);
}
private static function gammp(int $n, float $x): float
{
if ($x < 0.5 * $n + 1) {
return self::gser($n, $x);
}
return 1 - self::gcf($n, $x);
}
// Return the incomplete gamma function P(n/2,x) evaluated by
// series representation. Algorithm from numerical recipe.
// Assume that n is a positive integer and x>0, won't check arguments.
// Relative error controlled by the eps parameter
private static function gser(int $n, float $x): float
{
/** @var float */
$gln = Gamma::ln($n / 2);
$a = 0.5 * $n;
$ap = $a;
$sum = 1.0 / $a;
$del = $sum;
for ($i = 1; $i < 101; ++$i) {
++$ap;
$del = $del * $x / $ap;
$sum += $del;
if ($del < $sum * self::EPS) {
break;
}
}
return $sum * exp(-$x + $a * log($x) - $gln);
}
// Return the incomplete gamma function Q(n/2,x) evaluated by
// its continued fraction representation. Algorithm from numerical recipe.
// Assume that n is a postive integer and x>0, won't check arguments.
// Relative error controlled by the eps parameter
private static function gcf(int $n, float $x): float
{
/** @var float */
$gln = Gamma::ln($n / 2);
$a = 0.5 * $n;
$b = $x + 1 - $a;
$fpmin = 1.e-300;
$c = 1 / $fpmin;
$d = 1 / $b;
$h = $d;
for ($i = 1; $i < 101; ++$i) {
$an = -$i * ($i - $a);
$b += 2;
$d = $an * $d + $b;
if (abs($d) < $fpmin) {
$d = $fpmin;
}
$c = $b + $an / $c;
if (abs($c) < $fpmin) {
$c = $fpmin;
}
$d = 1 / $d;
$del = $d * $c;
$h = $h * $del;
if (abs($del - 1) < self::EPS) {
break;
}
}
return $h * exp(-$x + $a * log($x) - $gln);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Binomial.php 0000644 00000023273 15242753725 0025143 0 ustar 00 getMessage();
}
if (($value < 0) || ($value > $trials)) {
return ExcelError::NAN();
}
if ($cumulative) {
return self::calculateCumulativeBinomial($value, $trials, $probability);
}
/** @var float */
$comb = Combinations::withoutRepetition($trials, $value);
return $comb * $probability ** $value
* (1 - $probability) ** ($trials - $value);
}
/**
* BINOM.DIST.RANGE.
*
* Returns returns the Binomial Distribution probability for the number of successes from a specified number
* of trials falling into a specified range.
*
* @param mixed $trials Integer number of trials
* Or can be an array of values
* @param mixed $probability Probability of success on each trial as a float
* Or can be an array of values
* @param mixed $successes The integer number of successes in trials
* Or can be an array of values
* @param mixed $limit Upper limit for successes in trials as null, or an integer
* If null, then this will indicate the same as the number of Successes
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function range($trials, $probability, $successes, $limit = null)
{
if (is_array($trials) || is_array($probability) || is_array($successes) || is_array($limit)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $successes, $limit);
}
$limit = $limit ?? $successes;
try {
$trials = DistributionValidations::validateInt($trials);
$probability = DistributionValidations::validateProbability($probability);
$successes = DistributionValidations::validateInt($successes);
$limit = DistributionValidations::validateInt($limit);
} catch (Exception $e) {
return $e->getMessage();
}
if (($successes < 0) || ($successes > $trials)) {
return ExcelError::NAN();
}
if (($limit < 0) || ($limit > $trials) || $limit < $successes) {
return ExcelError::NAN();
}
$summer = 0;
for ($i = $successes; $i <= $limit; ++$i) {
/** @var float */
$comb = Combinations::withoutRepetition($trials, $i);
$summer += $comb * $probability ** $i
* (1 - $probability) ** ($trials - $i);
}
return $summer;
}
/**
* NEGBINOMDIST.
*
* Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
* there will be number_f failures before the number_s-th success, when the constant
* probability of a success is probability_s. This function is similar to the binomial
* distribution, except that the number of successes is fixed, and the number of trials is
* variable. Like the binomial, trials are assumed to be independent.
*
* @param mixed $failures Number of Failures as an integer
* Or can be an array of values
* @param mixed $successes Threshold number of Successes as an integer
* Or can be an array of values
* @param mixed $probability Probability of success on each trial as a float
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*
* TODO Add support for the cumulative flag not present for NEGBINOMDIST, but introduced for NEGBINOM.DIST
* The cumulative default should be false to reflect the behaviour of NEGBINOMDIST
*/
public static function negative($failures, $successes, $probability)
{
if (is_array($failures) || is_array($successes) || is_array($probability)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $failures, $successes, $probability);
}
try {
$failures = DistributionValidations::validateInt($failures);
$successes = DistributionValidations::validateInt($successes);
$probability = DistributionValidations::validateProbability($probability);
} catch (Exception $e) {
return $e->getMessage();
}
if (($failures < 0) || ($successes < 1)) {
return ExcelError::NAN();
}
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
if (($failures + $successes - 1) <= 0) {
return ExcelError::NAN();
}
}
/** @var float */
$comb = Combinations::withoutRepetition($failures + $successes - 1, $successes - 1);
return $comb
* ($probability ** $successes) * ((1 - $probability) ** $failures);
}
/**
* BINOM.INV.
*
* Returns the smallest value for which the cumulative binomial distribution is greater
* than or equal to a criterion value
*
* @param mixed $trials number of Bernoulli trials as an integer
* Or can be an array of values
* @param mixed $probability probability of a success on each trial as a float
* Or can be an array of values
* @param mixed $alpha criterion value as a float
* Or can be an array of values
*
* @return array|int|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverse($trials, $probability, $alpha)
{
if (is_array($trials) || is_array($probability) || is_array($alpha)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $trials, $probability, $alpha);
}
try {
$trials = DistributionValidations::validateInt($trials);
$probability = DistributionValidations::validateProbability($probability);
$alpha = DistributionValidations::validateFloat($alpha);
} catch (Exception $e) {
return $e->getMessage();
}
if ($trials < 0) {
return ExcelError::NAN();
} elseif (($alpha < 0.0) || ($alpha > 1.0)) {
return ExcelError::NAN();
}
$successes = 0;
while ($successes <= $trials) {
$result = self::calculateCumulativeBinomial($successes, $trials, $probability);
if ($result >= $alpha) {
break;
}
++$successes;
}
return $successes;
}
/**
* @return float|int
*/
private static function calculateCumulativeBinomial(int $value, int $trials, float $probability)
{
$summer = 0;
for ($i = 0; $i <= $value; ++$i) {
/** @var float */
$comb = Combinations::withoutRepetition($trials, $i);
$summer += $comb * $probability ** $i
* (1 - $probability) ** ($trials - $i);
}
return $summer;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/DistributionValidations.php 0000644 00000001246 15242753725 0030262 0 ustar 00 1.0) {
throw new Exception(ExcelError::NAN());
}
return $probability;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php 0000644 00000004543 15242753725 0025042 0 ustar 00 getMessage();
}
if (($value < 0) || ($mean < 0)) {
return ExcelError::NAN();
}
if ($cumulative) {
$summer = 0;
$floor = floor($value);
for ($i = 0; $i <= $floor; ++$i) {
/** @var float */
$fact = MathTrig\Factorial::fact($i);
$summer += $mean ** $i / $fact;
}
return exp(0 - $mean) * $summer;
}
/** @var float */
$fact = MathTrig\Factorial::fact($value);
return (exp(0 - $mean) * $mean ** $value) / $fact;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/HyperGeometric.php 0000644 00000006325 15242753725 0026336 0 ustar 00 getMessage();
}
if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
return ExcelError::NAN();
}
if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
return ExcelError::NAN();
}
if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
return ExcelError::NAN();
}
$successesPopulationAndSample = (float) Combinations::withoutRepetition($populationSuccesses, $sampleSuccesses);
$numbersPopulationAndSample = (float) Combinations::withoutRepetition($populationNumber, $sampleNumber);
$adjustedPopulationAndSample = (float) Combinations::withoutRepetition(
$populationNumber - $populationSuccesses,
$sampleNumber - $sampleSuccesses
);
return $successesPopulationAndSample * $adjustedPopulationAndSample / $numbersPopulationAndSample;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Normal.php 0000644 00000015774 15242753725 0024650 0 ustar 00 getMessage();
}
if ($stdDev < 0) {
return ExcelError::NAN();
}
if ($cumulative) {
return 0.5 * (1 + Engineering\Erf::erfValue(($value - $mean) / ($stdDev * sqrt(2))));
}
return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (($value - $mean) ** 2 / (2 * ($stdDev * $stdDev))));
}
/**
* NORMINV.
*
* Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
*
* @param mixed $probability Float probability for which we want the value
* Or can be an array of values
* @param mixed $mean Mean Value as a float
* Or can be an array of values
* @param mixed $stdDev Standard Deviation as a float
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverse($probability, $mean, $stdDev)
{
if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
}
try {
$probability = DistributionValidations::validateProbability($probability);
$mean = DistributionValidations::validateFloat($mean);
$stdDev = DistributionValidations::validateFloat($stdDev);
} catch (Exception $e) {
return $e->getMessage();
}
if ($stdDev < 0) {
return ExcelError::NAN();
}
return (self::inverseNcdf($probability) * $stdDev) + $mean;
}
/*
* inverse_ncdf.php
* -------------------
* begin : Friday, January 16, 2004
* copyright : (C) 2004 Michael Nickerson
* email : nickersonm@yahoo.com
*
*/
private static function inverseNcdf(float $p): float
{
// Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
// PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
// a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
// I have not checked the accuracy of this implementation. Be aware that PHP
// will truncate the coeficcients to 14 digits.
// You have permission to use and distribute this function freely for
// whatever purpose you want, but please show common courtesy and give credit
// where credit is due.
// Input paramater is $p - probability - where 0 < p < 1.
// Coefficients in rational approximations
static $a = [
1 => -3.969683028665376e+01,
2 => 2.209460984245205e+02,
3 => -2.759285104469687e+02,
4 => 1.383577518672690e+02,
5 => -3.066479806614716e+01,
6 => 2.506628277459239e+00,
];
static $b = [
1 => -5.447609879822406e+01,
2 => 1.615858368580409e+02,
3 => -1.556989798598866e+02,
4 => 6.680131188771972e+01,
5 => -1.328068155288572e+01,
];
static $c = [
1 => -7.784894002430293e-03,
2 => -3.223964580411365e-01,
3 => -2.400758277161838e+00,
4 => -2.549732539343734e+00,
5 => 4.374664141464968e+00,
6 => 2.938163982698783e+00,
];
static $d = [
1 => 7.784695709041462e-03,
2 => 3.224671290700398e-01,
3 => 2.445134137142996e+00,
4 => 3.754408661907416e+00,
];
// Define lower and upper region break-points.
$p_low = 0.02425; //Use lower region approx. below this
$p_high = 1 - $p_low; //Use upper region approx. above this
if (0 < $p && $p < $p_low) {
// Rational approximation for lower region.
$q = sqrt(-2 * log($p));
return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
(((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
} elseif ($p_high < $p && $p < 1) {
// Rational approximation for upper region.
$q = sqrt(-2 * log(1 - $p));
return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
(((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
}
// Rational approximation for central region.
$q = $p - 0.5;
$r = $q * $q;
return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php 0000644 00000012265 15242753725 0024432 0 ustar 00 getMessage();
}
if ((((int) $value) == ((float) $value)) && $value <= 0.0) {
return ExcelError::NAN();
}
return self::gammaValue($value);
}
/**
* GAMMADIST.
*
* Returns the gamma distribution.
*
* @param mixed $value Float Value at which you want to evaluate the distribution
* Or can be an array of values
* @param mixed $a Parameter to the distribution as a float
* Or can be an array of values
* @param mixed $b Parameter to the distribution as a float
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function distribution($value, $a, $b, $cumulative)
{
if (is_array($value) || is_array($a) || is_array($b) || is_array($cumulative)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $a, $b, $cumulative);
}
try {
$value = DistributionValidations::validateFloat($value);
$a = DistributionValidations::validateFloat($a);
$b = DistributionValidations::validateFloat($b);
$cumulative = DistributionValidations::validateBool($cumulative);
} catch (Exception $e) {
return $e->getMessage();
}
if (($value < 0) || ($a <= 0) || ($b <= 0)) {
return ExcelError::NAN();
}
return self::calculateDistribution($value, $a, $b, $cumulative);
}
/**
* GAMMAINV.
*
* Returns the inverse of the Gamma distribution.
*
* @param mixed $probability Float probability at which you want to evaluate the distribution
* Or can be an array of values
* @param mixed $alpha Parameter to the distribution as a float
* Or can be an array of values
* @param mixed $beta Parameter to the distribution as a float
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverse($probability, $alpha, $beta)
{
if (is_array($probability) || is_array($alpha) || is_array($beta)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $alpha, $beta);
}
try {
$probability = DistributionValidations::validateProbability($probability);
$alpha = DistributionValidations::validateFloat($alpha);
$beta = DistributionValidations::validateFloat($beta);
} catch (Exception $e) {
return $e->getMessage();
}
if (($alpha <= 0.0) || ($beta <= 0.0)) {
return ExcelError::NAN();
}
return self::calculateInverse($probability, $alpha, $beta);
}
/**
* GAMMALN.
*
* Returns the natural logarithm of the gamma function.
*
* @param mixed $value Float Value at which you want to evaluate the distribution
* Or can be an array of values
*
* @return array|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function ln($value)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
try {
$value = DistributionValidations::validateFloat($value);
} catch (Exception $e) {
return $e->getMessage();
}
if ($value <= 0) {
return ExcelError::NAN();
}
return log(self::gammaValue($value));
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/F.php 0000644 00000005056 15242753725 0023575 0 ustar 00 getMessage();
}
if ($value < 0 || $u < 1 || $v < 1) {
return ExcelError::NAN();
}
if ($cumulative) {
$adjustedValue = ($u * $value) / ($u * $value + $v);
return Beta::incompleteBeta($adjustedValue, $u / 2, $v / 2);
}
return (Gamma::gammaValue(($v + $u) / 2) /
(Gamma::gammaValue($u / 2) * Gamma::gammaValue($v / 2))) *
(($u / $v) ** ($u / 2)) *
(($value ** (($u - 2) / 2)) / ((1 + ($u / $v) * $value) ** (($u + $v) / 2)));
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php 0000644 00000004233 15242753725 0025007 0 ustar 00 getMessage();
}
if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
return ExcelError::NAN();
}
if ($cumulative) {
return 1 - exp(0 - ($value / $beta) ** $alpha);
}
return ($alpha / $beta ** $alpha) * $value ** ($alpha - 1) * exp(0 - ($value / $beta) ** $alpha);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php 0000644 00000011321 15242753725 0025152 0 ustar 00 getMessage();
}
if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
return ExcelError::NAN();
}
return self::calculateDistribution($value, $degrees, $tails);
}
/**
* TINV.
*
* Returns the one-tailed probability of the chi-squared distribution.
*
* @param mixed $probability Float probability for the function
* Or can be an array of values
* @param mixed $degrees Integer value for degrees of freedom
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function inverse($probability, $degrees)
{
if (is_array($probability) || is_array($degrees)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
}
try {
$probability = DistributionValidations::validateProbability($probability);
$degrees = DistributionValidations::validateInt($degrees);
} catch (Exception $e) {
return $e->getMessage();
}
if ($degrees <= 0) {
return ExcelError::NAN();
}
$callback = function ($value) use ($degrees) {
return self::distribution($value, $degrees, 2);
};
$newtonRaphson = new NewtonRaphson($callback);
return $newtonRaphson->execute($probability);
}
/**
* @return float
*/
private static function calculateDistribution(float $value, int $degrees, int $tails)
{
// tdist, which finds the probability that corresponds to a given value
// of t with k degrees of freedom. This algorithm is translated from a
// pascal function on p81 of "Statistical Computing in Pascal" by D
// Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
// London). The above Pascal algorithm is itself a translation of the
// fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
// Laboratory as reported in (among other places) "Applied Statistics
// Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
// Horwood Ltd.; W. Sussex, England).
$tterm = $degrees;
$ttheta = atan2($value, sqrt($tterm));
$tc = cos($ttheta);
$ts = sin($ttheta);
if (($degrees % 2) === 1) {
$ti = 3;
$tterm = $tc;
} else {
$ti = 2;
$tterm = 1;
}
$tsum = $tterm;
while ($ti < $degrees) {
$tterm *= $tc * $tc * ($ti - 1) / $ti;
$tsum += $tterm;
$ti += 2;
}
$tsum *= $ts;
if (($degrees % 2) == 1) {
$tsum = Functions::M_2DIVPI * ($tsum + $ttheta);
}
$tValue = 0.5 * (1 + $tsum);
if ($tails == 1) {
return 1 - abs($tValue);
}
return 1 - abs((1 - $tValue) - $tValue);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/AggregateBase.php 0000644 00000004066 15242753725 0023227 0 ustar 00 $returnValue)) {
$returnValue = $arg;
}
}
}
if ($returnValue === null) {
return 0;
}
return $returnValue;
}
/**
* MAXA.
*
* Returns the greatest value in a list of arguments, including numbers, text, and logical values
*
* Excel Function:
* MAXA(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float
*/
public static function maxA(...$args)
{
$returnValue = null;
// Loop through arguments
$aArgs = Functions::flattenArray($args);
foreach ($aArgs as $arg) {
if (ErrorValue::isError($arg)) {
$returnValue = $arg;
break;
}
// Is it a numeric value?
if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
$arg = self::datatypeAdjustmentAllowStrings($arg);
if (($returnValue === null) || ($arg > $returnValue)) {
$returnValue = $arg;
}
}
}
if ($returnValue === null) {
return 0;
}
return $returnValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php 0000644 00000004704 15242753725 0021457 0 ustar 00 = $count) {
return ExcelError::NAN();
}
rsort($mArgs);
return $mArgs[$entry];
}
return ExcelError::VALUE();
}
/**
* SMALL.
*
* Returns the nth smallest value in a data set. You can use this function to
* select a value based on its relative standing.
*
* Excel Function:
* SMALL(value1[,value2[, ...]],entry)
*
* @param mixed $args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function small(...$args)
{
$aArgs = Functions::flattenArray($args);
$entry = array_pop($aArgs);
if ((is_numeric($entry)) && (!is_string($entry))) {
$entry = (int) floor($entry);
$mArgs = self::filter($aArgs);
$count = Counts::COUNT($mArgs);
--$entry;
if ($count === 0 || $entry < 0 || $entry >= $count) {
return ExcelError::NAN();
}
sort($mArgs);
return $mArgs[$entry];
}
return ExcelError::VALUE();
}
/**
* @param mixed[] $args Data values
*/
protected static function filter(array $args): array
{
$mArgs = [];
foreach ($args as $arg) {
// Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) {
$mArgs[] = $arg;
}
}
return $mArgs;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php 0000644 00000034246 15242753725 0022010 0 ustar 00 $value) {
if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
unset($array1[$key], $array2[$key]);
}
}
}
/**
* @param mixed $array1 should be array, but scalar is made into one
* @param mixed $array2 should be array, but scalar is made into one
*/
private static function checkTrendArrays(&$array1, &$array2): void
{
if (!is_array($array1)) {
$array1 = [$array1];
}
if (!is_array($array2)) {
$array2 = [$array2];
}
$array1 = Functions::flattenArray($array1);
$array2 = Functions::flattenArray($array2);
self::filterTrendValues($array1, $array2);
self::filterTrendValues($array2, $array1);
// Reset the array indexes
$array1 = array_merge($array1);
$array2 = array_merge($array2);
}
protected static function validateTrendArrays(array $yValues, array $xValues): void
{
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount === 0) || ($yValueCount !== $xValueCount)) {
throw new Exception(ExcelError::NA());
} elseif ($yValueCount === 1) {
throw new Exception(ExcelError::DIV0());
}
}
/**
* CORREL.
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
* @param mixed $yValues array of mixed Data Series Y
* @param null|mixed $xValues array of mixed Data Series X
*
* @return float|string
*/
public static function CORREL($yValues, $xValues = null)
{
if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
return ExcelError::VALUE();
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getCorrelation();
}
/**
* COVAR.
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues array of mixed Data Series X
*
* @return float|string
*/
public static function COVAR($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getCovariance();
}
/**
* FORECAST.
*
* Calculates, or predicts, a future value by using existing values.
* The predicted value is a y-value for a given x-value.
*
* @param mixed $xValue Float value of X for which we want to find Y
* Or can be an array of values
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues of mixed Data Series X
*
* @return array|bool|float|string
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function FORECAST($xValue, $yValues, $xValues)
{
if (is_array($xValue)) {
return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $xValue, $yValues, $xValues);
}
try {
$xValue = StatisticalValidations::validateFloat($xValue);
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getValueOfYForX($xValue);
}
/**
* GROWTH.
*
* Returns values along a predicted exponential Trend
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
* @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
*
* @return float[]
*/
public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
{
$yValues = Functions::flattenArray($yValues);
$xValues = Functions::flattenArray($xValues);
$newValues = Functions::flattenArray($newValues);
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
if (empty($newValues)) {
$newValues = $bestFitExponential->getXValues();
}
$returnArray = [];
foreach ($newValues as $xValue) {
$returnArray[0][] = [$bestFitExponential->getValueOfYForX($xValue)];
}
return $returnArray; //* @phpstan-ignore-line
}
/**
* INTERCEPT.
*
* Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string
*/
public static function INTERCEPT($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getIntersect();
}
/**
* LINEST.
*
* Calculates the statistics for a line by using the "least squares" method to calculate a straight line
* that best fits your data, and then returns an array that describes the line.
*
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
* @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
*
* @return array|int|string The result, or a string containing an error
*/
public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
{
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
if ($xValues === null) {
$xValues = $yValues;
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
if ($stats === true) {
return [
[
$bestFitLinear->getSlope(),
$bestFitLinear->getIntersect(),
],
[
$bestFitLinear->getSlopeSE(),
($const === false) ? ExcelError::NA() : $bestFitLinear->getIntersectSE(),
],
[
$bestFitLinear->getGoodnessOfFit(),
$bestFitLinear->getStdevOfResiduals(),
],
[
$bestFitLinear->getF(),
$bestFitLinear->getDFResiduals(),
],
[
$bestFitLinear->getSSRegression(),
$bestFitLinear->getSSResiduals(),
],
];
}
return [
$bestFitLinear->getSlope(),
$bestFitLinear->getIntersect(),
];
}
/**
* LOGEST.
*
* Calculates an exponential curve that best fits the X and Y data series,
* and then returns an array that describes the line.
*
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
* @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
*
* @return array|int|string The result, or a string containing an error
*/
public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
{
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
if ($xValues === null) {
$xValues = $yValues;
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
foreach ($yValues as $value) {
if ($value < 0.0) {
return ExcelError::NAN();
}
}
$bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
if ($stats === true) {
return [
[
$bestFitExponential->getSlope(),
$bestFitExponential->getIntersect(),
],
[
$bestFitExponential->getSlopeSE(),
($const === false) ? ExcelError::NA() : $bestFitExponential->getIntersectSE(),
],
[
$bestFitExponential->getGoodnessOfFit(),
$bestFitExponential->getStdevOfResiduals(),
],
[
$bestFitExponential->getF(),
$bestFitExponential->getDFResiduals(),
],
[
$bestFitExponential->getSSRegression(),
$bestFitExponential->getSSResiduals(),
],
];
}
return [
$bestFitExponential->getSlope(),
$bestFitExponential->getIntersect(),
];
}
/**
* RSQ.
*
* Returns the square of the Pearson product moment correlation coefficient through data points
* in known_y's and known_x's.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string The result, or a string containing an error
*/
public static function RSQ($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getGoodnessOfFit();
}
/**
* SLOPE.
*
* Returns the slope of the linear regression line through data points in known_y's and known_x's.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string The result, or a string containing an error
*/
public static function SLOPE($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getSlope();
}
/**
* STEYX.
*
* Returns the standard error of the predicted y-value for each x in the regression.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string
*/
public static function STEYX($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getStdevOfResiduals();
}
/**
* TREND.
*
* Returns values along a linear Trend
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
* @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
*
* @return float[]
*/
public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
{
$yValues = Functions::flattenArray($yValues);
$xValues = Functions::flattenArray($xValues);
$newValues = Functions::flattenArray($newValues);
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
if (empty($newValues)) {
$newValues = $bestFitLinear->getXValues();
}
$returnArray = [];
foreach ($newValues as $xValue) {
$returnArray[0][] = [$bestFitLinear->getValueOfYForX($xValue)];
}
return $returnArray; //* @phpstan-ignore-line
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Percentiles.php 0000644 00000014343 15242753725 0023022 0 ustar 00 getMessage();
}
if (($entry < 0) || ($entry > 1)) {
return ExcelError::NAN();
}
$mArgs = self::percentileFilterValues($aArgs);
$mValueCount = count($mArgs);
if ($mValueCount > 0) {
sort($mArgs);
$count = Counts::COUNT($mArgs);
$index = $entry * ($count - 1);
$iBase = floor($index);
if ($index == $iBase) {
return $mArgs[$index];
}
$iNext = $iBase + 1;
$iProportion = $index - $iBase;
return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion);
}
return ExcelError::NAN();
}
/**
* PERCENTRANK.
*
* Returns the rank of a value in a data set as a percentage of the data set.
* Note that the returned rank is simply rounded to the appropriate significant digits,
* rather than floored (as MS Excel), so value 3 for a value set of 1, 2, 3, 4 will return
* 0.667 rather than 0.666
*
* @param mixed $valueSet An array of (float) values, or a reference to, a list of numbers
* @param mixed $value The number whose rank you want to find
* @param mixed $significance The (integer) number of significant digits for the returned percentage value
*
* @return float|string (string if result is an error)
*/
public static function PERCENTRANK($valueSet, $value, $significance = 3)
{
$valueSet = Functions::flattenArray($valueSet);
$value = Functions::flattenSingleValue($value);
$significance = ($significance === null) ? 3 : Functions::flattenSingleValue($significance);
try {
$value = StatisticalValidations::validateFloat($value);
$significance = StatisticalValidations::validateInt($significance);
} catch (Exception $e) {
return $e->getMessage();
}
$valueSet = self::rankFilterValues($valueSet);
$valueCount = count($valueSet);
if ($valueCount == 0) {
return ExcelError::NA();
}
sort($valueSet, SORT_NUMERIC);
$valueAdjustor = $valueCount - 1;
if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
return ExcelError::NA();
}
$pos = array_search($value, $valueSet);
if ($pos === false) {
$pos = 0;
$testValue = $valueSet[0];
while ($testValue < $value) {
$testValue = $valueSet[++$pos];
}
--$pos;
$pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
}
return round(((float) $pos) / $valueAdjustor, $significance);
}
/**
* QUARTILE.
*
* Returns the quartile of a data set.
*
* Excel Function:
* QUARTILE(value1[,value2[, ...]],entry)
*
* @param mixed $args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function QUARTILE(...$args)
{
$aArgs = Functions::flattenArray($args);
$entry = array_pop($aArgs);
try {
$entry = StatisticalValidations::validateFloat($entry);
} catch (Exception $e) {
return $e->getMessage();
}
$entry = floor($entry);
$entry /= 4;
if (($entry < 0) || ($entry > 1)) {
return ExcelError::NAN();
}
return self::PERCENTILE($aArgs, $entry);
}
/**
* RANK.
*
* Returns the rank of a number in a list of numbers.
*
* @param mixed $value The number whose rank you want to find
* @param mixed $valueSet An array of float values, or a reference to, a list of numbers
* @param mixed $order Order to sort the values in the value set
*
* @return float|string The result, or a string containing an error (0 = Descending, 1 = Ascending)
*/
public static function RANK($value, $valueSet, $order = self::RANK_SORT_DESCENDING)
{
$value = Functions::flattenSingleValue($value);
$valueSet = Functions::flattenArray($valueSet);
$order = ($order === null) ? self::RANK_SORT_DESCENDING : Functions::flattenSingleValue($order);
try {
$value = StatisticalValidations::validateFloat($value);
$order = StatisticalValidations::validateInt($order);
} catch (Exception $e) {
return $e->getMessage();
}
$valueSet = self::rankFilterValues($valueSet);
if ($order === self::RANK_SORT_DESCENDING) {
rsort($valueSet, SORT_NUMERIC);
} else {
sort($valueSet, SORT_NUMERIC);
}
$pos = array_search($value, $valueSet);
if ($pos === false) {
return ExcelError::NA();
}
return ++$pos;
}
protected static function percentileFilterValues(array $dataSet): array
{
return array_filter(
$dataSet,
function ($value): bool {
return is_numeric($value) && !is_string($value);
}
);
}
protected static function rankFilterValues(array $dataSet): array
{
return array_filter(
$dataSet,
function ($value): bool {
return is_numeric($value);
}
);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php 0000644 00000011755 15242753725 0022464 0 ustar 00 1) {
$summerA *= $aCount;
$summerB *= $summerB;
return ($summerA - $summerB) / ($aCount * ($aCount - 1));
}
return $returnValue;
}
/**
* VARA.
*
* Estimates variance based on a sample, including numbers, text, and logical values
*
* Excel Function:
* VARA(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARA(...$args)
{
$returnValue = ExcelError::DIV0();
$summerA = $summerB = 0.0;
// Loop through arguments
$aArgs = Functions::flattenArrayIndexed($args);
$aCount = 0;
foreach ($aArgs as $k => $arg) {
if ((is_string($arg)) && (Functions::isValue($k))) {
return ExcelError::VALUE();
} elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
} else {
// Is it a numeric value?
if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
$arg = self::datatypeAdjustmentAllowStrings($arg);
$summerA += ($arg * $arg);
$summerB += $arg;
++$aCount;
}
}
}
if ($aCount > 1) {
$summerA *= $aCount;
$summerB *= $summerB;
return ($summerA - $summerB) / ($aCount * ($aCount - 1));
}
return $returnValue;
}
/**
* VARP.
*
* Calculates variance based on the entire population
*
* Excel Function:
* VARP(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARP(...$args)
{
// Return value
$returnValue = ExcelError::DIV0();
$summerA = $summerB = 0.0;
// Loop through arguments
$aArgs = Functions::flattenArray($args);
$aCount = 0;
foreach ($aArgs as $arg) {
$arg = self::datatypeAdjustmentBooleans($arg);
// Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) {
$summerA += ($arg * $arg);
$summerB += $arg;
++$aCount;
}
}
if ($aCount > 0) {
$summerA *= $aCount;
$summerB *= $summerB;
return ($summerA - $summerB) / ($aCount * $aCount);
}
return $returnValue;
}
/**
* VARPA.
*
* Calculates variance based on the entire population, including numbers, text, and logical values
*
* Excel Function:
* VARPA(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARPA(...$args)
{
$returnValue = ExcelError::DIV0();
$summerA = $summerB = 0.0;
// Loop through arguments
$aArgs = Functions::flattenArrayIndexed($args);
$aCount = 0;
foreach ($aArgs as $k => $arg) {
if ((is_string($arg)) && (Functions::isValue($k))) {
return ExcelError::VALUE();
} elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
} else {
// Is it a numeric value?
if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
$arg = self::datatypeAdjustmentAllowStrings($arg);
$summerA += ($arg * $arg);
$summerB += $arg;
++$aCount;
}
}
}
if ($aCount > 0) {
$summerA *= $aCount;
$summerB *= $summerB;
return ($summerA - $summerB) / ($aCount * $aCount);
}
return $returnValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php 0000644 00000001715 15242753725 0025226 0 ustar 00 $arg) {
$arg = self::testAcceptedBoolean($arg, $k);
// Is it a numeric value?
// Strings containing numeric values are only counted if they are string literals (not cell values)
// and then only in MS Excel and in Open Office, not in Gnumeric
if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
return ExcelError::VALUE();
}
if (self::isAcceptedCountable($arg, $k)) {
$returnValue += abs($arg - $aMean);
++$aCount;
}
}
// Return
if ($aCount === 0) {
return ExcelError::DIV0();
}
return $returnValue / $aCount;
}
/**
* AVERAGE.
*
* Returns the average (arithmetic mean) of the arguments
*
* Excel Function:
* AVERAGE(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function average(...$args)
{
$returnValue = $aCount = 0;
// Loop through arguments
foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
$arg = self::testAcceptedBoolean($arg, $k);
// Is it a numeric value?
// Strings containing numeric values are only counted if they are string literals (not cell values)
// and then only in MS Excel and in Open Office, not in Gnumeric
if ((is_string($arg)) && (!is_numeric($arg)) && (!Functions::isCellValue($k))) {
return ExcelError::VALUE();
}
if (self::isAcceptedCountable($arg, $k)) {
$returnValue += $arg;
++$aCount;
}
}
// Return
if ($aCount > 0) {
return $returnValue / $aCount;
}
return ExcelError::DIV0();
}
/**
* AVERAGEA.
*
* Returns the average of its arguments, including numbers, text, and logical values
*
* Excel Function:
* AVERAGEA(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function averageA(...$args)
{
$returnValue = null;
$aCount = 0;
// Loop through arguments
foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
if (is_numeric($arg)) {
// do nothing
} elseif (is_bool($arg)) {
$arg = (int) $arg;
} elseif (!Functions::isMatrixValue($k)) {
$arg = 0;
} else {
return ExcelError::VALUE();
}
$returnValue += $arg;
++$aCount;
}
if ($aCount > 0) {
return $returnValue / $aCount;
}
return ExcelError::DIV0();
}
/**
* MEDIAN.
*
* Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
*
* Excel Function:
* MEDIAN(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function median(...$args)
{
$aArgs = Functions::flattenArray($args);
$returnValue = ExcelError::NAN();
$aArgs = self::filterArguments($aArgs);
$valueCount = count($aArgs);
if ($valueCount > 0) {
sort($aArgs, SORT_NUMERIC);
$valueCount = $valueCount / 2;
if ($valueCount == floor($valueCount)) {
$returnValue = ($aArgs[$valueCount--] + $aArgs[$valueCount]) / 2;
} else {
$valueCount = floor($valueCount);
$returnValue = $aArgs[$valueCount];
}
}
return $returnValue;
}
/**
* MODE.
*
* Returns the most frequently occurring, or repetitive, value in an array or range of data
*
* Excel Function:
* MODE(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function mode(...$args)
{
$returnValue = ExcelError::NA();
// Loop through arguments
$aArgs = Functions::flattenArray($args);
$aArgs = self::filterArguments($aArgs);
if (!empty($aArgs)) {
return self::modeCalc($aArgs);
}
return $returnValue;
}
protected static function filterArguments(array $args): array
{
return array_filter(
$args,
function ($value) {
// Is it a numeric value?
return is_numeric($value) && (!is_string($value));
}
);
}
/**
* Special variant of array_count_values that isn't limited to strings and integers,
* but can work with floating point numbers as values.
*
* @return float|string
*/
private static function modeCalc(array $data)
{
$frequencyArray = [];
$index = 0;
$maxfreq = 0;
$maxfreqkey = '';
$maxfreqdatum = '';
foreach ($data as $datum) {
$found = false;
++$index;
foreach ($frequencyArray as $key => $value) {
if ((string) $value['value'] == (string) $datum) {
++$frequencyArray[$key]['frequency'];
$freq = $frequencyArray[$key]['frequency'];
if ($freq > $maxfreq) {
$maxfreq = $freq;
$maxfreqkey = $key;
$maxfreqdatum = $datum;
} elseif ($freq == $maxfreq) {
if ($frequencyArray[$key]['index'] < $frequencyArray[$maxfreqkey]['index']) {
$maxfreqkey = $key;
$maxfreqdatum = $datum;
}
}
$found = true;
break;
}
}
if ($found === false) {
$frequencyArray[] = [
'value' => $datum,
'frequency' => 1,
'index' => $index,
];
}
}
if ($maxfreq <= 1) {
return ExcelError::NA();
}
return $maxfreqdatum;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Deviations.php 0000644 00000010563 15242753725 0022652 0 ustar 00 $arg) {
// Is it a numeric value?
if (
(is_bool($arg)) &&
((!Functions::isCellValue($k)) ||
(Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))
) {
$arg = (int) $arg;
}
if ((is_numeric($arg)) && (!is_string($arg))) {
$returnValue += ($arg - $aMean) ** 2;
++$aCount;
}
}
return $aCount === 0 ? ExcelError::VALUE() : $returnValue;
}
/**
* KURT.
*
* Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
* or flatness of a distribution compared with the normal distribution. Positive
* kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
* relatively flat distribution.
*
* @param array ...$args Data Series
*
* @return float|string
*/
public static function kurtosis(...$args)
{
$aArgs = Functions::flattenArrayIndexed($args);
$mean = Averages::average($aArgs);
if (!is_numeric($mean)) {
return ExcelError::DIV0();
}
$stdDev = StandardDeviations::STDEV($aArgs);
if ($stdDev > 0) {
$count = $summer = 0;
foreach ($aArgs as $k => $arg) {
if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
} else {
// Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) {
$summer += (($arg - $mean) / $stdDev) ** 4;
++$count;
}
}
}
if ($count > 3) {
return $summer * ($count * ($count + 1) /
(($count - 1) * ($count - 2) * ($count - 3))) - (3 * ($count - 1) ** 2 /
(($count - 2) * ($count - 3)));
}
}
return ExcelError::DIV0();
}
/**
* SKEW.
*
* Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
* of a distribution around its mean. Positive skewness indicates a distribution with an
* asymmetric tail extending toward more positive values. Negative skewness indicates a
* distribution with an asymmetric tail extending toward more negative values.
*
* @param array ...$args Data Series
*
* @return float|int|string The result, or a string containing an error
*/
public static function skew(...$args)
{
$aArgs = Functions::flattenArrayIndexed($args);
$mean = Averages::average($aArgs);
if (!is_numeric($mean)) {
return ExcelError::DIV0();
}
$stdDev = StandardDeviations::STDEV($aArgs);
if ($stdDev === 0.0 || is_string($stdDev)) {
return ExcelError::DIV0();
}
$count = $summer = 0;
// Loop through arguments
foreach ($aArgs as $k => $arg) {
if ((is_bool($arg)) && (!Functions::isMatrixValue($k))) {
} elseif (!is_numeric($arg)) {
return ExcelError::VALUE();
} else {
// Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) {
$summer += (($arg - $mean) / $stdDev) ** 3;
++$count;
}
}
}
if ($count > 2) {
return $summer * ($count / (($count - 1) * ($count - 2)));
}
return ExcelError::DIV0();
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php 0000644 00000001425 15242753725 0023065 0 ustar 00 getMessage();
}
if ($stdDev <= 0) {
return ExcelError::NAN();
}
return ($value - $mean) / $stdDev;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Minimum.php 0000644 00000004416 15242753725 0022160 0 ustar 00 $arg) {
$arg = self::testAcceptedBoolean($arg, $k);
// Is it a numeric value?
// Strings containing numeric values are only counted if they are string literals (not cell values)
// and then only in MS Excel and in Open Office, not in Gnumeric
if (self::isAcceptedCountable($arg, $k, true)) {
++$returnValue;
}
}
return $returnValue;
}
/**
* COUNTA.
*
* Counts the number of cells that are not empty within the list of arguments
*
* Excel Function:
* COUNTA(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return int
*/
public static function COUNTA(...$args)
{
$returnValue = 0;
// Loop through arguments
$aArgs = Functions::flattenArrayIndexed($args);
foreach ($aArgs as $k => $arg) {
// Nulls are counted if literals, but not if cell values
if ($arg !== null || (!Functions::isCellValue($k))) {
++$returnValue;
}
}
return $returnValue;
}
/**
* COUNTBLANK.
*
* Counts the number of empty cells within the list of arguments
*
* Excel Function:
* COUNTBLANK(value1[,value2[, ...]])
*
* @param mixed $range Data values
*
* @return int
*/
public static function COUNTBLANK($range)
{
if ($range === null) {
return 1;
}
if (!is_array($range) || array_key_exists(0, $range)) {
throw new CalcException('Must specify range of cells, not any kind of literal');
}
$returnValue = 0;
// Loop through arguments
$aArgs = Functions::flattenArray($range);
foreach ($aArgs as $arg) {
// Is it a blank cell?
if (($arg === null) || ((is_string($arg)) && ($arg == ''))) {
++$returnValue;
}
}
return $returnValue;
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php 0000644 00000007240 15242753725 0023235 0 ustar 00 getMessage();
}
if ($numObjs < $numInSet) {
return ExcelError::NAN();
}
$result1 = MathTrig\Factorial::fact($numObjs);
if (is_string($result1)) {
return $result1;
}
$result2 = MathTrig\Factorial::fact($numObjs - $numInSet);
if (is_string($result2)) {
return $result2;
}
// phpstan thinks result1 and result2 can be arrays; they can't.
$result = round($result1 / $result2); // @phpstan-ignore-line
return IntOrFloat::evaluate($result);
}
/**
* PERMUTATIONA.
*
* Returns the number of permutations for a given number of objects (with repetitions)
* that can be selected from the total objects.
*
* @param mixed $numObjs Integer number of different objects
* Or can be an array of values
* @param mixed $numInSet Integer number of objects in each permutation
* Or can be an array of values
*
* @return array|float|int|string Number of permutations, or a string containing an error
* If an array of numbers is passed as an argument, then the returned result will also be an array
* with the same dimensions
*/
public static function PERMUTATIONA($numObjs, $numInSet)
{
if (is_array($numObjs) || is_array($numInSet)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
}
try {
$numObjs = StatisticalValidations::validateInt($numObjs);
$numInSet = StatisticalValidations::validateInt($numInSet);
} catch (Exception $e) {
return $e->getMessage();
}
if ($numObjs < 0 || $numInSet < 0) {
return ExcelError::NAN();
}
$result = $numObjs ** $numInSet;
return IntOrFloat::evaluate($result);
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/MaxMinBase.php 0000644 00000000631 15242753725 0022524 0 ustar 00 getMessage();
}
if (($alpha <= 0) || ($alpha >= 1) || ($stdDev <= 0) || ($size < 1)) {
return ExcelError::NAN();
}
/** @var float */
$temp = Distributions\StandardNormal::inverse(1 - $alpha / 2);
return Functions::scalar($temp * $stdDev / sqrt($size));
}
}
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/functions 0000644 00000026520 15242753725 0021355 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Jezyk polski (Polish)
##
############################################################
##
## Funkcje baz danych (Cube Functions)
##
CUBEKPIMEMBER = ELEMENT.KPI.MODUŁU
CUBEMEMBER = ELEMENT.MODUŁU
CUBEMEMBERPROPERTY = WŁAŚCIWOŚĆ.ELEMENTU.MODUŁU
CUBERANKEDMEMBER = USZEREGOWANY.ELEMENT.MODUŁU
CUBESET = ZESTAW.MODUŁÓW
CUBESETCOUNT = LICZNIK.MODUŁÓW.ZESTAWU
CUBEVALUE = WARTOŚĆ.MODUŁU
##
## Funkcje baz danych (Database Functions)
##
DAVERAGE = BD.ŚREDNIA
DCOUNT = BD.ILE.REKORDÓW
DCOUNTA = BD.ILE.REKORDÓW.A
DGET = BD.POLE
DMAX = BD.MAX
DMIN = BD.MIN
DPRODUCT = BD.ILOCZYN
DSTDEV = BD.ODCH.STANDARD
DSTDEVP = BD.ODCH.STANDARD.POPUL
DSUM = BD.SUMA
DVAR = BD.WARIANCJA
DVARP = BD.WARIANCJA.POPUL
##
## Funkcje daty i godziny (Date & Time Functions)
##
DATE = DATA
DATEDIF = DATA.RÓŻNICA
DATESTRING = DATA.CIĄG.ZNAK
DATEVALUE = DATA.WARTOŚĆ
DAY = DZIEŃ
DAYS = DNI
DAYS360 = DNI.360
EDATE = NR.SER.DATY
EOMONTH = NR.SER.OST.DN.MIES
HOUR = GODZINA
ISOWEEKNUM = ISO.NUM.TYG
MINUTE = MINUTA
MONTH = MIESIĄC
NETWORKDAYS = DNI.ROBOCZE
NETWORKDAYS.INTL = DNI.ROBOCZE.NIESTAND
NOW = TERAZ
SECOND = SEKUNDA
THAIDAYOFWEEK = TAJ.DZIEŃ.TYGODNIA
THAIMONTHOFYEAR = TAJ.MIESIĄC.ROKU
THAIYEAR = TAJ.ROK
TIME = CZAS
TIMEVALUE = CZAS.WARTOŚĆ
TODAY = DZIŚ
WEEKDAY = DZIEŃ.TYG
WEEKNUM = NUM.TYG
WORKDAY = DZIEŃ.ROBOCZY
WORKDAY.INTL = DZIEŃ.ROBOCZY.NIESTAND
YEAR = ROK
YEARFRAC = CZĘŚĆ.ROKU
##
## Funkcje inżynierskie (Engineering Functions)
##
BESSELI = BESSEL.I
BESSELJ = BESSEL.J
BESSELK = BESSEL.K
BESSELY = BESSEL.Y
BIN2DEC = DWÓJK.NA.DZIES
BIN2HEX = DWÓJK.NA.SZESN
BIN2OCT = DWÓJK.NA.ÓSM
BITAND = BITAND
BITLSHIFT = BIT.PRZESUNIĘCIE.W.LEWO
BITOR = BITOR
BITRSHIFT = BIT.PRZESUNIĘCIE.W.PRAWO
BITXOR = BITXOR
COMPLEX = LICZBA.ZESP
CONVERT = KONWERTUJ
DEC2BIN = DZIES.NA.DWÓJK
DEC2HEX = DZIES.NA.SZESN
DEC2OCT = DZIES.NA.ÓSM
DELTA = CZY.RÓWNE
ERF = FUNKCJA.BŁ
ERF.PRECISE = FUNKCJA.BŁ.DOKŁ
ERFC = KOMP.FUNKCJA.BŁ
ERFC.PRECISE = KOMP.FUNKCJA.BŁ.DOKŁ
GESTEP = SPRAWDŹ.PRÓG
HEX2BIN = SZESN.NA.DWÓJK
HEX2DEC = SZESN.NA.DZIES
HEX2OCT = SZESN.NA.ÓSM
IMABS = MODUŁ.LICZBY.ZESP
IMAGINARY = CZ.UROJ.LICZBY.ZESP
IMARGUMENT = ARG.LICZBY.ZESP
IMCONJUGATE = SPRZĘŻ.LICZBY.ZESP
IMCOS = COS.LICZBY.ZESP
IMCOSH = COSH.LICZBY.ZESP
IMCOT = COT.LICZBY.ZESP
IMCSC = CSC.LICZBY.ZESP
IMCSCH = CSCH.LICZBY.ZESP
IMDIV = ILORAZ.LICZB.ZESP
IMEXP = EXP.LICZBY.ZESP
IMLN = LN.LICZBY.ZESP
IMLOG10 = LOG10.LICZBY.ZESP
IMLOG2 = LOG2.LICZBY.ZESP
IMPOWER = POTĘGA.LICZBY.ZESP
IMPRODUCT = ILOCZYN.LICZB.ZESP
IMREAL = CZ.RZECZ.LICZBY.ZESP
IMSEC = SEC.LICZBY.ZESP
IMSECH = SECH.LICZBY.ZESP
IMSIN = SIN.LICZBY.ZESP
IMSINH = SINH.LICZBY.ZESP
IMSQRT = PIERWIASTEK.LICZBY.ZESP
IMSUB = RÓŻN.LICZB.ZESP
IMSUM = SUMA.LICZB.ZESP
IMTAN = TAN.LICZBY.ZESP
OCT2BIN = ÓSM.NA.DWÓJK
OCT2DEC = ÓSM.NA.DZIES
OCT2HEX = ÓSM.NA.SZESN
##
## Funkcje finansowe (Financial Functions)
##
ACCRINT = NAL.ODS
ACCRINTM = NAL.ODS.WYKUP
AMORDEGRC = AMORT.NIELIN
AMORLINC = AMORT.LIN
COUPDAYBS = WYPŁ.DNI.OD.POCZ
COUPDAYS = WYPŁ.DNI
COUPDAYSNC = WYPŁ.DNI.NAST
COUPNCD = WYPŁ.DATA.NAST
COUPNUM = WYPŁ.LICZBA
COUPPCD = WYPŁ.DATA.POPRZ
CUMIPMT = SPŁAC.ODS
CUMPRINC = SPŁAC.KAPIT
DB = DB
DDB = DDB
DISC = STOPA.DYSK
DOLLARDE = CENA.DZIES
DOLLARFR = CENA.UŁAM
DURATION = ROCZ.PRZYCH
EFFECT = EFEKTYWNA
FV = FV
FVSCHEDULE = WART.PRZYSZŁ.KAP
INTRATE = STOPA.PROC
IPMT = IPMT
IRR = IRR
ISPMT = ISPMT
MDURATION = ROCZ.PRZYCH.M
MIRR = MIRR
NOMINAL = NOMINALNA
NPER = NPER
NPV = NPV
ODDFPRICE = CENA.PIERW.OKR
ODDFYIELD = RENT.PIERW.OKR
ODDLPRICE = CENA.OST.OKR
ODDLYIELD = RENT.OST.OKR
PDURATION = O.CZAS.TRWANIA
PMT = PMT
PPMT = PPMT
PRICE = CENA
PRICEDISC = CENA.DYSK
PRICEMAT = CENA.WYKUP
PV = PV
RATE = RATE
RECEIVED = KWOTA.WYKUP
RRI = RÓWNOW.STOPA.PROC
SLN = SLN
SYD = SYD
TBILLEQ = RENT.EKW.BS
TBILLPRICE = CENA.BS
TBILLYIELD = RENT.BS
VDB = VDB
XIRR = XIRR
XNPV = XNPV
YIELD = RENTOWNOŚĆ
YIELDDISC = RENT.DYSK
YIELDMAT = RENT.WYKUP
##
## Funkcje informacyjne (Information Functions)
##
CELL = KOMÓRKA
ERROR.TYPE = NR.BŁĘDU
INFO = INFO
ISBLANK = CZY.PUSTA
ISERR = CZY.BŁ
ISERROR = CZY.BŁĄD
ISEVEN = CZY.PARZYSTE
ISFORMULA = CZY.FORMUŁA
ISLOGICAL = CZY.LOGICZNA
ISNA = CZY.BRAK
ISNONTEXT = CZY.NIE.TEKST
ISNUMBER = CZY.LICZBA
ISODD = CZY.NIEPARZYSTE
ISREF = CZY.ADR
ISTEXT = CZY.TEKST
N = N
NA = BRAK
SHEET = ARKUSZ
SHEETS = ARKUSZE
TYPE = TYP
##
## Funkcje logiczne (Logical Functions)
##
AND = ORAZ
FALSE = FAŁSZ
IF = JEŻELI
IFERROR = JEŻELI.BŁĄD
IFNA = JEŻELI.ND
IFS = WARUNKI
NOT = NIE
OR = LUB
SWITCH = PRZEŁĄCZ
TRUE = PRAWDA
XOR = XOR
##
## Funkcje wyszukiwania i odwołań (Lookup & Reference Functions)
##
ADDRESS = ADRES
AREAS = OBSZARY
CHOOSE = WYBIERZ
COLUMN = NR.KOLUMNY
COLUMNS = LICZBA.KOLUMN
FORMULATEXT = FORMUŁA.TEKST
GETPIVOTDATA = WEŹDANETABELI
HLOOKUP = WYSZUKAJ.POZIOMO
HYPERLINK = HIPERŁĄCZE
INDEX = INDEKS
INDIRECT = ADR.POŚR
LOOKUP = WYSZUKAJ
MATCH = PODAJ.POZYCJĘ
OFFSET = PRZESUNIĘCIE
ROW = WIERSZ
ROWS = ILE.WIERSZY
RTD = DANE.CZASU.RZECZ
TRANSPOSE = TRANSPONUJ
VLOOKUP = WYSZUKAJ.PIONOWO
##
## Funkcje matematyczne i trygonometryczne (Math & Trig Functions)
##
ABS = MODUŁ.LICZBY
ACOS = ACOS
ACOSH = ACOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = AGREGUJ
ARABIC = ARABSKIE
ASIN = ASIN
ASINH = ASINH
ATAN = ATAN
ATAN2 = ATAN2
ATANH = ATANH
BASE = PODSTAWA
CEILING.MATH = ZAOKR.W.GÓRĘ.MATEMATYCZNE
CEILING.PRECISE = ZAOKR.W.GÓRĘ.DOKŁ
COMBIN = KOMBINACJE
COMBINA = KOMBINACJE.A
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = DZIESIĘTNA
DEGREES = STOPNIE
ECMA.CEILING = ECMA.ZAOKR.W.GÓRĘ
EVEN = ZAOKR.DO.PARZ
EXP = EXP
FACT = SILNIA
FACTDOUBLE = SILNIA.DWUKR
FLOOR.MATH = ZAOKR.W.DÓŁ.MATEMATYCZNE
FLOOR.PRECISE = ZAOKR.W.DÓŁ.DOKŁ
GCD = NAJW.WSP.DZIEL
INT = ZAOKR.DO.CAŁK
ISO.CEILING = ISO.ZAOKR.W.GÓRĘ
LCM = NAJMN.WSP.WIEL
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = WYZNACZNIK.MACIERZY
MINVERSE = MACIERZ.ODW
MMULT = MACIERZ.ILOCZYN
MOD = MOD
MROUND = ZAOKR.DO.WIELOKR
MULTINOMIAL = WIELOMIAN
MUNIT = MACIERZ.JEDNOSTKOWA
ODD = ZAOKR.DO.NPARZ
PI = PI
POWER = POTĘGA
PRODUCT = ILOCZYN
QUOTIENT = CZ.CAŁK.DZIELENIA
RADIANS = RADIANY
RAND = LOS
RANDBETWEEN = LOS.ZAKR
ROMAN = RZYMSKIE
ROUND = ZAOKR
ROUNDBAHTDOWN = ZAOKR.DÓŁ.BAT
ROUNDBAHTUP = ZAOKR.GÓRA.BAT
ROUNDDOWN = ZAOKR.DÓŁ
ROUNDUP = ZAOKR.GÓRA
SEC = SEC
SECH = SECH
SERIESSUM = SUMA.SZER.POT
SIGN = ZNAK.LICZBY
SIN = SIN
SINH = SINH
SQRT = PIERWIASTEK
SQRTPI = PIERW.PI
SUBTOTAL = SUMY.CZĘŚCIOWE
SUM = SUMA
SUMIF = SUMA.JEŻELI
SUMIFS = SUMA.WARUNKÓW
SUMPRODUCT = SUMA.ILOCZYNÓW
SUMSQ = SUMA.KWADRATÓW
SUMX2MY2 = SUMA.X2.M.Y2
SUMX2PY2 = SUMA.X2.P.Y2
SUMXMY2 = SUMA.XMY.2
TAN = TAN
TANH = TANH
TRUNC = LICZBA.CAŁK
##
## Funkcje statystyczne (Statistical Functions)
##
AVEDEV = ODCH.ŚREDNIE
AVERAGE = ŚREDNIA
AVERAGEA = ŚREDNIA.A
AVERAGEIF = ŚREDNIA.JEŻELI
AVERAGEIFS = ŚREDNIA.WARUNKÓW
BETA.DIST = ROZKŁ.BETA
BETA.INV = ROZKŁ.BETA.ODWR
BINOM.DIST = ROZKŁ.DWUM
BINOM.DIST.RANGE = ROZKŁ.DWUM.ZAKRES
BINOM.INV = ROZKŁ.DWUM.ODWR
CHISQ.DIST = ROZKŁ.CHI
CHISQ.DIST.RT = ROZKŁ.CHI.PS
CHISQ.INV = ROZKŁ.CHI.ODWR
CHISQ.INV.RT = ROZKŁ.CHI.ODWR.PS
CHISQ.TEST = CHI.TEST
CONFIDENCE.NORM = UFNOŚĆ.NORM
CONFIDENCE.T = UFNOŚĆ.T
CORREL = WSP.KORELACJI
COUNT = ILE.LICZB
COUNTA = ILE.NIEPUSTYCH
COUNTBLANK = LICZ.PUSTE
COUNTIF = LICZ.JEŻELI
COUNTIFS = LICZ.WARUNKI
COVARIANCE.P = KOWARIANCJA.POPUL
COVARIANCE.S = KOWARIANCJA.PRÓBKI
DEVSQ = ODCH.KWADRATOWE
EXPON.DIST = ROZKŁ.EXP
F.DIST = ROZKŁ.F
F.DIST.RT = ROZKŁ.F.PS
F.INV = ROZKŁ.F.ODWR
F.INV.RT = ROZKŁ.F.ODWR.PS
F.TEST = F.TEST
FISHER = ROZKŁAD.FISHER
FISHERINV = ROZKŁAD.FISHER.ODW
FORECAST.ETS = REGLINX.ETS
FORECAST.ETS.CONFINT = REGLINX.ETS.CONFINT
FORECAST.ETS.SEASONALITY = REGLINX.ETS.SEZONOWOŚĆ
FORECAST.ETS.STAT = REGLINX.ETS.STATYSTYKA
FORECAST.LINEAR = REGLINX.LINIOWA
FREQUENCY = CZĘSTOŚĆ
GAMMA = GAMMA
GAMMA.DIST = ROZKŁ.GAMMA
GAMMA.INV = ROZKŁ.GAMMA.ODWR
GAMMALN = ROZKŁAD.LIN.GAMMA
GAMMALN.PRECISE = ROZKŁAD.LIN.GAMMA.DOKŁ
GAUSS = GAUSS
GEOMEAN = ŚREDNIA.GEOMETRYCZNA
GROWTH = REGEXPW
HARMEAN = ŚREDNIA.HARMONICZNA
HYPGEOM.DIST = ROZKŁ.HIPERGEOM
INTERCEPT = ODCIĘTA
KURT = KURTOZA
LARGE = MAX.K
LINEST = REGLINP
LOGEST = REGEXPP
LOGNORM.DIST = ROZKŁ.LOG
LOGNORM.INV = ROZKŁ.LOG.ODWR
MAX = MAX
MAXA = MAX.A
MAXIFS = MAKS.WARUNKÓW
MEDIAN = MEDIANA
MIN = MIN
MINA = MIN.A
MINIFS = MIN.WARUNKÓW
MODE.MULT = WYST.NAJCZĘŚCIEJ.TABL
MODE.SNGL = WYST.NAJCZĘŚCIEJ.WART
NEGBINOM.DIST = ROZKŁ.DWUM.PRZEC
NORM.DIST = ROZKŁ.NORMALNY
NORM.INV = ROZKŁ.NORMALNY.ODWR
NORM.S.DIST = ROZKŁ.NORMALNY.S
NORM.S.INV = ROZKŁ.NORMALNY.S.ODWR
PEARSON = PEARSON
PERCENTILE.EXC = PERCENTYL.PRZEDZ.OTW
PERCENTILE.INC = PERCENTYL.PRZEDZ.ZAMK
PERCENTRANK.EXC = PROC.POZ.PRZEDZ.OTW
PERCENTRANK.INC = PROC.POZ.PRZEDZ.ZAMK
PERMUT = PERMUTACJE
PERMUTATIONA = PERMUTACJE.A
PHI = PHI
POISSON.DIST = ROZKŁ.POISSON
PROB = PRAWDPD
QUARTILE.EXC = KWARTYL.PRZEDZ.OTW
QUARTILE.INC = KWARTYL.PRZEDZ.ZAMK
RANK.AVG = POZYCJA.ŚR
RANK.EQ = POZYCJA.NAJW
RSQ = R.KWADRAT
SKEW = SKOŚNOŚĆ
SKEW.P = SKOŚNOŚĆ.P
SLOPE = NACHYLENIE
SMALL = MIN.K
STANDARDIZE = NORMALIZUJ
STDEV.P = ODCH.STAND.POPUL
STDEV.S = ODCH.STANDARD.PRÓBKI
STDEVA = ODCH.STANDARDOWE.A
STDEVPA = ODCH.STANDARD.POPUL.A
STEYX = REGBŁSTD
T.DIST = ROZKŁ.T
T.DIST.2T = ROZKŁ.T.DS
T.DIST.RT = ROZKŁ.T.PS
T.INV = ROZKŁ.T.ODWR
T.INV.2T = ROZKŁ.T.ODWR.DS
T.TEST = T.TEST
TREND = REGLINW
TRIMMEAN = ŚREDNIA.WEWN
VAR.P = WARIANCJA.POP
VAR.S = WARIANCJA.PRÓBKI
VARA = WARIANCJA.A
VARPA = WARIANCJA.POPUL.A
WEIBULL.DIST = ROZKŁ.WEIBULL
Z.TEST = Z.TEST
##
## Funkcje tekstowe (Text Functions)
##
BAHTTEXT = BAT.TEKST
CHAR = ZNAK
CLEAN = OCZYŚĆ
CODE = KOD
CONCAT = ZŁĄCZ.TEKST
DOLLAR = KWOTA
EXACT = PORÓWNAJ
FIND = ZNAJDŹ
FIXED = ZAOKR.DO.TEKST
ISTHAIDIGIT = CZY.CYFRA.TAJ
LEFT = LEWY
LEN = DŁ
LOWER = LITERY.MAŁE
MID = FRAGMENT.TEKSTU
NUMBERSTRING = LICZBA.CIĄG.ZNAK
NUMBERVALUE = WARTOŚĆ.LICZBOWA
PROPER = Z.WIELKIEJ.LITERY
REPLACE = ZASTĄP
REPT = POWT
RIGHT = PRAWY
SEARCH = SZUKAJ.TEKST
SUBSTITUTE = PODSTAW
T = T
TEXT = TEKST
TEXTJOIN = POŁĄCZ.TEKSTY
THAIDIGIT = TAJ.CYFRA
THAINUMSOUND = TAJ.DŹWIĘK.NUM
THAINUMSTRING = TAJ.CIĄG.NUM
THAISTRINGLENGTH = TAJ.DŁUGOŚĆ.CIĄGU
TRIM = USUŃ.ZBĘDNE.ODSTĘPY
UNICHAR = ZNAK.UNICODE
UNICODE = UNICODE
UPPER = LITERY.WIELKIE
VALUE = WARTOŚĆ
##
## Funkcje sieci Web (Web Functions)
##
ENCODEURL = ENCODEURL
FILTERXML = FILTERXML
WEBSERVICE = WEBSERVICE
##
## Funkcje zgodności (Compatibility Functions)
##
BETADIST = ROZKŁAD.BETA
BETAINV = ROZKŁAD.BETA.ODW
BINOMDIST = ROZKŁAD.DWUM
CEILING = ZAOKR.W.GÓRĘ
CHIDIST = ROZKŁAD.CHI
CHIINV = ROZKŁAD.CHI.ODW
CHITEST = TEST.CHI
CONCATENATE = ZŁĄCZ.TEKSTY
CONFIDENCE = UFNOŚĆ
COVAR = KOWARIANCJA
CRITBINOM = PRÓG.ROZKŁAD.DWUM
EXPONDIST = ROZKŁAD.EXP
FDIST = ROZKŁAD.F
FINV = ROZKŁAD.F.ODW
FLOOR = ZAOKR.W.DÓŁ
FORECAST = REGLINX
FTEST = TEST.F
GAMMADIST = ROZKŁAD.GAMMA
GAMMAINV = ROZKŁAD.GAMMA.ODW
HYPGEOMDIST = ROZKŁAD.HIPERGEOM
LOGINV = ROZKŁAD.LOG.ODW
LOGNORMDIST = ROZKŁAD.LOG
MODE = WYST.NAJCZĘŚCIEJ
NEGBINOMDIST = ROZKŁAD.DWUM.PRZEC
NORMDIST = ROZKŁAD.NORMALNY
NORMINV = ROZKŁAD.NORMALNY.ODW
NORMSDIST = ROZKŁAD.NORMALNY.S
NORMSINV = ROZKŁAD.NORMALNY.S.ODW
PERCENTILE = PERCENTYL
PERCENTRANK = PROCENT.POZYCJA
POISSON = ROZKŁAD.POISSON
QUARTILE = KWARTYL
RANK = POZYCJA
STDEV = ODCH.STANDARDOWE
STDEVP = ODCH.STANDARD.POPUL
TDIST = ROZKŁAD.T
TINV = ROZKŁAD.T.ODW
TTEST = TEST.T
VAR = WARIANCJA
VARP = WARIANCJA.POPUL
WEIBULL = ROZKŁAD.WEIBULL
ZTEST = TEST.Z
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pl/config 0000644 00000000517 15242753725 0020610 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Jezyk polski (Polish)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #ZERO!
DIV0 = #DZIEL/0!
VALUE = #ARG!
REF = #ADR!
NAME = #NAZWA?
NUM = #LICZBA!
NA = #N/D!
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/functions 0000644 00000025234 15242753725 0021357 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Italiano (Italian)
##
############################################################
##
## Funzioni cubo (Cube Functions)
##
CUBEKPIMEMBER = MEMBRO.KPI.CUBO
CUBEMEMBER = MEMBRO.CUBO
CUBEMEMBERPROPERTY = PROPRIETÀ.MEMBRO.CUBO
CUBERANKEDMEMBER = MEMBRO.CUBO.CON.RANGO
CUBESET = SET.CUBO
CUBESETCOUNT = CONTA.SET.CUBO
CUBEVALUE = VALORE.CUBO
##
## Funzioni di database (Database Functions)
##
DAVERAGE = DB.MEDIA
DCOUNT = DB.CONTA.NUMERI
DCOUNTA = DB.CONTA.VALORI
DGET = DB.VALORI
DMAX = DB.MAX
DMIN = DB.MIN
DPRODUCT = DB.PRODOTTO
DSTDEV = DB.DEV.ST
DSTDEVP = DB.DEV.ST.POP
DSUM = DB.SOMMA
DVAR = DB.VAR
DVARP = DB.VAR.POP
##
## Funzioni data e ora (Date & Time Functions)
##
DATE = DATA
DATEDIF = DATA.DIFF
DATESTRING = DATA.STRINGA
DATEVALUE = DATA.VALORE
DAY = GIORNO
DAYS = GIORNI
DAYS360 = GIORNO360
EDATE = DATA.MESE
EOMONTH = FINE.MESE
HOUR = ORA
ISOWEEKNUM = NUM.SETTIMANA.ISO
MINUTE = MINUTO
MONTH = MESE
NETWORKDAYS = GIORNI.LAVORATIVI.TOT
NETWORKDAYS.INTL = GIORNI.LAVORATIVI.TOT.INTL
NOW = ADESSO
SECOND = SECONDO
THAIDAYOFWEEK = THAIGIORNODELLASETTIMANA
THAIMONTHOFYEAR = THAIMESEDELLANNO
THAIYEAR = THAIANNO
TIME = ORARIO
TIMEVALUE = ORARIO.VALORE
TODAY = OGGI
WEEKDAY = GIORNO.SETTIMANA
WEEKNUM = NUM.SETTIMANA
WORKDAY = GIORNO.LAVORATIVO
WORKDAY.INTL = GIORNO.LAVORATIVO.INTL
YEAR = ANNO
YEARFRAC = FRAZIONE.ANNO
##
## Funzioni ingegneristiche (Engineering Functions)
##
BESSELI = BESSEL.I
BESSELJ = BESSEL.J
BESSELK = BESSEL.K
BESSELY = BESSEL.Y
BIN2DEC = BINARIO.DECIMALE
BIN2HEX = BINARIO.HEX
BIN2OCT = BINARIO.OCT
BITAND = BITAND
BITLSHIFT = BIT.SPOSTA.SX
BITOR = BITOR
BITRSHIFT = BIT.SPOSTA.DX
BITXOR = BITXOR
COMPLEX = COMPLESSO
CONVERT = CONVERTI
DEC2BIN = DECIMALE.BINARIO
DEC2HEX = DECIMALE.HEX
DEC2OCT = DECIMALE.OCT
DELTA = DELTA
ERF = FUNZ.ERRORE
ERF.PRECISE = FUNZ.ERRORE.PRECISA
ERFC = FUNZ.ERRORE.COMP
ERFC.PRECISE = FUNZ.ERRORE.COMP.PRECISA
GESTEP = SOGLIA
HEX2BIN = HEX.BINARIO
HEX2DEC = HEX.DECIMALE
HEX2OCT = HEX.OCT
IMABS = COMP.MODULO
IMAGINARY = COMP.IMMAGINARIO
IMARGUMENT = COMP.ARGOMENTO
IMCONJUGATE = COMP.CONIUGATO
IMCOS = COMP.COS
IMCOSH = COMP.COSH
IMCOT = COMP.COT
IMCSC = COMP.CSC
IMCSCH = COMP.CSCH
IMDIV = COMP.DIV
IMEXP = COMP.EXP
IMLN = COMP.LN
IMLOG10 = COMP.LOG10
IMLOG2 = COMP.LOG2
IMPOWER = COMP.POTENZA
IMPRODUCT = COMP.PRODOTTO
IMREAL = COMP.PARTE.REALE
IMSEC = COMP.SEC
IMSECH = COMP.SECH
IMSIN = COMP.SEN
IMSINH = COMP.SENH
IMSQRT = COMP.RADQ
IMSUB = COMP.DIFF
IMSUM = COMP.SOMMA
IMTAN = COMP.TAN
OCT2BIN = OCT.BINARIO
OCT2DEC = OCT.DECIMALE
OCT2HEX = OCT.HEX
##
## Funzioni finanziarie (Financial Functions)
##
ACCRINT = INT.MATURATO.PER
ACCRINTM = INT.MATURATO.SCAD
AMORDEGRC = AMMORT.DEGR
AMORLINC = AMMORT.PER
COUPDAYBS = GIORNI.CED.INIZ.LIQ
COUPDAYS = GIORNI.CED
COUPDAYSNC = GIORNI.CED.NUOVA
COUPNCD = DATA.CED.SUCC
COUPNUM = NUM.CED
COUPPCD = DATA.CED.PREC
CUMIPMT = INT.CUMUL
CUMPRINC = CAP.CUM
DB = AMMORT.FISSO
DDB = AMMORT
DISC = TASSO.SCONTO
DOLLARDE = VALUTA.DEC
DOLLARFR = VALUTA.FRAZ
DURATION = DURATA
EFFECT = EFFETTIVO
FV = VAL.FUT
FVSCHEDULE = VAL.FUT.CAPITALE
INTRATE = TASSO.INT
IPMT = INTERESSI
IRR = TIR.COST
ISPMT = INTERESSE.RATA
MDURATION = DURATA.M
MIRR = TIR.VAR
NOMINAL = NOMINALE
NPER = NUM.RATE
NPV = VAN
ODDFPRICE = PREZZO.PRIMO.IRR
ODDFYIELD = REND.PRIMO.IRR
ODDLPRICE = PREZZO.ULTIMO.IRR
ODDLYIELD = REND.ULTIMO.IRR
PDURATION = DURATA.P
PMT = RATA
PPMT = P.RATA
PRICE = PREZZO
PRICEDISC = PREZZO.SCONT
PRICEMAT = PREZZO.SCAD
PV = VA
RATE = TASSO
RECEIVED = RICEV.SCAD
RRI = RIT.INVEST.EFFETT
SLN = AMMORT.COST
SYD = AMMORT.ANNUO
TBILLEQ = BOT.EQUIV
TBILLPRICE = BOT.PREZZO
TBILLYIELD = BOT.REND
VDB = AMMORT.VAR
XIRR = TIR.X
XNPV = VAN.X
YIELD = REND
YIELDDISC = REND.TITOLI.SCONT
YIELDMAT = REND.SCAD
##
## Funzioni relative alle informazioni (Information Functions)
##
CELL = CELLA
ERROR.TYPE = ERRORE.TIPO
INFO = AMBIENTE.INFO
ISBLANK = VAL.VUOTO
ISERR = VAL.ERR
ISERROR = VAL.ERRORE
ISEVEN = VAL.PARI
ISFORMULA = VAL.FORMULA
ISLOGICAL = VAL.LOGICO
ISNA = VAL.NON.DISP
ISNONTEXT = VAL.NON.TESTO
ISNUMBER = VAL.NUMERO
ISODD = VAL.DISPARI
ISREF = VAL.RIF
ISTEXT = VAL.TESTO
N = NUM
NA = NON.DISP
SHEET = FOGLIO
SHEETS = FOGLI
TYPE = TIPO
##
## Funzioni logiche (Logical Functions)
##
AND = E
FALSE = FALSO
IF = SE
IFERROR = SE.ERRORE
IFNA = SE.NON.DISP.
IFS = PIÙ.SE
NOT = NON
OR = O
SWITCH = SWITCH
TRUE = VERO
XOR = XOR
##
## Funzioni di ricerca e di riferimento (Lookup & Reference Functions)
##
ADDRESS = INDIRIZZO
AREAS = AREE
CHOOSE = SCEGLI
COLUMN = RIF.COLONNA
COLUMNS = COLONNE
FORMULATEXT = TESTO.FORMULA
GETPIVOTDATA = INFO.DATI.TAB.PIVOT
HLOOKUP = CERCA.ORIZZ
HYPERLINK = COLLEG.IPERTESTUALE
INDEX = INDICE
INDIRECT = INDIRETTO
LOOKUP = CERCA
MATCH = CONFRONTA
OFFSET = SCARTO
ROW = RIF.RIGA
ROWS = RIGHE
RTD = DATITEMPOREALE
TRANSPOSE = MATR.TRASPOSTA
VLOOKUP = CERCA.VERT
##
## Funzioni matematiche e trigonometriche (Math & Trig Functions)
##
ABS = ASS
ACOS = ARCCOS
ACOSH = ARCCOSH
ACOT = ARCCOT
ACOTH = ARCCOTH
AGGREGATE = AGGREGA
ARABIC = ARABO
ASIN = ARCSEN
ASINH = ARCSENH
ATAN = ARCTAN
ATAN2 = ARCTAN.2
ATANH = ARCTANH
BASE = BASE
CEILING.MATH = ARROTONDA.ECCESSO.MAT
CEILING.PRECISE = ARROTONDA.ECCESSO.PRECISA
COMBIN = COMBINAZIONE
COMBINA = COMBINAZIONE.VALORI
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = DECIMALE
DEGREES = GRADI
ECMA.CEILING = ECMA.ARROTONDA.ECCESSO
EVEN = PARI
EXP = EXP
FACT = FATTORIALE
FACTDOUBLE = FATT.DOPPIO
FLOOR.MATH = ARROTONDA.DIFETTO.MAT
FLOOR.PRECISE = ARROTONDA.DIFETTO.PRECISA
GCD = MCD
INT = INT
ISO.CEILING = ISO.ARROTONDA.ECCESSO
LCM = MCM
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = MATR.DETERM
MINVERSE = MATR.INVERSA
MMULT = MATR.PRODOTTO
MOD = RESTO
MROUND = ARROTONDA.MULTIPLO
MULTINOMIAL = MULTINOMIALE
MUNIT = MATR.UNIT
ODD = DISPARI
PI = PI.GRECO
POWER = POTENZA
PRODUCT = PRODOTTO
QUOTIENT = QUOZIENTE
RADIANS = RADIANTI
RAND = CASUALE
RANDBETWEEN = CASUALE.TRA
ROMAN = ROMANO
ROUND = ARROTONDA
ROUNDBAHTDOWN = ARROTBAHTGIU
ROUNDBAHTUP = ARROTBAHTSU
ROUNDDOWN = ARROTONDA.PER.DIF
ROUNDUP = ARROTONDA.PER.ECC
SEC = SEC
SECH = SECH
SERIESSUM = SOMMA.SERIE
SIGN = SEGNO
SIN = SEN
SINH = SENH
SQRT = RADQ
SQRTPI = RADQ.PI.GRECO
SUBTOTAL = SUBTOTALE
SUM = SOMMA
SUMIF = SOMMA.SE
SUMIFS = SOMMA.PIÙ.SE
SUMPRODUCT = MATR.SOMMA.PRODOTTO
SUMSQ = SOMMA.Q
SUMX2MY2 = SOMMA.DIFF.Q
SUMX2PY2 = SOMMA.SOMMA.Q
SUMXMY2 = SOMMA.Q.DIFF
TAN = TAN
TANH = TANH
TRUNC = TRONCA
##
## Funzioni statistiche (Statistical Functions)
##
AVEDEV = MEDIA.DEV
AVERAGE = MEDIA
AVERAGEA = MEDIA.VALORI
AVERAGEIF = MEDIA.SE
AVERAGEIFS = MEDIA.PIÙ.SE
BETA.DIST = DISTRIB.BETA.N
BETA.INV = INV.BETA.N
BINOM.DIST = DISTRIB.BINOM.N
BINOM.DIST.RANGE = INTERVALLO.DISTRIB.BINOM.N.
BINOM.INV = INV.BINOM
CHISQ.DIST = DISTRIB.CHI.QUAD
CHISQ.DIST.RT = DISTRIB.CHI.QUAD.DS
CHISQ.INV = INV.CHI.QUAD
CHISQ.INV.RT = INV.CHI.QUAD.DS
CHISQ.TEST = TEST.CHI.QUAD
CONFIDENCE.NORM = CONFIDENZA.NORM
CONFIDENCE.T = CONFIDENZA.T
CORREL = CORRELAZIONE
COUNT = CONTA.NUMERI
COUNTA = CONTA.VALORI
COUNTBLANK = CONTA.VUOTE
COUNTIF = CONTA.SE
COUNTIFS = CONTA.PIÙ.SE
COVARIANCE.P = COVARIANZA.P
COVARIANCE.S = COVARIANZA.C
DEVSQ = DEV.Q
EXPON.DIST = DISTRIB.EXP.N
F.DIST = DISTRIBF
F.DIST.RT = DISTRIB.F.DS
F.INV = INVF
F.INV.RT = INV.F.DS
F.TEST = TESTF
FISHER = FISHER
FISHERINV = INV.FISHER
FORECAST.ETS = PREVISIONE.ETS
FORECAST.ETS.CONFINT = PREVISIONE.ETS.INTCONF
FORECAST.ETS.SEASONALITY = PREVISIONE.ETS.STAGIONALITÀ
FORECAST.ETS.STAT = PREVISIONE.ETS.STAT
FORECAST.LINEAR = PREVISIONE.LINEARE
FREQUENCY = FREQUENZA
GAMMA = GAMMA
GAMMA.DIST = DISTRIB.GAMMA.N
GAMMA.INV = INV.GAMMA.N
GAMMALN = LN.GAMMA
GAMMALN.PRECISE = LN.GAMMA.PRECISA
GAUSS = GAUSS
GEOMEAN = MEDIA.GEOMETRICA
GROWTH = CRESCITA
HARMEAN = MEDIA.ARMONICA
HYPGEOM.DIST = DISTRIB.IPERGEOM.N
INTERCEPT = INTERCETTA
KURT = CURTOSI
LARGE = GRANDE
LINEST = REGR.LIN
LOGEST = REGR.LOG
LOGNORM.DIST = DISTRIB.LOGNORM.N
LOGNORM.INV = INV.LOGNORM.N
MAX = MAX
MAXA = MAX.VALORI
MAXIFS = MAX.PIÙ.SE
MEDIAN = MEDIANA
MIN = MIN
MINA = MIN.VALORI
MINIFS = MIN.PIÙ.SE
MODE.MULT = MODA.MULT
MODE.SNGL = MODA.SNGL
NEGBINOM.DIST = DISTRIB.BINOM.NEG.N
NORM.DIST = DISTRIB.NORM.N
NORM.INV = INV.NORM.N
NORM.S.DIST = DISTRIB.NORM.ST.N
NORM.S.INV = INV.NORM.S
PEARSON = PEARSON
PERCENTILE.EXC = ESC.PERCENTILE
PERCENTILE.INC = INC.PERCENTILE
PERCENTRANK.EXC = ESC.PERCENT.RANGO
PERCENTRANK.INC = INC.PERCENT.RANGO
PERMUT = PERMUTAZIONE
PERMUTATIONA = PERMUTAZIONE.VALORI
PHI = PHI
POISSON.DIST = DISTRIB.POISSON
PROB = PROBABILITÀ
QUARTILE.EXC = ESC.QUARTILE
QUARTILE.INC = INC.QUARTILE
RANK.AVG = RANGO.MEDIA
RANK.EQ = RANGO.UG
RSQ = RQ
SKEW = ASIMMETRIA
SKEW.P = ASIMMETRIA.P
SLOPE = PENDENZA
SMALL = PICCOLO
STANDARDIZE = NORMALIZZA
STDEV.P = DEV.ST.P
STDEV.S = DEV.ST.C
STDEVA = DEV.ST.VALORI
STDEVPA = DEV.ST.POP.VALORI
STEYX = ERR.STD.YX
T.DIST = DISTRIB.T.N
T.DIST.2T = DISTRIB.T.2T
T.DIST.RT = DISTRIB.T.DS
T.INV = INVT
T.INV.2T = INV.T.2T
T.TEST = TESTT
TREND = TENDENZA
TRIMMEAN = MEDIA.TRONCATA
VAR.P = VAR.P
VAR.S = VAR.C
VARA = VAR.VALORI
VARPA = VAR.POP.VALORI
WEIBULL.DIST = DISTRIB.WEIBULL
Z.TEST = TESTZ
##
## Funzioni di testo (Text Functions)
##
BAHTTEXT = BAHTTESTO
CHAR = CODICE.CARATT
CLEAN = LIBERA
CODE = CODICE
CONCAT = CONCAT
DOLLAR = VALUTA
EXACT = IDENTICO
FIND = TROVA
FIXED = FISSO
ISTHAIDIGIT = ÈTHAICIFRA
LEFT = SINISTRA
LEN = LUNGHEZZA
LOWER = MINUSC
MID = STRINGA.ESTRAI
NUMBERSTRING = NUMERO.STRINGA
NUMBERVALUE = NUMERO.VALORE
PHONETIC = FURIGANA
PROPER = MAIUSC.INIZ
REPLACE = RIMPIAZZA
REPT = RIPETI
RIGHT = DESTRA
SEARCH = RICERCA
SUBSTITUTE = SOSTITUISCI
T = T
TEXT = TESTO
TEXTJOIN = TESTO.UNISCI
THAIDIGIT = THAICIFRA
THAINUMSOUND = THAINUMSUONO
THAINUMSTRING = THAISZÁMKAR
THAISTRINGLENGTH = THAILUNGSTRINGA
TRIM = ANNULLA.SPAZI
UNICHAR = CARATT.UNI
UNICODE = UNICODE
UPPER = MAIUSC
VALUE = VALORE
##
## Funzioni Web (Web Functions)
##
ENCODEURL = CODIFICA.URL
FILTERXML = FILTRO.XML
WEBSERVICE = SERVIZIO.WEB
##
## Funzioni di compatibilità (Compatibility Functions)
##
BETADIST = DISTRIB.BETA
BETAINV = INV.BETA
BINOMDIST = DISTRIB.BINOM
CEILING = ARROTONDA.ECCESSO
CHIDIST = DISTRIB.CHI
CHIINV = INV.CHI
CHITEST = TEST.CHI
CONCATENATE = CONCATENA
CONFIDENCE = CONFIDENZA
COVAR = COVARIANZA
CRITBINOM = CRIT.BINOM
EXPONDIST = DISTRIB.EXP
FDIST = DISTRIB.F
FINV = INV.F
FLOOR = ARROTONDA.DIFETTO
FORECAST = PREVISIONE
FTEST = TEST.F
GAMMADIST = DISTRIB.GAMMA
GAMMAINV = INV.GAMMA
HYPGEOMDIST = DISTRIB.IPERGEOM
LOGINV = INV.LOGNORM
LOGNORMDIST = DISTRIB.LOGNORM
MODE = MODA
NEGBINOMDIST = DISTRIB.BINOM.NEG
NORMDIST = DISTRIB.NORM
NORMINV = INV.NORM
NORMSDIST = DISTRIB.NORM.ST
NORMSINV = INV.NORM.ST
PERCENTILE = PERCENTILE
PERCENTRANK = PERCENT.RANGO
POISSON = POISSON
QUARTILE = QUARTILE
RANK = RANGO
STDEV = DEV.ST
STDEVP = DEV.ST.POP
TDIST = DISTRIB.T
TINV = INV.T
TTEST = TEST.T
VAR = VAR
VARP = VAR.POP
WEIBULL = WEIBULL
ZTEST = TEST.Z
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/it/config 0000644 00000000455 15242753725 0020612 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Italiano (Italian)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL
DIV0
VALUE = #VALORE!
REF = #RIF!
NAME = #NOME?
NUM
NA = #N/D
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/functions 0000644 00000147436 15242753725 0021344 0 ustar 00 ##
## PhpSpreadsheet
##
##
## Data in this file derived from information provided by web-junior (http://www.web-junior.net/)
##
##
##
## Add-in and Automation functions Функции надстроек и автоматизации
##
GETPIVOTDATA = ПОЛУЧИТЬ.ДАННЫЕ.СВОДНОЙ.ТАБЛИЦЫ ## Возвращает данные, хранящиеся в отчете сводной таблицы.
##
## Cube functions Функции Куб
##
CUBEKPIMEMBER = КУБЭЛЕМЕНТКИП ## Возвращает свойство ключевого индикатора производительности «(КИП)» и отображает имя «КИП» в ячейке. «КИП» представляет собой количественную величину, такую как ежемесячная валовая прибыль или ежеквартальная текучесть кадров, используемой для контроля эффективности работы организации.
CUBEMEMBER = КУБЭЛЕМЕНТ ## Возвращает элемент или кортеж из куба. Используется для проверки существования элемента или кортежа в кубе.
CUBEMEMBERPROPERTY = КУБСВОЙСТВОЭЛЕМЕНТА ## Возвращает значение свойства элемента из куба. Используется для проверки существования имени элемента в кубе и возвращает указанное свойство для этого элемента.
CUBERANKEDMEMBER = КУБПОРЭЛЕМЕНТ ## Возвращает n-ый или ранжированный элемент в множество. Используется для возвращения одного или нескольких элементов в множество, например, лучшего продавца или 10 лучших студентов.
CUBESET = КУБМНОЖ ## Определяет вычислительное множество элементов или кортежей, отправляя на сервер выражение, которое создает множество, а затем возвращает его в Microsoft Office Excel.
CUBESETCOUNT = КУБЧИСЛОЭЛМНОЖ ## Возвращает число элементов множества.
CUBEVALUE = КУБЗНАЧЕНИЕ ## Возвращает обобщенное значение из куба.
##
## Database functions Функции для работы с базами данных
##
DAVERAGE = ДСРЗНАЧ ## Возвращает среднее значение выбранных записей базы данных.
DCOUNT = БСЧЁТ ## Подсчитывает количество числовых ячеек в базе данных.
DCOUNTA = БСЧЁТА ## Подсчитывает количество непустых ячеек в базе данных.
DGET = БИЗВЛЕЧЬ ## Извлекает из базы данных одну запись, удовлетворяющую заданному условию.
DMAX = ДМАКС ## Возвращает максимальное значение среди выделенных записей базы данных.
DMIN = ДМИН ## Возвращает минимальное значение среди выделенных записей базы данных.
DPRODUCT = БДПРОИЗВЕД ## Перемножает значения определенного поля в записях базы данных, удовлетворяющих условию.
DSTDEV = ДСТАНДОТКЛ ## Оценивает стандартное отклонение по выборке для выделенных записей базы данных.
DSTDEVP = ДСТАНДОТКЛП ## Вычисляет стандартное отклонение по генеральной совокупности для выделенных записей базы данных
DSUM = БДСУММ ## Суммирует числа в поле для записей базы данных, удовлетворяющих условию.
DVAR = БДДИСП ## Оценивает дисперсию по выборке из выделенных записей базы данных
DVARP = БДДИСПП ## Вычисляет дисперсию по генеральной совокупности для выделенных записей базы данных
##
## Date and time functions Функции даты и времени
##
DATE = ДАТА ## Возвращает заданную дату в числовом формате.
DATEVALUE = ДАТАЗНАЧ ## Преобразует дату из текстового формата в числовой формат.
DAY = ДЕНЬ ## Преобразует дату в числовом формате в день месяца.
DAYS360 = ДНЕЙ360 ## Вычисляет количество дней между двумя датами на основе 360-дневного года.
EDATE = ДАТАМЕС ## Возвращает дату в числовом формате, отстоящую на заданное число месяцев вперед или назад от начальной даты.
EOMONTH = КОНМЕСЯЦА ## Возвращает дату в числовом формате для последнего дня месяца, отстоящего вперед или назад на заданное число месяцев.
HOUR = ЧАС ## Преобразует дату в числовом формате в часы.
MINUTE = МИНУТЫ ## Преобразует дату в числовом формате в минуты.
MONTH = МЕСЯЦ ## Преобразует дату в числовом формате в месяцы.
NETWORKDAYS = ЧИСТРАБДНИ ## Возвращает количество рабочих дней между двумя датами.
NOW = ТДАТА ## Возвращает текущую дату и время в числовом формате.
SECOND = СЕКУНДЫ ## Преобразует дату в числовом формате в секунды.
TIME = ВРЕМЯ ## Возвращает заданное время в числовом формате.
TIMEVALUE = ВРЕМЗНАЧ ## Преобразует время из текстового формата в числовой формат.
TODAY = СЕГОДНЯ ## Возвращает текущую дату в числовом формате.
WEEKDAY = ДЕНЬНЕД ## Преобразует дату в числовом формате в день недели.
WEEKNUM = НОМНЕДЕЛИ ## Преобразует числовое представление в число, которое указывает, на какую неделю года приходится указанная дата.
WORKDAY = РАБДЕНЬ ## Возвращает дату в числовом формате, отстоящую вперед или назад на заданное количество рабочих дней.
YEAR = ГОД ## Преобразует дату в числовом формате в год.
YEARFRAC = ДОЛЯГОДА ## Возвращает долю года, которую составляет количество дней между начальной и конечной датами.
##
## Engineering functions Инженерные функции
##
BESSELI = БЕССЕЛЬ.I ## Возвращает модифицированную функцию Бесселя In(x).
BESSELJ = БЕССЕЛЬ.J ## Возвращает функцию Бесселя Jn(x).
BESSELK = БЕССЕЛЬ.K ## Возвращает модифицированную функцию Бесселя Kn(x).
BESSELY = БЕССЕЛЬ.Y ## Возвращает функцию Бесселя Yn(x).
BIN2DEC = ДВ.В.ДЕС ## Преобразует двоичное число в десятичное.
BIN2HEX = ДВ.В.ШЕСТН ## Преобразует двоичное число в шестнадцатеричное.
BIN2OCT = ДВ.В.ВОСЬМ ## Преобразует двоичное число в восьмеричное.
COMPLEX = КОМПЛЕКСН ## Преобразует коэффициенты при вещественной и мнимой частях комплексного числа в комплексное число.
CONVERT = ПРЕОБР ## Преобразует число из одной системы единиц измерения в другую.
DEC2BIN = ДЕС.В.ДВ ## Преобразует десятичное число в двоичное.
DEC2HEX = ДЕС.В.ШЕСТН ## Преобразует десятичное число в шестнадцатеричное.
DEC2OCT = ДЕС.В.ВОСЬМ ## Преобразует десятичное число в восьмеричное.
DELTA = ДЕЛЬТА ## Проверяет равенство двух значений.
ERF = ФОШ ## Возвращает функцию ошибки.
ERFC = ДФОШ ## Возвращает дополнительную функцию ошибки.
GESTEP = ПОРОГ ## Проверяет, не превышает ли данное число порогового значения.
HEX2BIN = ШЕСТН.В.ДВ ## Преобразует шестнадцатеричное число в двоичное.
HEX2DEC = ШЕСТН.В.ДЕС ## Преобразует шестнадцатеричное число в десятичное.
HEX2OCT = ШЕСТН.В.ВОСЬМ ## Преобразует шестнадцатеричное число в восьмеричное.
IMABS = МНИМ.ABS ## Возвращает абсолютную величину (модуль) комплексного числа.
IMAGINARY = МНИМ.ЧАСТЬ ## Возвращает коэффициент при мнимой части комплексного числа.
IMARGUMENT = МНИМ.АРГУМЕНТ ## Возвращает значение аргумента комплексного числа (тета) — угол, выраженный в радианах.
IMCONJUGATE = МНИМ.СОПРЯЖ ## Возвращает комплексно-сопряженное комплексное число.
IMCOS = МНИМ.COS ## Возвращает косинус комплексного числа.
IMDIV = МНИМ.ДЕЛ ## Возвращает частное от деления двух комплексных чисел.
IMEXP = МНИМ.EXP ## Возвращает экспоненту комплексного числа.
IMLN = МНИМ.LN ## Возвращает натуральный логарифм комплексного числа.
IMLOG10 = МНИМ.LOG10 ## Возвращает обычный (десятичный) логарифм комплексного числа.
IMLOG2 = МНИМ.LOG2 ## Возвращает двоичный логарифм комплексного числа.
IMPOWER = МНИМ.СТЕПЕНЬ ## Возвращает комплексное число, возведенное в целую степень.
IMPRODUCT = МНИМ.ПРОИЗВЕД ## Возвращает произведение от 2 до 29 комплексных чисел.
IMREAL = МНИМ.ВЕЩ ## Возвращает коэффициент при вещественной части комплексного числа.
IMSIN = МНИМ.SIN ## Возвращает синус комплексного числа.
IMSQRT = МНИМ.КОРЕНЬ ## Возвращает значение квадратного корня из комплексного числа.
IMSUB = МНИМ.РАЗН ## Возвращает разность двух комплексных чисел.
IMSUM = МНИМ.СУММ ## Возвращает сумму комплексных чисел.
OCT2BIN = ВОСЬМ.В.ДВ ## Преобразует восьмеричное число в двоичное.
OCT2DEC = ВОСЬМ.В.ДЕС ## Преобразует восьмеричное число в десятичное.
OCT2HEX = ВОСЬМ.В.ШЕСТН ## Преобразует восьмеричное число в шестнадцатеричное.
##
## Financial functions Финансовые функции
##
ACCRINT = НАКОПДОХОД ## Возвращает накопленный процент по ценным бумагам с периодической выплатой процентов.
ACCRINTM = НАКОПДОХОДПОГАШ ## Возвращает накопленный процент по ценным бумагам, проценты по которым выплачиваются в срок погашения.
AMORDEGRC = АМОРУМ ## Возвращает величину амортизации для каждого периода, используя коэффициент амортизации.
AMORLINC = АМОРУВ ## Возвращает величину амортизации для каждого периода.
COUPDAYBS = ДНЕЙКУПОНДО ## Возвращает количество дней от начала действия купона до даты соглашения.
COUPDAYS = ДНЕЙКУПОН ## Возвращает число дней в периоде купона, содержащем дату соглашения.
COUPDAYSNC = ДНЕЙКУПОНПОСЛЕ ## Возвращает число дней от даты соглашения до срока следующего купона.
COUPNCD = ДАТАКУПОНПОСЛЕ ## Возвращает следующую дату купона после даты соглашения.
COUPNUM = ЧИСЛКУПОН ## Возвращает количество купонов, которые могут быть оплачены между датой соглашения и сроком вступления в силу.
COUPPCD = ДАТАКУПОНДО ## Возвращает предыдущую дату купона перед датой соглашения.
CUMIPMT = ОБЩПЛАТ ## Возвращает общую выплату, произведенную между двумя периодическими выплатами.
CUMPRINC = ОБЩДОХОД ## Возвращает общую выплату по займу между двумя периодами.
DB = ФУО ## Возвращает величину амортизации актива для заданного периода, рассчитанную методом фиксированного уменьшения остатка.
DDB = ДДОБ ## Возвращает величину амортизации актива за данный период, используя метод двойного уменьшения остатка или иной явно указанный метод.
DISC = СКИДКА ## Возвращает норму скидки для ценных бумаг.
DOLLARDE = РУБЛЬ.ДЕС ## Преобразует цену в рублях, выраженную в виде дроби, в цену в рублях, выраженную десятичным числом.
DOLLARFR = РУБЛЬ.ДРОБЬ ## Преобразует цену в рублях, выраженную десятичным числом, в цену в рублях, выраженную в виде дроби.
DURATION = ДЛИТ ## Возвращает ежегодную продолжительность действия ценных бумаг с периодическими выплатами по процентам.
EFFECT = ЭФФЕКТ ## Возвращает действующие ежегодные процентные ставки.
FV = БС ## Возвращает будущую стоимость инвестиции.
FVSCHEDULE = БЗРАСПИС ## Возвращает будущую стоимость первоначальной основной суммы после начисления ряда сложных процентов.
INTRATE = ИНОРМА ## Возвращает процентную ставку для полностью инвестированных ценных бумаг.
IPMT = ПРПЛТ ## Возвращает величину выплаты прибыли на вложения за данный период.
IRR = ВСД ## Возвращает внутреннюю ставку доходности для ряда потоков денежных средств.
ISPMT = ПРОЦПЛАТ ## Вычисляет выплаты за указанный период инвестиции.
MDURATION = МДЛИТ ## Возвращает модифицированную длительность Маколея для ценных бумаг с предполагаемой номинальной стоимостью 100 рублей.
MIRR = МВСД ## Возвращает внутреннюю ставку доходности, при которой положительные и отрицательные денежные потоки имеют разные значения ставки.
NOMINAL = НОМИНАЛ ## Возвращает номинальную годовую процентную ставку.
NPER = КПЕР ## Возвращает общее количество периодов выплаты для данного вклада.
NPV = ЧПС ## Возвращает чистую приведенную стоимость инвестиции, основанной на серии периодических денежных потоков и ставке дисконтирования.
ODDFPRICE = ЦЕНАПЕРВНЕРЕГ ## Возвращает цену за 100 рублей нарицательной стоимости ценных бумаг с нерегулярным первым периодом.
ODDFYIELD = ДОХОДПЕРВНЕРЕГ ## Возвращает доход по ценным бумагам с нерегулярным первым периодом.
ODDLPRICE = ЦЕНАПОСЛНЕРЕГ ## Возвращает цену за 100 рублей нарицательной стоимости ценных бумаг с нерегулярным последним периодом.
ODDLYIELD = ДОХОДПОСЛНЕРЕГ ## Возвращает доход по ценным бумагам с нерегулярным последним периодом.
PMT = ПЛТ ## Возвращает величину выплаты за один период аннуитета.
PPMT = ОСПЛТ ## Возвращает величину выплат в погашение основной суммы по инвестиции за заданный период.
PRICE = ЦЕНА ## Возвращает цену за 100 рублей нарицательной стоимости ценных бумаг, по которым производится периодическая выплата процентов.
PRICEDISC = ЦЕНАСКИДКА ## Возвращает цену за 100 рублей номинальной стоимости ценных бумаг, на которые сделана скидка.
PRICEMAT = ЦЕНАПОГАШ ## Возвращает цену за 100 рублей номинальной стоимости ценных бумаг, проценты по которым выплачиваются в срок погашения.
PV = ПС ## Возвращает приведенную (к текущему моменту) стоимость инвестиции.
RATE = СТАВКА ## Возвращает процентную ставку по аннуитету за один период.
RECEIVED = ПОЛУЧЕНО ## Возвращает сумму, полученную к сроку погашения полностью обеспеченных ценных бумаг.
SLN = АПЛ ## Возвращает величину линейной амортизации актива за один период.
SYD = АСЧ ## Возвращает величину амортизации актива за данный период, рассчитанную методом суммы годовых чисел.
TBILLEQ = РАВНОКЧЕК ## Возвращает эквивалентный облигации доход по казначейскому чеку.
TBILLPRICE = ЦЕНАКЧЕК ## Возвращает цену за 100 рублей нарицательной стоимости для казначейского чека.
TBILLYIELD = ДОХОДКЧЕК ## Возвращает доход по казначейскому чеку.
VDB = ПУО ## Возвращает величину амортизации актива для указанного или частичного периода при использовании метода сокращающегося баланса.
XIRR = ЧИСТВНДОХ ## Возвращает внутреннюю ставку доходности для графика денежных потоков, которые не обязательно носят периодический характер.
XNPV = ЧИСТНЗ ## Возвращает чистую приведенную стоимость для денежных потоков, которые не обязательно являются периодическими.
YIELD = ДОХОД ## Возвращает доход от ценных бумаг, по которым производятся периодические выплаты процентов.
YIELDDISC = ДОХОДСКИДКА ## Возвращает годовой доход по ценным бумагам, на которые сделана скидка (пример — казначейские чеки).
YIELDMAT = ДОХОДПОГАШ ## Возвращает годовой доход от ценных бумаг, проценты по которым выплачиваются в срок погашения.
##
## Information functions Информационные функции
##
CELL = ЯЧЕЙКА ## Возвращает информацию о формате, расположении или содержимом ячейки.
ERROR.TYPE = ТИП.ОШИБКИ ## Возвращает числовой код, соответствующий типу ошибки.
INFO = ИНФОРМ ## Возвращает информацию о текущей операционной среде.
ISBLANK = ЕПУСТО ## Возвращает значение ИСТИНА, если аргумент является ссылкой на пустую ячейку.
ISERR = ЕОШ ## Возвращает значение ИСТИНА, если аргумент ссылается на любое значение ошибки, кроме #Н/Д.
ISERROR = ЕОШИБКА ## Возвращает значение ИСТИНА, если аргумент ссылается на любое значение ошибки.
ISEVEN = ЕЧЁТН ## Возвращает значение ИСТИНА, если значение аргумента является четным числом.
ISLOGICAL = ЕЛОГИЧ ## Возвращает значение ИСТИНА, если аргумент ссылается на логическое значение.
ISNA = ЕНД ## Возвращает значение ИСТИНА, если аргумент ссылается на значение ошибки #Н/Д.
ISNONTEXT = ЕНЕТЕКСТ ## Возвращает значение ИСТИНА, если значение аргумента не является текстом.
ISNUMBER = ЕЧИСЛО ## Возвращает значение ИСТИНА, если аргумент ссылается на число.
ISODD = ЕНЕЧЁТ ## Возвращает значение ИСТИНА, если значение аргумента является нечетным числом.
ISREF = ЕССЫЛКА ## Возвращает значение ИСТИНА, если значение аргумента является ссылкой.
ISTEXT = ЕТЕКСТ ## Возвращает значение ИСТИНА, если значение аргумента является текстом.
N = Ч ## Возвращает значение, преобразованное в число.
NA = НД ## Возвращает значение ошибки #Н/Д.
TYPE = ТИП ## Возвращает число, обозначающее тип данных значения.
##
## Logical functions Логические функции
##
AND = И ## Renvoie VRAI si tous ses arguments sont VRAI.
FALSE = ЛОЖЬ ## Возвращает логическое значение ЛОЖЬ.
IF = ЕСЛИ ## Выполняет проверку условия.
IFERROR = ЕСЛИОШИБКА ## Возвращает введённое значение, если вычисление по формуле вызывает ошибку; в противном случае функция возвращает результат вычисления.
NOT = НЕ ## Меняет логическое значение своего аргумента на противоположное.
OR = ИЛИ ## Возвращает значение ИСТИНА, если хотя бы один аргумент имеет значение ИСТИНА.
TRUE = ИСТИНА ## Возвращает логическое значение ИСТИНА.
##
## Lookup and reference functions Функции ссылки и поиска
##
ADDRESS = АДРЕС ## Возвращает ссылку на отдельную ячейку листа в виде текста.
AREAS = ОБЛАСТИ ## Возвращает количество областей в ссылке.
CHOOSE = ВЫБОР ## Выбирает значение из списка значений по индексу.
COLUMN = СТОЛБЕЦ ## Возвращает номер столбца, на который указывает ссылка.
COLUMNS = ЧИСЛСТОЛБ ## Возвращает количество столбцов в ссылке.
HLOOKUP = ГПР ## Ищет в первой строке массива и возвращает значение отмеченной ячейки
HYPERLINK = ГИПЕРССЫЛКА ## Создает ссылку, открывающую документ, который находится на сервере сети, в интрасети или в Интернете.
INDEX = ИНДЕКС ## Использует индекс для выбора значения из ссылки или массива.
INDIRECT = ДВССЫЛ ## Возвращает ссылку, заданную текстовым значением.
LOOKUP = ПРОСМОТР ## Ищет значения в векторе или массиве.
MATCH = ПОИСКПОЗ ## Ищет значения в ссылке или массиве.
OFFSET = СМЕЩ ## Возвращает смещение ссылки относительно заданной ссылки.
ROW = СТРОКА ## Возвращает номер строки, определяемой ссылкой.
ROWS = ЧСТРОК ## Возвращает количество строк в ссылке.
RTD = ДРВ ## Извлекает данные реального времени из программ, поддерживающих автоматизацию COM (Программирование объектов. Стандартное средство для работы с объектами некоторого приложения из другого приложения или средства разработки. Программирование объектов (ранее называемое программированием OLE) является функцией модели COM (Component Object Model, модель компонентных объектов).).
TRANSPOSE = ТРАНСП ## Возвращает транспонированный массив.
VLOOKUP = ВПР ## Ищет значение в первом столбце массива и возвращает значение из ячейки в найденной строке и указанном столбце.
##
## Math and trigonometry functions Математические и тригонометрические функции
##
ABS = ABS ## Возвращает модуль (абсолютную величину) числа.
ACOS = ACOS ## Возвращает арккосинус числа.
ACOSH = ACOSH ## Возвращает гиперболический арккосинус числа.
ASIN = ASIN ## Возвращает арксинус числа.
ASINH = ASINH ## Возвращает гиперболический арксинус числа.
ATAN = ATAN ## Возвращает арктангенс числа.
ATAN2 = ATAN2 ## Возвращает арктангенс для заданных координат x и y.
ATANH = ATANH ## Возвращает гиперболический арктангенс числа.
CEILING = ОКРВВЕРХ ## Округляет число до ближайшего целого или до ближайшего кратного указанному значению.
COMBIN = ЧИСЛКОМБ ## Возвращает количество комбинаций для заданного числа объектов.
COS = COS ## Возвращает косинус числа.
COSH = COSH ## Возвращает гиперболический косинус числа.
DEGREES = ГРАДУСЫ ## Преобразует радианы в градусы.
EVEN = ЧЁТН ## Округляет число до ближайшего четного целого.
EXP = EXP ## Возвращает число e, возведенное в указанную степень.
FACT = ФАКТР ## Возвращает факториал числа.
FACTDOUBLE = ДВФАКТР ## Возвращает двойной факториал числа.
FLOOR = ОКРВНИЗ ## Округляет число до ближайшего меньшего по модулю значения.
GCD = НОД ## Возвращает наибольший общий делитель.
INT = ЦЕЛОЕ ## Округляет число до ближайшего меньшего целого.
LCM = НОК ## Возвращает наименьшее общее кратное.
LN = LN ## Возвращает натуральный логарифм числа.
LOG = LOG ## Возвращает логарифм числа по заданному основанию.
LOG10 = LOG10 ## Возвращает десятичный логарифм числа.
MDETERM = МОПРЕД ## Возвращает определитель матрицы массива.
MINVERSE = МОБР ## Возвращает обратную матрицу массива.
MMULT = МУМНОЖ ## Возвращает произведение матриц двух массивов.
MOD = ОСТАТ ## Возвращает остаток от деления.
MROUND = ОКРУГЛТ ## Возвращает число, округленное с требуемой точностью.
MULTINOMIAL = МУЛЬТИНОМ ## Возвращает мультиномиальный коэффициент множества чисел.
ODD = НЕЧЁТ ## Округляет число до ближайшего нечетного целого.
PI = ПИ ## Возвращает число пи.
POWER = СТЕПЕНЬ ## Возвращает результат возведения числа в степень.
PRODUCT = ПРОИЗВЕД ## Возвращает произведение аргументов.
QUOTIENT = ЧАСТНОЕ ## Возвращает целую часть частного при делении.
RADIANS = РАДИАНЫ ## Преобразует градусы в радианы.
RAND = СЛЧИС ## Возвращает случайное число в интервале от 0 до 1.
RANDBETWEEN = СЛУЧМЕЖДУ ## Возвращает случайное число в интервале между двумя заданными числами.
ROMAN = РИМСКОЕ ## Преобразует арабские цифры в римские в виде текста.
ROUND = ОКРУГЛ ## Округляет число до указанного количества десятичных разрядов.
ROUNDDOWN = ОКРУГЛВНИЗ ## Округляет число до ближайшего меньшего по модулю значения.
ROUNDUP = ОКРУГЛВВЕРХ ## Округляет число до ближайшего большего по модулю значения.
SERIESSUM = РЯД.СУММ ## Возвращает сумму степенного ряда, вычисленную по формуле.
SIGN = ЗНАК ## Возвращает знак числа.
SIN = SIN ## Возвращает синус заданного угла.
SINH = SINH ## Возвращает гиперболический синус числа.
SQRT = КОРЕНЬ ## Возвращает положительное значение квадратного корня.
SQRTPI = КОРЕНЬПИ ## Возвращает квадратный корень из значения выражения (число * ПИ).
SUBTOTAL = ПРОМЕЖУТОЧНЫЕ.ИТОГИ ## Возвращает промежуточный итог в списке или базе данных.
SUM = СУММ ## Суммирует аргументы.
SUMIF = СУММЕСЛИ ## Суммирует ячейки, удовлетворяющие заданному условию.
SUMIFS = СУММЕСЛИМН ## Суммирует диапазон ячеек, удовлетворяющих нескольким условиям.
SUMPRODUCT = СУММПРОИЗВ ## Возвращает сумму произведений соответствующих элементов массивов.
SUMSQ = СУММКВ ## Возвращает сумму квадратов аргументов.
SUMX2MY2 = СУММРАЗНКВ ## Возвращает сумму разностей квадратов соответствующих значений в двух массивах.
SUMX2PY2 = СУММСУММКВ ## Возвращает сумму сумм квадратов соответствующих элементов двух массивов.
SUMXMY2 = СУММКВРАЗН ## Возвращает сумму квадратов разностей соответствующих значений в двух массивах.
TAN = TAN ## Возвращает тангенс числа.
TANH = TANH ## Возвращает гиперболический тангенс числа.
TRUNC = ОТБР ## Отбрасывает дробную часть числа.
##
## Statistical functions Статистические функции
##
AVEDEV = СРОТКЛ ## Возвращает среднее арифметическое абсолютных значений отклонений точек данных от среднего.
AVERAGE = СРЗНАЧ ## Возвращает среднее арифметическое аргументов.
AVERAGEA = СРЗНАЧА ## Возвращает среднее арифметическое аргументов, включая числа, текст и логические значения.
AVERAGEIF = СРЗНАЧЕСЛИ ## Возвращает среднее значение (среднее арифметическое) всех ячеек в диапазоне, которые удовлетворяют данному условию.
AVERAGEIFS = СРЗНАЧЕСЛИМН ## Возвращает среднее значение (среднее арифметическое) всех ячеек, которые удовлетворяют нескольким условиям.
BETADIST = БЕТАРАСП ## Возвращает интегральную функцию бета-распределения.
BETAINV = БЕТАОБР ## Возвращает обратную интегральную функцию указанного бета-распределения.
BINOMDIST = БИНОМРАСП ## Возвращает отдельное значение биномиального распределения.
CHIDIST = ХИ2РАСП ## Возвращает одностороннюю вероятность распределения хи-квадрат.
CHIINV = ХИ2ОБР ## Возвращает обратное значение односторонней вероятности распределения хи-квадрат.
CHITEST = ХИ2ТЕСТ ## Возвращает тест на независимость.
CONFIDENCE = ДОВЕРИТ ## Возвращает доверительный интервал для среднего значения по генеральной совокупности.
CORREL = КОРРЕЛ ## Возвращает коэффициент корреляции между двумя множествами данных.
COUNT = СЧЁТ ## Подсчитывает количество чисел в списке аргументов.
COUNTA = СЧЁТЗ ## Подсчитывает количество значений в списке аргументов.
COUNTBLANK = СЧИТАТЬПУСТОТЫ ## Подсчитывает количество пустых ячеек в диапазоне
COUNTIF = СЧЁТЕСЛИ ## Подсчитывает количество ячеек в диапазоне, удовлетворяющих заданному условию
COUNTIFS = СЧЁТЕСЛИМН ## Подсчитывает количество ячеек внутри диапазона, удовлетворяющих нескольким условиям.
COVAR = КОВАР ## Возвращает ковариацию, среднее произведений парных отклонений
CRITBINOM = КРИТБИНОМ ## Возвращает наименьшее значение, для которого интегральное биномиальное распределение меньше или равно заданному критерию.
DEVSQ = КВАДРОТКЛ ## Возвращает сумму квадратов отклонений.
EXPONDIST = ЭКСПРАСП ## Возвращает экспоненциальное распределение.
FDIST = FРАСП ## Возвращает F-распределение вероятности.
FINV = FРАСПОБР ## Возвращает обратное значение для F-распределения вероятности.
FISHER = ФИШЕР ## Возвращает преобразование Фишера.
FISHERINV = ФИШЕРОБР ## Возвращает обратное преобразование Фишера.
FORECAST = ПРЕДСКАЗ ## Возвращает значение линейного тренда.
FREQUENCY = ЧАСТОТА ## Возвращает распределение частот в виде вертикального массива.
FTEST = ФТЕСТ ## Возвращает результат F-теста.
GAMMADIST = ГАММАРАСП ## Возвращает гамма-распределение.
GAMMAINV = ГАММАОБР ## Возвращает обратное гамма-распределение.
GAMMALN = ГАММАНЛОГ ## Возвращает натуральный логарифм гамма функции, Γ(x).
GEOMEAN = СРГЕОМ ## Возвращает среднее геометрическое.
GROWTH = РОСТ ## Возвращает значения в соответствии с экспоненциальным трендом.
HARMEAN = СРГАРМ ## Возвращает среднее гармоническое.
HYPGEOMDIST = ГИПЕРГЕОМЕТ ## Возвращает гипергеометрическое распределение.
INTERCEPT = ОТРЕЗОК ## Возвращает отрезок, отсекаемый на оси линией линейной регрессии.
KURT = ЭКСЦЕСС ## Возвращает эксцесс множества данных.
LARGE = НАИБОЛЬШИЙ ## Возвращает k-ое наибольшее значение в множестве данных.
LINEST = ЛИНЕЙН ## Возвращает параметры линейного тренда.
LOGEST = ЛГРФПРИБЛ ## Возвращает параметры экспоненциального тренда.
LOGINV = ЛОГНОРМОБР ## Возвращает обратное логарифмическое нормальное распределение.
LOGNORMDIST = ЛОГНОРМРАСП ## Возвращает интегральное логарифмическое нормальное распределение.
MAX = МАКС ## Возвращает наибольшее значение в списке аргументов.
MAXA = МАКСА ## Возвращает наибольшее значение в списке аргументов, включая числа, текст и логические значения.
MEDIAN = МЕДИАНА ## Возвращает медиану заданных чисел.
MIN = МИН ## Возвращает наименьшее значение в списке аргументов.
MINA = МИНА ## Возвращает наименьшее значение в списке аргументов, включая числа, текст и логические значения.
MODE = МОДА ## Возвращает значение моды множества данных.
NEGBINOMDIST = ОТРБИНОМРАСП ## Возвращает отрицательное биномиальное распределение.
NORMDIST = НОРМРАСП ## Возвращает нормальную функцию распределения.
NORMINV = НОРМОБР ## Возвращает обратное нормальное распределение.
NORMSDIST = НОРМСТРАСП ## Возвращает стандартное нормальное интегральное распределение.
NORMSINV = НОРМСТОБР ## Возвращает обратное значение стандартного нормального распределения.
PEARSON = ПИРСОН ## Возвращает коэффициент корреляции Пирсона.
PERCENTILE = ПЕРСЕНТИЛЬ ## Возвращает k-ую персентиль для значений диапазона.
PERCENTRANK = ПРОЦЕНТРАНГ ## Возвращает процентную норму значения в множестве данных.
PERMUT = ПЕРЕСТ ## Возвращает количество перестановок для заданного числа объектов.
POISSON = ПУАССОН ## Возвращает распределение Пуассона.
PROB = ВЕРОЯТНОСТЬ ## Возвращает вероятность того, что значение из диапазона находится внутри заданных пределов.
QUARTILE = КВАРТИЛЬ ## Возвращает квартиль множества данных.
RANK = РАНГ ## Возвращает ранг числа в списке чисел.
RSQ = КВПИРСОН ## Возвращает квадрат коэффициента корреляции Пирсона.
SKEW = СКОС ## Возвращает асимметрию распределения.
SLOPE = НАКЛОН ## Возвращает наклон линии линейной регрессии.
SMALL = НАИМЕНЬШИЙ ## Возвращает k-ое наименьшее значение в множестве данных.
STANDARDIZE = НОРМАЛИЗАЦИЯ ## Возвращает нормализованное значение.
STDEV = СТАНДОТКЛОН ## Оценивает стандартное отклонение по выборке.
STDEVA = СТАНДОТКЛОНА ## Оценивает стандартное отклонение по выборке, включая числа, текст и логические значения.
STDEVP = СТАНДОТКЛОНП ## Вычисляет стандартное отклонение по генеральной совокупности.
STDEVPA = СТАНДОТКЛОНПА ## Вычисляет стандартное отклонение по генеральной совокупности, включая числа, текст и логические значения.
STEYX = СТОШYX ## Возвращает стандартную ошибку предсказанных значений y для каждого значения x в регрессии.
TDIST = СТЬЮДРАСП ## Возвращает t-распределение Стьюдента.
TINV = СТЬЮДРАСПОБР ## Возвращает обратное t-распределение Стьюдента.
TREND = ТЕНДЕНЦИЯ ## Возвращает значения в соответствии с линейным трендом.
TRIMMEAN = УРЕЗСРЕДНЕЕ ## Возвращает среднее внутренности множества данных.
TTEST = ТТЕСТ ## Возвращает вероятность, соответствующую критерию Стьюдента.
VAR = ДИСП ## Оценивает дисперсию по выборке.
VARA = ДИСПА ## Оценивает дисперсию по выборке, включая числа, текст и логические значения.
VARP = ДИСПР ## Вычисляет дисперсию для генеральной совокупности.
VARPA = ДИСПРА ## Вычисляет дисперсию для генеральной совокупности, включая числа, текст и логические значения.
WEIBULL = ВЕЙБУЛЛ ## Возвращает распределение Вейбулла.
ZTEST = ZТЕСТ ## Возвращает двустороннее P-значение z-теста.
##
## Text functions Текстовые функции
##
ASC = ASC ## Для языков с двухбайтовыми наборами знаков (например, катакана) преобразует полноширинные (двухбайтовые) знаки в полуширинные (однобайтовые).
BAHTTEXT = БАТТЕКСТ ## Преобразует число в текст, используя денежный формат ß (БАТ).
CHAR = СИМВОЛ ## Возвращает знак с заданным кодом.
CLEAN = ПЕЧСИМВ ## Удаляет все непечатаемые знаки из текста.
CODE = КОДСИМВ ## Возвращает числовой код первого знака в текстовой строке.
CONCATENATE = СЦЕПИТЬ ## Объединяет несколько текстовых элементов в один.
DOLLAR = РУБЛЬ ## Преобразует число в текст, используя денежный формат.
EXACT = СОВПАД ## Проверяет идентичность двух текстовых значений.
FIND = НАЙТИ ## Ищет вхождения одного текстового значения в другом (с учетом регистра).
FINDB = НАЙТИБ ## Ищет вхождения одного текстового значения в другом (с учетом регистра).
FIXED = ФИКСИРОВАННЫЙ ## Форматирует число и преобразует его в текст с заданным числом десятичных знаков.
JIS = JIS ## Для языков с двухбайтовыми наборами знаков (например, катакана) преобразует полуширинные (однобайтовые) знаки в текстовой строке в полноширинные (двухбайтовые).
LEFT = ЛЕВСИМВ ## Возвращает крайние слева знаки текстового значения.
LEFTB = ЛЕВБ ## Возвращает крайние слева знаки текстового значения.
LEN = ДЛСТР ## Возвращает количество знаков в текстовой строке.
LENB = ДЛИНБ ## Возвращает количество знаков в текстовой строке.
LOWER = СТРОЧН ## Преобразует все буквы текста в строчные.
MID = ПСТР ## Возвращает заданное число знаков из строки текста, начиная с указанной позиции.
MIDB = ПСТРБ ## Возвращает заданное число знаков из строки текста, начиная с указанной позиции.
PHONETIC = PHONETIC ## Извлекает фонетические (фуригана) знаки из текстовой строки.
PROPER = ПРОПНАЧ ## Преобразует первую букву в каждом слове текста в прописную.
REPLACE = ЗАМЕНИТЬ ## Заменяет знаки в тексте.
REPLACEB = ЗАМЕНИТЬБ ## Заменяет знаки в тексте.
REPT = ПОВТОР ## Повторяет текст заданное число раз.
RIGHT = ПРАВСИМВ ## Возвращает крайние справа знаки текстовой строки.
RIGHTB = ПРАВБ ## Возвращает крайние справа знаки текстовой строки.
SEARCH = ПОИСК ## Ищет вхождения одного текстового значения в другом (без учета регистра).
SEARCHB = ПОИСКБ ## Ищет вхождения одного текстового значения в другом (без учета регистра).
SUBSTITUTE = ПОДСТАВИТЬ ## Заменяет в текстовой строке старый текст новым.
T = Т ## Преобразует аргументы в текст.
TEXT = ТЕКСТ ## Форматирует число и преобразует его в текст.
TRIM = СЖПРОБЕЛЫ ## Удаляет из текста пробелы.
UPPER = ПРОПИСН ## Преобразует все буквы текста в прописные.
VALUE = ЗНАЧЕН ## Преобразует текстовый аргумент в число.
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/bg/config 0000644 00000000430 15242753725 0020557 0 ustar 00 ##
## PhpSpreadsheet
##
##
##
ArgumentSeparator = ;
##
## (For future use)
##
currencySymbol = лв
##
## Excel Error Codes (For future use)
##
NULL = #ПРАЗНО!
DIV0 = #ДЕЛ/0!
VALUE = #СТОЙНОСТ!
REF = #РЕФ!
NAME = #ИМЕ?
NUM = #ЧИСЛО!
NA = #Н/Д
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/functions 0000644 00000023147 15242753725 0021772 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Português Brasileiro (Brazilian Portuguese)
##
############################################################
##
## Funções de cubo (Cube Functions)
##
CUBEKPIMEMBER = MEMBROKPICUBO
CUBEMEMBER = MEMBROCUBO
CUBEMEMBERPROPERTY = PROPRIEDADEMEMBROCUBO
CUBERANKEDMEMBER = MEMBROCLASSIFICADOCUBO
CUBESET = CONJUNTOCUBO
CUBESETCOUNT = CONTAGEMCONJUNTOCUBO
CUBEVALUE = VALORCUBO
##
## Funções de banco de dados (Database Functions)
##
DAVERAGE = BDMÉDIA
DCOUNT = BDCONTAR
DCOUNTA = BDCONTARA
DGET = BDEXTRAIR
DMAX = BDMÁX
DMIN = BDMÍN
DPRODUCT = BDMULTIPL
DSTDEV = BDEST
DSTDEVP = BDDESVPA
DSUM = BDSOMA
DVAR = BDVAREST
DVARP = BDVARP
##
## Funções de data e hora (Date & Time Functions)
##
DATE = DATA
DATEDIF = DATADIF
DATESTRING = DATA.SÉRIE
DATEVALUE = DATA.VALOR
DAY = DIA
DAYS = DIAS
DAYS360 = DIAS360
EDATE = DATAM
EOMONTH = FIMMÊS
HOUR = HORA
ISOWEEKNUM = NÚMSEMANAISO
MINUTE = MINUTO
MONTH = MÊS
NETWORKDAYS = DIATRABALHOTOTAL
NETWORKDAYS.INTL = DIATRABALHOTOTAL.INTL
NOW = AGORA
SECOND = SEGUNDO
TIME = TEMPO
TIMEVALUE = VALOR.TEMPO
TODAY = HOJE
WEEKDAY = DIA.DA.SEMANA
WEEKNUM = NÚMSEMANA
WORKDAY = DIATRABALHO
WORKDAY.INTL = DIATRABALHO.INTL
YEAR = ANO
YEARFRAC = FRAÇÃOANO
##
## Funções de engenharia (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BINADEC
BIN2HEX = BINAHEX
BIN2OCT = BINAOCT
BITAND = BITAND
BITLSHIFT = DESLOCESQBIT
BITOR = BITOR
BITRSHIFT = DESLOCDIRBIT
BITXOR = BITXOR
COMPLEX = COMPLEXO
CONVERT = CONVERTER
DEC2BIN = DECABIN
DEC2HEX = DECAHEX
DEC2OCT = DECAOCT
DELTA = DELTA
ERF = FUNERRO
ERF.PRECISE = FUNERRO.PRECISO
ERFC = FUNERROCOMPL
ERFC.PRECISE = FUNERROCOMPL.PRECISO
GESTEP = DEGRAU
HEX2BIN = HEXABIN
HEX2DEC = HEXADEC
HEX2OCT = HEXAOCT
IMABS = IMABS
IMAGINARY = IMAGINÁRIO
IMARGUMENT = IMARG
IMCONJUGATE = IMCONJ
IMCOS = IMCOS
IMCOSH = IMCOSH
IMCOT = IMCOT
IMCSC = IMCOSEC
IMCSCH = IMCOSECH
IMDIV = IMDIV
IMEXP = IMEXP
IMLN = IMLN
IMLOG10 = IMLOG10
IMLOG2 = IMLOG2
IMPOWER = IMPOT
IMPRODUCT = IMPROD
IMREAL = IMREAL
IMSEC = IMSEC
IMSECH = IMSECH
IMSIN = IMSENO
IMSINH = IMSENH
IMSQRT = IMRAIZ
IMSUB = IMSUBTR
IMSUM = IMSOMA
IMTAN = IMTAN
OCT2BIN = OCTABIN
OCT2DEC = OCTADEC
OCT2HEX = OCTAHEX
##
## Funções financeiras (Financial Functions)
##
ACCRINT = JUROSACUM
ACCRINTM = JUROSACUMV
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = CUPDIASINLIQ
COUPDAYS = CUPDIAS
COUPDAYSNC = CUPDIASPRÓX
COUPNCD = CUPDATAPRÓX
COUPNUM = CUPNÚM
COUPPCD = CUPDATAANT
CUMIPMT = PGTOJURACUM
CUMPRINC = PGTOCAPACUM
DB = BD
DDB = BDD
DISC = DESC
DOLLARDE = MOEDADEC
DOLLARFR = MOEDAFRA
DURATION = DURAÇÃO
EFFECT = EFETIVA
FV = VF
FVSCHEDULE = VFPLANO
INTRATE = TAXAJUROS
IPMT = IPGTO
IRR = TIR
ISPMT = ÉPGTO
MDURATION = MDURAÇÃO
MIRR = MTIR
NOMINAL = NOMINAL
NPER = NPER
NPV = VPL
ODDFPRICE = PREÇOPRIMINC
ODDFYIELD = LUCROPRIMINC
ODDLPRICE = PREÇOÚLTINC
ODDLYIELD = LUCROÚLTINC
PDURATION = DURAÇÃOP
PMT = PGTO
PPMT = PPGTO
PRICE = PREÇO
PRICEDISC = PREÇODESC
PRICEMAT = PREÇOVENC
PV = VP
RATE = TAXA
RECEIVED = RECEBER
RRI = TAXAJURO
SLN = DPD
SYD = SDA
TBILLEQ = OTN
TBILLPRICE = OTNVALOR
TBILLYIELD = OTNLUCRO
VDB = BDV
XIRR = XTIR
XNPV = XVPL
YIELD = LUCRO
YIELDDISC = LUCRODESC
YIELDMAT = LUCROVENC
##
## Funções de informação (Information Functions)
##
CELL = CÉL
ERROR.TYPE = TIPO.ERRO
INFO = INFORMAÇÃO
ISBLANK = ÉCÉL.VAZIA
ISERR = ÉERRO
ISERROR = ÉERROS
ISEVEN = ÉPAR
ISFORMULA = ÉFÓRMULA
ISLOGICAL = ÉLÓGICO
ISNA = É.NÃO.DISP
ISNONTEXT = É.NÃO.TEXTO
ISNUMBER = ÉNÚM
ISODD = ÉIMPAR
ISREF = ÉREF
ISTEXT = ÉTEXTO
N = N
NA = NÃO.DISP
SHEET = PLAN
SHEETS = PLANS
TYPE = TIPO
##
## Funções lógicas (Logical Functions)
##
AND = E
FALSE = FALSO
IF = SE
IFERROR = SEERRO
IFNA = SENÃODISP
IFS = SES
NOT = NÃO
OR = OU
SWITCH = PARÂMETRO
TRUE = VERDADEIRO
XOR = XOR
##
## Funções de pesquisa e referência (Lookup & Reference Functions)
##
ADDRESS = ENDEREÇO
AREAS = ÁREAS
CHOOSE = ESCOLHER
COLUMN = COL
COLUMNS = COLS
FORMULATEXT = FÓRMULATEXTO
GETPIVOTDATA = INFODADOSTABELADINÂMICA
HLOOKUP = PROCH
HYPERLINK = HIPERLINK
INDEX = ÍNDICE
INDIRECT = INDIRETO
LOOKUP = PROC
MATCH = CORRESP
OFFSET = DESLOC
ROW = LIN
ROWS = LINS
RTD = RTD
TRANSPOSE = TRANSPOR
VLOOKUP = PROCV
*RC = LC
##
## Funções matemáticas e trigonométricas (Math & Trig Functions)
##
ABS = ABS
ACOS = ACOS
ACOSH = ACOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = AGREGAR
ARABIC = ARÁBICO
ASIN = ASEN
ASINH = ASENH
ATAN = ATAN
ATAN2 = ATAN2
ATANH = ATANH
BASE = BASE
CEILING.MATH = TETO.MAT
CEILING.PRECISE = TETO.PRECISO
COMBIN = COMBIN
COMBINA = COMBINA
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = COSEC
CSCH = COSECH
DECIMAL = DECIMAL
DEGREES = GRAUS
ECMA.CEILING = ECMA.TETO
EVEN = PAR
EXP = EXP
FACT = FATORIAL
FACTDOUBLE = FATDUPLO
FLOOR.MATH = ARREDMULTB.MAT
FLOOR.PRECISE = ARREDMULTB.PRECISO
GCD = MDC
INT = INT
ISO.CEILING = ISO.TETO
LCM = MMC
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = MATRIZ.DETERM
MINVERSE = MATRIZ.INVERSO
MMULT = MATRIZ.MULT
MOD = MOD
MROUND = MARRED
MULTINOMIAL = MULTINOMIAL
MUNIT = MUNIT
ODD = ÍMPAR
PI = PI
POWER = POTÊNCIA
PRODUCT = MULT
QUOTIENT = QUOCIENTE
RADIANS = RADIANOS
RAND = ALEATÓRIO
RANDBETWEEN = ALEATÓRIOENTRE
ROMAN = ROMANO
ROUND = ARRED
ROUNDDOWN = ARREDONDAR.PARA.BAIXO
ROUNDUP = ARREDONDAR.PARA.CIMA
SEC = SEC
SECH = SECH
SERIESSUM = SOMASEQÜÊNCIA
SIGN = SINAL
SIN = SEN
SINH = SENH
SQRT = RAIZ
SQRTPI = RAIZPI
SUBTOTAL = SUBTOTAL
SUM = SOMA
SUMIF = SOMASE
SUMIFS = SOMASES
SUMPRODUCT = SOMARPRODUTO
SUMSQ = SOMAQUAD
SUMX2MY2 = SOMAX2DY2
SUMX2PY2 = SOMAX2SY2
SUMXMY2 = SOMAXMY2
TAN = TAN
TANH = TANH
TRUNC = TRUNCAR
##
## Funções estatísticas (Statistical Functions)
##
AVEDEV = DESV.MÉDIO
AVERAGE = MÉDIA
AVERAGEA = MÉDIAA
AVERAGEIF = MÉDIASE
AVERAGEIFS = MÉDIASES
BETA.DIST = DIST.BETA
BETA.INV = INV.BETA
BINOM.DIST = DISTR.BINOM
BINOM.DIST.RANGE = INTERV.DISTR.BINOM
BINOM.INV = INV.BINOM
CHISQ.DIST = DIST.QUIQUA
CHISQ.DIST.RT = DIST.QUIQUA.CD
CHISQ.INV = INV.QUIQUA
CHISQ.INV.RT = INV.QUIQUA.CD
CHISQ.TEST = TESTE.QUIQUA
CONFIDENCE.NORM = INT.CONFIANÇA.NORM
CONFIDENCE.T = INT.CONFIANÇA.T
CORREL = CORREL
COUNT = CONT.NÚM
COUNTA = CONT.VALORES
COUNTBLANK = CONTAR.VAZIO
COUNTIF = CONT.SE
COUNTIFS = CONT.SES
COVARIANCE.P = COVARIAÇÃO.P
COVARIANCE.S = COVARIAÇÃO.S
DEVSQ = DESVQ
EXPON.DIST = DISTR.EXPON
F.DIST = DIST.F
F.DIST.RT = DIST.F.CD
F.INV = INV.F
F.INV.RT = INV.F.CD
F.TEST = TESTE.F
FISHER = FISHER
FISHERINV = FISHERINV
FORECAST.ETS = PREVISÃO.ETS
FORECAST.ETS.CONFINT = PREVISÃO.ETS.CONFINT
FORECAST.ETS.SEASONALITY = PREVISÃO.ETS.SAZONALIDADE
FORECAST.ETS.STAT = PREVISÃO.ETS.STAT
FORECAST.LINEAR = PREVISÃO.LINEAR
FREQUENCY = FREQÜÊNCIA
GAMMA = GAMA
GAMMA.DIST = DIST.GAMA
GAMMA.INV = INV.GAMA
GAMMALN = LNGAMA
GAMMALN.PRECISE = LNGAMA.PRECISO
GAUSS = GAUSS
GEOMEAN = MÉDIA.GEOMÉTRICA
GROWTH = CRESCIMENTO
HARMEAN = MÉDIA.HARMÔNICA
HYPGEOM.DIST = DIST.HIPERGEOM.N
INTERCEPT = INTERCEPÇÃO
KURT = CURT
LARGE = MAIOR
LINEST = PROJ.LIN
LOGEST = PROJ.LOG
LOGNORM.DIST = DIST.LOGNORMAL.N
LOGNORM.INV = INV.LOGNORMAL
MAX = MÁXIMO
MAXA = MÁXIMOA
MAXIFS = MÁXIMOSES
MEDIAN = MED
MIN = MÍNIMO
MINA = MÍNIMOA
MINIFS = MÍNIMOSES
MODE.MULT = MODO.MULT
MODE.SNGL = MODO.ÚNICO
NEGBINOM.DIST = DIST.BIN.NEG.N
NORM.DIST = DIST.NORM.N
NORM.INV = INV.NORM.N
NORM.S.DIST = DIST.NORMP.N
NORM.S.INV = INV.NORMP.N
PEARSON = PEARSON
PERCENTILE.EXC = PERCENTIL.EXC
PERCENTILE.INC = PERCENTIL.INC
PERCENTRANK.EXC = ORDEM.PORCENTUAL.EXC
PERCENTRANK.INC = ORDEM.PORCENTUAL.INC
PERMUT = PERMUT
PERMUTATIONA = PERMUTAS
PHI = PHI
POISSON.DIST = DIST.POISSON
PROB = PROB
QUARTILE.EXC = QUARTIL.EXC
QUARTILE.INC = QUARTIL.INC
RANK.AVG = ORDEM.MÉD
RANK.EQ = ORDEM.EQ
RSQ = RQUAD
SKEW = DISTORÇÃO
SKEW.P = DISTORÇÃO.P
SLOPE = INCLINAÇÃO
SMALL = MENOR
STANDARDIZE = PADRONIZAR
STDEV.P = DESVPAD.P
STDEV.S = DESVPAD.A
STDEVA = DESVPADA
STDEVPA = DESVPADPA
STEYX = EPADYX
T.DIST = DIST.T
T.DIST.2T = DIST.T.BC
T.DIST.RT = DIST.T.CD
T.INV = INV.T
T.INV.2T = INV.T.BC
T.TEST = TESTE.T
TREND = TENDÊNCIA
TRIMMEAN = MÉDIA.INTERNA
VAR.P = VAR.P
VAR.S = VAR.A
VARA = VARA
VARPA = VARPA
WEIBULL.DIST = DIST.WEIBULL
Z.TEST = TESTE.Z
##
## Funções de texto (Text Functions)
##
BAHTTEXT = BAHTTEXT
CHAR = CARACT
CLEAN = TIRAR
CODE = CÓDIGO
CONCAT = CONCAT
DOLLAR = MOEDA
EXACT = EXATO
FIND = PROCURAR
FIXED = DEF.NÚM.DEC
LEFT = ESQUERDA
LEN = NÚM.CARACT
LOWER = MINÚSCULA
MID = EXT.TEXTO
NUMBERSTRING = SEQÜÊNCIA.NÚMERO
NUMBERVALUE = VALORNUMÉRICO
PHONETIC = FONÉTICA
PROPER = PRI.MAIÚSCULA
REPLACE = MUDAR
REPT = REPT
RIGHT = DIREITA
SEARCH = LOCALIZAR
SUBSTITUTE = SUBSTITUIR
T = T
TEXT = TEXTO
TEXTJOIN = UNIRTEXTO
TRIM = ARRUMAR
UNICHAR = CARACTUNICODE
UNICODE = UNICODE
UPPER = MAIÚSCULA
VALUE = VALOR
##
## Funções da Web (Web Functions)
##
ENCODEURL = CODIFURL
FILTERXML = FILTROXML
WEBSERVICE = SERVIÇOWEB
##
## Funções de compatibilidade (Compatibility Functions)
##
BETADIST = DISTBETA
BETAINV = BETA.ACUM.INV
BINOMDIST = DISTRBINOM
CEILING = TETO
CHIDIST = DIST.QUI
CHIINV = INV.QUI
CHITEST = TESTE.QUI
CONCATENATE = CONCATENAR
CONFIDENCE = INT.CONFIANÇA
COVAR = COVAR
CRITBINOM = CRIT.BINOM
EXPONDIST = DISTEXPON
FDIST = DISTF
FINV = INVF
FLOOR = ARREDMULTB
FORECAST = PREVISÃO
FTEST = TESTEF
GAMMADIST = DISTGAMA
GAMMAINV = INVGAMA
HYPGEOMDIST = DIST.HIPERGEOM
LOGINV = INVLOG
LOGNORMDIST = DIST.LOGNORMAL
MODE = MODO
NEGBINOMDIST = DIST.BIN.NEG
NORMDIST = DISTNORM
NORMINV = INV.NORM
NORMSDIST = DISTNORMP
NORMSINV = INV.NORMP
PERCENTILE = PERCENTIL
PERCENTRANK = ORDEM.PORCENTUAL
POISSON = POISSON
QUARTILE = QUARTIL
RANK = ORDEM
STDEV = DESVPAD
STDEVP = DESVPADP
TDIST = DISTT
TINV = INVT
TTEST = TESTET
VAR = VAR
VARP = VARP
WEIBULL = WEIBULL
ZTEST = TESTEZ
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/br/config 0000644 00000000520 15242753725 0021215 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Português Brasileiro (Brazilian Portuguese)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #NULO!
DIV0
VALUE = #VALOR!
REF
NAME = #NOME?
NUM = #NÚM!
NA = #N/D
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/functions 0000644 00000023777 15242753725 0021400 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Português (Portuguese)
##
############################################################
##
## Funções de cubo (Cube Functions)
##
CUBEKPIMEMBER = MEMBROKPICUBO
CUBEMEMBER = MEMBROCUBO
CUBEMEMBERPROPERTY = PROPRIEDADEMEMBROCUBO
CUBERANKEDMEMBER = MEMBROCLASSIFICADOCUBO
CUBESET = CONJUNTOCUBO
CUBESETCOUNT = CONTARCONJUNTOCUBO
CUBEVALUE = VALORCUBO
##
## Funções de base de dados (Database Functions)
##
DAVERAGE = BDMÉDIA
DCOUNT = BDCONTAR
DCOUNTA = BDCONTAR.VAL
DGET = BDOBTER
DMAX = BDMÁX
DMIN = BDMÍN
DPRODUCT = BDMULTIPL
DSTDEV = BDDESVPAD
DSTDEVP = BDDESVPADP
DSUM = BDSOMA
DVAR = BDVAR
DVARP = BDVARP
##
## Funções de data e hora (Date & Time Functions)
##
DATE = DATA
DATEDIF = DATADIF
DATESTRING = DATA.CADEIA
DATEVALUE = DATA.VALOR
DAY = DIA
DAYS = DIAS
DAYS360 = DIAS360
EDATE = DATAM
EOMONTH = FIMMÊS
HOUR = HORA
ISOWEEKNUM = NUMSEMANAISO
MINUTE = MINUTO
MONTH = MÊS
NETWORKDAYS = DIATRABALHOTOTAL
NETWORKDAYS.INTL = DIATRABALHOTOTAL.INTL
NOW = AGORA
SECOND = SEGUNDO
THAIDAYOFWEEK = DIA.DA.SEMANA.TAILANDÊS
THAIMONTHOFYEAR = MÊS.DO.ANO.TAILANDÊS
THAIYEAR = ANO.TAILANDÊS
TIME = TEMPO
TIMEVALUE = VALOR.TEMPO
TODAY = HOJE
WEEKDAY = DIA.SEMANA
WEEKNUM = NÚMSEMANA
WORKDAY = DIATRABALHO
WORKDAY.INTL = DIATRABALHO.INTL
YEAR = ANO
YEARFRAC = FRAÇÃOANO
##
## Funções de engenharia (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BINADEC
BIN2HEX = BINAHEX
BIN2OCT = BINAOCT
BITAND = BIT.E
BITLSHIFT = BITDESL.ESQ
BITOR = BIT.OU
BITRSHIFT = BITDESL.DIR
BITXOR = BIT.XOU
COMPLEX = COMPLEXO
CONVERT = CONVERTER
DEC2BIN = DECABIN
DEC2HEX = DECAHEX
DEC2OCT = DECAOCT
DELTA = DELTA
ERF = FUNCERRO
ERF.PRECISE = FUNCERRO.PRECISO
ERFC = FUNCERROCOMPL
ERFC.PRECISE = FUNCERROCOMPL.PRECISO
GESTEP = DEGRAU
HEX2BIN = HEXABIN
HEX2DEC = HEXADEC
HEX2OCT = HEXAOCT
IMABS = IMABS
IMAGINARY = IMAGINÁRIO
IMARGUMENT = IMARG
IMCONJUGATE = IMCONJ
IMCOS = IMCOS
IMCOSH = IMCOSH
IMCOT = IMCOT
IMCSC = IMCSC
IMCSCH = IMCSCH
IMDIV = IMDIV
IMEXP = IMEXP
IMLN = IMLN
IMLOG10 = IMLOG10
IMLOG2 = IMLOG2
IMPOWER = IMPOT
IMPRODUCT = IMPROD
IMREAL = IMREAL
IMSEC = IMSEC
IMSECH = IMSECH
IMSIN = IMSENO
IMSINH = IMSENOH
IMSQRT = IMRAIZ
IMSUB = IMSUBTR
IMSUM = IMSOMA
IMTAN = IMTAN
OCT2BIN = OCTABIN
OCT2DEC = OCTADEC
OCT2HEX = OCTAHEX
##
## Funções financeiras (Financial Functions)
##
ACCRINT = JUROSACUM
ACCRINTM = JUROSACUMV
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = CUPDIASINLIQ
COUPDAYS = CUPDIAS
COUPDAYSNC = CUPDIASPRÓX
COUPNCD = CUPDATAPRÓX
COUPNUM = CUPNÚM
COUPPCD = CUPDATAANT
CUMIPMT = PGTOJURACUM
CUMPRINC = PGTOCAPACUM
DB = BD
DDB = BDD
DISC = DESC
DOLLARDE = MOEDADEC
DOLLARFR = MOEDAFRA
DURATION = DURAÇÃO
EFFECT = EFETIVA
FV = VF
FVSCHEDULE = VFPLANO
INTRATE = TAXAJUROS
IPMT = IPGTO
IRR = TIR
ISPMT = É.PGTO
MDURATION = MDURAÇÃO
MIRR = MTIR
NOMINAL = NOMINAL
NPER = NPER
NPV = VAL
ODDFPRICE = PREÇOPRIMINC
ODDFYIELD = LUCROPRIMINC
ODDLPRICE = PREÇOÚLTINC
ODDLYIELD = LUCROÚLTINC
PDURATION = PDURAÇÃO
PMT = PGTO
PPMT = PPGTO
PRICE = PREÇO
PRICEDISC = PREÇODESC
PRICEMAT = PREÇOVENC
PV = VA
RATE = TAXA
RECEIVED = RECEBER
RRI = DEVOLVERTAXAJUROS
SLN = AMORT
SYD = AMORTD
TBILLEQ = OTN
TBILLPRICE = OTNVALOR
TBILLYIELD = OTNLUCRO
VDB = BDV
XIRR = XTIR
XNPV = XVAL
YIELD = LUCRO
YIELDDISC = LUCRODESC
YIELDMAT = LUCROVENC
##
## Funções de informação (Information Functions)
##
CELL = CÉL
ERROR.TYPE = TIPO.ERRO
INFO = INFORMAÇÃO
ISBLANK = É.CÉL.VAZIA
ISERR = É.ERROS
ISERROR = É.ERRO
ISEVEN = ÉPAR
ISFORMULA = É.FORMULA
ISLOGICAL = É.LÓGICO
ISNA = É.NÃO.DISP
ISNONTEXT = É.NÃO.TEXTO
ISNUMBER = É.NÚM
ISODD = ÉÍMPAR
ISREF = É.REF
ISTEXT = É.TEXTO
N = N
NA = NÃO.DISP
SHEET = FOLHA
SHEETS = FOLHAS
TYPE = TIPO
##
## Funções lógicas (Logical Functions)
##
AND = E
FALSE = FALSO
IF = SE
IFERROR = SE.ERRO
IFNA = SEND
IFS = SE.S
NOT = NÃO
OR = OU
SWITCH = PARÂMETRO
TRUE = VERDADEIRO
XOR = XOU
##
## Funções de pesquisa e referência (Lookup & Reference Functions)
##
ADDRESS = ENDEREÇO
AREAS = ÁREAS
CHOOSE = SELECIONAR
COLUMN = COL
COLUMNS = COLS
FORMULATEXT = FÓRMULA.TEXTO
GETPIVOTDATA = OBTERDADOSDIN
HLOOKUP = PROCH
HYPERLINK = HIPERLIGAÇÃO
INDEX = ÍNDICE
INDIRECT = INDIRETO
LOOKUP = PROC
MATCH = CORRESP
OFFSET = DESLOCAMENTO
ROW = LIN
ROWS = LINS
RTD = RTD
TRANSPOSE = TRANSPOR
VLOOKUP = PROCV
*RC = LC
##
## Funções matemáticas e trigonométricas (Math & Trig Functions)
##
ABS = ABS
ACOS = ACOS
ACOSH = ACOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = AGREGAR
ARABIC = ÁRABE
ASIN = ASEN
ASINH = ASENH
ATAN = ATAN
ATAN2 = ATAN2
ATANH = ATANH
BASE = BASE
CEILING.MATH = ARRED.EXCESSO.MAT
CEILING.PRECISE = ARRED.EXCESSO.PRECISO
COMBIN = COMBIN
COMBINA = COMBIN.R
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = DECIMAL
DEGREES = GRAUS
ECMA.CEILING = ARRED.EXCESSO.ECMA
EVEN = PAR
EXP = EXP
FACT = FATORIAL
FACTDOUBLE = FATDUPLO
FLOOR.MATH = ARRED.DEFEITO.MAT
FLOOR.PRECISE = ARRED.DEFEITO.PRECISO
GCD = MDC
INT = INT
ISO.CEILING = ARRED.EXCESSO.ISO
LCM = MMC
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = MATRIZ.DETERM
MINVERSE = MATRIZ.INVERSA
MMULT = MATRIZ.MULT
MOD = RESTO
MROUND = MARRED
MULTINOMIAL = POLINOMIAL
MUNIT = UNIDM
ODD = ÍMPAR
PI = PI
POWER = POTÊNCIA
PRODUCT = PRODUTO
QUOTIENT = QUOCIENTE
RADIANS = RADIANOS
RAND = ALEATÓRIO
RANDBETWEEN = ALEATÓRIOENTRE
ROMAN = ROMANO
ROUND = ARRED
ROUNDBAHTDOWN = ARREDOND.BAHT.BAIXO
ROUNDBAHTUP = ARREDOND.BAHT.CIMA
ROUNDDOWN = ARRED.PARA.BAIXO
ROUNDUP = ARRED.PARA.CIMA
SEC = SEC
SECH = SECH
SERIESSUM = SOMASÉRIE
SIGN = SINAL
SIN = SEN
SINH = SENH
SQRT = RAIZQ
SQRTPI = RAIZPI
SUBTOTAL = SUBTOTAL
SUM = SOMA
SUMIF = SOMA.SE
SUMIFS = SOMA.SE.S
SUMPRODUCT = SOMARPRODUTO
SUMSQ = SOMARQUAD
SUMX2MY2 = SOMAX2DY2
SUMX2PY2 = SOMAX2SY2
SUMXMY2 = SOMAXMY2
TAN = TAN
TANH = TANH
TRUNC = TRUNCAR
##
## Funções estatísticas (Statistical Functions)
##
AVEDEV = DESV.MÉDIO
AVERAGE = MÉDIA
AVERAGEA = MÉDIAA
AVERAGEIF = MÉDIA.SE
AVERAGEIFS = MÉDIA.SE.S
BETA.DIST = DIST.BETA
BETA.INV = INV.BETA
BINOM.DIST = DISTR.BINOM
BINOM.DIST.RANGE = DIST.BINOM.INTERVALO
BINOM.INV = INV.BINOM
CHISQ.DIST = DIST.CHIQ
CHISQ.DIST.RT = DIST.CHIQ.DIR
CHISQ.INV = INV.CHIQ
CHISQ.INV.RT = INV.CHIQ.DIR
CHISQ.TEST = TESTE.CHIQ
CONFIDENCE.NORM = INT.CONFIANÇA.NORM
CONFIDENCE.T = INT.CONFIANÇA.T
CORREL = CORREL
COUNT = CONTAR
COUNTA = CONTAR.VAL
COUNTBLANK = CONTAR.VAZIO
COUNTIF = CONTAR.SE
COUNTIFS = CONTAR.SE.S
COVARIANCE.P = COVARIÂNCIA.P
COVARIANCE.S = COVARIÂNCIA.S
DEVSQ = DESVQ
EXPON.DIST = DIST.EXPON
F.DIST = DIST.F
F.DIST.RT = DIST.F.DIR
F.INV = INV.F
F.INV.RT = INV.F.DIR
F.TEST = TESTE.F
FISHER = FISHER
FISHERINV = FISHERINV
FORECAST.ETS = PREVISÃO.ETS
FORECAST.ETS.CONFINT = PREVISÃO.ETS.CONFINT
FORECAST.ETS.SEASONALITY = PREVISÃO.ETS.SAZONALIDADE
FORECAST.ETS.STAT = PREVISÃO.ETS.ESTATÍSTICA
FORECAST.LINEAR = PREVISÃO.LINEAR
FREQUENCY = FREQUÊNCIA
GAMMA = GAMA
GAMMA.DIST = DIST.GAMA
GAMMA.INV = INV.GAMA
GAMMALN = LNGAMA
GAMMALN.PRECISE = LNGAMA.PRECISO
GAUSS = GAUSS
GEOMEAN = MÉDIA.GEOMÉTRICA
GROWTH = CRESCIMENTO
HARMEAN = MÉDIA.HARMÓNICA
HYPGEOM.DIST = DIST.HIPGEOM
INTERCEPT = INTERCETAR
KURT = CURT
LARGE = MAIOR
LINEST = PROJ.LIN
LOGEST = PROJ.LOG
LOGNORM.DIST = DIST.NORMLOG
LOGNORM.INV = INV.NORMALLOG
MAX = MÁXIMO
MAXA = MÁXIMOA
MAXIFS = MÁXIMO.SE.S
MEDIAN = MED
MIN = MÍNIMO
MINA = MÍNIMOA
MINIFS = MÍNIMO.SE.S
MODE.MULT = MODO.MÚLT
MODE.SNGL = MODO.SIMPLES
NEGBINOM.DIST = DIST.BINOM.NEG
NORM.DIST = DIST.NORMAL
NORM.INV = INV.NORMAL
NORM.S.DIST = DIST.S.NORM
NORM.S.INV = INV.S.NORM
PEARSON = PEARSON
PERCENTILE.EXC = PERCENTIL.EXC
PERCENTILE.INC = PERCENTIL.INC
PERCENTRANK.EXC = ORDEM.PERCENTUAL.EXC
PERCENTRANK.INC = ORDEM.PERCENTUAL.INC
PERMUT = PERMUTAR
PERMUTATIONA = PERMUTAR.R
PHI = PHI
POISSON.DIST = DIST.POISSON
PROB = PROB
QUARTILE.EXC = QUARTIL.EXC
QUARTILE.INC = QUARTIL.INC
RANK.AVG = ORDEM.MÉD
RANK.EQ = ORDEM.EQ
RSQ = RQUAD
SKEW = DISTORÇÃO
SKEW.P = DISTORÇÃO.P
SLOPE = DECLIVE
SMALL = MENOR
STANDARDIZE = NORMALIZAR
STDEV.P = DESVPAD.P
STDEV.S = DESVPAD.S
STDEVA = DESVPADA
STDEVPA = DESVPADPA
STEYX = EPADYX
T.DIST = DIST.T
T.DIST.2T = DIST.T.2C
T.DIST.RT = DIST.T.DIR
T.INV = INV.T
T.INV.2T = INV.T.2C
T.TEST = TESTE.T
TREND = TENDÊNCIA
TRIMMEAN = MÉDIA.INTERNA
VAR.P = VAR.P
VAR.S = VAR.S
VARA = VARA
VARPA = VARPA
WEIBULL.DIST = DIST.WEIBULL
Z.TEST = TESTE.Z
##
## Funções de texto (Text Functions)
##
BAHTTEXT = TEXTO.BAHT
CHAR = CARÁT
CLEAN = LIMPARB
CODE = CÓDIGO
CONCAT = CONCAT
DOLLAR = MOEDA
EXACT = EXATO
FIND = LOCALIZAR
FIXED = FIXA
ISTHAIDIGIT = É.DÍGITO.TAILANDÊS
LEFT = ESQUERDA
LEN = NÚM.CARAT
LOWER = MINÚSCULAS
MID = SEG.TEXTO
NUMBERSTRING = NÚMERO.CADEIA
NUMBERVALUE = VALOR.NÚMERO
PHONETIC = FONÉTICA
PROPER = INICIAL.MAIÚSCULA
REPLACE = SUBSTITUIR
REPT = REPETIR
RIGHT = DIREITA
SEARCH = PROCURAR
SUBSTITUTE = SUBST
T = T
TEXT = TEXTO
TEXTJOIN = UNIRTEXTO
THAIDIGIT = DÍGITO.TAILANDÊS
THAINUMSOUND = SOM.NÚM.TAILANDÊS
THAINUMSTRING = CADEIA.NÚM.TAILANDÊS
THAISTRINGLENGTH = COMP.CADEIA.TAILANDÊS
TRIM = COMPACTAR
UNICHAR = UNICARÁT
UNICODE = UNICODE
UPPER = MAIÚSCULAS
VALUE = VALOR
##
## Funções da Web (Web Functions)
##
ENCODEURL = CODIFICAÇÃOURL
FILTERXML = FILTRARXML
WEBSERVICE = SERVIÇOWEB
##
## Funções de compatibilidade (Compatibility Functions)
##
BETADIST = DISTBETA
BETAINV = BETA.ACUM.INV
BINOMDIST = DISTRBINOM
CEILING = ARRED.EXCESSO
CHIDIST = DIST.CHI
CHIINV = INV.CHI
CHITEST = TESTE.CHI
CONCATENATE = CONCATENAR
CONFIDENCE = INT.CONFIANÇA
COVAR = COVAR
CRITBINOM = CRIT.BINOM
EXPONDIST = DISTEXPON
FDIST = DISTF
FINV = INVF
FLOOR = ARRED.DEFEITO
FORECAST = PREVISÃO
FTEST = TESTEF
GAMMADIST = DISTGAMA
GAMMAINV = INVGAMA
HYPGEOMDIST = DIST.HIPERGEOM
LOGINV = INVLOG
LOGNORMDIST = DIST.NORMALLOG
MODE = MODA
NEGBINOMDIST = DIST.BIN.NEG
NORMDIST = DIST.NORM
NORMINV = INV.NORM
NORMSDIST = DIST.NORMP
NORMSINV = INV.NORMP
PERCENTILE = PERCENTIL
PERCENTRANK = ORDEM.PERCENTUAL
POISSON = POISSON
QUARTILE = QUARTIL
RANK = ORDEM
STDEV = DESVPAD
STDEVP = DESVPADP
TDIST = DISTT
TINV = INVT
TTEST = TESTET
VAR = VAR
VARP = VARP
WEIBULL = WEIBULL
ZTEST = TESTEZ
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/pt/config 0000644 00000000473 15242753725 0020621 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Português (Portuguese)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #NULO!
DIV0
VALUE = #VALOR!
REF
NAME = #NOME?
NUM = #NÚM!
NA = #N/D
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/functions 0000644 00000023241 15242753725 0021367 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Svenska (Swedish)
##
############################################################
##
## Kubfunktioner (Cube Functions)
##
CUBEKPIMEMBER = KUBKPIMEDLEM
CUBEMEMBER = KUBMEDLEM
CUBEMEMBERPROPERTY = KUBMEDLEMSEGENSKAP
CUBERANKEDMEMBER = KUBRANGORDNADMEDLEM
CUBESET = KUBUPPSÄTTNING
CUBESETCOUNT = KUBUPPSÄTTNINGANTAL
CUBEVALUE = KUBVÄRDE
##
## Databasfunktioner (Database Functions)
##
DAVERAGE = DMEDEL
DCOUNT = DANTAL
DCOUNTA = DANTALV
DGET = DHÄMTA
DMAX = DMAX
DMIN = DMIN
DPRODUCT = DPRODUKT
DSTDEV = DSTDAV
DSTDEVP = DSTDAVP
DSUM = DSUMMA
DVAR = DVARIANS
DVARP = DVARIANSP
##
## Tid- och datumfunktioner (Date & Time Functions)
##
DATE = DATUM
DATEVALUE = DATUMVÄRDE
DAY = DAG
DAYS = DAGAR
DAYS360 = DAGAR360
EDATE = EDATUM
EOMONTH = SLUTMÅNAD
HOUR = TIMME
ISOWEEKNUM = ISOVECKONR
MINUTE = MINUT
MONTH = MÅNAD
NETWORKDAYS = NETTOARBETSDAGAR
NETWORKDAYS.INTL = NETTOARBETSDAGAR.INT
NOW = NU
SECOND = SEKUND
THAIDAYOFWEEK = THAIVECKODAG
THAIMONTHOFYEAR = THAIMÅNAD
THAIYEAR = THAIÅR
TIME = KLOCKSLAG
TIMEVALUE = TIDVÄRDE
TODAY = IDAG
WEEKDAY = VECKODAG
WEEKNUM = VECKONR
WORKDAY = ARBETSDAGAR
WORKDAY.INTL = ARBETSDAGAR.INT
YEAR = ÅR
YEARFRAC = ÅRDEL
##
## Tekniska funktioner (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BIN.TILL.DEC
BIN2HEX = BIN.TILL.HEX
BIN2OCT = BIN.TILL.OKT
BITAND = BITOCH
BITLSHIFT = BITVSKIFT
BITOR = BITELLER
BITRSHIFT = BITHSKIFT
BITXOR = BITXELLER
COMPLEX = KOMPLEX
CONVERT = KONVERTERA
DEC2BIN = DEC.TILL.BIN
DEC2HEX = DEC.TILL.HEX
DEC2OCT = DEC.TILL.OKT
DELTA = DELTA
ERF = FELF
ERF.PRECISE = FELF.EXAKT
ERFC = FELFK
ERFC.PRECISE = FELFK.EXAKT
GESTEP = SLSTEG
HEX2BIN = HEX.TILL.BIN
HEX2DEC = HEX.TILL.DEC
HEX2OCT = HEX.TILL.OKT
IMABS = IMABS
IMAGINARY = IMAGINÄR
IMARGUMENT = IMARGUMENT
IMCONJUGATE = IMKONJUGAT
IMCOS = IMCOS
IMCOSH = IMCOSH
IMCOT = IMCOT
IMCSC = IMCSC
IMCSCH = IMCSCH
IMDIV = IMDIV
IMEXP = IMEUPPHÖJT
IMLN = IMLN
IMLOG10 = IMLOG10
IMLOG2 = IMLOG2
IMPOWER = IMUPPHÖJT
IMPRODUCT = IMPRODUKT
IMREAL = IMREAL
IMSEC = IMSEK
IMSECH = IMSEKH
IMSIN = IMSIN
IMSINH = IMSINH
IMSQRT = IMROT
IMSUB = IMDIFF
IMSUM = IMSUM
IMTAN = IMTAN
OCT2BIN = OKT.TILL.BIN
OCT2DEC = OKT.TILL.DEC
OCT2HEX = OKT.TILL.HEX
##
## Finansiella funktioner (Financial Functions)
##
ACCRINT = UPPLRÄNTA
ACCRINTM = UPPLOBLRÄNTA
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = KUPDAGBB
COUPDAYS = KUPDAGB
COUPDAYSNC = KUPDAGNK
COUPNCD = KUPNKD
COUPNUM = KUPANT
COUPPCD = KUPFKD
CUMIPMT = KUMRÄNTA
CUMPRINC = KUMPRIS
DB = DB
DDB = DEGAVSKR
DISC = DISK
DOLLARDE = DECTAL
DOLLARFR = BRÅK
DURATION = LÖPTID
EFFECT = EFFRÄNTA
FV = SLUTVÄRDE
FVSCHEDULE = FÖRRÄNTNING
INTRATE = ÅRSRÄNTA
IPMT = RBETALNING
IRR = IR
ISPMT = RALÅN
MDURATION = MLÖPTID
MIRR = MODIR
NOMINAL = NOMRÄNTA
NPER = PERIODER
NPV = NETNUVÄRDE
ODDFPRICE = UDDAFPRIS
ODDFYIELD = UDDAFAVKASTNING
ODDLPRICE = UDDASPRIS
ODDLYIELD = UDDASAVKASTNING
PDURATION = PLÖPTID
PMT = BETALNING
PPMT = AMORT
PRICE = PRIS
PRICEDISC = PRISDISK
PRICEMAT = PRISFÖRF
PV = NUVÄRDE
RATE = RÄNTA
RECEIVED = BELOPP
RRI = AVKPÅINVEST
SLN = LINAVSKR
SYD = ÅRSAVSKR
TBILLEQ = SSVXEKV
TBILLPRICE = SSVXPRIS
TBILLYIELD = SSVXRÄNTA
VDB = VDEGRAVSKR
XIRR = XIRR
XNPV = XNUVÄRDE
YIELD = NOMAVK
YIELDDISC = NOMAVKDISK
YIELDMAT = NOMAVKFÖRF
##
## Informationsfunktioner (Information Functions)
##
CELL = CELL
ERROR.TYPE = FEL.TYP
INFO = INFO
ISBLANK = ÄRTOM
ISERR = ÄRF
ISERROR = ÄRFEL
ISEVEN = ÄRJÄMN
ISFORMULA = ÄRFORMEL
ISLOGICAL = ÄRLOGISK
ISNA = ÄRSAKNAD
ISNONTEXT = ÄREJTEXT
ISNUMBER = ÄRTAL
ISODD = ÄRUDDA
ISREF = ÄRREF
ISTEXT = ÄRTEXT
N = N
NA = SAKNAS
SHEET = BLAD
SHEETS = ANTALBLAD
TYPE = VÄRDETYP
##
## Logiska funktioner (Logical Functions)
##
AND = OCH
FALSE = FALSKT
IF = OM
IFERROR = OMFEL
IFNA = OMSAKNAS
IFS = IFS
NOT = ICKE
OR = ELLER
SWITCH = VÄXLA
TRUE = SANT
XOR = XELLER
##
## Sök- och referensfunktioner (Lookup & Reference Functions)
##
ADDRESS = ADRESS
AREAS = OMRÅDEN
CHOOSE = VÄLJ
COLUMN = KOLUMN
COLUMNS = KOLUMNER
FORMULATEXT = FORMELTEXT
GETPIVOTDATA = HÄMTA.PIVOTDATA
HLOOKUP = LETAKOLUMN
HYPERLINK = HYPERLÄNK
INDEX = INDEX
INDIRECT = INDIREKT
LOOKUP = LETAUPP
MATCH = PASSA
OFFSET = FÖRSKJUTNING
ROW = RAD
ROWS = RADER
RTD = RTD
TRANSPOSE = TRANSPONERA
VLOOKUP = LETARAD
*RC = RK
##
## Matematiska och trigonometriska funktioner (Math & Trig Functions)
##
ABS = ABS
ACOS = ARCCOS
ACOSH = ARCCOSH
ACOT = ARCCOT
ACOTH = ARCCOTH
AGGREGATE = MÄNGD
ARABIC = ARABISKA
ASIN = ARCSIN
ASINH = ARCSINH
ATAN = ARCTAN
ATAN2 = ARCTAN2
ATANH = ARCTANH
BASE = BAS
CEILING.MATH = RUNDA.UPP.MATEMATISKT
CEILING.PRECISE = RUNDA.UPP.EXAKT
COMBIN = KOMBIN
COMBINA = KOMBINA
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = DECIMAL
DEGREES = GRADER
ECMA.CEILING = ECMA.RUNDA.UPP
EVEN = JÄMN
EXP = EXP
FACT = FAKULTET
FACTDOUBLE = DUBBELFAKULTET
FLOOR.MATH = RUNDA.NER.MATEMATISKT
FLOOR.PRECISE = RUNDA.NER.EXAKT
GCD = SGD
INT = HELTAL
ISO.CEILING = ISO.RUNDA.UPP
LCM = MGM
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = MDETERM
MINVERSE = MINVERT
MMULT = MMULT
MOD = REST
MROUND = MAVRUNDA
MULTINOMIAL = MULTINOMIAL
MUNIT = MENHET
ODD = UDDA
PI = PI
POWER = UPPHÖJT.TILL
PRODUCT = PRODUKT
QUOTIENT = KVOT
RADIANS = RADIANER
RAND = SLUMP
RANDBETWEEN = SLUMP.MELLAN
ROMAN = ROMERSK
ROUND = AVRUNDA
ROUNDBAHTDOWN = AVRUNDABAHTNEDÅT
ROUNDBAHTUP = AVRUNDABAHTUPPÅT
ROUNDDOWN = AVRUNDA.NEDÅT
ROUNDUP = AVRUNDA.UPPÅT
SEC = SEK
SECH = SEKH
SERIESSUM = SERIESUMMA
SIGN = TECKEN
SIN = SIN
SINH = SINH
SQRT = ROT
SQRTPI = ROTPI
SUBTOTAL = DELSUMMA
SUM = SUMMA
SUMIF = SUMMA.OM
SUMIFS = SUMMA.OMF
SUMPRODUCT = PRODUKTSUMMA
SUMSQ = KVADRATSUMMA
SUMX2MY2 = SUMMAX2MY2
SUMX2PY2 = SUMMAX2PY2
SUMXMY2 = SUMMAXMY2
TAN = TAN
TANH = TANH
TRUNC = AVKORTA
##
## Statistiska funktioner (Statistical Functions)
##
AVEDEV = MEDELAVV
AVERAGE = MEDEL
AVERAGEA = AVERAGEA
AVERAGEIF = MEDEL.OM
AVERAGEIFS = MEDEL.OMF
BETA.DIST = BETA.FÖRD
BETA.INV = BETA.INV
BINOM.DIST = BINOM.FÖRD
BINOM.DIST.RANGE = BINOM.FÖRD.INTERVALL
BINOM.INV = BINOM.INV
CHISQ.DIST = CHI2.FÖRD
CHISQ.DIST.RT = CHI2.FÖRD.RT
CHISQ.INV = CHI2.INV
CHISQ.INV.RT = CHI2.INV.RT
CHISQ.TEST = CHI2.TEST
CONFIDENCE.NORM = KONFIDENS.NORM
CONFIDENCE.T = KONFIDENS.T
CORREL = KORREL
COUNT = ANTAL
COUNTA = ANTALV
COUNTBLANK = ANTAL.TOMMA
COUNTIF = ANTAL.OM
COUNTIFS = ANTAL.OMF
COVARIANCE.P = KOVARIANS.P
COVARIANCE.S = KOVARIANS.S
DEVSQ = KVADAVV
EXPON.DIST = EXPON.FÖRD
F.DIST = F.FÖRD
F.DIST.RT = F.FÖRD.RT
F.INV = F.INV
F.INV.RT = F.INV.RT
F.TEST = F.TEST
FISHER = FISHER
FISHERINV = FISHERINV
FORECAST.ETS = PROGNOS.ETS
FORECAST.ETS.CONFINT = PROGNOS.ETS.KONFINT
FORECAST.ETS.SEASONALITY = PROGNOS.ETS.SÄSONGSBEROENDE
FORECAST.ETS.STAT = PROGNOS.ETS.STAT
FORECAST.LINEAR = PROGNOS.LINJÄR
FREQUENCY = FREKVENS
GAMMA = GAMMA
GAMMA.DIST = GAMMA.FÖRD
GAMMA.INV = GAMMA.INV
GAMMALN = GAMMALN
GAMMALN.PRECISE = GAMMALN.EXAKT
GAUSS = GAUSS
GEOMEAN = GEOMEDEL
GROWTH = EXPTREND
HARMEAN = HARMMEDEL
HYPGEOM.DIST = HYPGEOM.FÖRD
INTERCEPT = SKÄRNINGSPUNKT
KURT = TOPPIGHET
LARGE = STÖRSTA
LINEST = REGR
LOGEST = EXPREGR
LOGNORM.DIST = LOGNORM.FÖRD
LOGNORM.INV = LOGNORM.INV
MAX = MAX
MAXA = MAXA
MAXIFS = MAXIFS
MEDIAN = MEDIAN
MIN = MIN
MINA = MINA
MINIFS = MINIFS
MODE.MULT = TYPVÄRDE.FLERA
MODE.SNGL = TYPVÄRDE.ETT
NEGBINOM.DIST = NEGBINOM.FÖRD
NORM.DIST = NORM.FÖRD
NORM.INV = NORM.INV
NORM.S.DIST = NORM.S.FÖRD
NORM.S.INV = NORM.S.INV
PEARSON = PEARSON
PERCENTILE.EXC = PERCENTIL.EXK
PERCENTILE.INC = PERCENTIL.INK
PERCENTRANK.EXC = PROCENTRANG.EXK
PERCENTRANK.INC = PROCENTRANG.INK
PERMUT = PERMUT
PERMUTATIONA = PERMUTATIONA
PHI = PHI
POISSON.DIST = POISSON.FÖRD
PROB = SANNOLIKHET
QUARTILE.EXC = KVARTIL.EXK
QUARTILE.INC = KVARTIL.INK
RANK.AVG = RANG.MED
RANK.EQ = RANG.EKV
RSQ = RKV
SKEW = SNEDHET
SKEW.P = SNEDHET.P
SLOPE = LUTNING
SMALL = MINSTA
STANDARDIZE = STANDARDISERA
STDEV.P = STDAV.P
STDEV.S = STDAV.S
STDEVA = STDEVA
STDEVPA = STDEVPA
STEYX = STDFELYX
T.DIST = T.FÖRD
T.DIST.2T = T.FÖRD.2T
T.DIST.RT = T.FÖRD.RT
T.INV = T.INV
T.INV.2T = T.INV.2T
T.TEST = T.TEST
TREND = TREND
TRIMMEAN = TRIMMEDEL
VAR.P = VARIANS.P
VAR.S = VARIANS.S
VARA = VARA
VARPA = VARPA
WEIBULL.DIST = WEIBULL.FÖRD
Z.TEST = Z.TEST
##
## Textfunktioner (Text Functions)
##
BAHTTEXT = BAHTTEXT
CHAR = TECKENKOD
CLEAN = STÄDA
CODE = KOD
CONCAT = SAMMAN
DOLLAR = VALUTA
EXACT = EXAKT
FIND = HITTA
FIXED = FASTTAL
LEFT = VÄNSTER
LEN = LÄNGD
LOWER = GEMENER
MID = EXTEXT
NUMBERVALUE = TALVÄRDE
PROPER = INITIAL
REPLACE = ERSÄTT
REPT = REP
RIGHT = HÖGER
SEARCH = SÖK
SUBSTITUTE = BYT.UT
T = T
TEXT = TEXT
TEXTJOIN = TEXTJOIN
THAIDIGIT = THAISIFFRA
THAINUMSOUND = THAITALLJUD
THAINUMSTRING = THAITALSTRÄNG
THAISTRINGLENGTH = THAISTRÄNGLÄNGD
TRIM = RENSA
UNICHAR = UNITECKENKOD
UNICODE = UNICODE
UPPER = VERSALER
VALUE = TEXTNUM
##
## Webbfunktioner (Web Functions)
##
ENCODEURL = KODAWEBBADRESS
FILTERXML = FILTRERAXML
WEBSERVICE = WEBBTJÄNST
##
## Kompatibilitetsfunktioner (Compatibility Functions)
##
BETADIST = BETAFÖRD
BETAINV = BETAINV
BINOMDIST = BINOMFÖRD
CEILING = RUNDA.UPP
CHIDIST = CHI2FÖRD
CHIINV = CHI2INV
CHITEST = CHI2TEST
CONCATENATE = SAMMANFOGA
CONFIDENCE = KONFIDENS
COVAR = KOVAR
CRITBINOM = KRITBINOM
EXPONDIST = EXPONFÖRD
FDIST = FFÖRD
FINV = FINV
FLOOR = RUNDA.NER
FORECAST = PREDIKTION
FTEST = FTEST
GAMMADIST = GAMMAFÖRD
GAMMAINV = GAMMAINV
HYPGEOMDIST = HYPGEOMFÖRD
LOGINV = LOGINV
LOGNORMDIST = LOGNORMFÖRD
MODE = TYPVÄRDE
NEGBINOMDIST = NEGBINOMFÖRD
NORMDIST = NORMFÖRD
NORMINV = NORMINV
NORMSDIST = NORMSFÖRD
NORMSINV = NORMSINV
PERCENTILE = PERCENTIL
PERCENTRANK = PROCENTRANG
POISSON = POISSON
QUARTILE = KVARTIL
RANK = RANG
STDEV = STDAV
STDEVP = STDAVP
TDIST = TFÖRD
TINV = TINV
TTEST = TTEST
VAR = VARIANS
VARP = VARIANSP
WEIBULL = WEIBULL
ZTEST = ZTEST
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/sv/config 0000644 00000000542 15242753725 0020623 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Svenska (Swedish)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #SKÄRNING!
DIV0 = #DIVISION/0!
VALUE = #VÄRDEFEL!
REF = #REFERENS!
NAME = #NAMN?
NUM = #OGILTIGT!
NA = #SAKNAS!
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/functions 0000644 00000026262 15242753725 0021343 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Suomi (Finnish)
##
############################################################
##
## Kuutiofunktiot (Cube Functions)
##
CUBEKPIMEMBER = KUUTIOKPIJÄSEN
CUBEMEMBER = KUUTIONJÄSEN
CUBEMEMBERPROPERTY = KUUTIONJÄSENENOMINAISUUS
CUBERANKEDMEMBER = KUUTIONLUOKITELTUJÄSEN
CUBESET = KUUTIOJOUKKO
CUBESETCOUNT = KUUTIOJOUKKOJENMÄÄRÄ
CUBEVALUE = KUUTIONARVO
##
## Tietokantafunktiot (Database Functions)
##
DAVERAGE = TKESKIARVO
DCOUNT = TLASKE
DCOUNTA = TLASKEA
DGET = TNOUDA
DMAX = TMAKS
DMIN = TMIN
DPRODUCT = TTULO
DSTDEV = TKESKIHAJONTA
DSTDEVP = TKESKIHAJONTAP
DSUM = TSUMMA
DVAR = TVARIANSSI
DVARP = TVARIANSSIP
##
## Päivämäärä- ja aikafunktiot (Date & Time Functions)
##
DATE = PÄIVÄYS
DATEDIF = PVMERO
DATESTRING = PVMMERKKIJONO
DATEVALUE = PÄIVÄYSARVO
DAY = PÄIVÄ
DAYS = PÄIVÄT
DAYS360 = PÄIVÄT360
EDATE = PÄIVÄ.KUUKAUSI
EOMONTH = KUUKAUSI.LOPPU
HOUR = TUNNIT
ISOWEEKNUM = VIIKKO.ISO.NRO
MINUTE = MINUUTIT
MONTH = KUUKAUSI
NETWORKDAYS = TYÖPÄIVÄT
NETWORKDAYS.INTL = TYÖPÄIVÄT.KANSVÄL
NOW = NYT
SECOND = SEKUNNIT
THAIDAYOFWEEK = THAI.VIIKONPÄIVÄ
THAIMONTHOFYEAR = THAI.KUUKAUSI
THAIYEAR = THAI.VUOSI
TIME = AIKA
TIMEVALUE = AIKA_ARVO
TODAY = TÄMÄ.PÄIVÄ
WEEKDAY = VIIKONPÄIVÄ
WEEKNUM = VIIKKO.NRO
WORKDAY = TYÖPÄIVÄ
WORKDAY.INTL = TYÖPÄIVÄ.KANSVÄL
YEAR = VUOSI
YEARFRAC = VUOSI.OSA
##
## Tekniset funktiot (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BINDES
BIN2HEX = BINHEKSA
BIN2OCT = BINOKT
BITAND = BITTI.JA
BITLSHIFT = BITTI.SIIRTO.V
BITOR = BITTI.TAI
BITRSHIFT = BITTI.SIIRTO.O
BITXOR = BITTI.EHDOTON.TAI
COMPLEX = KOMPLEKSI
CONVERT = MUUNNA
DEC2BIN = DESBIN
DEC2HEX = DESHEKSA
DEC2OCT = DESOKT
DELTA = SAMA.ARVO
ERF = VIRHEFUNKTIO
ERF.PRECISE = VIRHEFUNKTIO.TARKKA
ERFC = VIRHEFUNKTIO.KOMPLEMENTTI
ERFC.PRECISE = VIRHEFUNKTIO.KOMPLEMENTTI.TARKKA
GESTEP = RAJA
HEX2BIN = HEKSABIN
HEX2DEC = HEKSADES
HEX2OCT = HEKSAOKT
IMABS = KOMPLEKSI.ABS
IMAGINARY = KOMPLEKSI.IMAG
IMARGUMENT = KOMPLEKSI.ARG
IMCONJUGATE = KOMPLEKSI.KONJ
IMCOS = KOMPLEKSI.COS
IMCOSH = IMCOSH
IMCOT = KOMPLEKSI.COT
IMCSC = KOMPLEKSI.KOSEK
IMCSCH = KOMPLEKSI.KOSEKH
IMDIV = KOMPLEKSI.OSAM
IMEXP = KOMPLEKSI.EKSP
IMLN = KOMPLEKSI.LN
IMLOG10 = KOMPLEKSI.LOG10
IMLOG2 = KOMPLEKSI.LOG2
IMPOWER = KOMPLEKSI.POT
IMPRODUCT = KOMPLEKSI.TULO
IMREAL = KOMPLEKSI.REAALI
IMSEC = KOMPLEKSI.SEK
IMSECH = KOMPLEKSI.SEKH
IMSIN = KOMPLEKSI.SIN
IMSINH = KOMPLEKSI.SINH
IMSQRT = KOMPLEKSI.NELIÖJ
IMSUB = KOMPLEKSI.EROTUS
IMSUM = KOMPLEKSI.SUM
IMTAN = KOMPLEKSI.TAN
OCT2BIN = OKTBIN
OCT2DEC = OKTDES
OCT2HEX = OKTHEKSA
##
## Rahoitusfunktiot (Financial Functions)
##
ACCRINT = KERTYNYT.KORKO
ACCRINTM = KERTYNYT.KORKO.LOPUSSA
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = KORKOPÄIVÄT.ALUSTA
COUPDAYS = KORKOPÄIVÄT
COUPDAYSNC = KORKOPÄIVÄT.SEURAAVA
COUPNCD = KORKOPÄIVÄ.SEURAAVA
COUPNUM = KORKOPÄIVÄ.JAKSOT
COUPPCD = KORKOPÄIVÄ.EDELLINEN
CUMIPMT = MAKSETTU.KORKO
CUMPRINC = MAKSETTU.LYHENNYS
DB = DB
DDB = DDB
DISC = DISKONTTOKORKO
DOLLARDE = VALUUTTA.DES
DOLLARFR = VALUUTTA.MURTO
DURATION = KESTO
EFFECT = KORKO.EFEKT
FV = TULEVA.ARVO
FVSCHEDULE = TULEVA.ARVO.ERIKORKO
INTRATE = KORKO.ARVOPAPERI
IPMT = IPMT
IRR = SISÄINEN.KORKO
ISPMT = ISPMT
MDURATION = KESTO.MUUNN
MIRR = MSISÄINEN
NOMINAL = KORKO.VUOSI
NPER = NJAKSO
NPV = NNA
ODDFPRICE = PARITON.ENS.NIMELLISARVO
ODDFYIELD = PARITON.ENS.TUOTTO
ODDLPRICE = PARITON.VIIM.NIMELLISARVO
ODDLYIELD = PARITON.VIIM.TUOTTO
PDURATION = KESTO.JAKSO
PMT = MAKSU
PPMT = PPMT
PRICE = HINTA
PRICEDISC = HINTA.DISK
PRICEMAT = HINTA.LUNASTUS
PV = NA
RATE = KORKO
RECEIVED = SAATU.HINTA
RRI = TOT.ROI
SLN = STP
SYD = VUOSIPOISTO
TBILLEQ = OBLIG.TUOTTOPROS
TBILLPRICE = OBLIG.HINTA
TBILLYIELD = OBLIG.TUOTTO
VDB = VDB
XIRR = SISÄINEN.KORKO.JAKSOTON
XNPV = NNA.JAKSOTON
YIELD = TUOTTO
YIELDDISC = TUOTTO.DISK
YIELDMAT = TUOTTO.ERÄP
##
## Tietofunktiot (Information Functions)
##
CELL = SOLU
ERROR.TYPE = VIRHEEN.LAJI
INFO = KUVAUS
ISBLANK = ONTYHJÄ
ISERR = ONVIRH
ISERROR = ONVIRHE
ISEVEN = ONPARILLINEN
ISFORMULA = ONKAAVA
ISLOGICAL = ONTOTUUS
ISNA = ONPUUTTUU
ISNONTEXT = ONEI_TEKSTI
ISNUMBER = ONLUKU
ISODD = ONPARITON
ISREF = ONVIITT
ISTEXT = ONTEKSTI
N = N
NA = PUUTTUU
SHEET = TAULUKKO
SHEETS = TAULUKOT
TYPE = TYYPPI
##
## Loogiset funktiot (Logical Functions)
##
AND = JA
FALSE = EPÄTOSI
IF = JOS
IFERROR = JOSVIRHE
IFNA = JOSPUUTTUU
IFS = JOSS
NOT = EI
OR = TAI
SWITCH = MUUTA
TRUE = TOSI
XOR = EHDOTON.TAI
##
## Haku- ja viitefunktiot (Lookup & Reference Functions)
##
ADDRESS = OSOITE
AREAS = ALUEET
CHOOSE = VALITSE.INDEKSI
COLUMN = SARAKE
COLUMNS = SARAKKEET
FORMULATEXT = KAAVA.TEKSTI
GETPIVOTDATA = NOUDA.PIVOT.TIEDOT
HLOOKUP = VHAKU
HYPERLINK = HYPERLINKKI
INDEX = INDEKSI
INDIRECT = EPÄSUORA
LOOKUP = HAKU
MATCH = VASTINE
OFFSET = SIIRTYMÄ
ROW = RIVI
ROWS = RIVIT
RTD = RTD
TRANSPOSE = TRANSPONOI
VLOOKUP = PHAKU
*RC = RS
##
## Matemaattiset ja trigonometriset funktiot (Math & Trig Functions)
##
ABS = ITSEISARVO
ACOS = ACOS
ACOSH = ACOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = KOOSTE
ARABIC = ARABIA
ASIN = ASIN
ASINH = ASINH
ATAN = ATAN
ATAN2 = ATAN2
ATANH = ATANH
BASE = PERUS
CEILING.MATH = PYÖRISTÄ.KERR.YLÖS.MATEMAATTINEN
CEILING.PRECISE = PYÖRISTÄ.KERR.YLÖS.TARKKA
COMBIN = KOMBINAATIO
COMBINA = KOMBINAATIOA
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = KOSEK
CSCH = KOSEKH
DECIMAL = DESIMAALI
DEGREES = ASTEET
ECMA.CEILING = ECMA.PYÖRISTÄ.KERR.YLÖS
EVEN = PARILLINEN
EXP = EKSPONENTTI
FACT = KERTOMA
FACTDOUBLE = KERTOMA.OSA
FLOOR.MATH = PYÖRISTÄ.KERR.ALAS.MATEMAATTINEN
FLOOR.PRECISE = PYÖRISTÄ.KERR.ALAS.TARKKA
GCD = SUURIN.YHT.TEKIJÄ
INT = KOKONAISLUKU
ISO.CEILING = ISO.PYÖRISTÄ.KERR.YLÖS
LCM = PIENIN.YHT.JAETTAVA
LN = LUONNLOG
LOG = LOG
LOG10 = LOG10
MDETERM = MDETERM
MINVERSE = MKÄÄNTEINEN
MMULT = MKERRO
MOD = JAKOJ
MROUND = PYÖRISTÄ.KERR
MULTINOMIAL = MULTINOMI
MUNIT = YKSIKKÖM
ODD = PARITON
PI = PII
POWER = POTENSSI
PRODUCT = TULO
QUOTIENT = OSAMÄÄRÄ
RADIANS = RADIAANIT
RAND = SATUNNAISLUKU
RANDBETWEEN = SATUNNAISLUKU.VÄLILTÄ
ROMAN = ROMAN
ROUND = PYÖRISTÄ
ROUNDBAHTDOWN = PYÖRISTÄ.BAHT.ALAS
ROUNDBAHTUP = PYÖRISTÄ.BAHT.YLÖS
ROUNDDOWN = PYÖRISTÄ.DES.ALAS
ROUNDUP = PYÖRISTÄ.DES.YLÖS
SEC = SEK
SECH = SEKH
SERIESSUM = SARJA.SUMMA
SIGN = ETUMERKKI
SIN = SIN
SINH = SINH
SQRT = NELIÖJUURI
SQRTPI = NELIÖJUURI.PII
SUBTOTAL = VÄLISUMMA
SUM = SUMMA
SUMIF = SUMMA.JOS
SUMIFS = SUMMA.JOS.JOUKKO
SUMPRODUCT = TULOJEN.SUMMA
SUMSQ = NELIÖSUMMA
SUMX2MY2 = NELIÖSUMMIEN.EROTUS
SUMX2PY2 = NELIÖSUMMIEN.SUMMA
SUMXMY2 = EROTUSTEN.NELIÖSUMMA
TAN = TAN
TANH = TANH
TRUNC = KATKAISE
##
## Tilastolliset funktiot (Statistical Functions)
##
AVEDEV = KESKIPOIKKEAMA
AVERAGE = KESKIARVO
AVERAGEA = KESKIARVOA
AVERAGEIF = KESKIARVO.JOS
AVERAGEIFS = KESKIARVO.JOS.JOUKKO
BETA.DIST = BEETA.JAKAUMA
BETA.INV = BEETA.KÄÄNT
BINOM.DIST = BINOMI.JAKAUMA
BINOM.DIST.RANGE = BINOMI.JAKAUMA.ALUE
BINOM.INV = BINOMIJAKAUMA.KÄÄNT
CHISQ.DIST = CHINELIÖ.JAKAUMA
CHISQ.DIST.RT = CHINELIÖ.JAKAUMA.OH
CHISQ.INV = CHINELIÖ.KÄÄNT
CHISQ.INV.RT = CHINELIÖ.KÄÄNT.OH
CHISQ.TEST = CHINELIÖ.TESTI
CONFIDENCE.NORM = LUOTTAMUSVÄLI.NORM
CONFIDENCE.T = LUOTTAMUSVÄLI.T
CORREL = KORRELAATIO
COUNT = LASKE
COUNTA = LASKE.A
COUNTBLANK = LASKE.TYHJÄT
COUNTIF = LASKE.JOS
COUNTIFS = LASKE.JOS.JOUKKO
COVARIANCE.P = KOVARIANSSI.P
COVARIANCE.S = KOVARIANSSI.S
DEVSQ = OIKAISTU.NELIÖSUMMA
EXPON.DIST = EKSPONENTIAALI.JAKAUMA
F.DIST = F.JAKAUMA
F.DIST.RT = F.JAKAUMA.OH
F.INV = F.KÄÄNT
F.INV.RT = F.KÄÄNT.OH
F.TEST = F.TESTI
FISHER = FISHER
FISHERINV = FISHER.KÄÄNT
FORECAST.ETS = ENNUSTE.ETS
FORECAST.ETS.CONFINT = ENNUSTE.ETS.CONFINT
FORECAST.ETS.SEASONALITY = ENNUSTE.ETS.KAUSIVAIHTELU
FORECAST.ETS.STAT = ENNUSTE.ETS.STAT
FORECAST.LINEAR = ENNUSTE.LINEAARINEN
FREQUENCY = TAAJUUS
GAMMA = GAMMA
GAMMA.DIST = GAMMA.JAKAUMA
GAMMA.INV = GAMMA.JAKAUMA.KÄÄNT
GAMMALN = GAMMALN
GAMMALN.PRECISE = GAMMALN.TARKKA
GAUSS = GAUSS
GEOMEAN = KESKIARVO.GEOM
GROWTH = KASVU
HARMEAN = KESKIARVO.HARM
HYPGEOM.DIST = HYPERGEOM_JAKAUMA
INTERCEPT = LEIKKAUSPISTE
KURT = KURT
LARGE = SUURI
LINEST = LINREGR
LOGEST = LOGREGR
LOGNORM.DIST = LOGNORM_JAKAUMA
LOGNORM.INV = LOGNORM.KÄÄNT
MAX = MAKS
MAXA = MAKSA
MAXIFS = MAKS.JOS
MEDIAN = MEDIAANI
MIN = MIN
MINA = MINA
MINIFS = MIN.JOS
MODE.MULT = MOODI.USEA
MODE.SNGL = MOODI.YKSI
NEGBINOM.DIST = BINOMI.JAKAUMA.NEG
NORM.DIST = NORMAALI.JAKAUMA
NORM.INV = NORMAALI.JAKAUMA.KÄÄNT
NORM.S.DIST = NORM_JAKAUMA.NORMIT
NORM.S.INV = NORM_JAKAUMA.KÄÄNT
PEARSON = PEARSON
PERCENTILE.EXC = PROSENTTIPISTE.ULK
PERCENTILE.INC = PROSENTTIPISTE.SIS
PERCENTRANK.EXC = PROSENTTIJÄRJESTYS.ULK
PERCENTRANK.INC = PROSENTTIJÄRJESTYS.SIS
PERMUT = PERMUTAATIO
PERMUTATIONA = PERMUTAATIOA
PHI = FII
POISSON.DIST = POISSON.JAKAUMA
PROB = TODENNÄKÖISYYS
QUARTILE.EXC = NELJÄNNES.ULK
QUARTILE.INC = NELJÄNNES.SIS
RANK.AVG = ARVON.MUKAAN.KESKIARVO
RANK.EQ = ARVON.MUKAAN.TASAN
RSQ = PEARSON.NELIÖ
SKEW = JAKAUMAN.VINOUS
SKEW.P = JAKAUMAN.VINOUS.POP
SLOPE = KULMAKERROIN
SMALL = PIENI
STANDARDIZE = NORMITA
STDEV.P = KESKIHAJONTA.P
STDEV.S = KESKIHAJONTA.S
STDEVA = KESKIHAJONTAA
STDEVPA = KESKIHAJONTAPA
STEYX = KESKIVIRHE
T.DIST = T.JAKAUMA
T.DIST.2T = T.JAKAUMA.2S
T.DIST.RT = T.JAKAUMA.OH
T.INV = T.KÄÄNT
T.INV.2T = T.KÄÄNT.2S
T.TEST = T.TESTI
TREND = SUUNTAUS
TRIMMEAN = KESKIARVO.TASATTU
VAR.P = VAR.P
VAR.S = VAR.S
VARA = VARA
VARPA = VARPA
WEIBULL.DIST = WEIBULL.JAKAUMA
Z.TEST = Z.TESTI
##
## Tekstifunktiot (Text Functions)
##
BAHTTEXT = BAHTTEKSTI
CHAR = MERKKI
CLEAN = SIIVOA
CODE = KOODI
CONCAT = YHDISTÄ
DOLLAR = VALUUTTA
EXACT = VERTAA
FIND = ETSI
FIXED = KIINTEÄ
ISTHAIDIGIT = ON.THAI.NUMERO
LEFT = VASEN
LEN = PITUUS
LOWER = PIENET
MID = POIMI.TEKSTI
NUMBERSTRING = NROMERKKIJONO
NUMBERVALUE = NROARVO
PHONETIC = FONEETTINEN
PROPER = ERISNIMI
REPLACE = KORVAA
REPT = TOISTA
RIGHT = OIKEA
SEARCH = KÄY.LÄPI
SUBSTITUTE = VAIHDA
T = T
TEXT = TEKSTI
TEXTJOIN = TEKSTI.YHDISTÄ
THAIDIGIT = THAI.NUMERO
THAINUMSOUND = THAI.LUKU.ÄÄNI
THAINUMSTRING = THAI.LUKU.MERKKIJONO
THAISTRINGLENGTH = THAI.MERKKIJONON.PITUUS
TRIM = POISTA.VÄLIT
UNICHAR = UNICODEMERKKI
UNICODE = UNICODE
UPPER = ISOT
VALUE = ARVO
##
## Verkkofunktiot (Web Functions)
##
ENCODEURL = URLKOODAUS
FILTERXML = SUODATA.XML
WEBSERVICE = VERKKOPALVELU
##
## Yhteensopivuusfunktiot (Compatibility Functions)
##
BETADIST = BEETAJAKAUMA
BETAINV = BEETAJAKAUMA.KÄÄNT
BINOMDIST = BINOMIJAKAUMA
CEILING = PYÖRISTÄ.KERR.YLÖS
CHIDIST = CHIJAKAUMA
CHIINV = CHIJAKAUMA.KÄÄNT
CHITEST = CHITESTI
CONCATENATE = KETJUTA
CONFIDENCE = LUOTTAMUSVÄLI
COVAR = KOVARIANSSI
CRITBINOM = BINOMIJAKAUMA.KRIT
EXPONDIST = EKSPONENTIAALIJAKAUMA
FDIST = FJAKAUMA
FINV = FJAKAUMA.KÄÄNT
FLOOR = PYÖRISTÄ.KERR.ALAS
FORECAST = ENNUSTE
FTEST = FTESTI
GAMMADIST = GAMMAJAKAUMA
GAMMAINV = GAMMAJAKAUMA.KÄÄNT
HYPGEOMDIST = HYPERGEOM.JAKAUMA
LOGINV = LOGNORM.JAKAUMA.KÄÄNT
LOGNORMDIST = LOGNORM.JAKAUMA
MODE = MOODI
NEGBINOMDIST = BINOMIJAKAUMA.NEG
NORMDIST = NORM.JAKAUMA
NORMINV = NORM.JAKAUMA.KÄÄNT
NORMSDIST = NORM.JAKAUMA.NORMIT
NORMSINV = NORM.JAKAUMA.NORMIT.KÄÄNT
PERCENTILE = PROSENTTIPISTE
PERCENTRANK = PROSENTTIJÄRJESTYS
POISSON = POISSON
QUARTILE = NELJÄNNES
RANK = ARVON.MUKAAN
STDEV = KESKIHAJONTA
STDEVP = KESKIHAJONTAP
TDIST = TJAKAUMA
TINV = TJAKAUMA.KÄÄNT
TTEST = TTESTI
VAR = VAR
VARP = VARP
WEIBULL = WEIBULL
ZTEST = ZTESTI
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fi/config 0000644 00000000521 15242753725 0020566 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Suomi (Finnish)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #TYHJÄ!
DIV0 = #JAKO/0!
VALUE = #ARVO!
REF = #VIITTAUS!
NAME = #NIMI?
NUM = #LUKU!
NA = #PUUTTUU!
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/functions 0000644 00000033315 15242753725 0021370 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## русский язык (Russian)
##
############################################################
##
## Функции кубов (Cube Functions)
##
CUBEKPIMEMBER = КУБЭЛЕМЕНТКИП
CUBEMEMBER = КУБЭЛЕМЕНТ
CUBEMEMBERPROPERTY = КУБСВОЙСТВОЭЛЕМЕНТА
CUBERANKEDMEMBER = КУБПОРЭЛЕМЕНТ
CUBESET = КУБМНОЖ
CUBESETCOUNT = КУБЧИСЛОЭЛМНОЖ
CUBEVALUE = КУБЗНАЧЕНИЕ
##
## Функции для работы с базами данных (Database Functions)
##
DAVERAGE = ДСРЗНАЧ
DCOUNT = БСЧЁТ
DCOUNTA = БСЧЁТА
DGET = БИЗВЛЕЧЬ
DMAX = ДМАКС
DMIN = ДМИН
DPRODUCT = БДПРОИЗВЕД
DSTDEV = ДСТАНДОТКЛ
DSTDEVP = ДСТАНДОТКЛП
DSUM = БДСУММ
DVAR = БДДИСП
DVARP = БДДИСПП
##
## Функции даты и времени (Date & Time Functions)
##
DATE = ДАТА
DATEDIF = РАЗНДАТ
DATESTRING = СТРОКАДАННЫХ
DATEVALUE = ДАТАЗНАЧ
DAY = ДЕНЬ
DAYS = ДНИ
DAYS360 = ДНЕЙ360
EDATE = ДАТАМЕС
EOMONTH = КОНМЕСЯЦА
HOUR = ЧАС
ISOWEEKNUM = НОМНЕДЕЛИ.ISO
MINUTE = МИНУТЫ
MONTH = МЕСЯЦ
NETWORKDAYS = ЧИСТРАБДНИ
NETWORKDAYS.INTL = ЧИСТРАБДНИ.МЕЖД
NOW = ТДАТА
SECOND = СЕКУНДЫ
THAIDAYOFWEEK = ТАЙДЕНЬНЕД
THAIMONTHOFYEAR = ТАЙМЕСЯЦ
THAIYEAR = ТАЙГОД
TIME = ВРЕМЯ
TIMEVALUE = ВРЕМЗНАЧ
TODAY = СЕГОДНЯ
WEEKDAY = ДЕНЬНЕД
WEEKNUM = НОМНЕДЕЛИ
WORKDAY = РАБДЕНЬ
WORKDAY.INTL = РАБДЕНЬ.МЕЖД
YEAR = ГОД
YEARFRAC = ДОЛЯГОДА
##
## Инженерные функции (Engineering Functions)
##
BESSELI = БЕССЕЛЬ.I
BESSELJ = БЕССЕЛЬ.J
BESSELK = БЕССЕЛЬ.K
BESSELY = БЕССЕЛЬ.Y
BIN2DEC = ДВ.В.ДЕС
BIN2HEX = ДВ.В.ШЕСТН
BIN2OCT = ДВ.В.ВОСЬМ
BITAND = БИТ.И
BITLSHIFT = БИТ.СДВИГЛ
BITOR = БИТ.ИЛИ
BITRSHIFT = БИТ.СДВИГП
BITXOR = БИТ.ИСКЛИЛИ
COMPLEX = КОМПЛЕКСН
CONVERT = ПРЕОБР
DEC2BIN = ДЕС.В.ДВ
DEC2HEX = ДЕС.В.ШЕСТН
DEC2OCT = ДЕС.В.ВОСЬМ
DELTA = ДЕЛЬТА
ERF = ФОШ
ERF.PRECISE = ФОШ.ТОЧН
ERFC = ДФОШ
ERFC.PRECISE = ДФОШ.ТОЧН
GESTEP = ПОРОГ
HEX2BIN = ШЕСТН.В.ДВ
HEX2DEC = ШЕСТН.В.ДЕС
HEX2OCT = ШЕСТН.В.ВОСЬМ
IMABS = МНИМ.ABS
IMAGINARY = МНИМ.ЧАСТЬ
IMARGUMENT = МНИМ.АРГУМЕНТ
IMCONJUGATE = МНИМ.СОПРЯЖ
IMCOS = МНИМ.COS
IMCOSH = МНИМ.COSH
IMCOT = МНИМ.COT
IMCSC = МНИМ.CSC
IMCSCH = МНИМ.CSCH
IMDIV = МНИМ.ДЕЛ
IMEXP = МНИМ.EXP
IMLN = МНИМ.LN
IMLOG10 = МНИМ.LOG10
IMLOG2 = МНИМ.LOG2
IMPOWER = МНИМ.СТЕПЕНЬ
IMPRODUCT = МНИМ.ПРОИЗВЕД
IMREAL = МНИМ.ВЕЩ
IMSEC = МНИМ.SEC
IMSECH = МНИМ.SECH
IMSIN = МНИМ.SIN
IMSINH = МНИМ.SINH
IMSQRT = МНИМ.КОРЕНЬ
IMSUB = МНИМ.РАЗН
IMSUM = МНИМ.СУММ
IMTAN = МНИМ.TAN
OCT2BIN = ВОСЬМ.В.ДВ
OCT2DEC = ВОСЬМ.В.ДЕС
OCT2HEX = ВОСЬМ.В.ШЕСТН
##
## Финансовые функции (Financial Functions)
##
ACCRINT = НАКОПДОХОД
ACCRINTM = НАКОПДОХОДПОГАШ
AMORDEGRC = АМОРУМ
AMORLINC = АМОРУВ
COUPDAYBS = ДНЕЙКУПОНДО
COUPDAYS = ДНЕЙКУПОН
COUPDAYSNC = ДНЕЙКУПОНПОСЛЕ
COUPNCD = ДАТАКУПОНПОСЛЕ
COUPNUM = ЧИСЛКУПОН
COUPPCD = ДАТАКУПОНДО
CUMIPMT = ОБЩПЛАТ
CUMPRINC = ОБЩДОХОД
DB = ФУО
DDB = ДДОБ
DISC = СКИДКА
DOLLARDE = РУБЛЬ.ДЕС
DOLLARFR = РУБЛЬ.ДРОБЬ
DURATION = ДЛИТ
EFFECT = ЭФФЕКТ
FV = БС
FVSCHEDULE = БЗРАСПИС
INTRATE = ИНОРМА
IPMT = ПРПЛТ
IRR = ВСД
ISPMT = ПРОЦПЛАТ
MDURATION = МДЛИТ
MIRR = МВСД
NOMINAL = НОМИНАЛ
NPER = КПЕР
NPV = ЧПС
ODDFPRICE = ЦЕНАПЕРВНЕРЕГ
ODDFYIELD = ДОХОДПЕРВНЕРЕГ
ODDLPRICE = ЦЕНАПОСЛНЕРЕГ
ODDLYIELD = ДОХОДПОСЛНЕРЕГ
PDURATION = ПДЛИТ
PMT = ПЛТ
PPMT = ОСПЛТ
PRICE = ЦЕНА
PRICEDISC = ЦЕНАСКИДКА
PRICEMAT = ЦЕНАПОГАШ
PV = ПС
RATE = СТАВКА
RECEIVED = ПОЛУЧЕНО
RRI = ЭКВ.СТАВКА
SLN = АПЛ
SYD = АСЧ
TBILLEQ = РАВНОКЧЕК
TBILLPRICE = ЦЕНАКЧЕК
TBILLYIELD = ДОХОДКЧЕК
USDOLLAR = ДОЛЛСША
VDB = ПУО
XIRR = ЧИСТВНДОХ
XNPV = ЧИСТНЗ
YIELD = ДОХОД
YIELDDISC = ДОХОДСКИДКА
YIELDMAT = ДОХОДПОГАШ
##
## Информационные функции (Information Functions)
##
CELL = ЯЧЕЙКА
ERROR.TYPE = ТИП.ОШИБКИ
INFO = ИНФОРМ
ISBLANK = ЕПУСТО
ISERR = ЕОШ
ISERROR = ЕОШИБКА
ISEVEN = ЕЧЁТН
ISFORMULA = ЕФОРМУЛА
ISLOGICAL = ЕЛОГИЧ
ISNA = ЕНД
ISNONTEXT = ЕНЕТЕКСТ
ISNUMBER = ЕЧИСЛО
ISODD = ЕНЕЧЁТ
ISREF = ЕССЫЛКА
ISTEXT = ЕТЕКСТ
N = Ч
NA = НД
SHEET = ЛИСТ
SHEETS = ЛИСТЫ
TYPE = ТИП
##
## Логические функции (Logical Functions)
##
AND = И
FALSE = ЛОЖЬ
IF = ЕСЛИ
IFERROR = ЕСЛИОШИБКА
IFNA = ЕСНД
IFS = УСЛОВИЯ
NOT = НЕ
OR = ИЛИ
SWITCH = ПЕРЕКЛЮЧ
TRUE = ИСТИНА
XOR = ИСКЛИЛИ
##
## Функции ссылки и поиска (Lookup & Reference Functions)
##
ADDRESS = АДРЕС
AREAS = ОБЛАСТИ
CHOOSE = ВЫБОР
COLUMN = СТОЛБЕЦ
COLUMNS = ЧИСЛСТОЛБ
FILTER = ФИЛЬТР
FORMULATEXT = Ф.ТЕКСТ
GETPIVOTDATA = ПОЛУЧИТЬ.ДАННЫЕ.СВОДНОЙ.ТАБЛИЦЫ
HLOOKUP = ГПР
HYPERLINK = ГИПЕРССЫЛКА
INDEX = ИНДЕКС
INDIRECT = ДВССЫЛ
LOOKUP = ПРОСМОТР
MATCH = ПОИСКПОЗ
OFFSET = СМЕЩ
ROW = СТРОКА
ROWS = ЧСТРОК
RTD = ДРВ
SORT = СОРТ
SORTBY = СОРТПО
TRANSPOSE = ТРАНСП
UNIQUE = УНИК
VLOOKUP = ВПР
XLOOKUP = ПРОСМОТРX
XMATCH = ПОИСКПОЗX
##
## Математические и тригонометрические функции (Math & Trig Functions)
##
ABS = ABS
ACOS = ACOS
ACOSH = ACOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = АГРЕГАТ
ARABIC = АРАБСКОЕ
ASIN = ASIN
ASINH = ASINH
ATAN = ATAN
ATAN2 = ATAN2
ATANH = ATANH
BASE = ОСНОВАНИЕ
CEILING.MATH = ОКРВВЕРХ.МАТ
CEILING.PRECISE = ОКРВВЕРХ.ТОЧН
COMBIN = ЧИСЛКОМБ
COMBINA = ЧИСЛКОМБА
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = ДЕС
DEGREES = ГРАДУСЫ
ECMA.CEILING = ECMA.ОКРВВЕРХ
EVEN = ЧЁТН
EXP = EXP
FACT = ФАКТР
FACTDOUBLE = ДВФАКТР
FLOOR.MATH = ОКРВНИЗ.МАТ
FLOOR.PRECISE = ОКРВНИЗ.ТОЧН
GCD = НОД
INT = ЦЕЛОЕ
ISO.CEILING = ISO.ОКРВВЕРХ
LCM = НОК
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = МОПРЕД
MINVERSE = МОБР
MMULT = МУМНОЖ
MOD = ОСТАТ
MROUND = ОКРУГЛТ
MULTINOMIAL = МУЛЬТИНОМ
MUNIT = МЕДИН
ODD = НЕЧЁТ
PI = ПИ
POWER = СТЕПЕНЬ
PRODUCT = ПРОИЗВЕД
QUOTIENT = ЧАСТНОЕ
RADIANS = РАДИАНЫ
RAND = СЛЧИС
RANDARRAY = СЛУЧМАССИВ
RANDBETWEEN = СЛУЧМЕЖДУ
ROMAN = РИМСКОЕ
ROUND = ОКРУГЛ
ROUNDBAHTDOWN = ОКРУГЛБАТВНИЗ
ROUNDBAHTUP = ОКРУГЛБАТВВЕРХ
ROUNDDOWN = ОКРУГЛВНИЗ
ROUNDUP = ОКРУГЛВВЕРХ
SEC = SEC
SECH = SECH
SERIESSUM = РЯД.СУММ
SEQUENCE = ПОСЛЕДОВ
SIGN = ЗНАК
SIN = SIN
SINH = SINH
SQRT = КОРЕНЬ
SQRTPI = КОРЕНЬПИ
SUBTOTAL = ПРОМЕЖУТОЧНЫЕ.ИТОГИ
SUM = СУММ
SUMIF = СУММЕСЛИ
SUMIFS = СУММЕСЛИМН
SUMPRODUCT = СУММПРОИЗВ
SUMSQ = СУММКВ
SUMX2MY2 = СУММРАЗНКВ
SUMX2PY2 = СУММСУММКВ
SUMXMY2 = СУММКВРАЗН
TAN = TAN
TANH = TANH
TRUNC = ОТБР
##
## Статистические функции (Statistical Functions)
##
AVEDEV = СРОТКЛ
AVERAGE = СРЗНАЧ
AVERAGEA = СРЗНАЧА
AVERAGEIF = СРЗНАЧЕСЛИ
AVERAGEIFS = СРЗНАЧЕСЛИМН
BETA.DIST = БЕТА.РАСП
BETA.INV = БЕТА.ОБР
BINOM.DIST = БИНОМ.РАСП
BINOM.DIST.RANGE = БИНОМ.РАСП.ДИАП
BINOM.INV = БИНОМ.ОБР
CHISQ.DIST = ХИ2.РАСП
CHISQ.DIST.RT = ХИ2.РАСП.ПХ
CHISQ.INV = ХИ2.ОБР
CHISQ.INV.RT = ХИ2.ОБР.ПХ
CHISQ.TEST = ХИ2.ТЕСТ
CONFIDENCE.NORM = ДОВЕРИТ.НОРМ
CONFIDENCE.T = ДОВЕРИТ.СТЬЮДЕНТ
CORREL = КОРРЕЛ
COUNT = СЧЁТ
COUNTA = СЧЁТЗ
COUNTBLANK = СЧИТАТЬПУСТОТЫ
COUNTIF = СЧЁТЕСЛИ
COUNTIFS = СЧЁТЕСЛИМН
COVARIANCE.P = КОВАРИАЦИЯ.Г
COVARIANCE.S = КОВАРИАЦИЯ.В
DEVSQ = КВАДРОТКЛ
EXPON.DIST = ЭКСП.РАСП
F.DIST = F.РАСП
F.DIST.RT = F.РАСП.ПХ
F.INV = F.ОБР
F.INV.RT = F.ОБР.ПХ
F.TEST = F.ТЕСТ
FISHER = ФИШЕР
FISHERINV = ФИШЕРОБР
FORECAST.ETS = ПРЕДСКАЗ.ETS
FORECAST.ETS.CONFINT = ПРЕДСКАЗ.ЕTS.ДОВИНТЕРВАЛ
FORECAST.ETS.SEASONALITY = ПРЕДСКАЗ.ETS.СЕЗОННОСТЬ
FORECAST.ETS.STAT = ПРЕДСКАЗ.ETS.СТАТ
FORECAST.LINEAR = ПРЕДСКАЗ.ЛИНЕЙН
FREQUENCY = ЧАСТОТА
GAMMA = ГАММА
GAMMA.DIST = ГАММА.РАСП
GAMMA.INV = ГАММА.ОБР
GAMMALN = ГАММАНЛОГ
GAMMALN.PRECISE = ГАММАНЛОГ.ТОЧН
GAUSS = ГАУСС
GEOMEAN = СРГЕОМ
GROWTH = РОСТ
HARMEAN = СРГАРМ
HYPGEOM.DIST = ГИПЕРГЕОМ.РАСП
INTERCEPT = ОТРЕЗОК
KURT = ЭКСЦЕСС
LARGE = НАИБОЛЬШИЙ
LINEST = ЛИНЕЙН
LOGEST = ЛГРФПРИБЛ
LOGNORM.DIST = ЛОГНОРМ.РАСП
LOGNORM.INV = ЛОГНОРМ.ОБР
MAX = МАКС
MAXA = МАКСА
MAXIFS = МАКСЕСЛИ
MEDIAN = МЕДИАНА
MIN = МИН
MINA = МИНА
MINIFS = МИНЕСЛИ
MODE.MULT = МОДА.НСК
MODE.SNGL = МОДА.ОДН
NEGBINOM.DIST = ОТРБИНОМ.РАСП
NORM.DIST = НОРМ.РАСП
NORM.INV = НОРМ.ОБР
NORM.S.DIST = НОРМ.СТ.РАСП
NORM.S.INV = НОРМ.СТ.ОБР
PEARSON = PEARSON
PERCENTILE.EXC = ПРОЦЕНТИЛЬ.ИСКЛ
PERCENTILE.INC = ПРОЦЕНТИЛЬ.ВКЛ
PERCENTRANK.EXC = ПРОЦЕНТРАНГ.ИСКЛ
PERCENTRANK.INC = ПРОЦЕНТРАНГ.ВКЛ
PERMUT = ПЕРЕСТ
PERMUTATIONA = ПЕРЕСТА
PHI = ФИ
POISSON.DIST = ПУАССОН.РАСП
PROB = ВЕРОЯТНОСТЬ
QUARTILE.EXC = КВАРТИЛЬ.ИСКЛ
QUARTILE.INC = КВАРТИЛЬ.ВКЛ
RANK.AVG = РАНГ.СР
RANK.EQ = РАНГ.РВ
RSQ = КВПИРСОН
SKEW = СКОС
SKEW.P = СКОС.Г
SLOPE = НАКЛОН
SMALL = НАИМЕНЬШИЙ
STANDARDIZE = НОРМАЛИЗАЦИЯ
STDEV.P = СТАНДОТКЛОН.Г
STDEV.S = СТАНДОТКЛОН.В
STDEVA = СТАНДОТКЛОНА
STDEVPA = СТАНДОТКЛОНПА
STEYX = СТОШYX
T.DIST = СТЬЮДЕНТ.РАСП
T.DIST.2T = СТЬЮДЕНТ.РАСП.2Х
T.DIST.RT = СТЬЮДЕНТ.РАСП.ПХ
T.INV = СТЬЮДЕНТ.ОБР
T.INV.2T = СТЬЮДЕНТ.ОБР.2Х
T.TEST = СТЬЮДЕНТ.ТЕСТ
TREND = ТЕНДЕНЦИЯ
TRIMMEAN = УРЕЗСРЕДНЕЕ
VAR.P = ДИСП.Г
VAR.S = ДИСП.В
VARA = ДИСПА
VARPA = ДИСПРА
WEIBULL.DIST = ВЕЙБУЛЛ.РАСП
Z.TEST = Z.ТЕСТ
##
## Текстовые функции (Text Functions)
##
ARRAYTOTEXT = МАССИВВТЕКСТ
BAHTTEXT = БАТТЕКСТ
CHAR = СИМВОЛ
CLEAN = ПЕЧСИМВ
CODE = КОДСИМВ
CONCAT = СЦЕП
DBCS = БДЦС
DOLLAR = РУБЛЬ
EXACT = СОВПАД
FIND = НАЙТИ
FINDB = НАЙТИБ
FIXED = ФИКСИРОВАННЫЙ
ISTHAIDIGIT = ЕТАЙЦИФРЫ
LEFT = ЛЕВСИМВ
LEFTB = ЛЕВБ
LEN = ДЛСТР
LENB = ДЛИНБ
LOWER = СТРОЧН
MID = ПСТР
MIDB = ПСТРБ
NUMBERSTRING = СТРОКАЧИСЕЛ
NUMBERVALUE = ЧЗНАЧ
PROPER = ПРОПНАЧ
REPLACE = ЗАМЕНИТЬ
REPLACEB = ЗАМЕНИТЬБ
REPT = ПОВТОР
RIGHT = ПРАВСИМВ
RIGHTB = ПРАВБ
SEARCH = ПОИСК
SEARCHB = ПОИСКБ
SUBSTITUTE = ПОДСТАВИТЬ
T = Т
TEXT = ТЕКСТ
TEXTJOIN = ОБЪЕДИНИТЬ
THAIDIGIT = ТАЙЦИФРА
THAINUMSOUND = ТАЙЧИСЛОВЗВУК
THAINUMSTRING = ТАЙЧИСЛОВСТРОКУ
THAISTRINGLENGTH = ТАЙДЛИНАСТРОКИ
TRIM = СЖПРОБЕЛЫ
UNICHAR = ЮНИСИМВ
UNICODE = UNICODE
UPPER = ПРОПИСН
VALUE = ЗНАЧЕН
VALUETOTEXT = ЗНАЧЕНИЕВТЕКСТ
##
## Веб-функции (Web Functions)
##
ENCODEURL = КОДИР.URL
FILTERXML = ФИЛЬТР.XML
WEBSERVICE = ВЕБСЛУЖБА
##
## Функции совместимости (Compatibility Functions)
##
BETADIST = БЕТАРАСП
BETAINV = БЕТАОБР
BINOMDIST = БИНОМРАСП
CEILING = ОКРВВЕРХ
CHIDIST = ХИ2РАСП
CHIINV = ХИ2ОБР
CHITEST = ХИ2ТЕСТ
CONCATENATE = СЦЕПИТЬ
CONFIDENCE = ДОВЕРИТ
COVAR = КОВАР
CRITBINOM = КРИТБИНОМ
EXPONDIST = ЭКСПРАСП
FDIST = FРАСП
FINV = FРАСПОБР
FLOOR = ОКРВНИЗ
FORECAST = ПРЕДСКАЗ
FTEST = ФТЕСТ
GAMMADIST = ГАММАРАСП
GAMMAINV = ГАММАОБР
HYPGEOMDIST = ГИПЕРГЕОМЕТ
LOGINV = ЛОГНОРМОБР
LOGNORMDIST = ЛОГНОРМРАСП
MODE = МОДА
NEGBINOMDIST = ОТРБИНОМРАСП
NORMDIST = НОРМРАСП
NORMINV = НОРМОБР
NORMSDIST = НОРМСТРАСП
NORMSINV = НОРМСТОБР
PERCENTILE = ПЕРСЕНТИЛЬ
PERCENTRANK = ПРОЦЕНТРАНГ
POISSON = ПУАССОН
QUARTILE = КВАРТИЛЬ
RANK = РАНГ
STDEV = СТАНДОТКЛОН
STDEVP = СТАНДОТКЛОНП
TDIST = СТЬЮДРАСП
TINV = СТЬЮДРАСПОБР
TTEST = ТТЕСТ
VAR = ДИСП
VARP = ДИСПР
WEIBULL = ВЕЙБУЛЛ
ZTEST = ZТЕСТ
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/ru/config 0000644 00000000566 15242753725 0020627 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## русский язык (Russian)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #ПУСТО!
DIV0 = #ДЕЛ/0!
VALUE = #ЗНАЧ!
REF = #ССЫЛКА!
NAME = #ИМЯ?
NUM = #ЧИСЛО!
NA = #Н/Д
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/functions 0000644 00000024411 15242753725 0021364 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Türkçe (Turkish)
##
############################################################
##
## Küp işlevleri (Cube Functions)
##
CUBEKPIMEMBER = KÜPKPIÜYESİ
CUBEMEMBER = KÜPÜYESİ
CUBEMEMBERPROPERTY = KÜPÜYEÖZELLİĞİ
CUBERANKEDMEMBER = DERECELİKÜPÜYESİ
CUBESET = KÜPKÜMESİ
CUBESETCOUNT = KÜPKÜMESAYISI
CUBEVALUE = KÜPDEĞERİ
##
## Veritabanı işlevleri (Database Functions)
##
DAVERAGE = VSEÇORT
DCOUNT = VSEÇSAY
DCOUNTA = VSEÇSAYDOLU
DGET = VAL
DMAX = VSEÇMAK
DMIN = VSEÇMİN
DPRODUCT = VSEÇÇARP
DSTDEV = VSEÇSTDSAPMA
DSTDEVP = VSEÇSTDSAPMAS
DSUM = VSEÇTOPLA
DVAR = VSEÇVAR
DVARP = VSEÇVARS
##
## Tarih ve saat işlevleri (Date & Time Functions)
##
DATE = TARİH
DATEDIF = ETARİHLİ
DATESTRING = TARİHDİZİ
DATEVALUE = TARİHSAYISI
DAY = GÜN
DAYS = GÜNSAY
DAYS360 = GÜN360
EDATE = SERİTARİH
EOMONTH = SERİAY
HOUR = SAAT
ISOWEEKNUM = ISOHAFTASAY
MINUTE = DAKİKA
MONTH = AY
NETWORKDAYS = TAMİŞGÜNÜ
NETWORKDAYS.INTL = TAMİŞGÜNÜ.ULUSL
NOW = ŞİMDİ
SECOND = SANİYE
THAIDAYOFWEEK = TAYHAFTANINGÜNÜ
THAIMONTHOFYEAR = TAYYILINAYI
THAIYEAR = TAYYILI
TIME = ZAMAN
TIMEVALUE = ZAMANSAYISI
TODAY = BUGÜN
WEEKDAY = HAFTANINGÜNÜ
WEEKNUM = HAFTASAY
WORKDAY = İŞGÜNÜ
WORKDAY.INTL = İŞGÜNÜ.ULUSL
YEAR = YIL
YEARFRAC = YILORAN
##
## Mühendislik işlevleri (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BIN2DEC
BIN2HEX = BIN2HEX
BIN2OCT = BIN2OCT
BITAND = BİTVE
BITLSHIFT = BİTSOLAKAYDIR
BITOR = BİTVEYA
BITRSHIFT = BİTSAĞAKAYDIR
BITXOR = BİTÖZELVEYA
COMPLEX = KARMAŞIK
CONVERT = ÇEVİR
DEC2BIN = DEC2BIN
DEC2HEX = DEC2HEX
DEC2OCT = DEC2OCT
DELTA = DELTA
ERF = HATAİŞLEV
ERF.PRECISE = HATAİŞLEV.DUYARLI
ERFC = TÜMHATAİŞLEV
ERFC.PRECISE = TÜMHATAİŞLEV.DUYARLI
GESTEP = BESINIR
HEX2BIN = HEX2BIN
HEX2DEC = HEX2DEC
HEX2OCT = HEX2OCT
IMABS = SANMUTLAK
IMAGINARY = SANAL
IMARGUMENT = SANBAĞ_DEĞİŞKEN
IMCONJUGATE = SANEŞLENEK
IMCOS = SANCOS
IMCOSH = SANCOSH
IMCOT = SANCOT
IMCSC = SANCSC
IMCSCH = SANCSCH
IMDIV = SANBÖL
IMEXP = SANÜS
IMLN = SANLN
IMLOG10 = SANLOG10
IMLOG2 = SANLOG2
IMPOWER = SANKUVVET
IMPRODUCT = SANÇARP
IMREAL = SANGERÇEK
IMSEC = SANSEC
IMSECH = SANSECH
IMSIN = SANSIN
IMSINH = SANSINH
IMSQRT = SANKAREKÖK
IMSUB = SANTOPLA
IMSUM = SANÇIKAR
IMTAN = SANTAN
OCT2BIN = OCT2BIN
OCT2DEC = OCT2DEC
OCT2HEX = OCT2HEX
##
## Finansal işlevler (Financial Functions)
##
ACCRINT = GERÇEKFAİZ
ACCRINTM = GERÇEKFAİZV
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = KUPONGÜNBD
COUPDAYS = KUPONGÜN
COUPDAYSNC = KUPONGÜNDSK
COUPNCD = KUPONGÜNSKT
COUPNUM = KUPONSAYI
COUPPCD = KUPONGÜNÖKT
CUMIPMT = TOPÖDENENFAİZ
CUMPRINC = TOPANAPARA
DB = AZALANBAKİYE
DDB = ÇİFTAZALANBAKİYE
DISC = İNDİRİM
DOLLARDE = LİRAON
DOLLARFR = LİRAKES
DURATION = SÜRE
EFFECT = ETKİN
FV = GD
FVSCHEDULE = GDPROGRAM
INTRATE = FAİZORANI
IPMT = FAİZTUTARI
IRR = İÇ_VERİM_ORANI
ISPMT = ISPMT
MDURATION = MSÜRE
MIRR = D_İÇ_VERİM_ORANI
NOMINAL = NOMİNAL
NPER = TAKSİT_SAYISI
NPV = NBD
ODDFPRICE = TEKYDEĞER
ODDFYIELD = TEKYÖDEME
ODDLPRICE = TEKSDEĞER
ODDLYIELD = TEKSÖDEME
PDURATION = PSÜRE
PMT = DEVRESEL_ÖDEME
PPMT = ANA_PARA_ÖDEMESİ
PRICE = DEĞER
PRICEDISC = DEĞERİND
PRICEMAT = DEĞERVADE
PV = BD
RATE = FAİZ_ORANI
RECEIVED = GETİRİ
RRI = GERÇEKLEŞENYATIRIMGETİRİSİ
SLN = DA
SYD = YAT
TBILLEQ = HTAHEŞ
TBILLPRICE = HTAHDEĞER
TBILLYIELD = HTAHÖDEME
VDB = DAB
XIRR = AİÇVERİMORANI
XNPV = ANBD
YIELD = ÖDEME
YIELDDISC = ÖDEMEİND
YIELDMAT = ÖDEMEVADE
##
## Bilgi işlevleri (Information Functions)
##
CELL = HÜCRE
ERROR.TYPE = HATA.TİPİ
INFO = BİLGİ
ISBLANK = EBOŞSA
ISERR = EHATA
ISERROR = EHATALIYSA
ISEVEN = ÇİFTMİ
ISFORMULA = EFORMÜLSE
ISLOGICAL = EMANTIKSALSA
ISNA = EYOKSA
ISNONTEXT = EMETİNDEĞİLSE
ISNUMBER = ESAYIYSA
ISODD = TEKMİ
ISREF = EREFSE
ISTEXT = EMETİNSE
N = S
NA = YOKSAY
SHEET = SAYFA
SHEETS = SAYFALAR
TYPE = TÜR
##
## Mantıksal işlevler (Logical Functions)
##
AND = VE
FALSE = YANLIŞ
IF = EĞER
IFERROR = EĞERHATA
IFNA = EĞERYOKSA
IFS = ÇOKEĞER
NOT = DEĞİL
OR = YADA
SWITCH = İLKEŞLEŞEN
TRUE = DOĞRU
XOR = ÖZELVEYA
##
## Arama ve başvuru işlevleri (Lookup & Reference Functions)
##
ADDRESS = ADRES
AREAS = ALANSAY
CHOOSE = ELEMAN
COLUMN = SÜTUN
COLUMNS = SÜTUNSAY
FORMULATEXT = FORMÜLMETNİ
GETPIVOTDATA = ÖZETVERİAL
HLOOKUP = YATAYARA
HYPERLINK = KÖPRÜ
INDEX = İNDİS
INDIRECT = DOLAYLI
LOOKUP = ARA
MATCH = KAÇINCI
OFFSET = KAYDIR
ROW = SATIR
ROWS = SATIRSAY
RTD = GZV
TRANSPOSE = DEVRİK_DÖNÜŞÜM
VLOOKUP = DÜŞEYARA
##
## Matematik ve trigonometri işlevleri (Math & Trig Functions)
##
ABS = MUTLAK
ACOS = ACOS
ACOSH = ACOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = TOPLAMA
ARABIC = ARAP
ASIN = ASİN
ASINH = ASİNH
ATAN = ATAN
ATAN2 = ATAN2
ATANH = ATANH
BASE = TABAN
CEILING.MATH = TAVANAYUVARLA.MATEMATİK
CEILING.PRECISE = TAVANAYUVARLA.DUYARLI
COMBIN = KOMBİNASYON
COMBINA = KOMBİNASYONA
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = ONDALIK
DEGREES = DERECE
ECMA.CEILING = ECMA.TAVAN
EVEN = ÇİFT
EXP = ÜS
FACT = ÇARPINIM
FACTDOUBLE = ÇİFTFAKTÖR
FLOOR.MATH = TABANAYUVARLA.MATEMATİK
FLOOR.PRECISE = TABANAYUVARLA.DUYARLI
GCD = OBEB
INT = TAMSAYI
ISO.CEILING = ISO.TAVAN
LCM = OKEK
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = DETERMİNANT
MINVERSE = DİZEY_TERS
MMULT = DÇARP
MOD = MOD
MROUND = KYUVARLA
MULTINOMIAL = ÇOKTERİMLİ
MUNIT = BİRİMMATRİS
ODD = TEK
PI = Pİ
POWER = KUVVET
PRODUCT = ÇARPIM
QUOTIENT = BÖLÜM
RADIANS = RADYAN
RAND = S_SAYI_ÜRET
RANDBETWEEN = RASTGELEARADA
ROMAN = ROMEN
ROUND = YUVARLA
ROUNDBAHTDOWN = BAHTAŞAĞIYUVARLA
ROUNDBAHTUP = BAHTYUKARIYUVARLA
ROUNDDOWN = AŞAĞIYUVARLA
ROUNDUP = YUKARIYUVARLA
SEC = SEC
SECH = SECH
SERIESSUM = SERİTOPLA
SIGN = İŞARET
SIN = SİN
SINH = SİNH
SQRT = KAREKÖK
SQRTPI = KAREKÖKPİ
SUBTOTAL = ALTTOPLAM
SUM = TOPLA
SUMIF = ETOPLA
SUMIFS = ÇOKETOPLA
SUMPRODUCT = TOPLA.ÇARPIM
SUMSQ = TOPKARE
SUMX2MY2 = TOPX2EY2
SUMX2PY2 = TOPX2AY2
SUMXMY2 = TOPXEY2
TAN = TAN
TANH = TANH
TRUNC = NSAT
##
## İstatistik işlevleri (Statistical Functions)
##
AVEDEV = ORTSAP
AVERAGE = ORTALAMA
AVERAGEA = ORTALAMAA
AVERAGEIF = EĞERORTALAMA
AVERAGEIFS = ÇOKEĞERORTALAMA
BETA.DIST = BETA.DAĞ
BETA.INV = BETA.TERS
BINOM.DIST = BİNOM.DAĞ
BINOM.DIST.RANGE = BİNOM.DAĞ.ARALIK
BINOM.INV = BİNOM.TERS
CHISQ.DIST = KİKARE.DAĞ
CHISQ.DIST.RT = KİKARE.DAĞ.SAĞK
CHISQ.INV = KİKARE.TERS
CHISQ.INV.RT = KİKARE.TERS.SAĞK
CHISQ.TEST = KİKARE.TEST
CONFIDENCE.NORM = GÜVENİLİRLİK.NORM
CONFIDENCE.T = GÜVENİLİRLİK.T
CORREL = KORELASYON
COUNT = BAĞ_DEĞ_SAY
COUNTA = BAĞ_DEĞ_DOLU_SAY
COUNTBLANK = BOŞLUKSAY
COUNTIF = EĞERSAY
COUNTIFS = ÇOKEĞERSAY
COVARIANCE.P = KOVARYANS.P
COVARIANCE.S = KOVARYANS.S
DEVSQ = SAPKARE
EXPON.DIST = ÜSTEL.DAĞ
F.DIST = F.DAĞ
F.DIST.RT = F.DAĞ.SAĞK
F.INV = F.TERS
F.INV.RT = F.TERS.SAĞK
F.TEST = F.TEST
FISHER = FISHER
FISHERINV = FISHERTERS
FORECAST.ETS = TAHMİN.ETS
FORECAST.ETS.CONFINT = TAHMİN.ETS.GVNARAL
FORECAST.ETS.SEASONALITY = TAHMİN.ETS.MEVSİMSELLİK
FORECAST.ETS.STAT = TAHMİN.ETS.İSTAT
FORECAST.LINEAR = TAHMİN.DOĞRUSAL
FREQUENCY = SIKLIK
GAMMA = GAMA
GAMMA.DIST = GAMA.DAĞ
GAMMA.INV = GAMA.TERS
GAMMALN = GAMALN
GAMMALN.PRECISE = GAMALN.DUYARLI
GAUSS = GAUSS
GEOMEAN = GEOORT
GROWTH = BÜYÜME
HARMEAN = HARORT
HYPGEOM.DIST = HİPERGEOM.DAĞ
INTERCEPT = KESMENOKTASI
KURT = BASIKLIK
LARGE = BÜYÜK
LINEST = DOT
LOGEST = LOT
LOGNORM.DIST = LOGNORM.DAĞ
LOGNORM.INV = LOGNORM.TERS
MAX = MAK
MAXA = MAKA
MAXIFS = ÇOKEĞERMAK
MEDIAN = ORTANCA
MIN = MİN
MINA = MİNA
MINIFS = ÇOKEĞERMİN
MODE.MULT = ENÇOK_OLAN.ÇOK
MODE.SNGL = ENÇOK_OLAN.TEK
NEGBINOM.DIST = NEGBİNOM.DAĞ
NORM.DIST = NORM.DAĞ
NORM.INV = NORM.TERS
NORM.S.DIST = NORM.S.DAĞ
NORM.S.INV = NORM.S.TERS
PEARSON = PEARSON
PERCENTILE.EXC = YÜZDEBİRLİK.HRC
PERCENTILE.INC = YÜZDEBİRLİK.DHL
PERCENTRANK.EXC = YÜZDERANK.HRC
PERCENTRANK.INC = YÜZDERANK.DHL
PERMUT = PERMÜTASYON
PERMUTATIONA = PERMÜTASYONA
PHI = PHI
POISSON.DIST = POISSON.DAĞ
PROB = OLASILIK
QUARTILE.EXC = DÖRTTEBİRLİK.HRC
QUARTILE.INC = DÖRTTEBİRLİK.DHL
RANK.AVG = RANK.ORT
RANK.EQ = RANK.EŞİT
RSQ = RKARE
SKEW = ÇARPIKLIK
SKEW.P = ÇARPIKLIK.P
SLOPE = EĞİM
SMALL = KÜÇÜK
STANDARDIZE = STANDARTLAŞTIRMA
STDEV.P = STDSAPMA.P
STDEV.S = STDSAPMA.S
STDEVA = STDSAPMAA
STDEVPA = STDSAPMASA
STEYX = STHYX
T.DIST = T.DAĞ
T.DIST.2T = T.DAĞ.2K
T.DIST.RT = T.DAĞ.SAĞK
T.INV = T.TERS
T.INV.2T = T.TERS.2K
T.TEST = T.TEST
TREND = EĞİLİM
TRIMMEAN = KIRPORTALAMA
VAR.P = VAR.P
VAR.S = VAR.S
VARA = VARA
VARPA = VARSA
WEIBULL.DIST = WEIBULL.DAĞ
Z.TEST = Z.TEST
##
## Metin işlevleri (Text Functions)
##
BAHTTEXT = BAHTMETİN
CHAR = DAMGA
CLEAN = TEMİZ
CODE = KOD
CONCAT = ARALIKBİRLEŞTİR
DOLLAR = LİRA
EXACT = ÖZDEŞ
FIND = BUL
FIXED = SAYIDÜZENLE
ISTHAIDIGIT = TAYRAKAMIYSA
LEFT = SOLDAN
LEN = UZUNLUK
LOWER = KÜÇÜKHARF
MID = PARÇAAL
NUMBERSTRING = SAYIDİZİ
NUMBERVALUE = SAYIDEĞERİ
PHONETIC = SES
PROPER = YAZIM.DÜZENİ
REPLACE = DEĞİŞTİR
REPT = YİNELE
RIGHT = SAĞDAN
SEARCH = MBUL
SUBSTITUTE = YERİNEKOY
T = M
TEXT = METNEÇEVİR
TEXTJOIN = METİNBİRLEŞTİR
THAIDIGIT = TAYRAKAM
THAINUMSOUND = TAYSAYISES
THAINUMSTRING = TAYSAYIDİZE
THAISTRINGLENGTH = TAYDİZEUZUNLUĞU
TRIM = KIRP
UNICHAR = UNICODEKARAKTERİ
UNICODE = UNICODE
UPPER = BÜYÜKHARF
VALUE = SAYIYAÇEVİR
##
## Metin işlevleri (Web Functions)
##
ENCODEURL = URLKODLA
FILTERXML = XMLFİLTRELE
WEBSERVICE = WEBHİZMETİ
##
## Uyumluluk işlevleri (Compatibility Functions)
##
BETADIST = BETADAĞ
BETAINV = BETATERS
BINOMDIST = BİNOMDAĞ
CEILING = TAVANAYUVARLA
CHIDIST = KİKAREDAĞ
CHIINV = KİKARETERS
CHITEST = KİKARETEST
CONCATENATE = BİRLEŞTİR
CONFIDENCE = GÜVENİRLİK
COVAR = KOVARYANS
CRITBINOM = KRİTİKBİNOM
EXPONDIST = ÜSTELDAĞ
FDIST = FDAĞ
FINV = FTERS
FLOOR = TABANAYUVARLA
FORECAST = TAHMİN
FTEST = FTEST
GAMMADIST = GAMADAĞ
GAMMAINV = GAMATERS
HYPGEOMDIST = HİPERGEOMDAĞ
LOGINV = LOGTERS
LOGNORMDIST = LOGNORMDAĞ
MODE = ENÇOK_OLAN
NEGBINOMDIST = NEGBİNOMDAĞ
NORMDIST = NORMDAĞ
NORMINV = NORMTERS
NORMSDIST = NORMSDAĞ
NORMSINV = NORMSTERS
PERCENTILE = YÜZDEBİRLİK
PERCENTRANK = YÜZDERANK
POISSON = POISSON
QUARTILE = DÖRTTEBİRLİK
RANK = RANK
STDEV = STDSAPMA
STDEVP = STDSAPMAS
TDIST = TDAĞ
TINV = TTERS
TTEST = TTEST
VAR = VAR
VARP = VARS
WEIBULL = WEIBULL
ZTEST = ZTEST
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/tr/config 0000644 00000000512 15242753725 0020615 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Türkçe (Turkish)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #BOŞ!
DIV0 = #SAYI/0!
VALUE = #DEĞER!
REF = #BAŞV!
NAME = #AD?
NUM = #SAYI!
NA = #YOK
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/functions 0000644 00000024714 15242753725 0021354 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Español (Spanish)
##
############################################################
##
## Funciones de cubo (Cube Functions)
##
CUBEKPIMEMBER = MIEMBROKPICUBO
CUBEMEMBER = MIEMBROCUBO
CUBEMEMBERPROPERTY = PROPIEDADMIEMBROCUBO
CUBERANKEDMEMBER = MIEMBRORANGOCUBO
CUBESET = CONJUNTOCUBO
CUBESETCOUNT = RECUENTOCONJUNTOCUBO
CUBEVALUE = VALORCUBO
##
## Funciones de base de datos (Database Functions)
##
DAVERAGE = BDPROMEDIO
DCOUNT = BDCONTAR
DCOUNTA = BDCONTARA
DGET = BDEXTRAER
DMAX = BDMAX
DMIN = BDMIN
DPRODUCT = BDPRODUCTO
DSTDEV = BDDESVEST
DSTDEVP = BDDESVESTP
DSUM = BDSUMA
DVAR = BDVAR
DVARP = BDVARP
##
## Funciones de fecha y hora (Date & Time Functions)
##
DATE = FECHA
DATEDIF = SIFECHA
DATESTRING = CADENA.FECHA
DATEVALUE = FECHANUMERO
DAY = DIA
DAYS = DIAS
DAYS360 = DIAS360
EDATE = FECHA.MES
EOMONTH = FIN.MES
HOUR = HORA
ISOWEEKNUM = ISO.NUM.DE.SEMANA
MINUTE = MINUTO
MONTH = MES
NETWORKDAYS = DIAS.LAB
NETWORKDAYS.INTL = DIAS.LAB.INTL
NOW = AHORA
SECOND = SEGUNDO
THAIDAYOFWEEK = DIASEMTAI
THAIMONTHOFYEAR = MESAÑOTAI
THAIYEAR = AÑOTAI
TIME = NSHORA
TIMEVALUE = HORANUMERO
TODAY = HOY
WEEKDAY = DIASEM
WEEKNUM = NUM.DE.SEMANA
WORKDAY = DIA.LAB
WORKDAY.INTL = DIA.LAB.INTL
YEAR = AÑO
YEARFRAC = FRAC.AÑO
##
## Funciones de ingeniería (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BIN.A.DEC
BIN2HEX = BIN.A.HEX
BIN2OCT = BIN.A.OCT
BITAND = BIT.Y
BITLSHIFT = BIT.DESPLIZQDA
BITOR = BIT.O
BITRSHIFT = BIT.DESPLDCHA
BITXOR = BIT.XO
COMPLEX = COMPLEJO
CONVERT = CONVERTIR
DEC2BIN = DEC.A.BIN
DEC2HEX = DEC.A.HEX
DEC2OCT = DEC.A.OCT
DELTA = DELTA
ERF = FUN.ERROR
ERF.PRECISE = FUN.ERROR.EXACTO
ERFC = FUN.ERROR.COMPL
ERFC.PRECISE = FUN.ERROR.COMPL.EXACTO
GESTEP = MAYOR.O.IGUAL
HEX2BIN = HEX.A.BIN
HEX2DEC = HEX.A.DEC
HEX2OCT = HEX.A.OCT
IMABS = IM.ABS
IMAGINARY = IMAGINARIO
IMARGUMENT = IM.ANGULO
IMCONJUGATE = IM.CONJUGADA
IMCOS = IM.COS
IMCOSH = IM.COSH
IMCOT = IM.COT
IMCSC = IM.CSC
IMCSCH = IM.CSCH
IMDIV = IM.DIV
IMEXP = IM.EXP
IMLN = IM.LN
IMLOG10 = IM.LOG10
IMLOG2 = IM.LOG2
IMPOWER = IM.POT
IMPRODUCT = IM.PRODUCT
IMREAL = IM.REAL
IMSEC = IM.SEC
IMSECH = IM.SECH
IMSIN = IM.SENO
IMSINH = IM.SENOH
IMSQRT = IM.RAIZ2
IMSUB = IM.SUSTR
IMSUM = IM.SUM
IMTAN = IM.TAN
OCT2BIN = OCT.A.BIN
OCT2DEC = OCT.A.DEC
OCT2HEX = OCT.A.HEX
##
## Funciones financieras (Financial Functions)
##
ACCRINT = INT.ACUM
ACCRINTM = INT.ACUM.V
AMORDEGRC = AMORTIZ.PROGRE
AMORLINC = AMORTIZ.LIN
COUPDAYBS = CUPON.DIAS.L1
COUPDAYS = CUPON.DIAS
COUPDAYSNC = CUPON.DIAS.L2
COUPNCD = CUPON.FECHA.L2
COUPNUM = CUPON.NUM
COUPPCD = CUPON.FECHA.L1
CUMIPMT = PAGO.INT.ENTRE
CUMPRINC = PAGO.PRINC.ENTRE
DB = DB
DDB = DDB
DISC = TASA.DESC
DOLLARDE = MONEDA.DEC
DOLLARFR = MONEDA.FRAC
DURATION = DURACION
EFFECT = INT.EFECTIVO
FV = VF
FVSCHEDULE = VF.PLAN
INTRATE = TASA.INT
IPMT = PAGOINT
IRR = TIR
ISPMT = INT.PAGO.DIR
MDURATION = DURACION.MODIF
MIRR = TIRM
NOMINAL = TASA.NOMINAL
NPER = NPER
NPV = VNA
ODDFPRICE = PRECIO.PER.IRREGULAR.1
ODDFYIELD = RENDTO.PER.IRREGULAR.1
ODDLPRICE = PRECIO.PER.IRREGULAR.2
ODDLYIELD = RENDTO.PER.IRREGULAR.2
PDURATION = P.DURACION
PMT = PAGO
PPMT = PAGOPRIN
PRICE = PRECIO
PRICEDISC = PRECIO.DESCUENTO
PRICEMAT = PRECIO.VENCIMIENTO
PV = VA
RATE = TASA
RECEIVED = CANTIDAD.RECIBIDA
RRI = RRI
SLN = SLN
SYD = SYD
TBILLEQ = LETRA.DE.TEST.EQV.A.BONO
TBILLPRICE = LETRA.DE.TES.PRECIO
TBILLYIELD = LETRA.DE.TES.RENDTO
VDB = DVS
XIRR = TIR.NO.PER
XNPV = VNA.NO.PER
YIELD = RENDTO
YIELDDISC = RENDTO.DESC
YIELDMAT = RENDTO.VENCTO
##
## Funciones de información (Information Functions)
##
CELL = CELDA
ERROR.TYPE = TIPO.DE.ERROR
INFO = INFO
ISBLANK = ESBLANCO
ISERR = ESERR
ISERROR = ESERROR
ISEVEN = ES.PAR
ISFORMULA = ESFORMULA
ISLOGICAL = ESLOGICO
ISNA = ESNOD
ISNONTEXT = ESNOTEXTO
ISNUMBER = ESNUMERO
ISODD = ES.IMPAR
ISREF = ESREF
ISTEXT = ESTEXTO
N = N
NA = NOD
SHEET = HOJA
SHEETS = HOJAS
TYPE = TIPO
##
## Funciones lógicas (Logical Functions)
##
AND = Y
FALSE = FALSO
IF = SI
IFERROR = SI.ERROR
IFNA = SI.ND
IFS = SI.CONJUNTO
NOT = NO
OR = O
SWITCH = CAMBIAR
TRUE = VERDADERO
XOR = XO
##
## Funciones de búsqueda y referencia (Lookup & Reference Functions)
##
ADDRESS = DIRECCION
AREAS = AREAS
CHOOSE = ELEGIR
COLUMN = COLUMNA
COLUMNS = COLUMNAS
FORMULATEXT = FORMULATEXTO
GETPIVOTDATA = IMPORTARDATOSDINAMICOS
HLOOKUP = BUSCARH
HYPERLINK = HIPERVINCULO
INDEX = INDICE
INDIRECT = INDIRECTO
LOOKUP = BUSCAR
MATCH = COINCIDIR
OFFSET = DESREF
ROW = FILA
ROWS = FILAS
RTD = RDTR
TRANSPOSE = TRANSPONER
VLOOKUP = BUSCARV
*RC = FC
##
## Funciones matemáticas y trigonométricas (Math & Trig Functions)
##
ABS = ABS
ACOS = ACOS
ACOSH = ACOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = AGREGAR
ARABIC = NUMERO.ARABE
ASIN = ASENO
ASINH = ASENOH
ATAN = ATAN
ATAN2 = ATAN2
ATANH = ATANH
BASE = BASE
CEILING.MATH = MULTIPLO.SUPERIOR.MAT
CEILING.PRECISE = MULTIPLO.SUPERIOR.EXACTO
COMBIN = COMBINAT
COMBINA = COMBINA
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = CONV.DECIMAL
DEGREES = GRADOS
ECMA.CEILING = MULTIPLO.SUPERIOR.ECMA
EVEN = REDONDEA.PAR
EXP = EXP
FACT = FACT
FACTDOUBLE = FACT.DOBLE
FLOOR.MATH = MULTIPLO.INFERIOR.MAT
FLOOR.PRECISE = MULTIPLO.INFERIOR.EXACTO
GCD = M.C.D
INT = ENTERO
ISO.CEILING = MULTIPLO.SUPERIOR.ISO
LCM = M.C.M
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = MDETERM
MINVERSE = MINVERSA
MMULT = MMULT
MOD = RESIDUO
MROUND = REDOND.MULT
MULTINOMIAL = MULTINOMIAL
MUNIT = M.UNIDAD
ODD = REDONDEA.IMPAR
PI = PI
POWER = POTENCIA
PRODUCT = PRODUCTO
QUOTIENT = COCIENTE
RADIANS = RADIANES
RAND = ALEATORIO
RANDBETWEEN = ALEATORIO.ENTRE
ROMAN = NUMERO.ROMANO
ROUND = REDONDEAR
ROUNDBAHTDOWN = REDONDEAR.BAHT.MAS
ROUNDBAHTUP = REDONDEAR.BAHT.MENOS
ROUNDDOWN = REDONDEAR.MENOS
ROUNDUP = REDONDEAR.MAS
SEC = SEC
SECH = SECH
SERIESSUM = SUMA.SERIES
SIGN = SIGNO
SIN = SENO
SINH = SENOH
SQRT = RAIZ
SQRTPI = RAIZ2PI
SUBTOTAL = SUBTOTALES
SUM = SUMA
SUMIF = SUMAR.SI
SUMIFS = SUMAR.SI.CONJUNTO
SUMPRODUCT = SUMAPRODUCTO
SUMSQ = SUMA.CUADRADOS
SUMX2MY2 = SUMAX2MENOSY2
SUMX2PY2 = SUMAX2MASY2
SUMXMY2 = SUMAXMENOSY2
TAN = TAN
TANH = TANH
TRUNC = TRUNCAR
##
## Funciones estadísticas (Statistical Functions)
##
AVEDEV = DESVPROM
AVERAGE = PROMEDIO
AVERAGEA = PROMEDIOA
AVERAGEIF = PROMEDIO.SI
AVERAGEIFS = PROMEDIO.SI.CONJUNTO
BETA.DIST = DISTR.BETA.N
BETA.INV = INV.BETA.N
BINOM.DIST = DISTR.BINOM.N
BINOM.DIST.RANGE = DISTR.BINOM.SERIE
BINOM.INV = INV.BINOM
CHISQ.DIST = DISTR.CHICUAD
CHISQ.DIST.RT = DISTR.CHICUAD.CD
CHISQ.INV = INV.CHICUAD
CHISQ.INV.RT = INV.CHICUAD.CD
CHISQ.TEST = PRUEBA.CHICUAD
CONFIDENCE.NORM = INTERVALO.CONFIANZA.NORM
CONFIDENCE.T = INTERVALO.CONFIANZA.T
CORREL = COEF.DE.CORREL
COUNT = CONTAR
COUNTA = CONTARA
COUNTBLANK = CONTAR.BLANCO
COUNTIF = CONTAR.SI
COUNTIFS = CONTAR.SI.CONJUNTO
COVARIANCE.P = COVARIANCE.P
COVARIANCE.S = COVARIANZA.M
DEVSQ = DESVIA2
EXPON.DIST = DISTR.EXP.N
F.DIST = DISTR.F.N
F.DIST.RT = DISTR.F.CD
F.INV = INV.F
F.INV.RT = INV.F.CD
F.TEST = PRUEBA.F.N
FISHER = FISHER
FISHERINV = PRUEBA.FISHER.INV
FORECAST.ETS = PRONOSTICO.ETS
FORECAST.ETS.CONFINT = PRONOSTICO.ETS.CONFINT
FORECAST.ETS.SEASONALITY = PRONOSTICO.ETS.ESTACIONALIDAD
FORECAST.ETS.STAT = PRONOSTICO.ETS.STAT
FORECAST.LINEAR = PRONOSTICO.LINEAL
FREQUENCY = FRECUENCIA
GAMMA = GAMMA
GAMMA.DIST = DISTR.GAMMA.N
GAMMA.INV = INV.GAMMA
GAMMALN = GAMMA.LN
GAMMALN.PRECISE = GAMMA.LN.EXACTO
GAUSS = GAUSS
GEOMEAN = MEDIA.GEOM
GROWTH = CRECIMIENTO
HARMEAN = MEDIA.ARMO
HYPGEOM.DIST = DISTR.HIPERGEOM.N
INTERCEPT = INTERSECCION.EJE
KURT = CURTOSIS
LARGE = K.ESIMO.MAYOR
LINEST = ESTIMACION.LINEAL
LOGEST = ESTIMACION.LOGARITMICA
LOGNORM.DIST = DISTR.LOGNORM
LOGNORM.INV = INV.LOGNORM
MAX = MAX
MAXA = MAXA
MAXIFS = MAX.SI.CONJUNTO
MEDIAN = MEDIANA
MIN = MIN
MINA = MINA
MINIFS = MIN.SI.CONJUNTO
MODE.MULT = MODA.VARIOS
MODE.SNGL = MODA.UNO
NEGBINOM.DIST = NEGBINOM.DIST
NORM.DIST = DISTR.NORM.N
NORM.INV = INV.NORM
NORM.S.DIST = DISTR.NORM.ESTAND.N
NORM.S.INV = INV.NORM.ESTAND
PEARSON = PEARSON
PERCENTILE.EXC = PERCENTIL.EXC
PERCENTILE.INC = PERCENTIL.INC
PERCENTRANK.EXC = RANGO.PERCENTIL.EXC
PERCENTRANK.INC = RANGO.PERCENTIL.INC
PERMUT = PERMUTACIONES
PERMUTATIONA = PERMUTACIONES.A
PHI = FI
POISSON.DIST = POISSON.DIST
PROB = PROBABILIDAD
QUARTILE.EXC = CUARTIL.EXC
QUARTILE.INC = CUARTIL.INC
RANK.AVG = JERARQUIA.MEDIA
RANK.EQ = JERARQUIA.EQV
RSQ = COEFICIENTE.R2
SKEW = COEFICIENTE.ASIMETRIA
SKEW.P = COEFICIENTE.ASIMETRIA.P
SLOPE = PENDIENTE
SMALL = K.ESIMO.MENOR
STANDARDIZE = NORMALIZACION
STDEV.P = DESVEST.P
STDEV.S = DESVEST.M
STDEVA = DESVESTA
STDEVPA = DESVESTPA
STEYX = ERROR.TIPICO.XY
T.DIST = DISTR.T.N
T.DIST.2T = DISTR.T.2C
T.DIST.RT = DISTR.T.CD
T.INV = INV.T
T.INV.2T = INV.T.2C
T.TEST = PRUEBA.T.N
TREND = TENDENCIA
TRIMMEAN = MEDIA.ACOTADA
VAR.P = VAR.P
VAR.S = VAR.S
VARA = VARA
VARPA = VARPA
WEIBULL.DIST = DISTR.WEIBULL
Z.TEST = PRUEBA.Z.N
##
## Funciones de texto (Text Functions)
##
BAHTTEXT = TEXTOBAHT
CHAR = CARACTER
CLEAN = LIMPIAR
CODE = CODIGO
CONCAT = CONCAT
DOLLAR = MONEDA
EXACT = IGUAL
FIND = ENCONTRAR
FIXED = DECIMAL
ISTHAIDIGIT = ESDIGITOTAI
LEFT = IZQUIERDA
LEN = LARGO
LOWER = MINUSC
MID = EXTRAE
NUMBERSTRING = CADENA.NUMERO
NUMBERVALUE = VALOR.NUMERO
PHONETIC = FONETICO
PROPER = NOMPROPIO
REPLACE = REEMPLAZAR
REPT = REPETIR
RIGHT = DERECHA
SEARCH = HALLAR
SUBSTITUTE = SUSTITUIR
T = T
TEXT = TEXTO
TEXTJOIN = UNIRCADENAS
THAIDIGIT = DIGITOTAI
THAINUMSOUND = SONNUMTAI
THAINUMSTRING = CADENANUMTAI
THAISTRINGLENGTH = LONGCADENATAI
TRIM = ESPACIOS
UNICHAR = UNICAR
UNICODE = UNICODE
UPPER = MAYUSC
VALUE = VALOR
##
## Funciones web (Web Functions)
##
ENCODEURL = URLCODIF
FILTERXML = XMLFILTRO
WEBSERVICE = SERVICIOWEB
##
## Funciones de compatibilidad (Compatibility Functions)
##
BETADIST = DISTR.BETA
BETAINV = DISTR.BETA.INV
BINOMDIST = DISTR.BINOM
CEILING = MULTIPLO.SUPERIOR
CHIDIST = DISTR.CHI
CHIINV = PRUEBA.CHI.INV
CHITEST = PRUEBA.CHI
CONCATENATE = CONCATENAR
CONFIDENCE = INTERVALO.CONFIANZA
COVAR = COVAR
CRITBINOM = BINOM.CRIT
EXPONDIST = DISTR.EXP
FDIST = DISTR.F
FINV = DISTR.F.INV
FLOOR = MULTIPLO.INFERIOR
FORECAST = PRONOSTICO
FTEST = PRUEBA.F
GAMMADIST = DISTR.GAMMA
GAMMAINV = DISTR.GAMMA.INV
HYPGEOMDIST = DISTR.HIPERGEOM
LOGINV = DISTR.LOG.INV
LOGNORMDIST = DISTR.LOG.NORM
MODE = MODA
NEGBINOMDIST = NEGBINOMDIST
NORMDIST = DISTR.NORM
NORMINV = DISTR.NORM.INV
NORMSDIST = DISTR.NORM.ESTAND
NORMSINV = DISTR.NORM.ESTAND.INV
PERCENTILE = PERCENTIL
PERCENTRANK = RANGO.PERCENTIL
POISSON = POISSON
QUARTILE = CUARTIL
RANK = JERARQUIA
STDEV = DESVEST
STDEVP = DESVESTP
TDIST = DISTR.T
TINV = DISTR.T.INV
TTEST = PRUEBA.T
VAR = VAR
VARP = VARP
WEIBULL = DIST.WEIBULL
ZTEST = PRUEBA.Z
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/es/config 0000644 00000000516 15242753725 0020603 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Español (Spanish)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #¡NULO!
DIV0 = #¡DIV/0!
VALUE = #¡VALOR!
REF = #¡REF!
NAME = #¿NOMBRE?
NUM = #¡NUM!
NA
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/functions 0000644 00000022455 15242753725 0021352 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Ceština (Czech)
##
############################################################
##
## Funkce pro práci s datovými krychlemi (Cube Functions)
##
CUBEKPIMEMBER = CUBEKPIMEMBER
CUBEMEMBER = CUBEMEMBER
CUBEMEMBERPROPERTY = CUBEMEMBERPROPERTY
CUBERANKEDMEMBER = CUBERANKEDMEMBER
CUBESET = CUBESET
CUBESETCOUNT = CUBESETCOUNT
CUBEVALUE = CUBEVALUE
##
## Funkce databáze (Database Functions)
##
DAVERAGE = DPRŮMĚR
DCOUNT = DPOČET
DCOUNTA = DPOČET2
DGET = DZÍSKAT
DMAX = DMAX
DMIN = DMIN
DPRODUCT = DSOUČIN
DSTDEV = DSMODCH.VÝBĚR
DSTDEVP = DSMODCH
DSUM = DSUMA
DVAR = DVAR.VÝBĚR
DVARP = DVAR
##
## Funkce data a času (Date & Time Functions)
##
DATE = DATUM
DATEVALUE = DATUMHODN
DAY = DEN
DAYS = DAYS
DAYS360 = ROK360
EDATE = EDATE
EOMONTH = EOMONTH
HOUR = HODINA
ISOWEEKNUM = ISOWEEKNUM
MINUTE = MINUTA
MONTH = MĚSÍC
NETWORKDAYS = NETWORKDAYS
NETWORKDAYS.INTL = NETWORKDAYS.INTL
NOW = NYNÍ
SECOND = SEKUNDA
TIME = ČAS
TIMEVALUE = ČASHODN
TODAY = DNES
WEEKDAY = DENTÝDNE
WEEKNUM = WEEKNUM
WORKDAY = WORKDAY
WORKDAY.INTL = WORKDAY.INTL
YEAR = ROK
YEARFRAC = YEARFRAC
##
## Inženýrské funkce (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BIN2DEC
BIN2HEX = BIN2HEX
BIN2OCT = BIN2OCT
BITAND = BITAND
BITLSHIFT = BITLSHIFT
BITOR = BITOR
BITRSHIFT = BITRSHIFT
BITXOR = BITXOR
COMPLEX = COMPLEX
CONVERT = CONVERT
DEC2BIN = DEC2BIN
DEC2HEX = DEC2HEX
DEC2OCT = DEC2OCT
DELTA = DELTA
ERF = ERF
ERF.PRECISE = ERF.PRECISE
ERFC = ERFC
ERFC.PRECISE = ERFC.PRECISE
GESTEP = GESTEP
HEX2BIN = HEX2BIN
HEX2DEC = HEX2DEC
HEX2OCT = HEX2OCT
IMABS = IMABS
IMAGINARY = IMAGINARY
IMARGUMENT = IMARGUMENT
IMCONJUGATE = IMCONJUGATE
IMCOS = IMCOS
IMCOSH = IMCOSH
IMCOT = IMCOT
IMCSC = IMCSC
IMCSCH = IMCSCH
IMDIV = IMDIV
IMEXP = IMEXP
IMLN = IMLN
IMLOG10 = IMLOG10
IMLOG2 = IMLOG2
IMPOWER = IMPOWER
IMPRODUCT = IMPRODUCT
IMREAL = IMREAL
IMSEC = IMSEC
IMSECH = IMSECH
IMSIN = IMSIN
IMSINH = IMSINH
IMSQRT = IMSQRT
IMSUB = IMSUB
IMSUM = IMSUM
IMTAN = IMTAN
OCT2BIN = OCT2BIN
OCT2DEC = OCT2DEC
OCT2HEX = OCT2HEX
##
## Finanční funkce (Financial Functions)
##
ACCRINT = ACCRINT
ACCRINTM = ACCRINTM
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = COUPDAYBS
COUPDAYS = COUPDAYS
COUPDAYSNC = COUPDAYSNC
COUPNCD = COUPNCD
COUPNUM = COUPNUM
COUPPCD = COUPPCD
CUMIPMT = CUMIPMT
CUMPRINC = CUMPRINC
DB = ODPIS.ZRYCH
DDB = ODPIS.ZRYCH2
DISC = DISC
DOLLARDE = DOLLARDE
DOLLARFR = DOLLARFR
DURATION = DURATION
EFFECT = EFFECT
FV = BUDHODNOTA
FVSCHEDULE = FVSCHEDULE
INTRATE = INTRATE
IPMT = PLATBA.ÚROK
IRR = MÍRA.VÝNOSNOSTI
ISPMT = ISPMT
MDURATION = MDURATION
MIRR = MOD.MÍRA.VÝNOSNOSTI
NOMINAL = NOMINAL
NPER = POČET.OBDOBÍ
NPV = ČISTÁ.SOUČHODNOTA
ODDFPRICE = ODDFPRICE
ODDFYIELD = ODDFYIELD
ODDLPRICE = ODDLPRICE
ODDLYIELD = ODDLYIELD
PDURATION = PDURATION
PMT = PLATBA
PPMT = PLATBA.ZÁKLAD
PRICE = PRICE
PRICEDISC = PRICEDISC
PRICEMAT = PRICEMAT
PV = SOUČHODNOTA
RATE = ÚROKOVÁ.MÍRA
RECEIVED = RECEIVED
RRI = RRI
SLN = ODPIS.LIN
SYD = ODPIS.NELIN
TBILLEQ = TBILLEQ
TBILLPRICE = TBILLPRICE
TBILLYIELD = TBILLYIELD
VDB = ODPIS.ZA.INT
XIRR = XIRR
XNPV = XNPV
YIELD = YIELD
YIELDDISC = YIELDDISC
YIELDMAT = YIELDMAT
##
## Informační funkce (Information Functions)
##
CELL = POLÍČKO
ERROR.TYPE = CHYBA.TYP
INFO = O.PROSTŘEDÍ
ISBLANK = JE.PRÁZDNÉ
ISERR = JE.CHYBA
ISERROR = JE.CHYBHODN
ISEVEN = ISEVEN
ISFORMULA = ISFORMULA
ISLOGICAL = JE.LOGHODN
ISNA = JE.NEDEF
ISNONTEXT = JE.NETEXT
ISNUMBER = JE.ČISLO
ISODD = ISODD
ISREF = JE.ODKAZ
ISTEXT = JE.TEXT
N = N
NA = NEDEF
SHEET = SHEET
SHEETS = SHEETS
TYPE = TYP
##
## Logické funkce (Logical Functions)
##
AND = A
FALSE = NEPRAVDA
IF = KDYŽ
IFERROR = IFERROR
IFNA = IFNA
IFS = IFS
NOT = NE
OR = NEBO
SWITCH = SWITCH
TRUE = PRAVDA
XOR = XOR
##
## Vyhledávací funkce a funkce pro odkazy (Lookup & Reference Functions)
##
ADDRESS = ODKAZ
AREAS = POČET.BLOKŮ
CHOOSE = ZVOLIT
COLUMN = SLOUPEC
COLUMNS = SLOUPCE
FORMULATEXT = FORMULATEXT
GETPIVOTDATA = ZÍSKATKONTDATA
HLOOKUP = VVYHLEDAT
HYPERLINK = HYPERTEXTOVÝ.ODKAZ
INDEX = INDEX
INDIRECT = NEPŘÍMÝ.ODKAZ
LOOKUP = VYHLEDAT
MATCH = POZVYHLEDAT
OFFSET = POSUN
ROW = ŘÁDEK
ROWS = ŘÁDKY
RTD = RTD
TRANSPOSE = TRANSPOZICE
VLOOKUP = SVYHLEDAT
##
## Matematické a trigonometrické funkce (Math & Trig Functions)
##
ABS = ABS
ACOS = ARCCOS
ACOSH = ARCCOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = AGGREGATE
ARABIC = ARABIC
ASIN = ARCSIN
ASINH = ARCSINH
ATAN = ARCTG
ATAN2 = ARCTG2
ATANH = ARCTGH
BASE = BASE
CEILING.MATH = CEILING.MATH
COMBIN = KOMBINACE
COMBINA = COMBINA
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = DECIMAL
DEGREES = DEGREES
EVEN = ZAOKROUHLIT.NA.SUDÉ
EXP = EXP
FACT = FAKTORIÁL
FACTDOUBLE = FACTDOUBLE
FLOOR.MATH = FLOOR.MATH
GCD = GCD
INT = CELÁ.ČÁST
LCM = LCM
LN = LN
LOG = LOGZ
LOG10 = LOG
MDETERM = DETERMINANT
MINVERSE = INVERZE
MMULT = SOUČIN.MATIC
MOD = MOD
MROUND = MROUND
MULTINOMIAL = MULTINOMIAL
MUNIT = MUNIT
ODD = ZAOKROUHLIT.NA.LICHÉ
PI = PI
POWER = POWER
PRODUCT = SOUČIN
QUOTIENT = QUOTIENT
RADIANS = RADIANS
RAND = NÁHČÍSLO
RANDBETWEEN = RANDBETWEEN
ROMAN = ROMAN
ROUND = ZAOKROUHLIT
ROUNDDOWN = ROUNDDOWN
ROUNDUP = ROUNDUP
SEC = SEC
SECH = SECH
SERIESSUM = SERIESSUM
SIGN = SIGN
SIN = SIN
SINH = SINH
SQRT = ODMOCNINA
SQRTPI = SQRTPI
SUBTOTAL = SUBTOTAL
SUM = SUMA
SUMIF = SUMIF
SUMIFS = SUMIFS
SUMPRODUCT = SOUČIN.SKALÁRNÍ
SUMSQ = SUMA.ČTVERCŮ
SUMX2MY2 = SUMX2MY2
SUMX2PY2 = SUMX2PY2
SUMXMY2 = SUMXMY2
TAN = TG
TANH = TGH
TRUNC = USEKNOUT
##
## Statistické funkce (Statistical Functions)
##
AVEDEV = PRŮMODCHYLKA
AVERAGE = PRŮMĚR
AVERAGEA = AVERAGEA
AVERAGEIF = AVERAGEIF
AVERAGEIFS = AVERAGEIFS
BETA.DIST = BETA.DIST
BETA.INV = BETA.INV
BINOM.DIST = BINOM.DIST
BINOM.DIST.RANGE = BINOM.DIST.RANGE
BINOM.INV = BINOM.INV
CHISQ.DIST = CHISQ.DIST
CHISQ.DIST.RT = CHISQ.DIST.RT
CHISQ.INV = CHISQ.INV
CHISQ.INV.RT = CHISQ.INV.RT
CHISQ.TEST = CHISQ.TEST
CONFIDENCE.NORM = CONFIDENCE.NORM
CONFIDENCE.T = CONFIDENCE.T
CORREL = CORREL
COUNT = POČET
COUNTA = POČET2
COUNTBLANK = COUNTBLANK
COUNTIF = COUNTIF
COUNTIFS = COUNTIFS
COVARIANCE.P = COVARIANCE.P
COVARIANCE.S = COVARIANCE.S
DEVSQ = DEVSQ
EXPON.DIST = EXPON.DIST
F.DIST = F.DIST
F.DIST.RT = F.DIST.RT
F.INV = F.INV
F.INV.RT = F.INV.RT
F.TEST = F.TEST
FISHER = FISHER
FISHERINV = FISHERINV
FORECAST.ETS = FORECAST.ETS
FORECAST.ETS.CONFINT = FORECAST.ETS.CONFINT
FORECAST.ETS.SEASONALITY = FORECAST.ETS.SEASONALITY
FORECAST.ETS.STAT = FORECAST.ETS.STAT
FORECAST.LINEAR = FORECAST.LINEAR
FREQUENCY = ČETNOSTI
GAMMA = GAMMA
GAMMA.DIST = GAMMA.DIST
GAMMA.INV = GAMMA.INV
GAMMALN = GAMMALN
GAMMALN.PRECISE = GAMMALN.PRECISE
GAUSS = GAUSS
GEOMEAN = GEOMEAN
GROWTH = LOGLINTREND
HARMEAN = HARMEAN
HYPGEOM.DIST = HYPGEOM.DIST
INTERCEPT = INTERCEPT
KURT = KURT
LARGE = LARGE
LINEST = LINREGRESE
LOGEST = LOGLINREGRESE
LOGNORM.DIST = LOGNORM.DIST
LOGNORM.INV = LOGNORM.INV
MAX = MAX
MAXA = MAXA
MAXIFS = MAXIFS
MEDIAN = MEDIAN
MIN = MIN
MINA = MINA
MINIFS = MINIFS
MODE.MULT = MODE.MULT
MODE.SNGL = MODE.SNGL
NEGBINOM.DIST = NEGBINOM.DIST
NORM.DIST = NORM.DIST
NORM.INV = NORM.INV
NORM.S.DIST = NORM.S.DIST
NORM.S.INV = NORM.S.INV
PEARSON = PEARSON
PERCENTILE.EXC = PERCENTIL.EXC
PERCENTILE.INC = PERCENTIL.INC
PERCENTRANK.EXC = PERCENTRANK.EXC
PERCENTRANK.INC = PERCENTRANK.INC
PERMUT = PERMUTACE
PERMUTATIONA = PERMUTATIONA
PHI = PHI
POISSON.DIST = POISSON.DIST
PROB = PROB
QUARTILE.EXC = QUARTIL.EXC
QUARTILE.INC = QUARTIL.INC
RANK.AVG = RANK.AVG
RANK.EQ = RANK.EQ
RSQ = RKQ
SKEW = SKEW
SKEW.P = SKEW.P
SLOPE = SLOPE
SMALL = SMALL
STANDARDIZE = STANDARDIZE
STDEV.P = SMODCH.P
STDEV.S = SMODCH.VÝBĚR.S
STDEVA = STDEVA
STDEVPA = STDEVPA
STEYX = STEYX
T.DIST = T.DIST
T.DIST.2T = T.DIST.2T
T.DIST.RT = T.DIST.RT
T.INV = T.INV
T.INV.2T = T.INV.2T
T.TEST = T.TEST
TREND = LINTREND
TRIMMEAN = TRIMMEAN
VAR.P = VAR.P
VAR.S = VAR.S
VARA = VARA
VARPA = VARPA
WEIBULL.DIST = WEIBULL.DIST
Z.TEST = Z.TEST
##
## Textové funkce (Text Functions)
##
BAHTTEXT = BAHTTEXT
CHAR = ZNAK
CLEAN = VYČISTIT
CODE = KÓD
CONCAT = CONCAT
DOLLAR = KČ
EXACT = STEJNÉ
FIND = NAJÍT
FIXED = ZAOKROUHLIT.NA.TEXT
LEFT = ZLEVA
LEN = DÉLKA
LOWER = MALÁ
MID = ČÁST
NUMBERVALUE = NUMBERVALUE
PHONETIC = ZVUKOVÉ
PROPER = VELKÁ2
REPLACE = NAHRADIT
REPT = OPAKOVAT
RIGHT = ZPRAVA
SEARCH = HLEDAT
SUBSTITUTE = DOSADIT
T = T
TEXT = HODNOTA.NA.TEXT
TEXTJOIN = TEXTJOIN
TRIM = PROČISTIT
UNICHAR = UNICHAR
UNICODE = UNICODE
UPPER = VELKÁ
VALUE = HODNOTA
##
## Webové funkce (Web Functions)
##
ENCODEURL = ENCODEURL
FILTERXML = FILTERXML
WEBSERVICE = WEBSERVICE
##
## Funkce pro kompatibilitu (Compatibility Functions)
##
BETADIST = BETADIST
BETAINV = BETAINV
BINOMDIST = BINOMDIST
CEILING = ZAOKR.NAHORU
CHIDIST = CHIDIST
CHIINV = CHIINV
CHITEST = CHITEST
CONCATENATE = CONCATENATE
CONFIDENCE = CONFIDENCE
COVAR = COVAR
CRITBINOM = CRITBINOM
EXPONDIST = EXPONDIST
FDIST = FDIST
FINV = FINV
FLOOR = ZAOKR.DOLŮ
FORECAST = FORECAST
FTEST = FTEST
GAMMADIST = GAMMADIST
GAMMAINV = GAMMAINV
HYPGEOMDIST = HYPGEOMDIST
LOGINV = LOGINV
LOGNORMDIST = LOGNORMDIST
MODE = MODE
NEGBINOMDIST = NEGBINOMDIST
NORMDIST = NORMDIST
NORMINV = NORMINV
NORMSDIST = NORMSDIST
NORMSINV = NORMSINV
PERCENTILE = PERCENTIL
PERCENTRANK = PERCENTRANK
POISSON = POISSON
QUARTILE = QUARTIL
RANK = RANK
STDEV = SMODCH.VÝBĚR
STDEVP = SMODCH
TDIST = TDIST
TINV = TINV
TTEST = TTEST
VAR = VAR.VÝBĚR
VARP = VAR
WEIBULL = WEIBULL
ZTEST = ZTEST
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/cs/config 0000644 00000000535 15242753725 0020602 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Ceština (Czech)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL
DIV0 = #DĚLENÍ_NULOU!
VALUE = #HODNOTA!
REF = #ODKAZ!
NAME = #NÁZEV?
NUM = #ČÍSLO!
NA = #NENÍ_K_DISPOZICI
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/en/uk/config 0000644 00000000107 15242753725 0021211 0 ustar 00 ##
## PhpSpreadsheet
##
##
## (For future use)
##
currencySymbol = £
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/functions 0000644 00000025073 15242753725 0021360 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Magyar (Hungarian)
##
############################################################
##
## Kockafüggvények (Cube Functions)
##
CUBEKPIMEMBER = KOCKA.FŐTELJMUT
CUBEMEMBER = KOCKA.TAG
CUBEMEMBERPROPERTY = KOCKA.TAG.TUL
CUBERANKEDMEMBER = KOCKA.HALM.ELEM
CUBESET = KOCKA.HALM
CUBESETCOUNT = KOCKA.HALM.DB
CUBEVALUE = KOCKA.ÉRTÉK
##
## Adatbázis-kezelő függvények (Database Functions)
##
DAVERAGE = AB.ÁTLAG
DCOUNT = AB.DARAB
DCOUNTA = AB.DARAB2
DGET = AB.MEZŐ
DMAX = AB.MAX
DMIN = AB.MIN
DPRODUCT = AB.SZORZAT
DSTDEV = AB.SZÓRÁS
DSTDEVP = AB.SZÓRÁS2
DSUM = AB.SZUM
DVAR = AB.VAR
DVARP = AB.VAR2
##
## Dátumfüggvények (Date & Time Functions)
##
DATE = DÁTUM
DATEDIF = DÁTUMTÓLIG
DATESTRING = DÁTUMSZÖVEG
DATEVALUE = DÁTUMÉRTÉK
DAY = NAP
DAYS = NAPOK
DAYS360 = NAP360
EDATE = KALK.DÁTUM
EOMONTH = HÓNAP.UTOLSÓ.NAP
HOUR = ÓRA
ISOWEEKNUM = ISO.HÉT.SZÁMA
MINUTE = PERCEK
MONTH = HÓNAP
NETWORKDAYS = ÖSSZ.MUNKANAP
NETWORKDAYS.INTL = ÖSSZ.MUNKANAP.INTL
NOW = MOST
SECOND = MPERC
THAIDAYOFWEEK = THAIHÉTNAPJA
THAIMONTHOFYEAR = THAIHÓNAP
THAIYEAR = THAIÉV
TIME = IDŐ
TIMEVALUE = IDŐÉRTÉK
TODAY = MA
WEEKDAY = HÉT.NAPJA
WEEKNUM = HÉT.SZÁMA
WORKDAY = KALK.MUNKANAP
WORKDAY.INTL = KALK.MUNKANAP.INTL
YEAR = ÉV
YEARFRAC = TÖRTÉV
##
## Mérnöki függvények (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BIN.DEC
BIN2HEX = BIN.HEX
BIN2OCT = BIN.OKT
BITAND = BIT.ÉS
BITLSHIFT = BIT.BAL.ELTOL
BITOR = BIT.VAGY
BITRSHIFT = BIT.JOBB.ELTOL
BITXOR = BIT.XVAGY
COMPLEX = KOMPLEX
CONVERT = KONVERTÁLÁS
DEC2BIN = DEC.BIN
DEC2HEX = DEC.HEX
DEC2OCT = DEC.OKT
DELTA = DELTA
ERF = HIBAF
ERF.PRECISE = HIBAF.PONTOS
ERFC = HIBAF.KOMPLEMENTER
ERFC.PRECISE = HIBAFKOMPLEMENTER.PONTOS
GESTEP = KÜSZÖBNÉL.NAGYOBB
HEX2BIN = HEX.BIN
HEX2DEC = HEX.DEC
HEX2OCT = HEX.OKT
IMABS = KÉPZ.ABSZ
IMAGINARY = KÉPZETES
IMARGUMENT = KÉPZ.ARGUMENT
IMCONJUGATE = KÉPZ.KONJUGÁLT
IMCOS = KÉPZ.COS
IMCOSH = KÉPZ.COSH
IMCOT = KÉPZ.COT
IMCSC = KÉPZ.CSC
IMCSCH = KÉPZ.CSCH
IMDIV = KÉPZ.HÁNYAD
IMEXP = KÉPZ.EXP
IMLN = KÉPZ.LN
IMLOG10 = KÉPZ.LOG10
IMLOG2 = KÉPZ.LOG2
IMPOWER = KÉPZ.HATV
IMPRODUCT = KÉPZ.SZORZAT
IMREAL = KÉPZ.VALÓS
IMSEC = KÉPZ.SEC
IMSECH = KÉPZ.SECH
IMSIN = KÉPZ.SIN
IMSINH = KÉPZ.SINH
IMSQRT = KÉPZ.GYÖK
IMSUB = KÉPZ.KÜL
IMSUM = KÉPZ.ÖSSZEG
IMTAN = KÉPZ.TAN
OCT2BIN = OKT.BIN
OCT2DEC = OKT.DEC
OCT2HEX = OKT.HEX
##
## Pénzügyi függvények (Financial Functions)
##
ACCRINT = IDŐSZAKI.KAMAT
ACCRINTM = LEJÁRATI.KAMAT
AMORDEGRC = ÉRTÉKCSÖKK.TÉNYEZŐVEL
AMORLINC = ÉRTÉKCSÖKK
COUPDAYBS = SZELVÉNYIDŐ.KEZDETTŐL
COUPDAYS = SZELVÉNYIDŐ
COUPDAYSNC = SZELVÉNYIDŐ.KIFIZETÉSTŐL
COUPNCD = ELSŐ.SZELVÉNYDÁTUM
COUPNUM = SZELVÉNYSZÁM
COUPPCD = UTOLSÓ.SZELVÉNYDÁTUM
CUMIPMT = ÖSSZES.KAMAT
CUMPRINC = ÖSSZES.TŐKERÉSZ
DB = KCS2
DDB = KCSA
DISC = LESZÁM
DOLLARDE = FORINT.DEC
DOLLARFR = FORINT.TÖRT
DURATION = KAMATÉRZ
EFFECT = TÉNYLEGES
FV = JBÉ
FVSCHEDULE = KJÉ
INTRATE = KAMATRÁTA
IPMT = RRÉSZLET
IRR = BMR
ISPMT = LRÉSZLETKAMAT
MDURATION = MKAMATÉRZ
MIRR = MEGTÉRÜLÉS
NOMINAL = NÉVLEGES
NPER = PER.SZÁM
NPV = NMÉ
ODDFPRICE = ELTÉRŐ.EÁR
ODDFYIELD = ELTÉRŐ.EHOZAM
ODDLPRICE = ELTÉRŐ.UÁR
ODDLYIELD = ELTÉRŐ.UHOZAM
PDURATION = KAMATÉRZ.PER
PMT = RÉSZLET
PPMT = PRÉSZLET
PRICE = ÁR
PRICEDISC = ÁR.LESZÁM
PRICEMAT = ÁR.LEJÁRAT
PV = MÉ
RATE = RÁTA
RECEIVED = KAPOTT
RRI = MR
SLN = LCSA
SYD = ÉSZÖ
TBILLEQ = KJEGY.EGYENÉRT
TBILLPRICE = KJEGY.ÁR
TBILLYIELD = KJEGY.HOZAM
VDB = ÉCSRI
XIRR = XBMR
XNPV = XNJÉ
YIELD = HOZAM
YIELDDISC = HOZAM.LESZÁM
YIELDMAT = HOZAM.LEJÁRAT
##
## Információs függvények (Information Functions)
##
CELL = CELLA
ERROR.TYPE = HIBA.TÍPUS
INFO = INFÓ
ISBLANK = ÜRES
ISERR = HIBA.E
ISERROR = HIBÁS
ISEVEN = PÁROSE
ISFORMULA = KÉPLET
ISLOGICAL = LOGIKAI
ISNA = NINCS
ISNONTEXT = NEM.SZÖVEG
ISNUMBER = SZÁM
ISODD = PÁRATLANE
ISREF = HIVATKOZÁS
ISTEXT = SZÖVEG.E
N = S
NA = HIÁNYZIK
SHEET = LAP
SHEETS = LAPOK
TYPE = TÍPUS
##
## Logikai függvények (Logical Functions)
##
AND = ÉS
FALSE = HAMIS
IF = HA
IFERROR = HAHIBA
IFNA = HAHIÁNYZIK
IFS = HAELSŐIGAZ
NOT = NEM
OR = VAGY
SWITCH = ÁTVÁLT
TRUE = IGAZ
XOR = XVAGY
##
## Keresési és hivatkozási függvények (Lookup & Reference Functions)
##
ADDRESS = CÍM
AREAS = TERÜLET
CHOOSE = VÁLASZT
COLUMN = OSZLOP
COLUMNS = OSZLOPOK
FORMULATEXT = KÉPLETSZÖVEG
GETPIVOTDATA = KIMUTATÁSADATOT.VESZ
HLOOKUP = VKERES
HYPERLINK = HIPERHIVATKOZÁS
INDEX = INDEX
INDIRECT = INDIREKT
LOOKUP = KERES
MATCH = HOL.VAN
OFFSET = ELTOLÁS
ROW = SOR
ROWS = SOROK
RTD = VIA
TRANSPOSE = TRANSZPONÁLÁS
VLOOKUP = FKERES
*RC = SO
##
## Matematikai és trigonometrikus függvények (Math & Trig Functions)
##
ABS = ABS
ACOS = ARCCOS
ACOSH = ACOSH
ACOT = ARCCOT
ACOTH = ARCCOTH
AGGREGATE = ÖSSZESÍT
ARABIC = ARAB
ASIN = ARCSIN
ASINH = ASINH
ATAN = ARCTAN
ATAN2 = ARCTAN2
ATANH = ATANH
BASE = ALAP
CEILING.MATH = PLAFON.MAT
CEILING.PRECISE = PLAFON.PONTOS
COMBIN = KOMBINÁCIÓK
COMBINA = KOMBINÁCIÓK.ISM
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = TIZEDES
DEGREES = FOK
ECMA.CEILING = ECMA.PLAFON
EVEN = PÁROS
EXP = KITEVŐ
FACT = FAKT
FACTDOUBLE = FAKTDUPLA
FLOOR.MATH = PADLÓ.MAT
FLOOR.PRECISE = PADLÓ.PONTOS
GCD = LKO
INT = INT
ISO.CEILING = ISO.PLAFON
LCM = LKT
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = MDETERM
MINVERSE = INVERZ.MÁTRIX
MMULT = MSZORZAT
MOD = MARADÉK
MROUND = TÖBBSZ.KEREKÍT
MULTINOMIAL = SZORHÁNYFAKT
MUNIT = MMÁTRIX
ODD = PÁRATLAN
PI = PI
POWER = HATVÁNY
PRODUCT = SZORZAT
QUOTIENT = KVÓCIENS
RADIANS = RADIÁN
RAND = VÉL
RANDBETWEEN = VÉLETLEN.KÖZÖTT
ROMAN = RÓMAI
ROUND = KEREKÍTÉS
ROUNDBAHTDOWN = BAHTKEREK.LE
ROUNDBAHTUP = BAHTKEREK.FEL
ROUNDDOWN = KEREK.LE
ROUNDUP = KEREK.FEL
SEC = SEC
SECH = SECH
SERIESSUM = SORÖSSZEG
SIGN = ELŐJEL
SIN = SIN
SINH = SINH
SQRT = GYÖK
SQRTPI = GYÖKPI
SUBTOTAL = RÉSZÖSSZEG
SUM = SZUM
SUMIF = SZUMHA
SUMIFS = SZUMHATÖBB
SUMPRODUCT = SZORZATÖSSZEG
SUMSQ = NÉGYZETÖSSZEG
SUMX2MY2 = SZUMX2BŐLY2
SUMX2PY2 = SZUMX2MEGY2
SUMXMY2 = SZUMXBŐLY2
TAN = TAN
TANH = TANH
TRUNC = CSONK
##
## Statisztikai függvények (Statistical Functions)
##
AVEDEV = ÁTL.ELTÉRÉS
AVERAGE = ÁTLAG
AVERAGEA = ÁTLAGA
AVERAGEIF = ÁTLAGHA
AVERAGEIFS = ÁTLAGHATÖBB
BETA.DIST = BÉTA.ELOSZL
BETA.INV = BÉTA.INVERZ
BINOM.DIST = BINOM.ELOSZL
BINOM.DIST.RANGE = BINOM.ELOSZL.TART
BINOM.INV = BINOM.INVERZ
CHISQ.DIST = KHINÉGYZET.ELOSZLÁS
CHISQ.DIST.RT = KHINÉGYZET.ELOSZLÁS.JOBB
CHISQ.INV = KHINÉGYZET.INVERZ
CHISQ.INV.RT = KHINÉGYZET.INVERZ.JOBB
CHISQ.TEST = KHINÉGYZET.PRÓBA
CONFIDENCE.NORM = MEGBÍZHATÓSÁG.NORM
CONFIDENCE.T = MEGBÍZHATÓSÁG.T
CORREL = KORREL
COUNT = DARAB
COUNTA = DARAB2
COUNTBLANK = DARABÜRES
COUNTIF = DARABTELI
COUNTIFS = DARABHATÖBB
COVARIANCE.P = KOVARIANCIA.S
COVARIANCE.S = KOVARIANCIA.M
DEVSQ = SQ
EXPON.DIST = EXP.ELOSZL
F.DIST = F.ELOSZL
F.DIST.RT = F.ELOSZLÁS.JOBB
F.INV = F.INVERZ
F.INV.RT = F.INVERZ.JOBB
F.TEST = F.PRÓB
FISHER = FISHER
FISHERINV = INVERZ.FISHER
FORECAST.ETS = ELŐREJELZÉS.ESIM
FORECAST.ETS.CONFINT = ELŐREJELZÉS.ESIM.KONFINT
FORECAST.ETS.SEASONALITY = ELŐREJELZÉS.ESIM.SZEZONALITÁS
FORECAST.ETS.STAT = ELŐREJELZÉS.ESIM.STAT
FORECAST.LINEAR = ELŐREJELZÉS.LINEÁRIS
FREQUENCY = GYAKORISÁG
GAMMA = GAMMA
GAMMA.DIST = GAMMA.ELOSZL
GAMMA.INV = GAMMA.INVERZ
GAMMALN = GAMMALN
GAMMALN.PRECISE = GAMMALN.PONTOS
GAUSS = GAUSS
GEOMEAN = MÉRTANI.KÖZÉP
GROWTH = NÖV
HARMEAN = HARM.KÖZÉP
HYPGEOM.DIST = HIPGEOM.ELOSZLÁS
INTERCEPT = METSZ
KURT = CSÚCSOSSÁG
LARGE = NAGY
LINEST = LIN.ILL
LOGEST = LOG.ILL
LOGNORM.DIST = LOGNORM.ELOSZLÁS
LOGNORM.INV = LOGNORM.INVERZ
MAX = MAX
MAXA = MAXA
MAXIFS = MAXHA
MEDIAN = MEDIÁN
MIN = MIN
MINA = MIN2
MINIFS = MINHA
MODE.MULT = MÓDUSZ.TÖBB
MODE.SNGL = MÓDUSZ.EGY
NEGBINOM.DIST = NEGBINOM.ELOSZLÁS
NORM.DIST = NORM.ELOSZLÁS
NORM.INV = NORM.INVERZ
NORM.S.DIST = NORM.S.ELOSZLÁS
NORM.S.INV = NORM.S.INVERZ
PEARSON = PEARSON
PERCENTILE.EXC = PERCENTILIS.KIZÁR
PERCENTILE.INC = PERCENTILIS.TARTALMAZ
PERCENTRANK.EXC = SZÁZALÉKRANG.KIZÁR
PERCENTRANK.INC = SZÁZALÉKRANG.TARTALMAZ
PERMUT = VARIÁCIÓK
PERMUTATIONA = VARIÁCIÓK.ISM
PHI = FI
POISSON.DIST = POISSON.ELOSZLÁS
PROB = VALÓSZÍNŰSÉG
QUARTILE.EXC = KVARTILIS.KIZÁR
QUARTILE.INC = KVARTILIS.TARTALMAZ
RANK.AVG = RANG.ÁTL
RANK.EQ = RANG.EGY
RSQ = RNÉGYZET
SKEW = FERDESÉG
SKEW.P = FERDESÉG.P
SLOPE = MEREDEKSÉG
SMALL = KICSI
STANDARDIZE = NORMALIZÁLÁS
STDEV.P = SZÓR.S
STDEV.S = SZÓR.M
STDEVA = SZÓRÁSA
STDEVPA = SZÓRÁSPA
STEYX = STHIBAYX
T.DIST = T.ELOSZL
T.DIST.2T = T.ELOSZLÁS.2SZ
T.DIST.RT = T.ELOSZLÁS.JOBB
T.INV = T.INVERZ
T.INV.2T = T.INVERZ.2SZ
T.TEST = T.PRÓB
TREND = TREND
TRIMMEAN = RÉSZÁTLAG
VAR.P = VAR.S
VAR.S = VAR.M
VARA = VARA
VARPA = VARPA
WEIBULL.DIST = WEIBULL.ELOSZLÁS
Z.TEST = Z.PRÓB
##
## Szövegműveletekhez használható függvények (Text Functions)
##
BAHTTEXT = BAHTSZÖVEG
CHAR = KARAKTER
CLEAN = TISZTÍT
CODE = KÓD
CONCAT = FŰZ
DOLLAR = FORINT
EXACT = AZONOS
FIND = SZÖVEG.TALÁL
FIXED = FIX
ISTHAIDIGIT = ON.THAI.NUMERO
LEFT = BAL
LEN = HOSSZ
LOWER = KISBETŰ
MID = KÖZÉP
NUMBERSTRING = SZÁM.BETŰVEL
NUMBERVALUE = SZÁMÉRTÉK
PHONETIC = FONETIKUS
PROPER = TNÉV
REPLACE = CSERE
REPT = SOKSZOR
RIGHT = JOBB
SEARCH = SZÖVEG.KERES
SUBSTITUTE = HELYETTE
T = T
TEXT = SZÖVEG
TEXTJOIN = SZÖVEGÖSSZEFŰZÉS
THAIDIGIT = THAISZÁM
THAINUMSOUND = THAISZÁMHANG
THAINUMSTRING = THAISZÁMKAR
THAISTRINGLENGTH = THAIKARHOSSZ
TRIM = KIMETSZ
UNICHAR = UNIKARAKTER
UNICODE = UNICODE
UPPER = NAGYBETŰS
VALUE = ÉRTÉK
##
## Webes függvények (Web Functions)
##
ENCODEURL = URL.KÓDOL
FILTERXML = XMLSZŰRÉS
WEBSERVICE = WEBSZOLGÁLTATÁS
##
## Kompatibilitási függvények (Compatibility Functions)
##
BETADIST = BÉTA.ELOSZLÁS
BETAINV = INVERZ.BÉTA
BINOMDIST = BINOM.ELOSZLÁS
CEILING = PLAFON
CHIDIST = KHI.ELOSZLÁS
CHIINV = INVERZ.KHI
CHITEST = KHI.PRÓBA
CONCATENATE = ÖSSZEFŰZ
CONFIDENCE = MEGBÍZHATÓSÁG
COVAR = KOVAR
CRITBINOM = KRITBINOM
EXPONDIST = EXP.ELOSZLÁS
FDIST = F.ELOSZLÁS
FINV = INVERZ.F
FLOOR = PADLÓ
FORECAST = ELŐREJELZÉS
FTEST = F.PRÓBA
GAMMADIST = GAMMA.ELOSZLÁS
GAMMAINV = INVERZ.GAMMA
HYPGEOMDIST = HIPERGEOM.ELOSZLÁS
LOGINV = INVERZ.LOG.ELOSZLÁS
LOGNORMDIST = LOG.ELOSZLÁS
MODE = MÓDUSZ
NEGBINOMDIST = NEGBINOM.ELOSZL
NORMDIST = NORM.ELOSZL
NORMINV = INVERZ.NORM
NORMSDIST = STNORMELOSZL
NORMSINV = INVERZ.STNORM
PERCENTILE = PERCENTILIS
PERCENTRANK = SZÁZALÉKRANG
POISSON = POISSON
QUARTILE = KVARTILIS
RANK = SORSZÁM
STDEV = SZÓRÁS
STDEVP = SZÓRÁSP
TDIST = T.ELOSZLÁS
TINV = INVERZ.T
TTEST = T.PRÓBA
VAR = VAR
VARP = VARP
WEIBULL = WEIBULL
ZTEST = Z.PRÓBA
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/hu/config 0000644 00000000531 15242753725 0020605 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Magyar (Hungarian)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #NULLA!
DIV0 = #ZÉRÓOSZTÓ!
VALUE = #ÉRTÉK!
REF = #HIV!
NAME = #NÉV?
NUM = #SZÁM!
NA = #HIÁNYZIK
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/functions 0000644 00000024441 15242753725 0021326 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Dansk (Danish)
##
############################################################
##
## Kubefunktioner (Cube Functions)
##
CUBEKPIMEMBER = KUBE.KPI.MEDLEM
CUBEMEMBER = KUBEMEDLEM
CUBEMEMBERPROPERTY = KUBEMEDLEM.EGENSKAB
CUBERANKEDMEMBER = KUBERANGERET.MEDLEM
CUBESET = KUBESÆT
CUBESETCOUNT = KUBESÆT.ANTAL
CUBEVALUE = KUBEVÆRDI
##
## Databasefunktioner (Database Functions)
##
DAVERAGE = DMIDDEL
DCOUNT = DTÆL
DCOUNTA = DTÆLV
DGET = DHENT
DMAX = DMAKS
DMIN = DMIN
DPRODUCT = DPRODUKT
DSTDEV = DSTDAFV
DSTDEVP = DSTDAFVP
DSUM = DSUM
DVAR = DVARIANS
DVARP = DVARIANSP
##
## Dato- og klokkeslætfunktioner (Date & Time Functions)
##
DATE = DATO
DATEDIF = DATO.FORSKEL
DATESTRING = DATOSTRENG
DATEVALUE = DATOVÆRDI
DAY = DAG
DAYS = DAGE
DAYS360 = DAGE360
EDATE = EDATO
EOMONTH = SLUT.PÅ.MÅNED
HOUR = TIME
ISOWEEKNUM = ISOUGE.NR
MINUTE = MINUT
MONTH = MÅNED
NETWORKDAYS = ANTAL.ARBEJDSDAGE
NETWORKDAYS.INTL = ANTAL.ARBEJDSDAGE.INTL
NOW = NU
SECOND = SEKUND
THAIDAYOFWEEK = THAILANDSKUGEDAG
THAIMONTHOFYEAR = THAILANDSKMÅNED
THAIYEAR = THAILANDSKÅR
TIME = TID
TIMEVALUE = TIDSVÆRDI
TODAY = IDAG
WEEKDAY = UGEDAG
WEEKNUM = UGE.NR
WORKDAY = ARBEJDSDAG
WORKDAY.INTL = ARBEJDSDAG.INTL
YEAR = ÅR
YEARFRAC = ÅR.BRØK
##
## Tekniske funktioner (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BIN.TIL.DEC
BIN2HEX = BIN.TIL.HEX
BIN2OCT = BIN.TIL.OKT
BITAND = BITOG
BITLSHIFT = BITLSKIFT
BITOR = BITELLER
BITRSHIFT = BITRSKIFT
BITXOR = BITXELLER
COMPLEX = KOMPLEKS
CONVERT = KONVERTER
DEC2BIN = DEC.TIL.BIN
DEC2HEX = DEC.TIL.HEX
DEC2OCT = DEC.TIL.OKT
DELTA = DELTA
ERF = FEJLFUNK
ERF.PRECISE = ERF.PRECISE
ERFC = FEJLFUNK.KOMP
ERFC.PRECISE = ERFC.PRECISE
GESTEP = GETRIN
HEX2BIN = HEX.TIL.BIN
HEX2DEC = HEX.TIL.DEC
HEX2OCT = HEX.TIL.OKT
IMABS = IMAGABS
IMAGINARY = IMAGINÆR
IMARGUMENT = IMAGARGUMENT
IMCONJUGATE = IMAGKONJUGERE
IMCOS = IMAGCOS
IMCOSH = IMAGCOSH
IMCOT = IMAGCOT
IMCSC = IMAGCSC
IMCSCH = IMAGCSCH
IMDIV = IMAGDIV
IMEXP = IMAGEKSP
IMLN = IMAGLN
IMLOG10 = IMAGLOG10
IMLOG2 = IMAGLOG2
IMPOWER = IMAGPOTENS
IMPRODUCT = IMAGPRODUKT
IMREAL = IMAGREELT
IMSEC = IMAGSEC
IMSECH = IMAGSECH
IMSIN = IMAGSIN
IMSINH = IMAGSINH
IMSQRT = IMAGKVROD
IMSUB = IMAGSUB
IMSUM = IMAGSUM
IMTAN = IMAGTAN
OCT2BIN = OKT.TIL.BIN
OCT2DEC = OKT.TIL.DEC
OCT2HEX = OKT.TIL.HEX
##
## Finansielle funktioner (Financial Functions)
##
ACCRINT = PÅLØBRENTE
ACCRINTM = PÅLØBRENTE.UDLØB
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = KUPONDAGE.SA
COUPDAYS = KUPONDAGE.A
COUPDAYSNC = KUPONDAGE.ANK
COUPNCD = KUPONDAG.NÆSTE
COUPNUM = KUPONBETALINGER
COUPPCD = KUPONDAG.FORRIGE
CUMIPMT = AKKUM.RENTE
CUMPRINC = AKKUM.HOVEDSTOL
DB = DB
DDB = DSA
DISC = DISKONTO
DOLLARDE = KR.DECIMAL
DOLLARFR = KR.BRØK
DURATION = VARIGHED
EFFECT = EFFEKTIV.RENTE
FV = FV
FVSCHEDULE = FVTABEL
INTRATE = RENTEFOD
IPMT = R.YDELSE
IRR = IA
ISPMT = ISPMT
MDURATION = MVARIGHED
MIRR = MIA
NOMINAL = NOMINEL
NPER = NPER
NPV = NUTIDSVÆRDI
ODDFPRICE = ULIGE.KURS.PÅLYDENDE
ODDFYIELD = ULIGE.FØRSTE.AFKAST
ODDLPRICE = ULIGE.SIDSTE.KURS
ODDLYIELD = ULIGE.SIDSTE.AFKAST
PDURATION = PVARIGHED
PMT = YDELSE
PPMT = H.YDELSE
PRICE = KURS
PRICEDISC = KURS.DISKONTO
PRICEMAT = KURS.UDLØB
PV = NV
RATE = RENTE
RECEIVED = MODTAGET.VED.UDLØB
RRI = RRI
SLN = LA
SYD = ÅRSAFSKRIVNING
TBILLEQ = STATSOBLIGATION
TBILLPRICE = STATSOBLIGATION.KURS
TBILLYIELD = STATSOBLIGATION.AFKAST
VDB = VSA
XIRR = INTERN.RENTE
XNPV = NETTO.NUTIDSVÆRDI
YIELD = AFKAST
YIELDDISC = AFKAST.DISKONTO
YIELDMAT = AFKAST.UDLØBSDATO
##
## Informationsfunktioner (Information Functions)
##
CELL = CELLE
ERROR.TYPE = FEJLTYPE
INFO = INFO
ISBLANK = ER.TOM
ISERR = ER.FJL
ISERROR = ER.FEJL
ISEVEN = ER.LIGE
ISFORMULA = ER.FORMEL
ISLOGICAL = ER.LOGISK
ISNA = ER.IKKE.TILGÆNGELIG
ISNONTEXT = ER.IKKE.TEKST
ISNUMBER = ER.TAL
ISODD = ER.ULIGE
ISREF = ER.REFERENCE
ISTEXT = ER.TEKST
N = TAL
NA = IKKE.TILGÆNGELIG
SHEET = ARK
SHEETS = ARK.FLERE
TYPE = VÆRDITYPE
##
## Logiske funktioner (Logical Functions)
##
AND = OG
FALSE = FALSK
IF = HVIS
IFERROR = HVIS.FEJL
IFNA = HVISIT
IFS = HVISER
NOT = IKKE
OR = ELLER
SWITCH = SKIFT
TRUE = SAND
XOR = XELLER
##
## Opslags- og referencefunktioner (Lookup & Reference Functions)
##
ADDRESS = ADRESSE
AREAS = OMRÅDER
CHOOSE = VÆLG
COLUMN = KOLONNE
COLUMNS = KOLONNER
FORMULATEXT = FORMELTEKST
GETPIVOTDATA = GETPIVOTDATA
HLOOKUP = VOPSLAG
HYPERLINK = HYPERLINK
INDEX = INDEKS
INDIRECT = INDIREKTE
LOOKUP = SLÅ.OP
MATCH = SAMMENLIGN
OFFSET = FORSKYDNING
ROW = RÆKKE
ROWS = RÆKKER
RTD = RTD
TRANSPOSE = TRANSPONER
VLOOKUP = LOPSLAG
*RC = RK
##
## Matematiske og trigonometriske funktioner (Math & Trig Functions)
##
ABS = ABS
ACOS = ARCCOS
ACOSH = ARCCOSH
ACOT = ARCCOT
ACOTH = ARCCOTH
AGGREGATE = SAMLING
ARABIC = ARABISK
ASIN = ARCSIN
ASINH = ARCSINH
ATAN = ARCTAN
ATAN2 = ARCTAN2
ATANH = ARCTANH
BASE = BASIS
CEILING.MATH = LOFT.MAT
CEILING.PRECISE = LOFT.PRECISE
COMBIN = KOMBIN
COMBINA = KOMBINA
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = DECIMAL
DEGREES = GRADER
ECMA.CEILING = ECMA.LOFT
EVEN = LIGE
EXP = EKSP
FACT = FAKULTET
FACTDOUBLE = DOBBELT.FAKULTET
FLOOR.MATH = AFRUND.BUND.MAT
FLOOR.PRECISE = AFRUND.GULV.PRECISE
GCD = STØRSTE.FÆLLES.DIVISOR
INT = HELTAL
ISO.CEILING = ISO.LOFT
LCM = MINDSTE.FÆLLES.MULTIPLUM
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = MDETERM
MINVERSE = MINVERT
MMULT = MPRODUKT
MOD = REST
MROUND = MAFRUND
MULTINOMIAL = MULTINOMIAL
MUNIT = MENHED
ODD = ULIGE
PI = PI
POWER = POTENS
PRODUCT = PRODUKT
QUOTIENT = KVOTIENT
RADIANS = RADIANER
RAND = SLUMP
RANDBETWEEN = SLUMPMELLEM
ROMAN = ROMERTAL
ROUND = AFRUND
ROUNDBAHTDOWN = RUNDBAHTNED
ROUNDBAHTUP = RUNDBAHTOP
ROUNDDOWN = RUND.NED
ROUNDUP = RUND.OP
SEC = SEC
SECH = SECH
SERIESSUM = SERIESUM
SIGN = FORTEGN
SIN = SIN
SINH = SINH
SQRT = KVROD
SQRTPI = KVRODPI
SUBTOTAL = SUBTOTAL
SUM = SUM
SUMIF = SUM.HVIS
SUMIFS = SUM.HVISER
SUMPRODUCT = SUMPRODUKT
SUMSQ = SUMKV
SUMX2MY2 = SUMX2MY2
SUMX2PY2 = SUMX2PY2
SUMXMY2 = SUMXMY2
TAN = TAN
TANH = TANH
TRUNC = AFKORT
##
## Statistiske funktioner (Statistical Functions)
##
AVEDEV = MAD
AVERAGE = MIDDEL
AVERAGEA = MIDDELV
AVERAGEIF = MIDDEL.HVIS
AVERAGEIFS = MIDDEL.HVISER
BETA.DIST = BETA.FORDELING
BETA.INV = BETA.INV
BINOM.DIST = BINOMIAL.FORDELING
BINOM.DIST.RANGE = BINOMIAL.DIST.INTERVAL
BINOM.INV = BINOMIAL.INV
CHISQ.DIST = CHI2.FORDELING
CHISQ.DIST.RT = CHI2.FORD.RT
CHISQ.INV = CHI2.INV
CHISQ.INV.RT = CHI2.INV.RT
CHISQ.TEST = CHI2.TEST
CONFIDENCE.NORM = KONFIDENS.NORM
CONFIDENCE.T = KONFIDENST
CORREL = KORRELATION
COUNT = TÆL
COUNTA = TÆLV
COUNTBLANK = ANTAL.BLANKE
COUNTIF = TÆL.HVIS
COUNTIFS = TÆL.HVISER
COVARIANCE.P = KOVARIANS.P
COVARIANCE.S = KOVARIANS.S
DEVSQ = SAK
EXPON.DIST = EKSP.FORDELING
F.DIST = F.FORDELING
F.DIST.RT = F.FORDELING.RT
F.INV = F.INV
F.INV.RT = F.INV.RT
F.TEST = F.TEST
FISHER = FISHER
FISHERINV = FISHERINV
FORECAST.ETS = PROGNOSE.ETS
FORECAST.ETS.CONFINT = PROGNOSE.ETS.CONFINT
FORECAST.ETS.SEASONALITY = PROGNOSE.ETS.SÆSONUDSVING
FORECAST.ETS.STAT = PROGNOSE.ETS.STAT
FORECAST.LINEAR = PROGNOSE.LINEÆR
FREQUENCY = FREKVENS
GAMMA = GAMMA
GAMMA.DIST = GAMMA.FORDELING
GAMMA.INV = GAMMA.INV
GAMMALN = GAMMALN
GAMMALN.PRECISE = GAMMALN.PRECISE
GAUSS = GAUSS
GEOMEAN = GEOMIDDELVÆRDI
GROWTH = FORØGELSE
HARMEAN = HARMIDDELVÆRDI
HYPGEOM.DIST = HYPGEO.FORDELING
INTERCEPT = SKÆRING
KURT = TOPSTEJL
LARGE = STØRSTE
LINEST = LINREGR
LOGEST = LOGREGR
LOGNORM.DIST = LOGNORM.FORDELING
LOGNORM.INV = LOGNORM.INV
MAX = MAKS
MAXA = MAKSV
MAXIFS = MAKSHVISER
MEDIAN = MEDIAN
MIN = MIN
MINA = MINV
MINIFS = MINHVISER
MODE.MULT = HYPPIGST.FLERE
MODE.SNGL = HYPPIGST.ENKELT
NEGBINOM.DIST = NEGBINOM.FORDELING
NORM.DIST = NORMAL.FORDELING
NORM.INV = NORM.INV
NORM.S.DIST = STANDARD.NORM.FORDELING
NORM.S.INV = STANDARD.NORM.INV
PEARSON = PEARSON
PERCENTILE.EXC = FRAKTIL.UDELAD
PERCENTILE.INC = FRAKTIL.MEDTAG
PERCENTRANK.EXC = PROCENTPLADS.UDELAD
PERCENTRANK.INC = PROCENTPLADS.MEDTAG
PERMUT = PERMUT
PERMUTATIONA = PERMUTATIONA
PHI = PHI
POISSON.DIST = POISSON.FORDELING
PROB = SANDSYNLIGHED
QUARTILE.EXC = KVARTIL.UDELAD
QUARTILE.INC = KVARTIL.MEDTAG
RANK.AVG = PLADS.GNSN
RANK.EQ = PLADS.LIGE
RSQ = FORKLARINGSGRAD
SKEW = SKÆVHED
SKEW.P = SKÆVHED.P
SLOPE = STIGNING
SMALL = MINDSTE
STANDARDIZE = STANDARDISER
STDEV.P = STDAFV.P
STDEV.S = STDAFV.S
STDEVA = STDAFVV
STDEVPA = STDAFVPV
STEYX = STFYX
T.DIST = T.FORDELING
T.DIST.2T = T.FORDELING.2T
T.DIST.RT = T.FORDELING.RT
T.INV = T.INV
T.INV.2T = T.INV.2T
T.TEST = T.TEST
TREND = TENDENS
TRIMMEAN = TRIMMIDDELVÆRDI
VAR.P = VARIANS.P
VAR.S = VARIANS.S
VARA = VARIANSV
VARPA = VARIANSPV
WEIBULL.DIST = WEIBULL.FORDELING
Z.TEST = Z.TEST
##
## Tekstfunktioner (Text Functions)
##
BAHTTEXT = BAHTTEKST
CHAR = TEGN
CLEAN = RENS
CODE = KODE
CONCAT = CONCAT
DOLLAR = KR
EXACT = EKSAKT
FIND = FIND
FIXED = FAST
ISTHAIDIGIT = ERTHAILANDSKCIFFER
LEFT = VENSTRE
LEN = LÆNGDE
LOWER = SMÅ.BOGSTAVER
MID = MIDT
NUMBERSTRING = TALSTRENG
NUMBERVALUE = TALVÆRDI
PHONETIC = FONETISK
PROPER = STORT.FORBOGSTAV
REPLACE = ERSTAT
REPT = GENTAG
RIGHT = HØJRE
SEARCH = SØG
SUBSTITUTE = UDSKIFT
T = T
TEXT = TEKST
TEXTJOIN = TEKST.KOMBINER
THAIDIGIT = THAILANDSKCIFFER
THAINUMSOUND = THAILANDSKNUMLYD
THAINUMSTRING = THAILANDSKNUMSTRENG
THAISTRINGLENGTH = THAILANDSKSTRENGLÆNGDE
TRIM = FJERN.OVERFLØDIGE.BLANKE
UNICHAR = UNICHAR
UNICODE = UNICODE
UPPER = STORE.BOGSTAVER
VALUE = VÆRDI
##
## Webfunktioner (Web Functions)
##
ENCODEURL = KODNINGSURL
FILTERXML = FILTRERXML
WEBSERVICE = WEBTJENESTE
##
## Kompatibilitetsfunktioner (Compatibility Functions)
##
BETADIST = BETAFORDELING
BETAINV = BETAINV
BINOMDIST = BINOMIALFORDELING
CEILING = AFRUND.LOFT
CHIDIST = CHIFORDELING
CHIINV = CHIINV
CHITEST = CHITEST
CONCATENATE = SAMMENKÆDNING
CONFIDENCE = KONFIDENSINTERVAL
COVAR = KOVARIANS
CRITBINOM = KRITBINOM
EXPONDIST = EKSPFORDELING
FDIST = FFORDELING
FINV = FINV
FLOOR = AFRUND.GULV
FORECAST = PROGNOSE
FTEST = FTEST
GAMMADIST = GAMMAFORDELING
GAMMAINV = GAMMAINV
HYPGEOMDIST = HYPGEOFORDELING
LOGINV = LOGINV
LOGNORMDIST = LOGNORMFORDELING
MODE = HYPPIGST
NEGBINOMDIST = NEGBINOMFORDELING
NORMDIST = NORMFORDELING
NORMINV = NORMINV
NORMSDIST = STANDARDNORMFORDELING
NORMSINV = STANDARDNORMINV
PERCENTILE = FRAKTIL
PERCENTRANK = PROCENTPLADS
POISSON = POISSON
QUARTILE = KVARTIL
RANK = PLADS
STDEV = STDAFV
STDEVP = STDAFVP
TDIST = TFORDELING
TINV = TINV
TTEST = TTEST
VAR = VARIANS
VARP = VARIANSP
WEIBULL = WEIBULL
ZTEST = ZTEST
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/da/config 0000644 00000000467 15242753725 0020565 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Dansk (Danish)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #NUL!
DIV0
VALUE = #VÆRDI!
REF = #REFERENCE!
NAME = #NAVN?
NUM
NA = #I/T
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/functions 0000644 00000024377 15242753725 0021363 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Nederlands (Dutch)
##
############################################################
##
## Kubusfuncties (Cube Functions)
##
CUBEKPIMEMBER = KUBUSKPILID
CUBEMEMBER = KUBUSLID
CUBEMEMBERPROPERTY = KUBUSLIDEIGENSCHAP
CUBERANKEDMEMBER = KUBUSGERANGSCHIKTLID
CUBESET = KUBUSSET
CUBESETCOUNT = KUBUSSETAANTAL
CUBEVALUE = KUBUSWAARDE
##
## Databasefuncties (Database Functions)
##
DAVERAGE = DBGEMIDDELDE
DCOUNT = DBAANTAL
DCOUNTA = DBAANTALC
DGET = DBLEZEN
DMAX = DBMAX
DMIN = DBMIN
DPRODUCT = DBPRODUCT
DSTDEV = DBSTDEV
DSTDEVP = DBSTDEVP
DSUM = DBSOM
DVAR = DBVAR
DVARP = DBVARP
##
## Datum- en tijdfuncties (Date & Time Functions)
##
DATE = DATUM
DATESTRING = DATUMNOTATIE
DATEVALUE = DATUMWAARDE
DAY = DAG
DAYS = DAGEN
DAYS360 = DAGEN360
EDATE = ZELFDE.DAG
EOMONTH = LAATSTE.DAG
HOUR = UUR
ISOWEEKNUM = ISO.WEEKNUMMER
MINUTE = MINUUT
MONTH = MAAND
NETWORKDAYS = NETTO.WERKDAGEN
NETWORKDAYS.INTL = NETWERKDAGEN.INTL
NOW = NU
SECOND = SECONDE
THAIDAYOFWEEK = THAIS.WEEKDAG
THAIMONTHOFYEAR = THAIS.MAAND.VAN.JAAR
THAIYEAR = THAIS.JAAR
TIME = TIJD
TIMEVALUE = TIJDWAARDE
TODAY = VANDAAG
WEEKDAY = WEEKDAG
WEEKNUM = WEEKNUMMER
WORKDAY = WERKDAG
WORKDAY.INTL = WERKDAG.INTL
YEAR = JAAR
YEARFRAC = JAAR.DEEL
##
## Technische functies (Engineering Functions)
##
BESSELI = BESSEL.I
BESSELJ = BESSEL.J
BESSELK = BESSEL.K
BESSELY = BESSEL.Y
BIN2DEC = BIN.N.DEC
BIN2HEX = BIN.N.HEX
BIN2OCT = BIN.N.OCT
BITAND = BIT.EN
BITLSHIFT = BIT.VERSCHUIF.LINKS
BITOR = BIT.OF
BITRSHIFT = BIT.VERSCHUIF.RECHTS
BITXOR = BIT.EX.OF
COMPLEX = COMPLEX
CONVERT = CONVERTEREN
DEC2BIN = DEC.N.BIN
DEC2HEX = DEC.N.HEX
DEC2OCT = DEC.N.OCT
DELTA = DELTA
ERF = FOUTFUNCTIE
ERF.PRECISE = FOUTFUNCTIE.NAUWKEURIG
ERFC = FOUT.COMPLEMENT
ERFC.PRECISE = FOUT.COMPLEMENT.NAUWKEURIG
GESTEP = GROTER.DAN
HEX2BIN = HEX.N.BIN
HEX2DEC = HEX.N.DEC
HEX2OCT = HEX.N.OCT
IMABS = C.ABS
IMAGINARY = C.IM.DEEL
IMARGUMENT = C.ARGUMENT
IMCONJUGATE = C.TOEGEVOEGD
IMCOS = C.COS
IMCOSH = C.COSH
IMCOT = C.COT
IMCSC = C.COSEC
IMCSCH = C.COSECH
IMDIV = C.QUOTIENT
IMEXP = C.EXP
IMLN = C.LN
IMLOG10 = C.LOG10
IMLOG2 = C.LOG2
IMPOWER = C.MACHT
IMPRODUCT = C.PRODUCT
IMREAL = C.REEEL.DEEL
IMSEC = C.SEC
IMSECH = C.SECH
IMSIN = C.SIN
IMSINH = C.SINH
IMSQRT = C.WORTEL
IMSUB = C.VERSCHIL
IMSUM = C.SOM
IMTAN = C.TAN
OCT2BIN = OCT.N.BIN
OCT2DEC = OCT.N.DEC
OCT2HEX = OCT.N.HEX
##
## Financiële functies (Financial Functions)
##
ACCRINT = SAMENG.RENTE
ACCRINTM = SAMENG.RENTE.V
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = COUP.DAGEN.BB
COUPDAYS = COUP.DAGEN
COUPDAYSNC = COUP.DAGEN.VV
COUPNCD = COUP.DATUM.NB
COUPNUM = COUP.AANTAL
COUPPCD = COUP.DATUM.VB
CUMIPMT = CUM.RENTE
CUMPRINC = CUM.HOOFDSOM
DB = DB
DDB = DDB
DISC = DISCONTO
DOLLARDE = EURO.DE
DOLLARFR = EURO.BR
DURATION = DUUR
EFFECT = EFFECT.RENTE
FV = TW
FVSCHEDULE = TOEK.WAARDE2
INTRATE = RENTEPERCENTAGE
IPMT = IBET
IRR = IR
ISPMT = ISBET
MDURATION = AANG.DUUR
MIRR = GIR
NOMINAL = NOMINALE.RENTE
NPER = NPER
NPV = NHW
ODDFPRICE = AFW.ET.PRIJS
ODDFYIELD = AFW.ET.REND
ODDLPRICE = AFW.LT.PRIJS
ODDLYIELD = AFW.LT.REND
PDURATION = PDUUR
PMT = BET
PPMT = PBET
PRICE = PRIJS.NOM
PRICEDISC = PRIJS.DISCONTO
PRICEMAT = PRIJS.VERVALDAG
PV = HW
RATE = RENTE
RECEIVED = OPBRENGST
RRI = RRI
SLN = LIN.AFSCHR
SYD = SYD
TBILLEQ = SCHATK.OBL
TBILLPRICE = SCHATK.PRIJS
TBILLYIELD = SCHATK.REND
VDB = VDB
XIRR = IR.SCHEMA
XNPV = NHW2
YIELD = RENDEMENT
YIELDDISC = REND.DISCONTO
YIELDMAT = REND.VERVAL
##
## Informatiefuncties (Information Functions)
##
CELL = CEL
ERROR.TYPE = TYPE.FOUT
INFO = INFO
ISBLANK = ISLEEG
ISERR = ISFOUT2
ISERROR = ISFOUT
ISEVEN = IS.EVEN
ISFORMULA = ISFORMULE
ISLOGICAL = ISLOGISCH
ISNA = ISNB
ISNONTEXT = ISGEENTEKST
ISNUMBER = ISGETAL
ISODD = IS.ONEVEN
ISREF = ISVERWIJZING
ISTEXT = ISTEKST
N = N
NA = NB
SHEET = BLAD
SHEETS = BLADEN
TYPE = TYPE
##
## Logische functies (Logical Functions)
##
AND = EN
FALSE = ONWAAR
IF = ALS
IFERROR = ALS.FOUT
IFNA = ALS.NB
IFS = ALS.VOORWAARDEN
NOT = NIET
OR = OF
SWITCH = SCHAKELEN
TRUE = WAAR
XOR = EX.OF
##
## Zoek- en verwijzingsfuncties (Lookup & Reference Functions)
##
ADDRESS = ADRES
AREAS = BEREIKEN
CHOOSE = KIEZEN
COLUMN = KOLOM
COLUMNS = KOLOMMEN
FORMULATEXT = FORMULETEKST
GETPIVOTDATA = DRAAITABEL.OPHALEN
HLOOKUP = HORIZ.ZOEKEN
HYPERLINK = HYPERLINK
INDEX = INDEX
INDIRECT = INDIRECT
LOOKUP = ZOEKEN
MATCH = VERGELIJKEN
OFFSET = VERSCHUIVING
ROW = RIJ
ROWS = RIJEN
RTD = RTG
TRANSPOSE = TRANSPONEREN
VLOOKUP = VERT.ZOEKEN
*RC = RK
##
## Wiskundige en trigonometrische functies (Math & Trig Functions)
##
ABS = ABS
ACOS = BOOGCOS
ACOSH = BOOGCOSH
ACOT = BOOGCOT
ACOTH = BOOGCOTH
AGGREGATE = AGGREGAAT
ARABIC = ARABISCH
ASIN = BOOGSIN
ASINH = BOOGSINH
ATAN = BOOGTAN
ATAN2 = BOOGTAN2
ATANH = BOOGTANH
BASE = BASIS
CEILING.MATH = AFRONDEN.BOVEN.WISK
CEILING.PRECISE = AFRONDEN.BOVEN.NAUWKEURIG
COMBIN = COMBINATIES
COMBINA = COMBIN.A
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = COSEC
CSCH = COSECH
DECIMAL = DECIMAAL
DEGREES = GRADEN
ECMA.CEILING = ECMA.AFRONDEN.BOVEN
EVEN = EVEN
EXP = EXP
FACT = FACULTEIT
FACTDOUBLE = DUBBELE.FACULTEIT
FLOOR.MATH = AFRONDEN.BENEDEN.WISK
FLOOR.PRECISE = AFRONDEN.BENEDEN.NAUWKEURIG
GCD = GGD
INT = INTEGER
ISO.CEILING = ISO.AFRONDEN.BOVEN
LCM = KGV
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = DETERMINANTMAT
MINVERSE = INVERSEMAT
MMULT = PRODUCTMAT
MOD = REST
MROUND = AFRONDEN.N.VEELVOUD
MULTINOMIAL = MULTINOMIAAL
MUNIT = EENHEIDMAT
ODD = ONEVEN
PI = PI
POWER = MACHT
PRODUCT = PRODUCT
QUOTIENT = QUOTIENT
RADIANS = RADIALEN
RAND = ASELECT
RANDBETWEEN = ASELECTTUSSEN
ROMAN = ROMEINS
ROUND = AFRONDEN
ROUNDBAHTDOWN = BAHT.AFR.NAAR.BENEDEN
ROUNDBAHTUP = BAHT.AFR.NAAR.BOVEN
ROUNDDOWN = AFRONDEN.NAAR.BENEDEN
ROUNDUP = AFRONDEN.NAAR.BOVEN
SEC = SEC
SECH = SECH
SERIESSUM = SOM.MACHTREEKS
SIGN = POS.NEG
SIN = SIN
SINH = SINH
SQRT = WORTEL
SQRTPI = WORTEL.PI
SUBTOTAL = SUBTOTAAL
SUM = SOM
SUMIF = SOM.ALS
SUMIFS = SOMMEN.ALS
SUMPRODUCT = SOMPRODUCT
SUMSQ = KWADRATENSOM
SUMX2MY2 = SOM.X2MINY2
SUMX2PY2 = SOM.X2PLUSY2
SUMXMY2 = SOM.XMINY.2
TAN = TAN
TANH = TANH
TRUNC = GEHEEL
##
## Statistische functies (Statistical Functions)
##
AVEDEV = GEM.DEVIATIE
AVERAGE = GEMIDDELDE
AVERAGEA = GEMIDDELDEA
AVERAGEIF = GEMIDDELDE.ALS
AVERAGEIFS = GEMIDDELDEN.ALS
BETA.DIST = BETA.VERD
BETA.INV = BETA.INV
BINOM.DIST = BINOM.VERD
BINOM.DIST.RANGE = BINOM.VERD.BEREIK
BINOM.INV = BINOMIALE.INV
CHISQ.DIST = CHIKW.VERD
CHISQ.DIST.RT = CHIKW.VERD.RECHTS
CHISQ.INV = CHIKW.INV
CHISQ.INV.RT = CHIKW.INV.RECHTS
CHISQ.TEST = CHIKW.TEST
CONFIDENCE.NORM = VERTROUWELIJKHEID.NORM
CONFIDENCE.T = VERTROUWELIJKHEID.T
CORREL = CORRELATIE
COUNT = AANTAL
COUNTA = AANTALARG
COUNTBLANK = AANTAL.LEGE.CELLEN
COUNTIF = AANTAL.ALS
COUNTIFS = AANTALLEN.ALS
COVARIANCE.P = COVARIANTIE.P
COVARIANCE.S = COVARIANTIE.S
DEVSQ = DEV.KWAD
EXPON.DIST = EXPON.VERD.N
F.DIST = F.VERD
F.DIST.RT = F.VERD.RECHTS
F.INV = F.INV
F.INV.RT = F.INV.RECHTS
F.TEST = F.TEST
FISHER = FISHER
FISHERINV = FISHER.INV
FORECAST.ETS = VOORSPELLEN.ETS
FORECAST.ETS.CONFINT = VOORSPELLEN.ETS.CONFINT
FORECAST.ETS.SEASONALITY = VOORSPELLEN.ETS.SEASONALITY
FORECAST.ETS.STAT = FORECAST.ETS.STAT
FORECAST.LINEAR = VOORSPELLEN.LINEAR
FREQUENCY = INTERVAL
GAMMA = GAMMA
GAMMA.DIST = GAMMA.VERD.N
GAMMA.INV = GAMMA.INV.N
GAMMALN = GAMMA.LN
GAMMALN.PRECISE = GAMMA.LN.NAUWKEURIG
GAUSS = GAUSS
GEOMEAN = MEETK.GEM
GROWTH = GROEI
HARMEAN = HARM.GEM
HYPGEOM.DIST = HYPGEOM.VERD
INTERCEPT = SNIJPUNT
KURT = KURTOSIS
LARGE = GROOTSTE
LINEST = LIJNSCH
LOGEST = LOGSCH
LOGNORM.DIST = LOGNORM.VERD
LOGNORM.INV = LOGNORM.INV
MAX = MAX
MAXA = MAXA
MAXIFS = MAX.ALS.VOORWAARDEN
MEDIAN = MEDIAAN
MIN = MIN
MINA = MINA
MINIFS = MIN.ALS.VOORWAARDEN
MODE.MULT = MODUS.MEERV
MODE.SNGL = MODUS.ENKELV
NEGBINOM.DIST = NEGBINOM.VERD
NORM.DIST = NORM.VERD.N
NORM.INV = NORM.INV.N
NORM.S.DIST = NORM.S.VERD
NORM.S.INV = NORM.S.INV
PEARSON = PEARSON
PERCENTILE.EXC = PERCENTIEL.EXC
PERCENTILE.INC = PERCENTIEL.INC
PERCENTRANK.EXC = PROCENTRANG.EXC
PERCENTRANK.INC = PROCENTRANG.INC
PERMUT = PERMUTATIES
PERMUTATIONA = PERMUTATIE.A
PHI = PHI
POISSON.DIST = POISSON.VERD
PROB = KANS
QUARTILE.EXC = KWARTIEL.EXC
QUARTILE.INC = KWARTIEL.INC
RANK.AVG = RANG.GEMIDDELDE
RANK.EQ = RANG.GELIJK
RSQ = R.KWADRAAT
SKEW = SCHEEFHEID
SKEW.P = SCHEEFHEID.P
SLOPE = RICHTING
SMALL = KLEINSTE
STANDARDIZE = NORMALISEREN
STDEV.P = STDEV.P
STDEV.S = STDEV.S
STDEVA = STDEVA
STDEVPA = STDEVPA
STEYX = STAND.FOUT.YX
T.DIST = T.DIST
T.DIST.2T = T.VERD.2T
T.DIST.RT = T.VERD.RECHTS
T.INV = T.INV
T.INV.2T = T.INV.2T
T.TEST = T.TEST
TREND = TREND
TRIMMEAN = GETRIMD.GEM
VAR.P = VAR.P
VAR.S = VAR.S
VARA = VARA
VARPA = VARPA
WEIBULL.DIST = WEIBULL.VERD
Z.TEST = Z.TEST
##
## Tekstfuncties (Text Functions)
##
BAHTTEXT = BAHT.TEKST
CHAR = TEKEN
CLEAN = WISSEN.CONTROL
CODE = CODE
CONCAT = TEKST.SAMENV
DOLLAR = EURO
EXACT = GELIJK
FIND = VIND.ALLES
FIXED = VAST
ISTHAIDIGIT = IS.THAIS.CIJFER
LEFT = LINKS
LEN = LENGTE
LOWER = KLEINE.LETTERS
MID = DEEL
NUMBERSTRING = GETALNOTATIE
NUMBERVALUE = NUMERIEKE.WAARDE
PHONETIC = FONETISCH
PROPER = BEGINLETTERS
REPLACE = VERVANGEN
REPT = HERHALING
RIGHT = RECHTS
SEARCH = VIND.SPEC
SUBSTITUTE = SUBSTITUEREN
T = T
TEXT = TEKST
TEXTJOIN = TEKST.COMBINEREN
THAIDIGIT = THAIS.CIJFER
THAINUMSOUND = THAIS.GETAL.GELUID
THAINUMSTRING = THAIS.GETAL.REEKS
THAISTRINGLENGTH = THAIS.REEKS.LENGTE
TRIM = SPATIES.WISSEN
UNICHAR = UNITEKEN
UNICODE = UNICODE
UPPER = HOOFDLETTERS
VALUE = WAARDE
##
## Webfuncties (Web Functions)
##
ENCODEURL = URL.CODEREN
FILTERXML = XML.FILTEREN
WEBSERVICE = WEBSERVICE
##
## Compatibiliteitsfuncties (Compatibility Functions)
##
BETADIST = BETAVERD
BETAINV = BETAINV
BINOMDIST = BINOMIALE.VERD
CEILING = AFRONDEN.BOVEN
CHIDIST = CHI.KWADRAAT
CHIINV = CHI.KWADRAAT.INV
CHITEST = CHI.TOETS
CONCATENATE = TEKST.SAMENVOEGEN
CONFIDENCE = BETROUWBAARHEID
COVAR = COVARIANTIE
CRITBINOM = CRIT.BINOM
EXPONDIST = EXPON.VERD
FDIST = F.VERDELING
FINV = F.INVERSE
FLOOR = AFRONDEN.BENEDEN
FORECAST = VOORSPELLEN
FTEST = F.TOETS
GAMMADIST = GAMMA.VERD
GAMMAINV = GAMMA.INV
HYPGEOMDIST = HYPERGEO.VERD
LOGINV = LOG.NORM.INV
LOGNORMDIST = LOG.NORM.VERD
MODE = MODUS
NEGBINOMDIST = NEG.BINOM.VERD
NORMDIST = NORM.VERD
NORMINV = NORM.INV
NORMSDIST = STAND.NORM.VERD
NORMSINV = STAND.NORM.INV
PERCENTILE = PERCENTIEL
PERCENTRANK = PERCENT.RANG
POISSON = POISSON
QUARTILE = KWARTIEL
RANK = RANG
STDEV = STDEV
STDEVP = STDEVP
TDIST = T.VERD
TINV = TINV
TTEST = T.TOETS
VAR = VAR
VARP = VARP
WEIBULL = WEIBULL
ZTEST = Z.TOETS
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nl/config 0000644 00000000514 15242753725 0020603 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Nederlands (Dutch)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #LEEG!
DIV0 = #DEEL/0!
VALUE = #WAARDE!
REF = #VERW!
NAME = #NAAM?
NUM = #GETAL!
NA = #N/B
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/functions 0000644 00000024740 15242753725 0021353 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Français (French)
##
############################################################
##
## Fonctions Cube (Cube Functions)
##
CUBEKPIMEMBER = MEMBREKPICUBE
CUBEMEMBER = MEMBRECUBE
CUBEMEMBERPROPERTY = PROPRIETEMEMBRECUBE
CUBERANKEDMEMBER = RANGMEMBRECUBE
CUBESET = JEUCUBE
CUBESETCOUNT = NBJEUCUBE
CUBEVALUE = VALEURCUBE
##
## Fonctions de base de données (Database Functions)
##
DAVERAGE = BDMOYENNE
DCOUNT = BDNB
DCOUNTA = BDNBVAL
DGET = BDLIRE
DMAX = BDMAX
DMIN = BDMIN
DPRODUCT = BDPRODUIT
DSTDEV = BDECARTYPE
DSTDEVP = BDECARTYPEP
DSUM = BDSOMME
DVAR = BDVAR
DVARP = BDVARP
##
## Fonctions de date et d’heure (Date & Time Functions)
##
DATE = DATE
DATEVALUE = DATEVAL
DAY = JOUR
DAYS = JOURS
DAYS360 = JOURS360
EDATE = MOIS.DECALER
EOMONTH = FIN.MOIS
HOUR = HEURE
ISOWEEKNUM = NO.SEMAINE.ISO
MINUTE = MINUTE
MONTH = MOIS
NETWORKDAYS = NB.JOURS.OUVRES
NETWORKDAYS.INTL = NB.JOURS.OUVRES.INTL
NOW = MAINTENANT
SECOND = SECONDE
TIME = TEMPS
TIMEVALUE = TEMPSVAL
TODAY = AUJOURDHUI
WEEKDAY = JOURSEM
WEEKNUM = NO.SEMAINE
WORKDAY = SERIE.JOUR.OUVRE
WORKDAY.INTL = SERIE.JOUR.OUVRE.INTL
YEAR = ANNEE
YEARFRAC = FRACTION.ANNEE
##
## Fonctions d’ingénierie (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BINDEC
BIN2HEX = BINHEX
BIN2OCT = BINOCT
BITAND = BITET
BITLSHIFT = BITDECALG
BITOR = BITOU
BITRSHIFT = BITDECALD
BITXOR = BITOUEXCLUSIF
COMPLEX = COMPLEXE
CONVERT = CONVERT
DEC2BIN = DECBIN
DEC2HEX = DECHEX
DEC2OCT = DECOCT
DELTA = DELTA
ERF = ERF
ERF.PRECISE = ERF.PRECIS
ERFC = ERFC
ERFC.PRECISE = ERFC.PRECIS
GESTEP = SUP.SEUIL
HEX2BIN = HEXBIN
HEX2DEC = HEXDEC
HEX2OCT = HEXOCT
IMABS = COMPLEXE.MODULE
IMAGINARY = COMPLEXE.IMAGINAIRE
IMARGUMENT = COMPLEXE.ARGUMENT
IMCONJUGATE = COMPLEXE.CONJUGUE
IMCOS = COMPLEXE.COS
IMCOSH = COMPLEXE.COSH
IMCOT = COMPLEXE.COT
IMCSC = COMPLEXE.CSC
IMCSCH = COMPLEXE.CSCH
IMDIV = COMPLEXE.DIV
IMEXP = COMPLEXE.EXP
IMLN = COMPLEXE.LN
IMLOG10 = COMPLEXE.LOG10
IMLOG2 = COMPLEXE.LOG2
IMPOWER = COMPLEXE.PUISSANCE
IMPRODUCT = COMPLEXE.PRODUIT
IMREAL = COMPLEXE.REEL
IMSEC = COMPLEXE.SEC
IMSECH = COMPLEXE.SECH
IMSIN = COMPLEXE.SIN
IMSINH = COMPLEXE.SINH
IMSQRT = COMPLEXE.RACINE
IMSUB = COMPLEXE.DIFFERENCE
IMSUM = COMPLEXE.SOMME
IMTAN = COMPLEXE.TAN
OCT2BIN = OCTBIN
OCT2DEC = OCTDEC
OCT2HEX = OCTHEX
##
## Fonctions financières (Financial Functions)
##
ACCRINT = INTERET.ACC
ACCRINTM = INTERET.ACC.MAT
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = NB.JOURS.COUPON.PREC
COUPDAYS = NB.JOURS.COUPONS
COUPDAYSNC = NB.JOURS.COUPON.SUIV
COUPNCD = DATE.COUPON.SUIV
COUPNUM = NB.COUPONS
COUPPCD = DATE.COUPON.PREC
CUMIPMT = CUMUL.INTER
CUMPRINC = CUMUL.PRINCPER
DB = DB
DDB = DDB
DISC = TAUX.ESCOMPTE
DOLLARDE = PRIX.DEC
DOLLARFR = PRIX.FRAC
DURATION = DUREE
EFFECT = TAUX.EFFECTIF
FV = VC
FVSCHEDULE = VC.PAIEMENTS
INTRATE = TAUX.INTERET
IPMT = INTPER
IRR = TRI
ISPMT = ISPMT
MDURATION = DUREE.MODIFIEE
MIRR = TRIM
NOMINAL = TAUX.NOMINAL
NPER = NPM
NPV = VAN
ODDFPRICE = PRIX.PCOUPON.IRREG
ODDFYIELD = REND.PCOUPON.IRREG
ODDLPRICE = PRIX.DCOUPON.IRREG
ODDLYIELD = REND.DCOUPON.IRREG
PDURATION = PDUREE
PMT = VPM
PPMT = PRINCPER
PRICE = PRIX.TITRE
PRICEDISC = VALEUR.ENCAISSEMENT
PRICEMAT = PRIX.TITRE.ECHEANCE
PV = VA
RATE = TAUX
RECEIVED = VALEUR.NOMINALE
RRI = TAUX.INT.EQUIV
SLN = AMORLIN
SYD = SYD
TBILLEQ = TAUX.ESCOMPTE.R
TBILLPRICE = PRIX.BON.TRESOR
TBILLYIELD = RENDEMENT.BON.TRESOR
VDB = VDB
XIRR = TRI.PAIEMENTS
XNPV = VAN.PAIEMENTS
YIELD = RENDEMENT.TITRE
YIELDDISC = RENDEMENT.SIMPLE
YIELDMAT = RENDEMENT.TITRE.ECHEANCE
##
## Fonctions d’information (Information Functions)
##
CELL = CELLULE
ERROR.TYPE = TYPE.ERREUR
INFO = INFORMATIONS
ISBLANK = ESTVIDE
ISERR = ESTERR
ISERROR = ESTERREUR
ISEVEN = EST.PAIR
ISFORMULA = ESTFORMULE
ISLOGICAL = ESTLOGIQUE
ISNA = ESTNA
ISNONTEXT = ESTNONTEXTE
ISNUMBER = ESTNUM
ISODD = EST.IMPAIR
ISREF = ESTREF
ISTEXT = ESTTEXTE
N = N
NA = NA
SHEET = FEUILLE
SHEETS = FEUILLES
TYPE = TYPE
##
## Fonctions logiques (Logical Functions)
##
AND = ET
FALSE = FAUX
IF = SI
IFERROR = SIERREUR
IFNA = SI.NON.DISP
IFS = SI.CONDITIONS
NOT = NON
OR = OU
SWITCH = SI.MULTIPLE
TRUE = VRAI
XOR = OUX
##
## Fonctions de recherche et de référence (Lookup & Reference Functions)
##
ADDRESS = ADRESSE
AREAS = ZONES
CHOOSE = CHOISIR
COLUMN = COLONNE
COLUMNS = COLONNES
FORMULATEXT = FORMULETEXTE
GETPIVOTDATA = LIREDONNEESTABCROISDYNAMIQUE
HLOOKUP = RECHERCHEH
HYPERLINK = LIEN_HYPERTEXTE
INDEX = INDEX
INDIRECT = INDIRECT
LOOKUP = RECHERCHE
MATCH = EQUIV
OFFSET = DECALER
ROW = LIGNE
ROWS = LIGNES
RTD = RTD
TRANSPOSE = TRANSPOSE
VLOOKUP = RECHERCHEV
*RC = LC
##
## Fonctions mathématiques et trigonométriques (Math & Trig Functions)
##
ABS = ABS
ACOS = ACOS
ACOSH = ACOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = AGREGAT
ARABIC = CHIFFRE.ARABE
ASIN = ASIN
ASINH = ASINH
ATAN = ATAN
ATAN2 = ATAN2
ATANH = ATANH
BASE = BASE
CEILING.MATH = PLAFOND.MATH
CEILING.PRECISE = PLAFOND.PRECIS
COMBIN = COMBIN
COMBINA = COMBINA
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = DECIMAL
DEGREES = DEGRES
ECMA.CEILING = ECMA.PLAFOND
EVEN = PAIR
EXP = EXP
FACT = FACT
FACTDOUBLE = FACTDOUBLE
FLOOR.MATH = PLANCHER.MATH
FLOOR.PRECISE = PLANCHER.PRECIS
GCD = PGCD
INT = ENT
ISO.CEILING = ISO.PLAFOND
LCM = PPCM
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = DETERMAT
MINVERSE = INVERSEMAT
MMULT = PRODUITMAT
MOD = MOD
MROUND = ARRONDI.AU.MULTIPLE
MULTINOMIAL = MULTINOMIALE
MUNIT = MATRICE.UNITAIRE
ODD = IMPAIR
PI = PI
POWER = PUISSANCE
PRODUCT = PRODUIT
QUOTIENT = QUOTIENT
RADIANS = RADIANS
RAND = ALEA
RANDBETWEEN = ALEA.ENTRE.BORNES
ROMAN = ROMAIN
ROUND = ARRONDI
ROUNDDOWN = ARRONDI.INF
ROUNDUP = ARRONDI.SUP
SEC = SEC
SECH = SECH
SERIESSUM = SOMME.SERIES
SIGN = SIGNE
SIN = SIN
SINH = SINH
SQRT = RACINE
SQRTPI = RACINE.PI
SUBTOTAL = SOUS.TOTAL
SUM = SOMME
SUMIF = SOMME.SI
SUMIFS = SOMME.SI.ENS
SUMPRODUCT = SOMMEPROD
SUMSQ = SOMME.CARRES
SUMX2MY2 = SOMME.X2MY2
SUMX2PY2 = SOMME.X2PY2
SUMXMY2 = SOMME.XMY2
TAN = TAN
TANH = TANH
TRUNC = TRONQUE
##
## Fonctions statistiques (Statistical Functions)
##
AVEDEV = ECART.MOYEN
AVERAGE = MOYENNE
AVERAGEA = AVERAGEA
AVERAGEIF = MOYENNE.SI
AVERAGEIFS = MOYENNE.SI.ENS
BETA.DIST = LOI.BETA.N
BETA.INV = BETA.INVERSE.N
BINOM.DIST = LOI.BINOMIALE.N
BINOM.DIST.RANGE = LOI.BINOMIALE.SERIE
BINOM.INV = LOI.BINOMIALE.INVERSE
CHISQ.DIST = LOI.KHIDEUX.N
CHISQ.DIST.RT = LOI.KHIDEUX.DROITE
CHISQ.INV = LOI.KHIDEUX.INVERSE
CHISQ.INV.RT = LOI.KHIDEUX.INVERSE.DROITE
CHISQ.TEST = CHISQ.TEST
CONFIDENCE.NORM = INTERVALLE.CONFIANCE.NORMAL
CONFIDENCE.T = INTERVALLE.CONFIANCE.STUDENT
CORREL = COEFFICIENT.CORRELATION
COUNT = NB
COUNTA = NBVAL
COUNTBLANK = NB.VIDE
COUNTIF = NB.SI
COUNTIFS = NB.SI.ENS
COVARIANCE.P = COVARIANCE.PEARSON
COVARIANCE.S = COVARIANCE.STANDARD
DEVSQ = SOMME.CARRES.ECARTS
EXPON.DIST = LOI.EXPONENTIELLE.N
F.DIST = LOI.F.N
F.DIST.RT = LOI.F.DROITE
F.INV = INVERSE.LOI.F.N
F.INV.RT = INVERSE.LOI.F.DROITE
F.TEST = F.TEST
FISHER = FISHER
FISHERINV = FISHER.INVERSE
FORECAST.ETS = PREVISION.ETS
FORECAST.ETS.CONFINT = PREVISION.ETS.CONFINT
FORECAST.ETS.SEASONALITY = PREVISION.ETS.CARACTERESAISONNIER
FORECAST.ETS.STAT = PREVISION.ETS.STAT
FORECAST.LINEAR = PREVISION.LINEAIRE
FREQUENCY = FREQUENCE
GAMMA = GAMMA
GAMMA.DIST = LOI.GAMMA.N
GAMMA.INV = LOI.GAMMA.INVERSE.N
GAMMALN = LNGAMMA
GAMMALN.PRECISE = LNGAMMA.PRECIS
GAUSS = GAUSS
GEOMEAN = MOYENNE.GEOMETRIQUE
GROWTH = CROISSANCE
HARMEAN = MOYENNE.HARMONIQUE
HYPGEOM.DIST = LOI.HYPERGEOMETRIQUE.N
INTERCEPT = ORDONNEE.ORIGINE
KURT = KURTOSIS
LARGE = GRANDE.VALEUR
LINEST = DROITEREG
LOGEST = LOGREG
LOGNORM.DIST = LOI.LOGNORMALE.N
LOGNORM.INV = LOI.LOGNORMALE.INVERSE.N
MAX = MAX
MAXA = MAXA
MAXIFS = MAX.SI
MEDIAN = MEDIANE
MIN = MIN
MINA = MINA
MINIFS = MIN.SI
MODE.MULT = MODE.MULTIPLE
MODE.SNGL = MODE.SIMPLE
NEGBINOM.DIST = LOI.BINOMIALE.NEG.N
NORM.DIST = LOI.NORMALE.N
NORM.INV = LOI.NORMALE.INVERSE.N
NORM.S.DIST = LOI.NORMALE.STANDARD.N
NORM.S.INV = LOI.NORMALE.STANDARD.INVERSE.N
PEARSON = PEARSON
PERCENTILE.EXC = CENTILE.EXCLURE
PERCENTILE.INC = CENTILE.INCLURE
PERCENTRANK.EXC = RANG.POURCENTAGE.EXCLURE
PERCENTRANK.INC = RANG.POURCENTAGE.INCLURE
PERMUT = PERMUTATION
PERMUTATIONA = PERMUTATIONA
PHI = PHI
POISSON.DIST = LOI.POISSON.N
PROB = PROBABILITE
QUARTILE.EXC = QUARTILE.EXCLURE
QUARTILE.INC = QUARTILE.INCLURE
RANK.AVG = MOYENNE.RANG
RANK.EQ = EQUATION.RANG
RSQ = COEFFICIENT.DETERMINATION
SKEW = COEFFICIENT.ASYMETRIE
SKEW.P = COEFFICIENT.ASYMETRIE.P
SLOPE = PENTE
SMALL = PETITE.VALEUR
STANDARDIZE = CENTREE.REDUITE
STDEV.P = ECARTYPE.PEARSON
STDEV.S = ECARTYPE.STANDARD
STDEVA = STDEVA
STDEVPA = STDEVPA
STEYX = ERREUR.TYPE.XY
T.DIST = LOI.STUDENT.N
T.DIST.2T = LOI.STUDENT.BILATERALE
T.DIST.RT = LOI.STUDENT.DROITE
T.INV = LOI.STUDENT.INVERSE.N
T.INV.2T = LOI.STUDENT.INVERSE.BILATERALE
T.TEST = T.TEST
TREND = TENDANCE
TRIMMEAN = MOYENNE.REDUITE
VAR.P = VAR.P.N
VAR.S = VAR.S
VARA = VARA
VARPA = VARPA
WEIBULL.DIST = LOI.WEIBULL.N
Z.TEST = Z.TEST
##
## Fonctions de texte (Text Functions)
##
BAHTTEXT = BAHTTEXT
CHAR = CAR
CLEAN = EPURAGE
CODE = CODE
CONCAT = CONCAT
DOLLAR = DEVISE
EXACT = EXACT
FIND = TROUVE
FIXED = CTXT
LEFT = GAUCHE
LEN = NBCAR
LOWER = MINUSCULE
MID = STXT
NUMBERVALUE = VALEURNOMBRE
PHONETIC = PHONETIQUE
PROPER = NOMPROPRE
REPLACE = REMPLACER
REPT = REPT
RIGHT = DROITE
SEARCH = CHERCHE
SUBSTITUTE = SUBSTITUE
T = T
TEXT = TEXTE
TEXTJOIN = JOINDRE.TEXTE
TRIM = SUPPRESPACE
UNICHAR = UNICAR
UNICODE = UNICODE
UPPER = MAJUSCULE
VALUE = CNUM
##
## Fonctions web (Web Functions)
##
ENCODEURL = URLENCODAGE
FILTERXML = FILTRE.XML
WEBSERVICE = SERVICEWEB
##
## Fonctions de compatibilité (Compatibility Functions)
##
BETADIST = LOI.BETA
BETAINV = BETA.INVERSE
BINOMDIST = LOI.BINOMIALE
CEILING = PLAFOND
CHIDIST = LOI.KHIDEUX
CHIINV = KHIDEUX.INVERSE
CHITEST = TEST.KHIDEUX
CONCATENATE = CONCATENER
CONFIDENCE = INTERVALLE.CONFIANCE
COVAR = COVARIANCE
CRITBINOM = CRITERE.LOI.BINOMIALE
EXPONDIST = LOI.EXPONENTIELLE
FDIST = LOI.F
FINV = INVERSE.LOI.F
FLOOR = PLANCHER
FORECAST = PREVISION
FTEST = TEST.F
GAMMADIST = LOI.GAMMA
GAMMAINV = LOI.GAMMA.INVERSE
HYPGEOMDIST = LOI.HYPERGEOMETRIQUE
LOGINV = LOI.LOGNORMALE.INVERSE
LOGNORMDIST = LOI.LOGNORMALE
MODE = MODE
NEGBINOMDIST = LOI.BINOMIALE.NEG
NORMDIST = LOI.NORMALE
NORMINV = LOI.NORMALE.INVERSE
NORMSDIST = LOI.NORMALE.STANDARD
NORMSINV = LOI.NORMALE.STANDARD.INVERSE
PERCENTILE = CENTILE
PERCENTRANK = RANG.POURCENTAGE
POISSON = LOI.POISSON
QUARTILE = QUARTILE
RANK = RANG
STDEV = ECARTYPE
STDEVP = ECARTYPEP
TDIST = LOI.STUDENT
TINV = LOI.STUDENT.INVERSE
TTEST = TEST.STUDENT
VAR = VAR
VARP = VAR.P
WEIBULL = LOI.WEIBULL
ZTEST = TEST.Z
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/fr/config 0000644 00000000460 15242753725 0020601 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Français (French)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL = #NUL!
DIV0
VALUE = #VALEUR!
REF
NAME = #NOM?
NUM = #NOMBRE!
NA
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/functions 0000644 00000023633 15242753725 0021334 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Deutsch (German)
##
############################################################
##
## Cubefunktionen (Cube Functions)
##
CUBEKPIMEMBER = CUBEKPIELEMENT
CUBEMEMBER = CUBEELEMENT
CUBEMEMBERPROPERTY = CUBEELEMENTEIGENSCHAFT
CUBERANKEDMEMBER = CUBERANGELEMENT
CUBESET = CUBEMENGE
CUBESETCOUNT = CUBEMENGENANZAHL
CUBEVALUE = CUBEWERT
##
## Datenbankfunktionen (Database Functions)
##
DAVERAGE = DBMITTELWERT
DCOUNT = DBANZAHL
DCOUNTA = DBANZAHL2
DGET = DBAUSZUG
DMAX = DBMAX
DMIN = DBMIN
DPRODUCT = DBPRODUKT
DSTDEV = DBSTDABW
DSTDEVP = DBSTDABWN
DSUM = DBSUMME
DVAR = DBVARIANZ
DVARP = DBVARIANZEN
##
## Datums- und Uhrzeitfunktionen (Date & Time Functions)
##
DATE = DATUM
DATEVALUE = DATWERT
DAY = TAG
DAYS = TAGE
DAYS360 = TAGE360
EDATE = EDATUM
EOMONTH = MONATSENDE
HOUR = STUNDE
ISOWEEKNUM = ISOKALENDERWOCHE
MINUTE = MINUTE
MONTH = MONAT
NETWORKDAYS = NETTOARBEITSTAGE
NETWORKDAYS.INTL = NETTOARBEITSTAGE.INTL
NOW = JETZT
SECOND = SEKUNDE
THAIDAYOFWEEK = THAIWOCHENTAG
THAIMONTHOFYEAR = THAIMONATDESJAHRES
THAIYEAR = THAIJAHR
TIME = ZEIT
TIMEVALUE = ZEITWERT
TODAY = HEUTE
WEEKDAY = WOCHENTAG
WEEKNUM = KALENDERWOCHE
WORKDAY = ARBEITSTAG
WORKDAY.INTL = ARBEITSTAG.INTL
YEAR = JAHR
YEARFRAC = BRTEILJAHRE
##
## Technische Funktionen (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BININDEZ
BIN2HEX = BININHEX
BIN2OCT = BININOKT
BITAND = BITUND
BITLSHIFT = BITLVERSCHIEB
BITOR = BITODER
BITRSHIFT = BITRVERSCHIEB
BITXOR = BITXODER
COMPLEX = KOMPLEXE
CONVERT = UMWANDELN
DEC2BIN = DEZINBIN
DEC2HEX = DEZINHEX
DEC2OCT = DEZINOKT
DELTA = DELTA
ERF = GAUSSFEHLER
ERF.PRECISE = GAUSSF.GENAU
ERFC = GAUSSFKOMPL
ERFC.PRECISE = GAUSSFKOMPL.GENAU
GESTEP = GGANZZAHL
HEX2BIN = HEXINBIN
HEX2DEC = HEXINDEZ
HEX2OCT = HEXINOKT
IMABS = IMABS
IMAGINARY = IMAGINÄRTEIL
IMARGUMENT = IMARGUMENT
IMCONJUGATE = IMKONJUGIERTE
IMCOS = IMCOS
IMCOSH = IMCOSHYP
IMCOT = IMCOT
IMCSC = IMCOSEC
IMCSCH = IMCOSECHYP
IMDIV = IMDIV
IMEXP = IMEXP
IMLN = IMLN
IMLOG10 = IMLOG10
IMLOG2 = IMLOG2
IMPOWER = IMAPOTENZ
IMPRODUCT = IMPRODUKT
IMREAL = IMREALTEIL
IMSEC = IMSEC
IMSECH = IMSECHYP
IMSIN = IMSIN
IMSINH = IMSINHYP
IMSQRT = IMWURZEL
IMSUB = IMSUB
IMSUM = IMSUMME
IMTAN = IMTAN
OCT2BIN = OKTINBIN
OCT2DEC = OKTINDEZ
OCT2HEX = OKTINHEX
##
## Finanzmathematische Funktionen (Financial Functions)
##
ACCRINT = AUFGELZINS
ACCRINTM = AUFGELZINSF
AMORDEGRC = AMORDEGRK
AMORLINC = AMORLINEARK
COUPDAYBS = ZINSTERMTAGVA
COUPDAYS = ZINSTERMTAGE
COUPDAYSNC = ZINSTERMTAGNZ
COUPNCD = ZINSTERMNZ
COUPNUM = ZINSTERMZAHL
COUPPCD = ZINSTERMVZ
CUMIPMT = KUMZINSZ
CUMPRINC = KUMKAPITAL
DB = GDA2
DDB = GDA
DISC = DISAGIO
DOLLARDE = NOTIERUNGDEZ
DOLLARFR = NOTIERUNGBRU
DURATION = DURATION
EFFECT = EFFEKTIV
FV = ZW
FVSCHEDULE = ZW2
INTRATE = ZINSSATZ
IPMT = ZINSZ
IRR = IKV
ISPMT = ISPMT
MDURATION = MDURATION
MIRR = QIKV
NOMINAL = NOMINAL
NPER = ZZR
NPV = NBW
ODDFPRICE = UNREGER.KURS
ODDFYIELD = UNREGER.REND
ODDLPRICE = UNREGLE.KURS
ODDLYIELD = UNREGLE.REND
PDURATION = PDURATION
PMT = RMZ
PPMT = KAPZ
PRICE = KURS
PRICEDISC = KURSDISAGIO
PRICEMAT = KURSFÄLLIG
PV = BW
RATE = ZINS
RECEIVED = AUSZAHLUNG
RRI = ZSATZINVEST
SLN = LIA
SYD = DIA
TBILLEQ = TBILLÄQUIV
TBILLPRICE = TBILLKURS
TBILLYIELD = TBILLRENDITE
VDB = VDB
XIRR = XINTZINSFUSS
XNPV = XKAPITALWERT
YIELD = RENDITE
YIELDDISC = RENDITEDIS
YIELDMAT = RENDITEFÄLL
##
## Informationsfunktionen (Information Functions)
##
CELL = ZELLE
ERROR.TYPE = FEHLER.TYP
INFO = INFO
ISBLANK = ISTLEER
ISERR = ISTFEHL
ISERROR = ISTFEHLER
ISEVEN = ISTGERADE
ISFORMULA = ISTFORMEL
ISLOGICAL = ISTLOG
ISNA = ISTNV
ISNONTEXT = ISTKTEXT
ISNUMBER = ISTZAHL
ISODD = ISTUNGERADE
ISREF = ISTBEZUG
ISTEXT = ISTTEXT
N = N
NA = NV
SHEET = BLATT
SHEETS = BLÄTTER
TYPE = TYP
##
## Logische Funktionen (Logical Functions)
##
AND = UND
FALSE = FALSCH
IF = WENN
IFERROR = WENNFEHLER
IFNA = WENNNV
IFS = WENNS
NOT = NICHT
OR = ODER
SWITCH = ERSTERWERT
TRUE = WAHR
XOR = XODER
##
## Nachschlage- und Verweisfunktionen (Lookup & Reference Functions)
##
ADDRESS = ADRESSE
AREAS = BEREICHE
CHOOSE = WAHL
COLUMN = SPALTE
COLUMNS = SPALTEN
FORMULATEXT = FORMELTEXT
GETPIVOTDATA = PIVOTDATENZUORDNEN
HLOOKUP = WVERWEIS
HYPERLINK = HYPERLINK
INDEX = INDEX
INDIRECT = INDIREKT
LOOKUP = VERWEIS
MATCH = VERGLEICH
OFFSET = BEREICH.VERSCHIEBEN
ROW = ZEILE
ROWS = ZEILEN
RTD = RTD
TRANSPOSE = MTRANS
VLOOKUP = SVERWEIS
*RC = ZS
##
## Mathematische und trigonometrische Funktionen (Math & Trig Functions)
##
ABS = ABS
ACOS = ARCCOS
ACOSH = ARCCOSHYP
ACOT = ARCCOT
ACOTH = ARCCOTHYP
AGGREGATE = AGGREGAT
ARABIC = ARABISCH
ASIN = ARCSIN
ASINH = ARCSINHYP
ATAN = ARCTAN
ATAN2 = ARCTAN2
ATANH = ARCTANHYP
BASE = BASIS
CEILING.MATH = OBERGRENZE.MATHEMATIK
CEILING.PRECISE = OBERGRENZE.GENAU
COMBIN = KOMBINATIONEN
COMBINA = KOMBINATIONEN2
COS = COS
COSH = COSHYP
COT = COT
COTH = COTHYP
CSC = COSEC
CSCH = COSECHYP
DECIMAL = DEZIMAL
DEGREES = GRAD
ECMA.CEILING = ECMA.OBERGRENZE
EVEN = GERADE
EXP = EXP
FACT = FAKULTÄT
FACTDOUBLE = ZWEIFAKULTÄT
FLOOR.MATH = UNTERGRENZE.MATHEMATIK
FLOOR.PRECISE = UNTERGRENZE.GENAU
GCD = GGT
INT = GANZZAHL
ISO.CEILING = ISO.OBERGRENZE
LCM = KGV
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = MDET
MINVERSE = MINV
MMULT = MMULT
MOD = REST
MROUND = VRUNDEN
MULTINOMIAL = POLYNOMIAL
MUNIT = MEINHEIT
ODD = UNGERADE
PI = PI
POWER = POTENZ
PRODUCT = PRODUKT
QUOTIENT = QUOTIENT
RADIANS = BOGENMASS
RAND = ZUFALLSZAHL
RANDBETWEEN = ZUFALLSBEREICH
ROMAN = RÖMISCH
ROUND = RUNDEN
ROUNDBAHTDOWN = RUNDBAHTNED
ROUNDBAHTUP = BAHTAUFRUNDEN
ROUNDDOWN = ABRUNDEN
ROUNDUP = AUFRUNDEN
SEC = SEC
SECH = SECHYP
SERIESSUM = POTENZREIHE
SIGN = VORZEICHEN
SIN = SIN
SINH = SINHYP
SQRT = WURZEL
SQRTPI = WURZELPI
SUBTOTAL = TEILERGEBNIS
SUM = SUMME
SUMIF = SUMMEWENN
SUMIFS = SUMMEWENNS
SUMPRODUCT = SUMMENPRODUKT
SUMSQ = QUADRATESUMME
SUMX2MY2 = SUMMEX2MY2
SUMX2PY2 = SUMMEX2PY2
SUMXMY2 = SUMMEXMY2
TAN = TAN
TANH = TANHYP
TRUNC = KÜRZEN
##
## Statistische Funktionen (Statistical Functions)
##
AVEDEV = MITTELABW
AVERAGE = MITTELWERT
AVERAGEA = MITTELWERTA
AVERAGEIF = MITTELWERTWENN
AVERAGEIFS = MITTELWERTWENNS
BETA.DIST = BETA.VERT
BETA.INV = BETA.INV
BINOM.DIST = BINOM.VERT
BINOM.DIST.RANGE = BINOM.VERT.BEREICH
BINOM.INV = BINOM.INV
CHISQ.DIST = CHIQU.VERT
CHISQ.DIST.RT = CHIQU.VERT.RE
CHISQ.INV = CHIQU.INV
CHISQ.INV.RT = CHIQU.INV.RE
CHISQ.TEST = CHIQU.TEST
CONFIDENCE.NORM = KONFIDENZ.NORM
CONFIDENCE.T = KONFIDENZ.T
CORREL = KORREL
COUNT = ANZAHL
COUNTA = ANZAHL2
COUNTBLANK = ANZAHLLEEREZELLEN
COUNTIF = ZÄHLENWENN
COUNTIFS = ZÄHLENWENNS
COVARIANCE.P = KOVARIANZ.P
COVARIANCE.S = KOVARIANZ.S
DEVSQ = SUMQUADABW
EXPON.DIST = EXPON.VERT
F.DIST = F.VERT
F.DIST.RT = F.VERT.RE
F.INV = F.INV
F.INV.RT = F.INV.RE
F.TEST = F.TEST
FISHER = FISHER
FISHERINV = FISHERINV
FORECAST.ETS = PROGNOSE.ETS
FORECAST.ETS.CONFINT = PROGNOSE.ETS.KONFINT
FORECAST.ETS.SEASONALITY = PROGNOSE.ETS.SAISONALITÄT
FORECAST.ETS.STAT = PROGNOSE.ETS.STAT
FORECAST.LINEAR = PROGNOSE.LINEAR
FREQUENCY = HÄUFIGKEIT
GAMMA = GAMMA
GAMMA.DIST = GAMMA.VERT
GAMMA.INV = GAMMA.INV
GAMMALN = GAMMALN
GAMMALN.PRECISE = GAMMALN.GENAU
GAUSS = GAUSS
GEOMEAN = GEOMITTEL
GROWTH = VARIATION
HARMEAN = HARMITTEL
HYPGEOM.DIST = HYPGEOM.VERT
INTERCEPT = ACHSENABSCHNITT
KURT = KURT
LARGE = KGRÖSSTE
LINEST = RGP
LOGEST = RKP
LOGNORM.DIST = LOGNORM.VERT
LOGNORM.INV = LOGNORM.INV
MAX = MAX
MAXA = MAXA
MAXIFS = MAXWENNS
MEDIAN = MEDIAN
MIN = MIN
MINA = MINA
MINIFS = MINWENNS
MODE.MULT = MODUS.VIELF
MODE.SNGL = MODUS.EINF
NEGBINOM.DIST = NEGBINOM.VERT
NORM.DIST = NORM.VERT
NORM.INV = NORM.INV
NORM.S.DIST = NORM.S.VERT
NORM.S.INV = NORM.S.INV
PEARSON = PEARSON
PERCENTILE.EXC = QUANTIL.EXKL
PERCENTILE.INC = QUANTIL.INKL
PERCENTRANK.EXC = QUANTILSRANG.EXKL
PERCENTRANK.INC = QUANTILSRANG.INKL
PERMUT = VARIATIONEN
PERMUTATIONA = VARIATIONEN2
PHI = PHI
POISSON.DIST = POISSON.VERT
PROB = WAHRSCHBEREICH
QUARTILE.EXC = QUARTILE.EXKL
QUARTILE.INC = QUARTILE.INKL
RANK.AVG = RANG.MITTELW
RANK.EQ = RANG.GLEICH
RSQ = BESTIMMTHEITSMASS
SKEW = SCHIEFE
SKEW.P = SCHIEFE.P
SLOPE = STEIGUNG
SMALL = KKLEINSTE
STANDARDIZE = STANDARDISIERUNG
STDEV.P = STABW.N
STDEV.S = STABW.S
STDEVA = STABWA
STDEVPA = STABWNA
STEYX = STFEHLERYX
T.DIST = T.VERT
T.DIST.2T = T.VERT.2S
T.DIST.RT = T.VERT.RE
T.INV = T.INV
T.INV.2T = T.INV.2S
T.TEST = T.TEST
TREND = TREND
TRIMMEAN = GESTUTZTMITTEL
VAR.P = VAR.P
VAR.S = VAR.S
VARA = VARIANZA
VARPA = VARIANZENA
WEIBULL.DIST = WEIBULL.VERT
Z.TEST = G.TEST
##
## Textfunktionen (Text Functions)
##
BAHTTEXT = BAHTTEXT
CHAR = ZEICHEN
CLEAN = SÄUBERN
CODE = CODE
CONCAT = TEXTKETTE
DOLLAR = DM
EXACT = IDENTISCH
FIND = FINDEN
FIXED = FEST
ISTHAIDIGIT = ISTTHAIZAHLENWORT
LEFT = LINKS
LEN = LÄNGE
LOWER = KLEIN
MID = TEIL
NUMBERVALUE = ZAHLENWERT
PROPER = GROSS2
REPLACE = ERSETZEN
REPT = WIEDERHOLEN
RIGHT = RECHTS
SEARCH = SUCHEN
SUBSTITUTE = WECHSELN
T = T
TEXT = TEXT
TEXTJOIN = TEXTVERKETTEN
THAIDIGIT = THAIZAHLENWORT
THAINUMSOUND = THAIZAHLSOUND
THAINUMSTRING = THAILANDSKNUMSTRENG
THAISTRINGLENGTH = THAIZEICHENFOLGENLÄNGE
TRIM = GLÄTTEN
UNICHAR = UNIZEICHEN
UNICODE = UNICODE
UPPER = GROSS
VALUE = WERT
##
## Webfunktionen (Web Functions)
##
ENCODEURL = URLCODIEREN
FILTERXML = XMLFILTERN
WEBSERVICE = WEBDIENST
##
## Kompatibilitätsfunktionen (Compatibility Functions)
##
BETADIST = BETAVERT
BETAINV = BETAINV
BINOMDIST = BINOMVERT
CEILING = OBERGRENZE
CHIDIST = CHIVERT
CHIINV = CHIINV
CHITEST = CHITEST
CONCATENATE = VERKETTEN
CONFIDENCE = KONFIDENZ
COVAR = KOVAR
CRITBINOM = KRITBINOM
EXPONDIST = EXPONVERT
FDIST = FVERT
FINV = FINV
FLOOR = UNTERGRENZE
FORECAST = SCHÄTZER
FTEST = FTEST
GAMMADIST = GAMMAVERT
GAMMAINV = GAMMAINV
HYPGEOMDIST = HYPGEOMVERT
LOGINV = LOGINV
LOGNORMDIST = LOGNORMVERT
MODE = MODALWERT
NEGBINOMDIST = NEGBINOMVERT
NORMDIST = NORMVERT
NORMINV = NORMINV
NORMSDIST = STANDNORMVERT
NORMSINV = STANDNORMINV
PERCENTILE = QUANTIL
PERCENTRANK = QUANTILSRANG
POISSON = POISSON
QUARTILE = QUARTILE
RANK = RANG
STDEV = STABW
STDEVP = STABWN
TDIST = TVERT
TINV = TINV
TTEST = TTEST
VAR = VARIANZ
VARP = VARIANZEN
WEIBULL = WEIBULL
ZTEST = GTEST
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/de/config 0000644 00000000452 15242753725 0020563 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Deutsch (German)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL
DIV0
VALUE = #WERT!
REF = #BEZUG!
NAME
NUM = #ZAHL!
NA = #NV
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/functions 0000644 00000024751 15242753725 0021345 0 ustar 00 ############################################################
##
## PhpSpreadsheet - function name translations
##
## Norsk Bokmål (Norwegian Bokmål)
##
############################################################
##
## Kubefunksjoner (Cube Functions)
##
CUBEKPIMEMBER = KUBEKPIMEDLEM
CUBEMEMBER = KUBEMEDLEM
CUBEMEMBERPROPERTY = KUBEMEDLEMEGENSKAP
CUBERANKEDMEMBER = KUBERANGERTMEDLEM
CUBESET = KUBESETT
CUBESETCOUNT = KUBESETTANTALL
CUBEVALUE = KUBEVERDI
##
## Databasefunksjoner (Database Functions)
##
DAVERAGE = DGJENNOMSNITT
DCOUNT = DANTALL
DCOUNTA = DANTALLA
DGET = DHENT
DMAX = DMAKS
DMIN = DMIN
DPRODUCT = DPRODUKT
DSTDEV = DSTDAV
DSTDEVP = DSTDAVP
DSUM = DSUMMER
DVAR = DVARIANS
DVARP = DVARIANSP
##
## Dato- og tidsfunksjoner (Date & Time Functions)
##
DATE = DATO
DATEDIF = DATODIFF
DATESTRING = DATOSTRENG
DATEVALUE = DATOVERDI
DAY = DAG
DAYS = DAGER
DAYS360 = DAGER360
EDATE = DAG.ETTER
EOMONTH = MÅNEDSSLUTT
HOUR = TIME
ISOWEEKNUM = ISOUKENR
MINUTE = MINUTT
MONTH = MÅNED
NETWORKDAYS = NETT.ARBEIDSDAGER
NETWORKDAYS.INTL = NETT.ARBEIDSDAGER.INTL
NOW = NÅ
SECOND = SEKUND
THAIDAYOFWEEK = THAIUKEDAG
THAIMONTHOFYEAR = THAIMÅNED
THAIYEAR = THAIÅR
TIME = TID
TIMEVALUE = TIDSVERDI
TODAY = IDAG
WEEKDAY = UKEDAG
WEEKNUM = UKENR
WORKDAY = ARBEIDSDAG
WORKDAY.INTL = ARBEIDSDAG.INTL
YEAR = ÅR
YEARFRAC = ÅRDEL
##
## Tekniske funksjoner (Engineering Functions)
##
BESSELI = BESSELI
BESSELJ = BESSELJ
BESSELK = BESSELK
BESSELY = BESSELY
BIN2DEC = BINTILDES
BIN2HEX = BINTILHEKS
BIN2OCT = BINTILOKT
BITAND = BITOG
BITLSHIFT = BITVFORSKYV
BITOR = BITELLER
BITRSHIFT = BITHFORSKYV
BITXOR = BITEKSKLUSIVELLER
COMPLEX = KOMPLEKS
CONVERT = KONVERTER
DEC2BIN = DESTILBIN
DEC2HEX = DESTILHEKS
DEC2OCT = DESTILOKT
DELTA = DELTA
ERF = FEILF
ERF.PRECISE = FEILF.PRESIS
ERFC = FEILFK
ERFC.PRECISE = FEILFK.PRESIS
GESTEP = GRENSEVERDI
HEX2BIN = HEKSTILBIN
HEX2DEC = HEKSTILDES
HEX2OCT = HEKSTILOKT
IMABS = IMABS
IMAGINARY = IMAGINÆR
IMARGUMENT = IMARGUMENT
IMCONJUGATE = IMKONJUGERT
IMCOS = IMCOS
IMCOSH = IMCOSH
IMCOT = IMCOT
IMCSC = IMCSC
IMCSCH = IMCSCH
IMDIV = IMDIV
IMEXP = IMEKSP
IMLN = IMLN
IMLOG10 = IMLOG10
IMLOG2 = IMLOG2
IMPOWER = IMOPPHØY
IMPRODUCT = IMPRODUKT
IMREAL = IMREELL
IMSEC = IMSEC
IMSECH = IMSECH
IMSIN = IMSIN
IMSINH = IMSINH
IMSQRT = IMROT
IMSUB = IMSUB
IMSUM = IMSUMMER
IMTAN = IMTAN
OCT2BIN = OKTTILBIN
OCT2DEC = OKTTILDES
OCT2HEX = OKTTILHEKS
##
## Økonomiske funksjoner (Financial Functions)
##
ACCRINT = PÅLØPT.PERIODISK.RENTE
ACCRINTM = PÅLØPT.FORFALLSRENTE
AMORDEGRC = AMORDEGRC
AMORLINC = AMORLINC
COUPDAYBS = OBLIG.DAGER.FF
COUPDAYS = OBLIG.DAGER
COUPDAYSNC = OBLIG.DAGER.NF
COUPNCD = OBLIG.DAGER.EF
COUPNUM = OBLIG.ANTALL
COUPPCD = OBLIG.DAG.FORRIGE
CUMIPMT = SAMLET.RENTE
CUMPRINC = SAMLET.HOVEDSTOL
DB = DAVSKR
DDB = DEGRAVS
DISC = DISKONTERT
DOLLARDE = DOLLARDE
DOLLARFR = DOLLARBR
DURATION = VARIGHET
EFFECT = EFFEKTIV.RENTE
FV = SLUTTVERDI
FVSCHEDULE = SVPLAN
INTRATE = RENTESATS
IPMT = RAVDRAG
IRR = IR
ISPMT = ER.AVDRAG
MDURATION = MVARIGHET
MIRR = MODIR
NOMINAL = NOMINELL
NPER = PERIODER
NPV = NNV
ODDFPRICE = AVVIKFP.PRIS
ODDFYIELD = AVVIKFP.AVKASTNING
ODDLPRICE = AVVIKSP.PRIS
ODDLYIELD = AVVIKSP.AVKASTNING
PDURATION = PVARIGHET
PMT = AVDRAG
PPMT = AMORT
PRICE = PRIS
PRICEDISC = PRIS.DISKONTERT
PRICEMAT = PRIS.FORFALL
PV = NÅVERDI
RATE = RENTE
RECEIVED = MOTTATT.AVKAST
RRI = REALISERT.AVKASTNING
SLN = LINAVS
SYD = ÅRSAVS
TBILLEQ = TBILLEKV
TBILLPRICE = TBILLPRIS
TBILLYIELD = TBILLAVKASTNING
VDB = VERDIAVS
XIRR = XIR
XNPV = XNNV
YIELD = AVKAST
YIELDDISC = AVKAST.DISKONTERT
YIELDMAT = AVKAST.FORFALL
##
## Informasjonsfunksjoner (Information Functions)
##
CELL = CELLE
ERROR.TYPE = FEIL.TYPE
INFO = INFO
ISBLANK = ERTOM
ISERR = ERF
ISERROR = ERFEIL
ISEVEN = ERPARTALL
ISFORMULA = ERFORMEL
ISLOGICAL = ERLOGISK
ISNA = ERIT
ISNONTEXT = ERIKKETEKST
ISNUMBER = ERTALL
ISODD = ERODDE
ISREF = ERREF
ISTEXT = ERTEKST
N = N
NA = IT
SHEET = ARK
SHEETS = ANTALL.ARK
TYPE = VERDITYPE
##
## Logiske funksjoner (Logical Functions)
##
AND = OG
FALSE = USANN
IF = HVIS
IFERROR = HVISFEIL
IFNA = HVIS.IT
IFS = HVIS.SETT
NOT = IKKE
OR = ELLER
SWITCH = BRYTER
TRUE = SANN
XOR = EKSKLUSIVELLER
##
## Oppslag- og referansefunksjoner (Lookup & Reference Functions)
##
ADDRESS = ADRESSE
AREAS = OMRÅDER
CHOOSE = VELG
COLUMN = KOLONNE
COLUMNS = KOLONNER
FORMULATEXT = FORMELTEKST
GETPIVOTDATA = HENTPIVOTDATA
HLOOKUP = FINN.KOLONNE
HYPERLINK = HYPERKOBLING
INDEX = INDEKS
INDIRECT = INDIREKTE
LOOKUP = SLÅ.OPP
MATCH = SAMMENLIGNE
OFFSET = FORSKYVNING
ROW = RAD
ROWS = RADER
RTD = RTD
TRANSPOSE = TRANSPONER
VLOOKUP = FINN.RAD
*RC = RK
##
## Matematikk- og trigonometrifunksjoner (Math & Trig Functions)
##
ABS = ABS
ACOS = ARCCOS
ACOSH = ARCCOSH
ACOT = ACOT
ACOTH = ACOTH
AGGREGATE = MENGDE
ARABIC = ARABISK
ASIN = ARCSIN
ASINH = ARCSINH
ATAN = ARCTAN
ATAN2 = ARCTAN2
ATANH = ARCTANH
BASE = GRUNNTALL
CEILING.MATH = AVRUND.GJELDENDE.MULTIPLUM.OPP.MATEMATISK
CEILING.PRECISE = AVRUND.GJELDENDE.MULTIPLUM.PRESIS
COMBIN = KOMBINASJON
COMBINA = KOMBINASJONA
COS = COS
COSH = COSH
COT = COT
COTH = COTH
CSC = CSC
CSCH = CSCH
DECIMAL = DESIMAL
DEGREES = GRADER
ECMA.CEILING = ECMA.AVRUND.GJELDENDE.MULTIPLUM
EVEN = AVRUND.TIL.PARTALL
EXP = EKSP
FACT = FAKULTET
FACTDOUBLE = DOBBELFAKT
FLOOR.MATH = AVRUND.GJELDENDE.MULTIPLUM.NED.MATEMATISK
FLOOR.PRECISE = AVRUND.GJELDENDE.MULTIPLUM.NED.PRESIS
GCD = SFF
INT = HELTALL
ISO.CEILING = ISO.AVRUND.GJELDENDE.MULTIPLUM
LCM = MFM
LN = LN
LOG = LOG
LOG10 = LOG10
MDETERM = MDETERM
MINVERSE = MINVERS
MMULT = MMULT
MOD = REST
MROUND = MRUND
MULTINOMIAL = MULTINOMINELL
MUNIT = MENHET
ODD = AVRUND.TIL.ODDETALL
PI = PI
POWER = OPPHØYD.I
PRODUCT = PRODUKT
QUOTIENT = KVOTIENT
RADIANS = RADIANER
RAND = TILFELDIG
RANDBETWEEN = TILFELDIGMELLOM
ROMAN = ROMERTALL
ROUND = AVRUND
ROUNDBAHTDOWN = RUNDAVBAHTNEDOVER
ROUNDBAHTUP = RUNDAVBAHTOPPOVER
ROUNDDOWN = AVRUND.NED
ROUNDUP = AVRUND.OPP
SEC = SEC
SECH = SECH
SERIESSUM = SUMMER.REKKE
SIGN = FORTEGN
SIN = SIN
SINH = SINH
SQRT = ROT
SQRTPI = ROTPI
SUBTOTAL = DELSUM
SUM = SUMMER
SUMIF = SUMMERHVIS
SUMIFS = SUMMER.HVIS.SETT
SUMPRODUCT = SUMMERPRODUKT
SUMSQ = SUMMERKVADRAT
SUMX2MY2 = SUMMERX2MY2
SUMX2PY2 = SUMMERX2PY2
SUMXMY2 = SUMMERXMY2
TAN = TAN
TANH = TANH
TRUNC = AVKORT
##
## Statistiske funksjoner (Statistical Functions)
##
AVEDEV = GJENNOMSNITTSAVVIK
AVERAGE = GJENNOMSNITT
AVERAGEA = GJENNOMSNITTA
AVERAGEIF = GJENNOMSNITTHVIS
AVERAGEIFS = GJENNOMSNITT.HVIS.SETT
BETA.DIST = BETA.FORDELING.N
BETA.INV = BETA.INV
BINOM.DIST = BINOM.FORDELING.N
BINOM.DIST.RANGE = BINOM.FORDELING.OMRÅDE
BINOM.INV = BINOM.INV
CHISQ.DIST = KJIKVADRAT.FORDELING
CHISQ.DIST.RT = KJIKVADRAT.FORDELING.H
CHISQ.INV = KJIKVADRAT.INV
CHISQ.INV.RT = KJIKVADRAT.INV.H
CHISQ.TEST = KJIKVADRAT.TEST
CONFIDENCE.NORM = KONFIDENS.NORM
CONFIDENCE.T = KONFIDENS.T
CORREL = KORRELASJON
COUNT = ANTALL
COUNTA = ANTALLA
COUNTBLANK = TELLBLANKE
COUNTIF = ANTALL.HVIS
COUNTIFS = ANTALL.HVIS.SETT
COVARIANCE.P = KOVARIANS.P
COVARIANCE.S = KOVARIANS.S
DEVSQ = AVVIK.KVADRERT
EXPON.DIST = EKSP.FORDELING.N
F.DIST = F.FORDELING
F.DIST.RT = F.FORDELING.H
F.INV = F.INV
F.INV.RT = F.INV.H
F.TEST = F.TEST
FISHER = FISHER
FISHERINV = FISHERINV
FORECAST.ETS = PROGNOSE.ETS
FORECAST.ETS.CONFINT = PROGNOSE.ETS.CONFINT
FORECAST.ETS.SEASONALITY = PROGNOSE.ETS.SESONGAVHENGIGHET
FORECAST.ETS.STAT = PROGNOSE.ETS.STAT
FORECAST.LINEAR = PROGNOSE.LINEÆR
FREQUENCY = FREKVENS
GAMMA = GAMMA
GAMMA.DIST = GAMMA.FORDELING
GAMMA.INV = GAMMA.INV
GAMMALN = GAMMALN
GAMMALN.PRECISE = GAMMALN.PRESIS
GAUSS = GAUSS
GEOMEAN = GJENNOMSNITT.GEOMETRISK
GROWTH = VEKST
HARMEAN = GJENNOMSNITT.HARMONISK
HYPGEOM.DIST = HYPGEOM.FORDELING.N
INTERCEPT = SKJÆRINGSPUNKT
KURT = KURT
LARGE = N.STØRST
LINEST = RETTLINJE
LOGEST = KURVE
LOGNORM.DIST = LOGNORM.FORDELING
LOGNORM.INV = LOGNORM.INV
MAX = STØRST
MAXA = MAKSA
MAXIFS = MAKS.HVIS.SETT
MEDIAN = MEDIAN
MIN = MIN
MINA = MINA
MINIFS = MIN.HVIS.SETT
MODE.MULT = MODUS.MULT
MODE.SNGL = MODUS.SNGL
NEGBINOM.DIST = NEGBINOM.FORDELING.N
NORM.DIST = NORM.FORDELING
NORM.INV = NORM.INV
NORM.S.DIST = NORM.S.FORDELING
NORM.S.INV = NORM.S.INV
PEARSON = PEARSON
PERCENTILE.EXC = PERSENTIL.EKS
PERCENTILE.INC = PERSENTIL.INK
PERCENTRANK.EXC = PROSENTDEL.EKS
PERCENTRANK.INC = PROSENTDEL.INK
PERMUT = PERMUTER
PERMUTATIONA = PERMUTASJONA
PHI = PHI
POISSON.DIST = POISSON.FORDELING
PROB = SANNSYNLIG
QUARTILE.EXC = KVARTIL.EKS
QUARTILE.INC = KVARTIL.INK
RANK.AVG = RANG.GJSN
RANK.EQ = RANG.EKV
RSQ = RKVADRAT
SKEW = SKJEVFORDELING
SKEW.P = SKJEVFORDELING.P
SLOPE = STIGNINGSTALL
SMALL = N.MINST
STANDARDIZE = NORMALISER
STDEV.P = STDAV.P
STDEV.S = STDAV.S
STDEVA = STDAVVIKA
STDEVPA = STDAVVIKPA
STEYX = STANDARDFEIL
T.DIST = T.FORDELING
T.DIST.2T = T.FORDELING.2T
T.DIST.RT = T.FORDELING.H
T.INV = T.INV
T.INV.2T = T.INV.2T
T.TEST = T.TEST
TREND = TREND
TRIMMEAN = TRIMMET.GJENNOMSNITT
VAR.P = VARIANS.P
VAR.S = VARIANS.S
VARA = VARIANSA
VARPA = VARIANSPA
WEIBULL.DIST = WEIBULL.DIST.N
Z.TEST = Z.TEST
##
## Tekstfunksjoner (Text Functions)
##
ASC = STIGENDE
BAHTTEXT = BAHTTEKST
CHAR = TEGNKODE
CLEAN = RENSK
CODE = KODE
CONCAT = KJED.SAMMEN
DOLLAR = VALUTA
EXACT = EKSAKT
FIND = FINN
FIXED = FASTSATT
ISTHAIDIGIT = ERTHAISIFFER
LEFT = VENSTRE
LEN = LENGDE
LOWER = SMÅ
MID = DELTEKST
NUMBERSTRING = TALLSTRENG
NUMBERVALUE = TALLVERDI
PHONETIC = FURIGANA
PROPER = STOR.FORBOKSTAV
REPLACE = ERSTATT
REPT = GJENTA
RIGHT = HØYRE
SEARCH = SØK
SUBSTITUTE = BYTT.UT
T = T
TEXT = TEKST
TEXTJOIN = TEKST.KOMBINER
THAIDIGIT = THAISIFFER
THAINUMSOUND = THAINUMLYD
THAINUMSTRING = THAINUMSTRENG
THAISTRINGLENGTH = THAISTRENGLENGDE
TRIM = TRIMME
UNICHAR = UNICODETEGN
UNICODE = UNICODE
UPPER = STORE
VALUE = VERDI
##
## Nettfunksjoner (Web Functions)
##
ENCODEURL = URL.KODE
FILTERXML = FILTRERXML
WEBSERVICE = NETTJENESTE
##
## Kompatibilitetsfunksjoner (Compatibility Functions)
##
BETADIST = BETA.FORDELING
BETAINV = INVERS.BETA.FORDELING
BINOMDIST = BINOM.FORDELING
CEILING = AVRUND.GJELDENDE.MULTIPLUM
CHIDIST = KJI.FORDELING
CHIINV = INVERS.KJI.FORDELING
CHITEST = KJI.TEST
CONCATENATE = KJEDE.SAMMEN
CONFIDENCE = KONFIDENS
COVAR = KOVARIANS
CRITBINOM = GRENSE.BINOM
EXPONDIST = EKSP.FORDELING
FDIST = FFORDELING
FINV = FFORDELING.INVERS
FLOOR = AVRUND.GJELDENDE.MULTIPLUM.NED
FORECAST = PROGNOSE
FTEST = FTEST
GAMMADIST = GAMMAFORDELING
GAMMAINV = GAMMAINV
HYPGEOMDIST = HYPGEOM.FORDELING
LOGINV = LOGINV
LOGNORMDIST = LOGNORMFORD
MODE = MODUS
NEGBINOMDIST = NEGBINOM.FORDELING
NORMDIST = NORMALFORDELING
NORMINV = NORMINV
NORMSDIST = NORMSFORDELING
NORMSINV = NORMSINV
PERCENTILE = PERSENTIL
PERCENTRANK = PROSENTDEL
POISSON = POISSON
QUARTILE = KVARTIL
RANK = RANG
STDEV = STDAV
STDEVP = STDAVP
TDIST = TFORDELING
TINV = TINV
TTEST = TTEST
VAR = VARIANS
VARP = VARIANSP
WEIBULL = WEIBULL.FORDELING
ZTEST = ZTEST
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/nb/config 0000644 00000000463 15242753725 0020574 0 ustar 00 ############################################################
##
## PhpSpreadsheet - locale settings
##
## Norsk Bokmål (Norwegian Bokmål)
##
############################################################
ArgumentSeparator = ;
##
## Error Codes
##
NULL
DIV0
VALUE = #VERDI!
REF
NAME = #NAVN?
NUM
NA = #N/D
phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx 0000644 00000324313 15242753725 0022411 0 ustar 00 PK ! [Content_Types].xml ( Ėˎ@EgQٞLLFi~`,'
Hـ{nޖ"f!q="$rUz3q O pÙ(W)Q`f>/K"~+^8J,զ۞_7NƉWfBPʭ~4r73T 201'B^dF(qUƑ1,LOܓǿD *uO^hrH^U})w>ޯ"C^*ډo#xO>zT2#J"*BF|LW8𡽭Z<Uтh}lYLa؞vػ}?P6j" ,ml(I:(1ikL0{s]`Br}> ëHK4D2pꦗƉwz
e
wpYi PK ! , t _rels/.rels ( N0;徦Bh.i7xUm(`{{`FAbǶ:?dxr,\
tLR,GwRDg#b;S\5>TRRQBȣK_*
8=Zy-Ԩy~q
9sS07WR,>")ȇ$ܤ^B\JC)DR FTq*F,~
at3փ;vl^">dXħюF]CsZ:2_6IoϠFݕ; PK ! vnh t xl/workbook.xmlVmo:>ӼR~Ac&qf;jqBh)Wر?oxB2^E9A+3V=FEj!2\FJyϧ
O+Ο drв$ii5%Vř)UeaX%fBq
_ 'MI+ՁZ`˜ղG+)p%OMm^ bS/-(2J^>V\Uno
sl&]U2"kuVgm9A18
ɳ}f:{DA=V
Z{\49_u5p]ťTK5˘Yz!:nXRcdMt#kjD2`ZqQ(**W
xo9bOs7n
_a$+9*7QDh.G4}-JfYp|e|\8fLt,QG>^{Ε02g!0%ە%$ |