Plugin Directory

Changeset 3473263


Ignore:
Timestamp:
03/03/2026 04:48:29 AM (4 weeks ago)
Author:
TobiasBg
Message:

Update to version 3.2.8 from GitHub

Location:
tablepress
Files:
2 added
66 edited
1 copied

Legend:

Unmodified
Added
Removed
  • tablepress/tags/3.2.8/blocks/blocks-manifest.php

    r3463039 r3473263  
    66        'apiVersion' => 3,
    77        'name' => 'tablepress/table',
    8         'version' => '3.2.7',
     8        'version' => '3.2.8',
    99        'title' => 'TablePress table',
    1010        'category' => 'media',
  • tablepress/tags/3.2.8/blocks/table/block.json

    r3463039 r3473263  
    33    "apiVersion": 3,
    44    "name": "tablepress/table",
    5     "version": "3.2.7",
     5    "version": "3.2.8",
    66    "title": "TablePress table",
    77    "category": "media",
  • tablepress/tags/3.2.8/classes/class-export.php

    r3463039 r3473263  
    158158        $active_content_triggers = array( '=', '+', '-', '@' );
    159159        if ( in_array( $cell_content[0], $active_content_triggers, true ) ) {
     160            // phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found -- Avoid concatenation of function names to prevent false positives in code scanners.
    160161            $functions_to_escape = array(
    161162                'cmd|',
    162                 'FORFILES|',
    163                 'rundll32',
    164                 'DDE(',
    165                 'IMPORTXML(',
    166                 'IMPORTFEED(',
    167                 'IMPORTHTML(',
    168                 'IMPORTRANGE(',
    169                 'IMPORTDATA(',
     163                'FOR' . 'FILES|',
     164                'rund' . 'll32',
     165                'DD' . 'E(',
     166                'IMPORT' . 'XML(',
     167                'IMPORT' . 'FEED(',
     168                'IMPORT' . 'HTML(',
     169                'IMPORT' . 'RANGE(',
     170                'IMPORT' . 'DATA(',
    170171                'IMAGE(',
    171172                'HYPERLINK(',
    172173                'WEBSERVICE(',
    173174            );
     175            // phpcs:enable
    174176
    175177            $fn_stripos = function_exists( 'mb_stripos' ) ? 'mb_stripos' : 'stripos';
  • tablepress/tags/3.2.8/classes/class-tablepress.php

    r3463039 r3473263  
    2828     * @const string
    2929     */
    30     public const version = '3.2.7'; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
     30    public const version = '3.2.8'; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
    3131
    3232    /**
     
    3838     * @const int
    3939     */
    40     public const db_version = 121; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
     40    public const db_version = 122; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
    4141
    4242    /**
  • tablepress/tags/3.2.8/libraries/evalmath.class.php

    r3381612 r3473263  
    400400
    401401                // Do we now have a function/variable/number?
    402             } elseif ( $ex && ! $expecting_operator ) {
     402            } elseif ( $ex && ! $expecting_operator ) { // @phpstan-ignore booleanNot.alwaysTrue
    403403                $expecting_operator = true;
    404404                $value = $match[1];
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Calculation/Calculation.php

    r3463039 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Calculation;
    44
     5use TablePress\Composer\Pcre\Preg; // many pregs in this program use u modifier, which has side-effects which make it unsuitable for this
    56use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Engine\BranchPruner;
    67use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Engine\CyclicReferenceStack;
     
    3738    //    Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
    3839    const CALCULATION_REGEXP_FUNCTION = '@?(?:_xlfn\.)?(?:_xlws\.)?((?:__xludf\.)?[\p{L}][\p{L}\p{N}\._]*)[\s]*\('; // TablePress: Add _ to allow the deprecated RAND_INT, RAND_FLOAT, NUMBER_FORMAT, and NUMBER_FORMAT_EU functions.
    39     //    Cell reference (cell or range of cells, with or without a sheet reference)
     40    //    Cell reference, with or without a sheet reference)
    4041    const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])';
    4142    // Used only to detect spill operator #
     
    9192    private BranchPruner $branchPruner;
    9293
    93     private bool $branchPruningEnabled = true;
     94    protected bool $branchPruningEnabled = true;
    9495
    9596    /**
     
    424425        if (is_string($value)) {
    425426            //    Error values cannot be "wrapped"
    426             if (preg_match('/^' . self::CALCULATION_REGEXP_ERROR . '$/i', $value, $match)) {
     427            if (Preg::isMatch('/^' . self::CALCULATION_REGEXP_ERROR . '$/i', $value, $match)) {
    427428                //    Return Excel errors "as is"
    428429                return $value;
     
    507508            $value = $cell->getValue();
    508509            if (is_string($value) && $cell->getDataType() === DataType::TYPE_FORMULA) {
    509                 $value = preg_replace_callback(
     510                $value = Preg::replaceCallback(
    510511                    self::CALCULATION_REGEXP_CELLREF_SPILL,
    511512                    fn (array $matches) => 'ANCHORARRAY(' . substr($matches[0], 0, -1) . ')',
     
    570571    public function parseFormula(string $formula)
    571572    {
    572         $formula = preg_replace_callback(
     573        $formula = Preg::replaceCallback(
    573574            self::CALCULATION_REGEXP_CELLREF_SPILL,
    574575            fn (array $matches) => 'ANCHORARRAY(' . substr($matches[0], 0, -1) . ')',
    575576            $formula
    576         ) ?? $formula;
     577        );
    577578        //    Basic validation that this is indeed a formula
    578579        //    We return an empty array if not
     
    678679        }
    679680
     681        // https://www.reddit.com/r/excel/comments/chr41y/cmd_formula_stopped_working_since_last_update/
    680682        if (preg_match('/^=\s*cmd\s*\|/miu', $formula) !== 0) {
    681             return self::wrapResult($formula);
     683            return ExcelError::REF(); // returns #BLOCKED in newer versions
    682684        }
    683685
     
    10741076    ];
    10751077
     1078    /** @param string[] $matches */
     1079    private static function unionForComma(array $matches): string
     1080    {
     1081        return $matches[1] . str_replace(',', '∪', $matches[2]);
     1082    }
     1083
     1084    private const CELL_OR_CELLRANGE_OR_DEFINED_NAME
     1085        = '(?:'
     1086        . self::CALCULATION_REGEXP_CELLREF // cell address
     1087        . '(?::' . self::CALCULATION_REGEXP_CELLREF . ')?' // optional range address, non-capturing
     1088        . '|' . self::CALCULATION_REGEXP_DEFINEDNAME
     1089        . ')'
     1090        ;
     1091
     1092    public const UNIONABLE_COMMAS = '/((?:[,(]|^)\s*)' // comma or open paren or start of string, followed by optional whitespace
     1093        . '([(]' // open paren
     1094        . self::CELL_OR_CELLRANGE_OR_DEFINED_NAME // cell address
     1095        . '(?:\s*,\s*' // optioonal whitespace, comma, optional whitespace, non-capturing
     1096        . self::CELL_OR_CELLRANGE_OR_DEFINED_NAME // cell address
     1097        . ')+' // one or more occurrences
     1098        . '\s*[)])/i'; // optional whitespace, end paren
     1099
    10761100    /**
    10771101     * @return array<int, mixed>|false
     
    10811105        if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
    10821106            return false;
     1107        }
     1108
     1109        $oldFormula = $formula;
     1110        $formula = Preg::replaceCallback(self::UNIONABLE_COMMAS, \Closure::fromCallable([self::class, 'unionForComma']), $formula); // @phpstan-ignore-line
     1111        if ($oldFormula !== $formula) {
     1112            $this->debugLog->writeDebugLog('Reformulated as %s', $formula);
    10831113        }
    10841114        $phpSpreadsheetFunctions = &self::getFunctionsAddress();
     
    12011231                        }
    12021232                    } elseif (is_string($expectedArgumentCount) && $expectedArgumentCount !== '*') {
    1203                         if (1 !== preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch)) {
     1233                        if (!Preg::isMatch('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch)) {
    12041234                            $argMatch = ['', '', '', ''];
    12051235                        }
     
    12581288                    // MS Excel allows this if the content is cell references; but doesn't allow actual values,
    12591289                    //    but at this point, we can't differentiate (so allow both)
    1260                     return $this->raiseFormulaError('Formula Error: Unexpected ,');
    1261                     /* The following code may be a better choice, but, with
    1262                        the other changes for this PR, I can no longer come up
    1263                        with a test case that gets here
     1290                    //return $this->raiseFormulaError('Formula Error: Unexpected ,');
     1291
    12641292                    $stack->push('Binary Operator', '∪');
    12651293
     
    12671295                    $expectingOperator = false;
    12681296
    1269                     continue;*/
     1297                    continue;
    12701298                }
    12711299
     
    12931321
    12941322                if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) {
    1295                     $val = (string) preg_replace('/\s/u', '', $val);
     1323                    // $val is known to be valid unicode from statement above, so Preg::replace is okay even with u modifier
     1324                    $val = Preg::replace('/\s/u', '', $val);
    12961325                    if (isset($phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) {    // it's a function
    12971326                        $valToUpper = strtoupper($val);
     
    20112040                    $this->executeNumericBinaryOperation($multiplier, $arg, '*', $stack);
    20122041                }
    2013             } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', StringHelper::convertToString($token ?? ''), $matches)) {
     2042            } elseif (Preg::isMatch('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', StringHelper::convertToString($token ?? ''), $matches)) {
    20142043                $cellRef = null;
    20152044
    20162045                /* Phpstan says matches[8/9/10] is never set,
    20172046                   and code coverage report seems to confirm.
    2018                    Appease PhpStan for now;
    2019                    probably delete this block later.
     2047                   regex101.com confirms - only 7 capturing groups.
     2048                   My theory is that this code expected regexp to
     2049                   match cell *or* cellRange, but it does not
     2050                   match the latter. Retain the code for now in case
     2051                   we do want to add the range match later.
     2052                   Probably delete this block later.
     2053                   Until delete happens, turn code coverage off.
    20202054                */
    20212055                if (isset($matches[self::$matchIndex8])) {
     2056                    // @codeCoverageIgnoreStart
    20222057                    if ($cell === null) {
    20232058                        // We can't access the range, so return a REF error
     
    20252060                    } else {
    20262061                        $cellRef = $matches[6] . $matches[7] . ':' . $matches[self::$matchIndex9] . $matches[self::$matchIndex10];
     2062                        $matches[2] = (string) $matches[2];
    20272063                        if ($matches[2] > '') {
    20282064                            $matches[2] = trim($matches[2], "\"'");
     
    20492085                        }
    20502086                    }
     2087                    // @codeCoverageIgnoreEnd
    20512088                } else {
    20522089                    if ($cell === null) {
     
    20552092                    } else {
    20562093                        $cellRef = $matches[6] . $matches[7];
     2094                        $matches[2] = (string) $matches[2];
    20572095                        if ($matches[2] > '') {
    20582096                            $matches[2] = trim($matches[2], "\"'");
     
    20962134                    }
    20972135                    if (is_string($cellValue)) {
    2098                         $cellValue = preg_replace('/"/', '""', $cellValue);
     2136                        $cellValue = Preg::replace('/"/', '""', $cellValue);
    20992137                    }
    21002138                    $this->debugLog->writeDebugLog('Scalar Result for cell %s is %s', $cellRef, $this->showTypeDetails($cellValue));
     
    28662904        $definedNameType = $namedRange->isFormula() ? 'Formula' : 'Range';
    28672905        if ($definedNameType === 'Range') {
    2868             if (preg_match('/^(.*!)?(.*)$/', $definedNameValue, $matches) === 1) {
    2869                 $matches2 = trim($matches[2]);
    2870                 $matches2 = preg_replace('/ +/', ' ∩ ', $matches2) ?? $matches2;
    2871                 $matches2 = preg_replace('/,/', ' ∪ ', $matches2) ?? $matches2;
     2906            if (Preg::isMatch('/^(.*!)?(.*)$/', $definedNameValue, $matches)) {
     2907                $matches2 = Preg::replace(
     2908                    ['/ +/', '/,/'],
     2909                    [' ∩ ', ' ∪ '],
     2910                    trim($matches[2])
     2911                );
    28722912                $definedNameValue = $matches[1] . $matches2;
    28732913            }
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Calculation/FormulaParser.php

    r3420898 r3473263  
    2222 * software or the use or other dealings in the software.
    2323 *
     24 * The following links are no longer valid.
    2425 * https://ewbi.blogs.com/develops/2007/03/excel_formula_p.html
    2526 * https://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
     27 *
     28 * @deprecated 5.5.0 No replacement.
    2629 */
    2730class FormulaParser
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Calculation/FormulaToken.php

    r3218835 r3473263  
    2222 * software or the use or other dealings in the software.
    2323 *
     24 * The following links are no longer valid.
    2425 * https://ewbi.blogs.com/develops/2007/03/excel_formula_p.html
    2526 * https://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
     27 *
     28 * @deprecated 5.5.0 No replacement.
    2629 */
    2730class FormulaToken
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Calculation/FunctionArray.php

    r3463039 r3473263  
    406406            'functionCall' => [TextData\Concatenate::class, 'actualCONCATENATE'],
    407407            'argumentCount' => '1+',
     408            'passCellReference' => true,
    408409        ],
    409410        'CONFIDENCE' => [
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Calculation/Functions.php

    r3385566 r3473263  
    44
    55use TablePress\PhpOffice\PhpSpreadsheet\Cell\Cell;
     6use TablePress\PhpOffice\PhpSpreadsheet\Cell\Coordinate;
    67use TablePress\PhpOffice\PhpSpreadsheet\Shared\Date;
    78use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
     
    382383        return $coordinate;
    383384    }
     385
     386    /** @param mixed[] $array */
     387    public static function convertArrayToCellRange(array $array): string
     388    {
     389        $retVal = '';
     390        $lastRow = $lastColumn = $firstRow = $firstColumn = 0;
     391        foreach ($array as $rowkey => $row) {
     392            if (!is_array($row) || !is_int($rowkey) || $rowkey < 1) {
     393                $firstRow = 0;
     394
     395                break;
     396            }
     397            if ($firstRow > $rowkey || $firstRow === 0) {
     398                $firstRow = $rowkey;
     399            }
     400            if ($lastRow < $rowkey) {
     401                $lastRow = $rowkey;
     402            }
     403            foreach ($row as $colkey => $cellValue) {
     404                if (!preg_match('/^[A-Z]{1,3}$/', $colkey)) {
     405                    $firstRow = 0;
     406
     407                    break 2;
     408                }
     409                $column = Coordinate::columnIndexFromString($colkey);
     410                if ($firstColumn > $column || $firstColumn === 0) {
     411                    $firstColumn = $column;
     412                }
     413                if ($lastColumn < $column) {
     414                    $lastColumn = $column;
     415                }
     416            }
     417        }
     418        if ($firstRow > 0 && $firstColumn > 0 && ($firstRow !== $lastRow || $firstColumn !== $lastColumn)) {
     419            $retVal = Coordinate::stringFromColumnIndex($firstColumn)
     420                . $firstRow
     421                . ':'
     422                . Coordinate::stringFromColumnIndex($lastColumn)
     423                . $lastRow;
     424        }
     425
     426        return $retVal;
     427    }
    384428}
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Calculation/MathTrig/Sum.php

    r3463039 r3473263  
    112112        }
    113113
     114        /** @var array<float|int> $wrkArray */
    114115        return array_sum($wrkArray);
    115116    }
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Calculation/TextData/Concatenate.php

    r3350024 r3473263  
    88use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
    99use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
     10use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Internal\ExcelArrayPseudoFunctions;
     11use TablePress\PhpOffice\PhpSpreadsheet\Cell\Cell;
    1012use TablePress\PhpOffice\PhpSpreadsheet\Cell\DataType;
    1113use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
     
    1820     * This implements the CONCAT function, *not* CONCATENATE.
    1921     *
    20      * @param mixed[] $args
     22     * @param mixed $args data to be concatenated
    2123     */
    2224    public static function CONCATENATE(...$args): string
     
    4850     * This implements the CONCATENATE function.
    4951     *
    50      * @param mixed[] $args data to be concatenated
     52     * @param mixed $args data to be concatenated
    5153     *
    5254     * @return array<string>|string
     
    5456    public static function actualCONCATENATE(...$args)
    5557    {
     58        $useSingle = false;
     59        $cell = null;
     60        $count = count($args);
     61        if ($args[$count - 1] instanceof Cell) {
     62            /** @var Cell */
     63            $cell = array_pop($args);
     64            $type = (($nullsafeVariable1 = $cell->getWorksheet()->getParent()) ? $nullsafeVariable1->getCalculationEngine()->getInstanceArrayReturnType() : null) ?? Calculation::getArrayReturnType();
     65            $useSingle = $type === Calculation::RETURN_ARRAY_AS_VALUE;
     66        }
    5667        if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_GNUMERIC) {
    5768            return self::CONCATENATE(...$args);
     
    5970        $result = '';
    6071        foreach ($args as $operand2) {
     72            if ($useSingle && $cell instanceof Cell && is_array($operand2)) {
     73                $temp = Functions::convertArrayToCellRange($operand2);
     74                if ($temp !== '') {
     75                    $operand2 = ExcelArrayPseudoFunctions::single($temp, $cell);
     76                }
     77            }
     78            /** @var null|array<mixed>|bool|float|int|string $operand2 */
    6179            $result = self::concatenate2Args($result, $operand2);
    6280            if (ErrorValue::isError($result, true) === true) {
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Cell/Cell.php

    r3463039 r3473263  
    227227        }
    228228        // Cells?->Worksheet?->Spreadsheet
    229         $binder ??= (($nullsafeVariable3 = ($nullsafeVariable10 = ($nullsafeVariable14 = $this->parent) ? $nullsafeVariable14->getParent() : null) ? $nullsafeVariable10->getParent() : null) ? $nullsafeVariable3->getValueBinder() : null) ?? self::getValueBinder();
     229        $binder ??= (($nullsafeVariable3 = ($nullsafeVariable13 = ($nullsafeVariable17 = $this->parent) ? $nullsafeVariable17->getParent() : null) ? $nullsafeVariable13->getParent() : null) ? $nullsafeVariable3->getValueBinder() : null) ?? self::getValueBinder();
    230230        if (!$binder->bindValue($this, $value)) {
    231231            throw new SpreadsheetException('Value could not be bound to cell.');
     
    291291                $value2 = StringHelper::convertToString($value, true);
    292292                // Cells?->Worksheet?->Spreadsheet
    293                 $binder = ($nullsafeVariable4 = ($nullsafeVariable11 = ($nullsafeVariable15 = $this->parent) ? $nullsafeVariable15->getParent() : null) ? $nullsafeVariable11->getParent() : null) ? $nullsafeVariable4->getValueBinder() : null;
     293                $binder = ($nullsafeVariable4 = ($nullsafeVariable5 = ($nullsafeVariable14 = $this->parent) ? $nullsafeVariable14->getParent() : null) ? $nullsafeVariable5->getParent() : null) ? $nullsafeVariable4->getValueBinder() : null;
    294294                $preserveCr = false;
    295295                if ($binder !== null && method_exists($binder, 'getPreserveCr')) {
     
    341341        $this->updateInCollection();
    342342        $cellCoordinate = $this->getCoordinate();
    343         self::updateIfCellIsTableHeader(($nullsafeVariable5 = $this->getParent()) ? $nullsafeVariable5->getParent() : null, $this, $oldValue, $value);
     343        self::updateIfCellIsTableHeader(($nullsafeVariable6 = $this->getParent()) ? $nullsafeVariable6->getParent() : null, $this, $oldValue, $value);
    344344        $worksheet = $this->getWorksheet();
    345345        $spreadsheet = $worksheet->getParent();
     
    358358        }
    359359
    360         return (($nullsafeVariable6 = $this->getParent()) ? $nullsafeVariable6->get($cellCoordinate) : null) ?? $this;
     360        return (($nullsafeVariable7 = $this->getParent()) ? $nullsafeVariable7->get($cellCoordinate) : null) ?? $this;
    361361    }
    362362    public const CALCULATE_DATE_TIME_ASIS = 0;
     
    437437            try {
    438438                $currentCalendar = SharedDate::getExcelCalendar();
    439                 SharedDate::setExcelCalendar(($nullsafeVariable7 = $this->getWorksheet()->getParent()) ? $nullsafeVariable7->getExcelCalendar() : null);
     439                SharedDate::setExcelCalendar(($nullsafeVariable8 = $this->getWorksheet()->getParent()) ? $nullsafeVariable8->getExcelCalendar() : null);
    440440                $thisworksheet = $this->getWorksheet();
    441441                $index = $thisworksheet->getParentOrThrow()->getActiveSheetIndex();
     
    10021002    public function isLocked(): bool
    10031003    {
    1004         $protected = ($nullsafeVariable8 = ($nullsafeVariable12 = ($nullsafeVariable16 = $this->parent) ? $nullsafeVariable16->getParent() : null) ? $nullsafeVariable12->getProtection() : null) ? $nullsafeVariable8->getSheet() : null;
     1004        $protected = ($nullsafeVariable9 = ($nullsafeVariable10 = ($nullsafeVariable15 = $this->parent) ? $nullsafeVariable15->getParent() : null) ? $nullsafeVariable10->getProtection() : null) ? $nullsafeVariable9->getSheet() : null;
    10051005        if ($protected !== true) {
    10061006            return false;
     
    10151015            return false;
    10161016        }
    1017         $protected = ($nullsafeVariable9 = ($nullsafeVariable13 = ($nullsafeVariable17 = $this->parent) ? $nullsafeVariable17->getParent() : null) ? $nullsafeVariable13->getProtection() : null) ? $nullsafeVariable9->getSheet() : null;
     1017        $protected = ($nullsafeVariable11 = ($nullsafeVariable12 = ($nullsafeVariable16 = $this->parent) ? $nullsafeVariable16->getParent() : null) ? $nullsafeVariable12->getProtection() : null) ? $nullsafeVariable11->getSheet() : null;
    10181018        if ($protected !== true) {
    10191019            return false;
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Cell/DefaultValueBinder.php

    r3463039 r3473263  
    55use TablePress\Composer\Pcre\Preg;
    66use DateTimeInterface;
    7 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Calculation;
     7use TablePress\PhpOffice\PhpSpreadsheet\Calculation\CalculationParserOnly;
    88use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
    99use TablePress\PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
     
    8888        }
    8989        if (strlen($value) > 1 && $value[0] === '=') {
    90             $calculation = new Calculation();
    91             $calculation->disableBranchPruning();
     90            $calculation = CalculationParserOnly::getParserInstance();
    9291
    9392            try {
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Helper/Downloader.php

    r3283748 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Helper;
    44
     5use DateTimeImmutable;
     6use DateTimeZone;
    57use TablePress\PhpOffice\PhpSpreadsheet\Exception;
    68
     
    9193        // If you're serving to IE over SSL, then the following may be needed
    9294        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
    93         header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
     95        $dt = new DateTimeImmutable('now', new DateTimeZone('UTC'));
     96        header('Last-Modified: ' . $dt->format('D, d M Y H:i:s') . ' GMT'); // always modified
    9497        header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
    9598        header('Pragma: public'); // HTTP/1.0
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Helper/Sample.php

    r3463039 r3473263  
    132132            $writer->save($path);
    133133            $this->logWrite($writer, $path, $callStartTime);
    134             if ($this->isCli() === false) {
    135                 // @codeCoverageIgnoreStart
    136                 echo '<a href="https://plugins.trac.wordpress.org/download.php?type=" . pathinfo($path, PATHINFO_EXTENSION) . '&name=' . basename($path) . '">Download ' . basename($path) . '</a><br />';
    137                 // @codeCoverageIgnoreEnd
    138             }
     134            $this->addDownloadLink($path);
    139135        }
    140136
    141137        $this->logEndingNotes();
     138    }
     139
     140    public function addDownloadLink(string $path): void
     141    {
     142        if ($this->isCli() === false) {
     143            // @codeCoverageIgnoreStart
     144            echo '<a href="https://plugins.trac.wordpress.org/download.php?type=" . pathinfo($path, PATHINFO_EXTENSION) . '&name=' . basename($path) . '">Download ' . basename($path) . '</a><br />';
     145            // @codeCoverageIgnoreEnd
     146        }
    142147    }
    143148
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Reader/BaseReader.php

    r3463039 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Reader;
    44
     5use Closure;
    56use TablePress\PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
    67use TablePress\PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
     
    8182    protected ?IValueBinder $valueBinder = null;
    8283
     84    /** @var null|Closure(string):bool function to return whether image path is okay */
     85    protected ?Closure $isWhitelisted = null;
     86
    8387    public function __construct()
    8488    {
     
    184188
    185189    /**
    186      * Allow external images. Use with caution.
    187      * Improper specification of these within a spreadsheet
     190     * USE WITH CAUTION (and in conjunction with setIsWhiteListed)!
     191     * Allow external images;
     192     * these can be specified within a spreadsheet
     193     * in a way that can subject the caller to security exploits.
     194     */
     195    public function setAllowExternalImages(bool $allowExternalImages): self
     196    {
     197        $this->allowExternalImages = $allowExternalImages;
     198
     199        return $this;
     200    }
     201
     202    public function getAllowExternalImages(): bool
     203    {
     204        return $this->allowExternalImages;
     205    }
     206
     207    /**
     208     * USE WITH CAUTION!
     209     * Supply a callback to determine whether a path should be whitelisted,
     210     * used in conjunction with setAllowExternalImages;
     211     * supplying a method which might return true
    188212     * can subject the caller to security exploits.
    189      */
    190     public function setAllowExternalImages(bool $allowExternalImages): self
    191     {
    192         $this->allowExternalImages = $allowExternalImages;
    193 
    194         return $this;
    195     }
    196 
    197     public function getAllowExternalImages(): bool
    198     {
    199         return $this->allowExternalImages;
     213     *
     214     * @param Closure(string):bool $isWhitelisted
     215     */
     216    public function setIsWhitelisted(Closure $isWhitelisted): self
     217    {
     218        $this->isWhitelisted = $isWhitelisted;
     219
     220        return $this;
    200221    }
    201222
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Reader/Html.php

    r3463039 r3473263  
    291291                 * @param string[] $attributeArray
    292292                 *
    293                  * @param-out string $cellContent In one case, it can be bool
     293                 * @param-out string $cellContentx
    294294                 * @param int|string $row
    295                  * @param mixed $cellContent
     295                 * @param mixed $cellContentx
    296296                 */
    297                 protected function flushCell(Worksheet $sheet, string $column, $row, &$cellContent, array $attributeArray): void
    298     {
     297                protected function flushCell(Worksheet $sheet, string $column, $row, &$cellContentx, array $attributeArray): void
     298    {
     299        $cellContent = $cellContentx;
    299300        if (is_string($cellContent)) {
    300301            //    Simple String content
     
    305306
    306307                // Set cell value explicitly if there is data-type attribute
     308                if (isset($attributeArray['data-checkbox'])) {
     309                    $sheet->getStyle($column . $row)
     310                        ->setCheckBox(true);
     311                }
    307312                if (isset($attributeArray['data-type'])) {
    308313                    $datatype = $attributeArray['data-type'];
     
    317322                    if ($datatype === DataType::TYPE_BOOL) {
    318323                        // This is the case where we can set cellContent to bool rather than string
    319                         $cellContent = self::convertBoolean($cellContent); //* @phpstan-ignore-line
    320                         if (!is_bool($cellContent)) {
    321                             $attributeArray['data-type'] = DataType::TYPE_STRING;
     324                        if ($cellContent === '☑') {
     325                            $cellContent = true;
     326                            $sheet->getStyle($column . $row)
     327                                ->setCheckBox(true);
     328                        } elseif ($cellContent === '☐') {
     329                            $cellContent = false;
     330                            $sheet->getStyle($column . $row)
     331                                ->setCheckBox(true);
     332                        } else {
     333                            $cellContent = self::convertBoolean($cellContent);
     334                            if (!is_bool($cellContent)) {
     335                                $attributeArray['data-type'] = DataType::TYPE_STRING;
     336                            }
    322337                        }
    323338                    }
     
    327342
    328343                    try {
    329                         $sheet->setCellValueExplicit($column . $row, $cellContent, $attributeArray['data-type']);
     344                        if (isset($attributeArray['data-formula'])) {
     345                            $sheet->setCellValueExplicit($column . $row, $attributeArray['data-formula'], DataType::TYPE_FORMULA);
     346                            $sheet->getCell($column . $row)
     347                                ->setCalculatedValue(
     348                                    $cellContent
     349                                );
     350                        } else {
     351                            $sheet->setCellValueExplicit($column . $row, $cellContent, $attributeArray['data-type']);
     352                        }
    330353                    } catch (SpreadsheetException $exception) {
    331354                        $sheet->setCellValue($column . $row, $cellContent);
     
    349372            $this->dataArray[$row][$column] = 'RICH TEXT: ' . StringHelper::convertToString($cellContent); // @codeCoverageIgnore
    350373        }
    351         $cellContent = (string) '';
     374        $cellContentx = '';
    352375    }
    353376
     
    12181241
    12191242        $drawing = new Drawing();
    1220         $drawing->setPath($src, false, null, $this->allowExternalImages);
     1243        $drawing->setPath($src, false, null, $this->allowExternalImages, $this->isWhitelisted);
    12211244        if ($drawing->getPath() === '') {
    12221245            return;
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Reader/Ods.php

    r3463039 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Reader;
    44
     5use Closure;
     6use TablePress\Composer\Pcre\Preg;
     7use DateTime;
     8use DateTimeZone;
    59use DOMAttr;
    610use DOMDocument;
     
    812use DOMNode;
    913use DOMText;
     14use TablePress\PhpOffice\PhpSpreadsheet\Cell\AddressRange;
    1015use TablePress\PhpOffice\PhpSpreadsheet\Cell\Coordinate;
    1116use TablePress\PhpOffice\PhpSpreadsheet\Cell\DataType;
     
    2227use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
    2328use TablePress\PhpOffice\PhpSpreadsheet\Spreadsheet;
     29use TablePress\PhpOffice\PhpSpreadsheet\Style\Alignment;
     30use TablePress\PhpOffice\PhpSpreadsheet\Style\Border;
     31use TablePress\PhpOffice\PhpSpreadsheet\Style\Borders;
     32use TablePress\PhpOffice\PhpSpreadsheet\Style\Fill;
    2433use TablePress\PhpOffice\PhpSpreadsheet\Style\NumberFormat;
     34use TablePress\PhpOffice\PhpSpreadsheet\Style\Protection;
    2535use TablePress\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
    2636use Throwable;
     
    142152     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
    143153     *
    144      * @return array<int, array{worksheetName: string, lastColumnLetter: string, lastColumnIndex: int, totalRows: int, totalColumns: int, sheetState: string}>
     154     * @return array<int, array{
     155     *   worksheetName: string,
     156     *   lastColumnLetter: string,
     157     *   lastColumnIndex: int,
     158     *   totalRows: int,
     159     *   totalColumns: int,
     160     *   sheetState: string
     161     * }>
    145162     */
    146163    public function listWorksheetInfo(string $filename): array
     
    243260    }
    244261
     262    /** @var array<string,
     263     *  array{
     264     *     font?:array{
     265     *       autoColor?: true,
     266     *       bold?: true,
     267     *       color?: array{rgb: string},
     268     *       italic?: true,
     269     *       name?: non-empty-string,
     270     *       size?: float|int,
     271     *       strikethrough?: true,
     272     *       underline?: 'double'|'single',
     273     *    },
     274     *    fill?:array{
     275     *      fillType?: string,
     276     *      startColor?: array{rgb: string},
     277     *    },
     278     *    alignment?:array{
     279     *      horizontal?: string,
     280     *      readOrder?: int,
     281     *      shrinkToFit?: bool,
     282     *      textRotation?: int,
     283     *      vertical?: string,
     284     *      wrapText?: bool,
     285     *    },
     286     *    protection?:array{
     287     *      locked?: string,
     288     *      hidden?: string,
     289     *    },
     290     *    borders?:array{
     291     *      bottom?: array{borderStyle:string, color:array{rgb: string}},
     292     *      left?: array{borderStyle:string, color:array{rgb: string}},
     293     *      right?: array{borderStyle:string, color:array{rgb: string}},
     294     *      top?: array{borderStyle:string, color:array{rgb: string}},
     295     *      diagonal?: array{borderStyle:string, color:array{rgb: string}},
     296     *      diagonalDirection?: int,
     297     *    },
     298     *  }>
     299     */
     300    private array $allStyles;
     301
     302    private int $highestDataIndex;
     303
    245304    /**
    246305     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
     
    270329        // Styles
    271330
     331        $this->allStyles = [];
    272332        $dom = new DOMDocument('1.01', 'UTF-8');
    273333        $dom->loadXML(
     
    275335                ->scan($zip->getFromName('styles.xml'))
    276336        );
     337        $officeNs = (string) $dom->lookupNamespaceUri('office');
     338        $styleNs = (string) $dom->lookupNamespaceUri('style');
     339        $fontNs = (string) $dom->lookupNamespaceUri('fo');
     340
     341        $automaticStyle0 = $this->readDataOnly ? null : $dom->getElementsByTagNameNS($officeNs, 'styles')->item(0);
     342        $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'default-style');
     343        foreach ($automaticStyles as $automaticStyle) {
     344            $styleFamily = $automaticStyle->getAttributeNS($styleNs, 'family');
     345            if ($styleFamily === 'table-cell') {
     346                $fonts = [];
     347                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'text-properties') as $textProperty) {
     348                    $fonts = $this->getFontStyles($textProperty, $styleNs, $fontNs);
     349                }
     350                if (!empty($fonts)) {
     351                    $spreadsheet->getDefaultStyle()
     352                        ->getFont()
     353                        ->applyFromArray($fonts);
     354                }
     355            }
     356        }
     357        $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'style');
     358        foreach ($automaticStyles as $automaticStyle) {
     359            $styleName = $automaticStyle->getAttributeNS($styleNs, 'name');
     360            $styleFamily = $automaticStyle->getAttributeNS($styleNs, 'family');
     361            if ($styleFamily === 'table-cell') {
     362                $fills = $fonts = [];
     363                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'text-properties') as $textProperty) {
     364                    $fonts = $this->getFontStyles($textProperty, $styleNs, $fontNs);
     365                }
     366                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'table-cell-properties') as $tableCellProperty) {
     367                    $fills = $this->getFillStyles($tableCellProperty, $fontNs);
     368                }
     369                if ($styleName !== '') {
     370                    if (!empty($fonts)) {
     371                        $this->allStyles[$styleName]['font'] = $fonts;
     372                        if ($styleName === 'Default') {
     373                            $spreadsheet->getDefaultStyle()
     374                                ->getFont()
     375                                ->applyFromArray($fonts);
     376                        }
     377                    }
     378                    if (!empty($fills)) {
     379                        $this->allStyles[$styleName]['fill'] = $fills;
     380                        if ($styleName === 'Default') {
     381                            $spreadsheet->getDefaultStyle()
     382                                ->getFill()
     383                                ->applyFromArray($fills);
     384                        }
     385                    }
     386                }
     387            }
     388        }
    277389
    278390        $pageSettings = new PageSettings($dom);
     
    286398        );
    287399
    288         $officeNs = (string) $dom->lookupNamespaceUri('office');
    289400        $tableNs = (string) $dom->lookupNamespaceUri('table');
    290401        $textNs = (string) $dom->lookupNamespaceUri('text');
    291402        $xlinkNs = (string) $dom->lookupNamespaceUri('xlink');
    292         $styleNs = (string) $dom->lookupNamespaceUri('style');
    293403
    294404        $pageSettings->readStyleCrossReferences($dom);
     
    297407        $definedNameReader = new DefinedNames($spreadsheet, $tableNs);
    298408        $columnWidths = [];
    299         $automaticStyle0 = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles')->item(0);
     409        $automaticStyle0 = $this->readDataOnly ? null : $dom->getElementsByTagNameNS($officeNs, 'automatic-styles')->item(0);
    300410        $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'style');
    301411        foreach ($automaticStyles as $automaticStyle) {
     
    310420                }
    311421            }
     422            if ($styleFamily === 'table-cell') {
     423                $fonts = $fills = $alignment1 = $alignment2 = $protection = $borders = [];
     424                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'text-properties') as $textProperty) {
     425                    $fonts = $this->getFontStyles($textProperty, $styleNs, $fontNs);
     426                }
     427                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'table-cell-properties') as $tableCellProperty) {
     428                    $fills = $this->getFillStyles($tableCellProperty, $fontNs);
     429                    $borders = $this->getBorderStyles($tableCellProperty, $fontNs, $styleNs);
     430                    $protection = $this->getProtectionStyles($tableCellProperty, $styleNs);
     431                }
     432                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'table-cell-properties') as $tableCellProperty) {
     433                    $alignment1 = $this->getAlignment1Styles($tableCellProperty, $styleNs, $fontNs);
     434                }
     435                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'paragraph-properties') as $paragraphProperty) {
     436                    $alignment2 = $this->getAlignment2Styles($paragraphProperty, $styleNs, $fontNs);
     437                }
     438                if ($styleName !== '') {
     439                    if (!empty($fonts)) {
     440                        $this->allStyles[$styleName]['font'] = $fonts;
     441                    }
     442                    if (!empty($fills)) {
     443                        $this->allStyles[$styleName]['fill'] = $fills;
     444                    }
     445                    $alignment = array_merge($alignment1, $alignment2);
     446                    if (!empty($alignment)) {
     447                        $this->allStyles[$styleName]['alignment'] = $alignment;
     448                    }
     449                    if (!empty($protection)) {
     450                        $this->allStyles[$styleName]['protection'] = $protection;
     451                    }
     452                    if (!empty($borders)) {
     453                        $this->allStyles[$styleName]['borders'] = $borders;
     454                    }
     455                }
     456            }
    312457        }
    313458
     
    346491                    // formula cells... during the load, all formulae should be correct, and we're simply
    347492                    // bringing the worksheet name in line with the formula, not the reverse
    348                     $spreadsheet->getActiveSheet()->setTitle((string) $worksheetName, false, false);
     493                    $spreadsheet->getActiveSheet()
     494                        ->setTitle((string) $worksheetName, false, false);
    349495                }
    350496
     
    352498                $rowID = 1;
    353499                $tableColumnIndex = 1;
     500                $this->highestDataIndex = AddressRange::MAX_COLUMN_INT;
    354501                foreach ($worksheetDataSet->childNodes as $childNode) {
    355502                    /** @var DOMElement $childNode */
     
    390537
    391538                            break;
    392                         case 'table-header-columns':
    393                         case 'table-columns':
    394                             $this->processTableHeaderColumns(
    395                                 $childNode,
    396                                 $tableNs,
    397                                 $columnWidths,
    398                                 $tableColumnIndex,
    399                                 $spreadsheet
    400                             );
    401 
    402                             break;
    403                         case 'table-column-group':
    404                             $this->processTableColumnGroup(
    405                                 $childNode,
    406                                 $tableNs,
    407                                 $columnWidths,
    408                                 $tableColumnIndex,
    409                                 $spreadsheet
    410                             );
    411 
    412                             break;
    413                         case 'table-column':
    414                             $this->processTableColumn(
    415                                 $childNode,
    416                                 $tableNs,
    417                                 $columnWidths,
    418                                 $tableColumnIndex,
    419                                 $spreadsheet
    420                             );
    421 
    422                             break;
    423539                        case 'table-row':
    424540                            $this->processTableRow(
     
    434550
    435551                            break;
     552                        case 'table-header-columns':
     553                        case 'table-columns':
     554                            $this->processTableColumnHeader(
     555                                $childNode,
     556                                $tableNs,
     557                                $columnWidths,
     558                                $tableColumnIndex,
     559                                $spreadsheet,
     560                                $this->readEmptyCells,
     561                                true
     562                            );
     563
     564                            break;
     565                        case 'table-column-group':
     566                            $this->processTableColumnGroup(
     567                                $childNode,
     568                                $tableNs,
     569                                $columnWidths,
     570                                $tableColumnIndex,
     571                                $spreadsheet,
     572                                $this->readEmptyCells,
     573                                true
     574                            );
     575
     576                            break;
     577                        case 'table-column':
     578                            $this->processTableColumn(
     579                                $childNode,
     580                                $tableNs,
     581                                $columnWidths,
     582                                $tableColumnIndex,
     583                                $spreadsheet,
     584                                $this->readEmptyCells,
     585                                true
     586                            );
     587
     588                            break;
    436589                    }
    437590                }
     
    449602                $spreadsheet->createSheet();
    450603            }
     604        }
     605
     606        foreach ($spreadsheets as $workbookData) {
     607            /** @var DOMElement $workbookData */
     608            $tables = $workbookData->getElementsByTagNameNS($tableNs, 'table');
     609
     610            $worksheetID = 0;
     611            foreach ($tables as $worksheetDataSet) {
     612                /** @var DOMElement $worksheetDataSet */
     613                $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name');
     614
     615                // Check loadSheetsOnly
     616                if (
     617                    $this->loadSheetsOnly !== null
     618                    && $worksheetName
     619                    && !in_array($worksheetName, $this->loadSheetsOnly)
     620                ) {
     621                    continue;
     622                }
     623
     624                // Create sheet
     625                $spreadsheet->setActiveSheetIndex($worksheetID);
     626                $highestDataColumn = $spreadsheet->getActiveSheet()->getHighestDataColumn();
     627                $this->highestDataIndex = Coordinate::columnIndexFromString($highestDataColumn);
     628
     629                // Go through every child of table element processing column widths
     630                $rowID = 1;
     631                $tableColumnIndex = 1;
     632                foreach ($worksheetDataSet->childNodes as $childNode) {
     633                    /** @var DOMElement $childNode */
     634                    if (empty($columnWidths) || $this->readEmptyCells) {
     635                        break;
     636                    }
     637
     638                    // Filter elements which are not under the "table" ns
     639                    if ($childNode->namespaceURI != $tableNs) {
     640                        continue;
     641                    }
     642
     643                    $key = self::extractNodeName($childNode->nodeName);
     644
     645                    switch ($key) {
     646                        case 'table-header-columns':
     647                        case 'table-columns':
     648                            $this->processTableColumnHeader(
     649                                $childNode,
     650                                $tableNs,
     651                                $columnWidths,
     652                                $tableColumnIndex,
     653                                $spreadsheet,
     654                                true,
     655                                false
     656                            );
     657
     658                            break;
     659                        case 'table-column-group':
     660                            $this->processTableColumnGroup(
     661                                $childNode,
     662                                $tableNs,
     663                                $columnWidths,
     664                                $tableColumnIndex,
     665                                $spreadsheet,
     666                                true,
     667                                false
     668                            );
     669
     670                            break;
     671                        case 'table-column':
     672                            $this->processTableColumn(
     673                                $childNode,
     674                                $tableNs,
     675                                $columnWidths,
     676                                $tableColumnIndex,
     677                                $spreadsheet,
     678                                true,
     679                                false
     680                            );
     681
     682                            break;
     683                    }
     684                }
     685                ++$worksheetID;
     686            }
    451687
    452688            $autoFilterReader->read($workbookData);
    453689            $definedNameReader->read($workbookData);
    454690        }
     691
    455692        $spreadsheet->setActiveSheetIndex(0);
    456693
     
    567804            $rowRepeats = 1;
    568805        }
     806        $worksheet = $spreadsheet->getSheetByName($worksheetName);
    569807
    570808        $columnID = 'A';
     
    574812                continue; // should just be whitespace
    575813            }
     814            if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
     815                $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
     816            } else {
     817                $colRepeats = 1;
     818            }
     819            $styleName = $cellData->getAttributeNS($tableNs, 'style-name');
     820
     821            // When a cell has number-columns-repeated, check if ANY column in the
     822            // repeated range passes the read filter. If not, skip the entire group.
     823            // If some columns pass, we need to fall through to the processing block
     824            // which will handle per-column filtering.
    576825            if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
    577                 if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
    578                     $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
    579                 } else {
    580                     $colRepeats = 1;
    581                 }
    582 
     826                if ($colRepeats <= 1) {
     827                    StringHelper::stringIncrement($columnID);
     828
     829                    continue;
     830                }
     831
     832                // Check if any column within this repeated group passes the filter
     833                $anyColumnPasses = false;
     834                $tempCol = $columnID;
    583835                for ($i = 0; $i < $colRepeats; ++$i) {
    584                     StringHelper::stringIncrement($columnID);
    585                 }
    586 
    587                 continue;
     836                    if ($i > 0) {
     837                        StringHelper::stringIncrement($tempCol);
     838                    }
     839                    if ($this->getReadFilter()->readCell($tempCol, $rowID, $worksheetName)) {
     840                        $anyColumnPasses = true;
     841
     842                        break;
     843                    }
     844                }
     845
     846                if (!$anyColumnPasses) {
     847                    for ($i = 0; $i < $colRepeats; ++$i) {
     848                        StringHelper::stringIncrement($columnID);
     849                    }
     850
     851                    continue;
     852                }
     853                // Fall through to process the cell, with per-column filter checks
     854            }
     855            if ($worksheet !== null && ($cellData->hasChildNodes() || ($cellData->nextSibling !== null)) && isset($this->allStyles[$styleName])) {
     856                $spannedRange = "$columnID$rowID";
     857                // the following is sufficient for ods,
     858                // and does no harm for xlsx/xls.
     859                $worksheet->getStyle($spannedRange)
     860                    ->applyFromArray($this->allStyles[$styleName]);
     861                // the rest of this block is needed for xlsx/xls,
     862                // and does no harm for ods.
     863                if (isset($this->allStyles[$styleName]['borders'])) {
     864                    $spannedRows = $cellData->getAttributeNS($tableNs, 'number-columns-spanned');
     865                    $spannedColumns = $cellData->getAttributeNS($tableNs, 'number-rows-spanned');
     866                    $spannedRows = max((int) $spannedRows, 1);
     867                    $spannedColumns = max((int) $spannedColumns, 1);
     868                    if ($spannedRows > 1 || $spannedColumns > 1) {
     869                        $endRow = $rowID + $spannedRows - 1;
     870                        $endCol = $columnID;
     871                        while ($spannedColumns > 1) {
     872                            StringHelper::stringIncrement($endCol);
     873                            --$spannedColumns;
     874                        }
     875                        $spannedRange .= ":$endCol$endRow";
     876                        $worksheet->getStyle($spannedRange)
     877                            ->getBorders()
     878                            ->applyFromArray(
     879                                $this->allStyles[$styleName]['borders']
     880                            );
     881                    }
     882                }
    588883            }
    589884
     
    658953
    659954            if (count($paragraphs) > 0) {
     955                $dataValue = null;
    660956                // Consolidate if there are multiple p records (maybe with spans as well)
    661957                $dataArray = [];
     
    672968
    673969                $type = $cellData->getAttributeNS($officeNs, 'value-type');
     970                $symbol = '';
     971                $leftHandCurrency = Preg::isMatch('/\$|£|¥/', $allCellDataText, $matches);
     972                if ($leftHandCurrency) {
     973                    $type = str_replace('float', 'currency', $type);
     974                    $symbol = (string) $matches[0];
     975                }
     976                $customFormatting = '';
     977                if ($this->formatCallback !== null) {
     978                    $temp = ($this->formatCallback)($type, $allCellDataText);
     979                    if ($temp !== '') {
     980                        $customFormatting = $temp;
     981                    }
     982                }
    674983
    675984                switch ($type) {
     
    6921001                        break;
    6931002                    case 'percentage':
     1003                        if (!str_contains($allCellDataText, '.')) {
     1004                            $formatting = NumberFormat::FORMAT_PERCENTAGE;
     1005                        } elseif (substr($allCellDataText, -3, 1) === '.') {
     1006                            $formatting = NumberFormat::FORMAT_PERCENTAGE_0;
     1007                        } else {
     1008                            $formatting = NumberFormat::FORMAT_PERCENTAGE_00;
     1009                        }
    6941010                        $type = DataType::TYPE_NUMERIC;
    6951011                        $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
    696 
    697                         // percentage should always be float
    698                         //if (floor($dataValue) == $dataValue) {
    699                         //    $dataValue = (int) $dataValue;
    700                         //}
    701                         $formatting = NumberFormat::FORMAT_PERCENTAGE_00;
    7021012
    7031013                        break;
     
    7061016                        $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
    7071017
    708                         if (floor($dataValue) == $dataValue) {
    709                             $dataValue = (int) $dataValue;
    710                         }
    711                         $formatting = NumberFormat::FORMAT_CURRENCY_USD_INTEGER;
     1018                        $currency = $cellData->getAttributeNS($officeNs, 'currency');
     1019                        if ($leftHandCurrency) {
     1020                            $typeValue = 'currency';
     1021                            $formatting = str_contains($allCellDataText, '.') ? NumberFormat::FORMAT_CURRENCY_USD : NumberFormat::FORMAT_CURRENCY_USD_INTEGER;
     1022                            if ($symbol !== '$') {
     1023                                $formatting = str_replace('$', $symbol, $formatting);
     1024                            }
     1025                        } elseif (str_contains($allCellDataText, '€')) {
     1026                            $typeValue = 'currency';
     1027                            $formatting = str_contains($allCellDataText, '.') ? NumberFormat::FORMAT_CURRENCY_EUR : NumberFormat::FORMAT_CURRENCY_EUR_INTEGER;
     1028                        }
    7121029
    7131030                        break;
     
    7161033                        $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
    7171034
     1035                        if ($dataValue !== floor($dataValue)) {
     1036                            // do nothing
     1037                        } elseif (substr($allCellDataText, -2, 1) === '.') {
     1038                            $formatting = NumberFormat::FORMAT_NUMBER_0;
     1039                        } elseif (substr($allCellDataText, -3, 1) === '.') {
     1040                            $formatting = NumberFormat::FORMAT_NUMBER_00;
     1041                        }
    7181042                        if (floor($dataValue) == $dataValue) {
    7191043                            if ($dataValue == (int) $dataValue) {
     
    7281052                        $dataValue = Date::convertIsoDate($value);
    7291053
    730                         if ($dataValue != floor($dataValue)) {
     1054                        if (Preg::isMatch('/^\d\d\d\d-\d\d-\d\d$/', $allCellDataText)) {
     1055                            $formatting = 'yyyy-mm-dd';
     1056                        } elseif (Preg::isMatch('/^\d\d?-[a-zA-Z]+-\d\d\d\d$/', $allCellDataText)) {
     1057                            $formatting = 'd-mmm-yyyy';
     1058                        } elseif ($dataValue != floor($dataValue)) {
    7311059                            $formatting = NumberFormat::FORMAT_DATE_XLSX15
    7321060                                . ' '
     
    7411069
    7421070                        $timeValue = $cellData->getAttributeNS($officeNs, 'time-value');
    743 
    744                         $dataValue = Date::PHPToExcel(
    745                             strtotime(
    746                                 '01-01-1970 ' . implode(':', sscanf($timeValue, 'PT%dH%dM%dS') ?? [])
    747                             )
    748                         );
    749                         $formatting = NumberFormat::FORMAT_DATE_TIME4;
     1071                        $minus = '';
     1072                        if (str_starts_with($timeValue, '-')) {
     1073                            $minus = '-';
     1074                            $timeValue = (string) substr($timeValue, 1);
     1075                        }
     1076                        $timeArray = sscanf($timeValue, 'PT%dH%dM%dS');
     1077                        if (is_array($timeArray)) {
     1078                            /** @var array{int, int, int} $timeArray */
     1079                            $days = intdiv($timeArray[0], 24);
     1080                            $hours = $timeArray[0] % 24;
     1081                            $dt = new DateTime("1899-12-30 $hours:{$timeArray[1]}:{$timeArray[2]}", new DateTimeZone('UTC'));
     1082                            $dt->modify("+$days days");
     1083                            $dataValue = Date::PHPToExcel($dt);
     1084                            if ($minus === '-') {
     1085                                $dataValue *= -1;
     1086                                $formatting = '[hh]:mm:ss';
     1087                            } else {
     1088                                $formatting = NumberFormat::FORMAT_DATE_TIME4;
     1089                            }
     1090                        }
    7501091
    7511092                        break;
    7521093                    default:
    7531094                        $dataValue = null;
     1095                }
     1096                if ($customFormatting !== '') {
     1097                    $formatting = $customFormatting;
    7541098                }
    7551099            } else {
     
    7641108            }
    7651109
    766             if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
    767                 $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
    768             } else {
    769                 $colRepeats = 1;
    770             }
    771 
    772             if ($type !== null) { // @phpstan-ignore-line
    773                 for ($i = 0; $i < $colRepeats; ++$i) {
    774                     if ($i > 0) {
    775                         StringHelper::stringIncrement($columnID);
    776                     }
    777 
    778                     if ($type !== DataType::TYPE_NULL) {
    779                         for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
    780                             $rID = $rowID + $rowAdjust;
    781 
    782                             $cell = $spreadsheet->getActiveSheet()
    783                                 ->getCell($columnID . $rID);
    784 
    785                             // Set value
    786                             if ($hasCalculatedValue) {
    787                                 $cell->setValueExplicit($cellDataFormula, $type);
    788                                 if ($cellDataType === 'array') {
    789                                     $cell->setFormulaAttributes(['t' => 'array', 'ref' => $cellDataRef]);
    790                                 }
    791                             } elseif ($type !== '' || $dataValue !== null) {
    792                                 $cell->setValueExplicit($dataValue, $type);
     1110            for ($i = 0; $i < $colRepeats; ++$i) {
     1111                if ($i > 0) {
     1112                    StringHelper::stringIncrement($columnID);
     1113                }
     1114
     1115                if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
     1116                    continue;
     1117                }
     1118
     1119                if ($type !== DataType::TYPE_NULL) {
     1120                    for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
     1121                        $rID = $rowID + $rowAdjust;
     1122
     1123                        $cell = $spreadsheet->getActiveSheet()
     1124                            ->getCell($columnID . $rID);
     1125
     1126                        // Set value
     1127                        if ($hasCalculatedValue) {
     1128                            $cell->setValueExplicit($cellDataFormula, $type);
     1129                            if ($cellDataType === 'array') {
     1130                                $cell->setFormulaAttributes(['t' => 'array', 'ref' => $cellDataRef]);
    7931131                            }
    794 
    795                             if ($hasCalculatedValue) {
    796                                 $cell->setCalculatedValue($dataValue, $type === DataType::TYPE_NUMERIC);
     1132                        } elseif ($type !== '' || $dataValue !== null) {
     1133                            $cell->setValueExplicit($dataValue, $type);
     1134                        }
     1135
     1136                        if ($hasCalculatedValue) {
     1137                            $cell->setCalculatedValue($dataValue, $type === DataType::TYPE_NUMERIC);
     1138                        }
     1139
     1140                        // Set other properties
     1141                        if ($formatting !== null) {
     1142                            $spreadsheet->getActiveSheet()
     1143                                ->getStyle($columnID . $rID)
     1144                                ->getNumberFormat()
     1145                                ->setFormatCode($formatting);
     1146                        } else {
     1147                            $spreadsheet->getActiveSheet()
     1148                                ->getStyle($columnID . $rID)
     1149                                ->getNumberFormat()
     1150                                ->setFormatCode(NumberFormat::FORMAT_GENERAL);
     1151                        }
     1152
     1153                        if ($hyperlink !== null) {
     1154                            if ($hyperlink[0] === '#') {
     1155                                $hyperlink = 'sheet://' . substr($hyperlink, 1);
    7971156                            }
    798 
    799                             // Set other properties
    800                             if ($formatting !== null) {
    801                                 $spreadsheet->getActiveSheet()
    802                                     ->getStyle($columnID . $rID)
    803                                     ->getNumberFormat()
    804                                     ->setFormatCode($formatting);
    805                             } else {
    806                                 $spreadsheet->getActiveSheet()
    807                                     ->getStyle($columnID . $rID)
    808                                     ->getNumberFormat()
    809                                     ->setFormatCode(NumberFormat::FORMAT_GENERAL);
    810                             }
    811 
    812                             if ($hyperlink !== null) {
    813                                 if ($hyperlink[0] === '#') {
    814                                     $hyperlink = 'sheet://' . substr($hyperlink, 1);
    815                                 }
    816                                 $cell->getHyperlink()
    817                                     ->setUrl($hyperlink);
    818                             }
     1157                            $cell->getHyperlink()
     1158                                ->setUrl($hyperlink);
    8191159                        }
    8201160                    }
     
    8441184     * @param string[] $columnWidths
    8451185     */
    846     private function processTableHeaderColumns(
     1186    private function processTableColumnHeader(
    8471187        DOMElement $childNode,
    8481188        string $tableNs,
    8491189        array $columnWidths,
    8501190        int &$tableColumnIndex,
    851         Spreadsheet $spreadsheet
     1191        Spreadsheet $spreadsheet,
     1192        bool $processWidths = true,
     1193        bool $processStyles = true
    8521194    ): void {
    8531195        foreach ($childNode->childNodes as $grandchildNode) {
     
    8611203                        $columnWidths,
    8621204                        $tableColumnIndex,
    863                         $spreadsheet
     1205                        $spreadsheet,
     1206                        $processWidths,
     1207                        $processStyles
    8641208                    );
    8651209
     
    8771221        array $columnWidths,
    8781222        int &$tableColumnIndex,
    879         Spreadsheet $spreadsheet
     1223        Spreadsheet $spreadsheet,
     1224        bool $processWidths = true,
     1225        bool $processStyles = true
    8801226    ): void {
    8811227        foreach ($childNode->childNodes as $grandchildNode) {
     
    8891235                        $columnWidths,
    8901236                        $tableColumnIndex,
    891                         $spreadsheet
     1237                        $spreadsheet,
     1238                        $processWidths,
     1239                        $processStyles
    8921240                    );
    8931241
     
    8951243                case 'table-header-columns':
    8961244                case 'table-columns':
    897                     $this->processTableHeaderColumns(
     1245                    $this->processTableColumnHeader(
    8981246                        $grandchildNode,
    8991247                        $tableNs,
    9001248                        $columnWidths,
    9011249                        $tableColumnIndex,
    902                         $spreadsheet
     1250                        $spreadsheet,
     1251                        $processWidths,
     1252                        $processStyles
    9031253                    );
    9041254
     
    9101260                        $columnWidths,
    9111261                        $tableColumnIndex,
    912                         $spreadsheet
     1262                        $spreadsheet,
     1263                        $processWidths,
     1264                        $processStyles
    9131265                    );
    9141266
     
    9261278        array $columnWidths,
    9271279        int &$tableColumnIndex,
    928         Spreadsheet $spreadsheet
     1280        Spreadsheet $spreadsheet,
     1281        bool $processWidths = true,
     1282        bool $processStyles = true
    9291283    ): void {
    9301284        if ($childNode->hasAttributeNS($tableNs, 'number-columns-repeated')) {
     
    9341288        }
    9351289        $tableStyleName = $childNode->getAttributeNS($tableNs, 'style-name');
    936         if (isset($columnWidths[$tableStyleName])) {
    937             $columnWidth = new HelperDimension($columnWidths[$tableStyleName]);
    938             $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex);
    939             for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0; --$rowRepeats2) {
    940                 /** @var string $tableColumnString */
    941                 $spreadsheet->getActiveSheet()
    942                     ->getColumnDimension($tableColumnString)
    943                     ->setWidth($columnWidth->toUnit('cm'), 'cm');
    944                 StringHelper::stringIncrement($tableColumnString);
     1290        if ($processWidths) {
     1291            if (isset($columnWidths[$tableStyleName])) {
     1292                $columnWidth = new HelperDimension($columnWidths[$tableStyleName]);
     1293                $tableColumnIndex2 = $tableColumnIndex;
     1294                $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex2);
     1295                for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0 && $tableColumnIndex2 <= AddressRange::MAX_COLUMN_INT; --$rowRepeats2) {
     1296                    if (!$this->readEmptyCells && $tableColumnIndex2 > $this->highestDataIndex) {
     1297                        break;
     1298                    }
     1299                    $spreadsheet->getActiveSheet()
     1300                        ->getColumnDimension($tableColumnString)
     1301                        ->setWidth($columnWidth->toUnit('cm'), 'cm');
     1302                    StringHelper::stringIncrement(
     1303                        $tableColumnString
     1304                    );
     1305                    ++$tableColumnIndex2;
     1306                }
     1307            }
     1308        }
     1309        if ($processStyles) {
     1310            $defaultStyleName = $childNode->getAttributeNS($tableNs, 'default-cell-style-name');
     1311            if ($defaultStyleName !== 'Default' && isset($this->allStyles[$defaultStyleName])) {
     1312                $tableColumnIndex2 = $tableColumnIndex;
     1313                $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex2);
     1314                for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0 && $tableColumnIndex2 <= AddressRange::MAX_COLUMN_INT; --$rowRepeats2) {
     1315                    $spreadsheet->getActiveSheet()
     1316                        ->getStyle($tableColumnString)
     1317                        ->applyFromArray(
     1318                            $this->allStyles[$defaultStyleName]
     1319                        );
     1320                    StringHelper::stringIncrement(
     1321                        $tableColumnString
     1322                    );
     1323                    ++$tableColumnIndex2;
     1324                }
    9451325            }
    9461326        }
     
    11001480        }
    11011481    }
     1482
     1483    /** @var null|Closure(string, string):string */
     1484    private ?Closure $formatCallback = null;
     1485
     1486    /** @param Closure(string, string):string $formatCallback */
     1487    public function setFormatCallback(Closure $formatCallback): void
     1488    {
     1489        $this->formatCallback = $formatCallback;
     1490    }
     1491
     1492    /** @return array{
     1493     *   autoColor?: true,
     1494     *   bold?: true,
     1495     *   color?: array{rgb: string},
     1496     *   italic?: true,
     1497     *   name?: non-empty-string,
     1498     *   size?: float|int,
     1499     *   strikethrough?: true,
     1500     *   underline?: 'double'|'single',
     1501     * }
     1502     */
     1503    protected function getFontStyles(DOMElement $textProperty, string $styleNs, string $fontNs): array
     1504    {
     1505        $fonts = [];
     1506        $temp = $textProperty->getAttributeNs($styleNs, 'font-name') ?: $textProperty->getAttributeNs($fontNs, 'font-family');
     1507        if ($temp !== '') {
     1508            $fonts['name'] = $temp;
     1509        }
     1510        $temp = $textProperty->getAttributeNs($fontNs, 'font-size');
     1511        if ($temp !== '' && str_ends_with($temp, 'pt')) {
     1512            $fonts['size'] = (float) substr($temp, 0, -2);
     1513        }
     1514        $temp = $textProperty->getAttributeNs($fontNs, 'font-style');
     1515        if ($temp === 'italic') {
     1516            $fonts['italic'] = true;
     1517        }
     1518        $temp = $textProperty->getAttributeNs($fontNs, 'font-weight');
     1519        if ($temp === 'bold') {
     1520            $fonts['bold'] = true;
     1521        }
     1522        $temp = $textProperty->getAttributeNs($fontNs, 'color');
     1523        if (Preg::isMatch('/^#[a-f0-9]{6}$/i', $temp)) {
     1524            $fonts['color'] = ['rgb' => (string) substr($temp, 1)];
     1525        }
     1526        $temp = $textProperty->getAttributeNs($styleNs, 'use-window-font-color');
     1527        if ($temp === 'true') {
     1528            $fonts['autoColor'] = true;
     1529        }
     1530        $temp = $textProperty->getAttributeNs($styleNs, 'text-underline-type');
     1531        if ($temp === '') {
     1532            $temp = $textProperty->getAttributeNs($styleNs, 'text-underline-style');
     1533            if ($temp !== '' && $temp !== 'none') {
     1534                $temp = 'single';
     1535            }
     1536        }
     1537        if ($temp === 'single' || $temp === 'double') {
     1538            $fonts['underline'] = $temp;
     1539        }
     1540        $temp = $textProperty->getAttributeNs($styleNs, 'text-line-through-type');
     1541        if ($temp !== '' && $temp !== 'none') {
     1542            $fonts['strikethrough'] = true;
     1543        }
     1544
     1545        return $fonts;
     1546    }
     1547
     1548    /** @return array{
     1549     *   fillType?: string,
     1550     *   startColor?: array{rgb: string},
     1551     * }
     1552     */
     1553    protected function getFillStyles(DOMElement $tableCellProperties, string $fontNs): array
     1554    {
     1555        $fills = [];
     1556        $temp = $tableCellProperties->getAttributeNs($fontNs, 'background-color');
     1557        if (Preg::isMatch('/^#[a-f0-9]{6}$/i', $temp)) {
     1558            $fills['fillType'] = Fill::FILL_SOLID;
     1559            $fills['startColor'] = ['rgb' => (string) substr($temp, 1)];
     1560        } elseif ($temp === 'transparent') {
     1561            $fills['fillType'] = Fill::FILL_NONE;
     1562        }
     1563
     1564        return $fills;
     1565    }
     1566
     1567    private const MAP_VERTICAL = [
     1568        'top' => Alignment::VERTICAL_TOP,
     1569        'middle' => Alignment::VERTICAL_CENTER,
     1570        'automatic' => Alignment::VERTICAL_JUSTIFY,
     1571        'bottom' => Alignment::VERTICAL_BOTTOM,
     1572    ];
     1573    private const MAP_HORIZONTAL = [
     1574        'center' => Alignment::HORIZONTAL_CENTER,
     1575        'end' => Alignment::HORIZONTAL_RIGHT,
     1576        'justify' => Alignment::HORIZONTAL_FILL,
     1577        'start' => Alignment::HORIZONTAL_LEFT,
     1578    ];
     1579
     1580    /** @return array{
     1581     *   shrinkToFit?: bool,
     1582     *   textRotation?: int,
     1583     *   vertical?: string,
     1584     *   wrapText?: bool,
     1585     * }
     1586     */
     1587    protected function getAlignment1Styles(DOMElement $tableCellProperties, string $styleNs, string $fontNs): array
     1588    {
     1589        $alignment1 = [];
     1590        $temp = $tableCellProperties->getAttributeNs($styleNs, 'rotation-angle');
     1591        if (is_numeric($temp)) {
     1592            $temp2 = (int) $temp;
     1593            if ($temp2 > 90) {
     1594                $temp2 -= 360;
     1595            }
     1596            if ($temp2 >= -90 && $temp2 <= 90) {
     1597                $alignment1['textRotation'] = (int) $temp2;
     1598            }
     1599        }
     1600        $temp = $tableCellProperties->getAttributeNs($styleNs, 'vertical-align');
     1601        $temp2 = self::MAP_VERTICAL[$temp] ?? '';
     1602        if ($temp2 !== '') {
     1603            $alignment1['vertical'] = $temp2;
     1604        }
     1605        $temp = $tableCellProperties->getAttributeNs($fontNs, 'wrap-option');
     1606        if ($temp === 'wrap') {
     1607            $alignment1['wrapText'] = true;
     1608        } elseif ($temp === 'no-wrap') {
     1609            $alignment1['wrapText'] = false;
     1610        }
     1611        $temp = $tableCellProperties->getAttributeNs($styleNs, 'shrink-to-fit');
     1612        if ($temp === 'true' || $temp === 'false') {
     1613            $alignment1['shrinkToFit'] = $temp === 'true';
     1614        }
     1615
     1616        return $alignment1;
     1617    }
     1618
     1619    /** @return array{
     1620     *   horizontal?: string,
     1621     *   readOrder?: int,
     1622     * }
     1623     */
     1624    protected function getAlignment2Styles(DOMElement $paragraphProperties, string $styleNs, string $fontNs): array
     1625    {
     1626        $alignment2 = [];
     1627        $temp = $paragraphProperties->getAttributeNs($fontNs, 'text-align');
     1628        $temp2 = self::MAP_HORIZONTAL[$temp] ?? '';
     1629        if ($temp2 !== '') {
     1630            $alignment2['horizontal'] = $temp2;
     1631        }
     1632        $temp = $paragraphProperties->getAttributeNs($fontNs, 'margin-left') ?: $paragraphProperties->getAttributeNs($fontNs, 'margin-right');
     1633        if (Preg::isMatch('/^\d+([.]\d+)?(cm|in|mm|pt)$/', $temp)) {
     1634            $dimension = new HelperDimension($temp);
     1635            $alignment2['indent'] = (int) round($dimension->toUnit('px') / Alignment::INDENT_UNITS_TO_PIXELS);
     1636        }
     1637
     1638        $temp = $paragraphProperties->getAttributeNs($styleNs, 'writing-mode');
     1639        if ($temp === 'rl-tb') {
     1640            $alignment2['readOrder'] = Alignment::READORDER_RTL;
     1641        } elseif ($temp === 'lr-tb') {
     1642            $alignment2['readOrder'] = Alignment::READORDER_LTR;
     1643        }
     1644
     1645        return $alignment2;
     1646    }
     1647
     1648    /** @return array{
     1649     *   locked?: string,
     1650     *   hidden?: string,
     1651     * }
     1652     */
     1653    protected function getProtectionStyles(DOMElement $tableCellProperties, string $styleNs): array
     1654    {
     1655        $protection = [];
     1656        $temp = $tableCellProperties->getAttributeNs($styleNs, 'cell-protect');
     1657        switch ($temp) {
     1658            case 'protected formula-hidden':
     1659                $protection['locked'] = Protection::PROTECTION_PROTECTED;
     1660                $protection['hidden'] = Protection::PROTECTION_PROTECTED;
     1661
     1662                break;
     1663            case 'formula-hidden':
     1664                $protection['locked'] = Protection::PROTECTION_UNPROTECTED;
     1665                $protection['hidden'] = Protection::PROTECTION_PROTECTED;
     1666
     1667                break;
     1668            case 'protected':
     1669                $protection['locked'] = Protection::PROTECTION_PROTECTED;
     1670                $protection['hidden'] = Protection::PROTECTION_UNPROTECTED;
     1671
     1672                break;
     1673            case 'none':
     1674                $protection['locked'] = Protection::PROTECTION_UNPROTECTED;
     1675                $protection['hidden'] = Protection::PROTECTION_UNPROTECTED;
     1676
     1677                break;
     1678        }
     1679
     1680        return $protection;
     1681    }
     1682
     1683    private const MAP_BORDER_STYLE = [ // default BORDER_THIN
     1684        'none' => Border::BORDER_NONE,
     1685        'hidden' => Border::BORDER_NONE,
     1686        'dotted' => Border::BORDER_DOTTED,
     1687        'dash-dot' => Border::BORDER_DASHDOT,
     1688        'dash-dot-dot' => Border::BORDER_DASHDOTDOT,
     1689        'dashed' => Border::BORDER_DASHED,
     1690        'double' => Border::BORDER_DOUBLE,
     1691    ];
     1692
     1693    private const MAP_BORDER_MEDIUM = [
     1694        Border::BORDER_THIN => Border::BORDER_MEDIUM,
     1695        Border::BORDER_DASHDOT => Border::BORDER_MEDIUMDASHDOT,
     1696        Border::BORDER_DASHDOTDOT => Border::BORDER_MEDIUMDASHDOTDOT,
     1697        Border::BORDER_DASHED => Border::BORDER_MEDIUMDASHED,
     1698    ];
     1699
     1700    private const MAP_BORDER_THICK = [
     1701        Border::BORDER_THIN => Border::BORDER_THICK,
     1702        Border::BORDER_DASHDOT => Border::BORDER_MEDIUMDASHDOT,
     1703        Border::BORDER_DASHDOTDOT => Border::BORDER_MEDIUMDASHDOTDOT,
     1704        Border::BORDER_DASHED => Border::BORDER_MEDIUMDASHED,
     1705    ];
     1706
     1707    /** @return array{
     1708     *   bottom?: array{borderStyle:string, color:array{rgb: string}},
     1709     *   top?: array{borderStyle:string, color:array{rgb: string}},
     1710     *   left?: array{borderStyle:string, color:array{rgb: string}},
     1711     *   right?: array{borderStyle:string, color:array{rgb: string}},
     1712     *   diagonal?: array{borderStyle:string, color:array{rgb: string}},
     1713     *   diagonalDirection?: int,
     1714     * }
     1715     */
     1716    protected function getBorderStyles(DOMElement $tableCellProperties, string $fontNs, string $styleNs): array
     1717    {
     1718        $borders = [];
     1719        $temp = $tableCellProperties->getAttributeNs($fontNs, 'border');
     1720        $diagonalIndex = Borders::DIAGONAL_NONE;
     1721        foreach (['bottom', 'left', 'right', 'top', 'diagonal-tl-br', 'diagonal-bl-tr'] as $direction) {
     1722            if (str_starts_with($direction, 'diagonal')) {
     1723                $directionIndex = 'diagonal';
     1724                $temp = $tableCellProperties->getAttributeNs($styleNs, $direction);
     1725            } else {
     1726                $directionIndex = $direction;
     1727                $temp = $tableCellProperties->getAttributeNs($fontNs, "border-$direction");
     1728            }
     1729            if (Preg::isMatch('/^(\d+(?:[.]\d+)?)pt\s+([-\w]+)\s+#([0-9a-fA-F]{6})$/', $temp, $matches)) {
     1730                $style = self::MAP_BORDER_STYLE[$matches[2]] ?? Border::BORDER_THIN;
     1731                $width = (float) $matches[1];
     1732                if ($width >= 2.5) {
     1733                    $style = self::MAP_BORDER_THICK[$style] ?? $style;
     1734                } elseif ($width >= 1.75) {
     1735                    $style = self::MAP_BORDER_MEDIUM[$style] ?? $style;
     1736                }
     1737                $color = $matches[3];
     1738                $borders[$directionIndex] = ['borderStyle' => $style, 'color' => ['rgb' => $matches[3]]];
     1739                if ($direction === 'diagonal-tl-br') {
     1740                    $diagonalIndex = Borders::DIAGONAL_DOWN;
     1741                } elseif ($direction === 'diagonal-bl-tr') {
     1742                    $diagonalIndex = ($diagonalIndex === Borders::DIAGONAL_NONE) ? Borders::DIAGONAL_UP : Borders::DIAGONAL_BOTH;
     1743                }
     1744            }
     1745        }
     1746        if ($diagonalIndex !== Borders::DIAGONAL_NONE) {
     1747            $borders['diagonalDirection'] = $diagonalIndex;
     1748        }
     1749
     1750        return $borders; // @phpstan-ignore-line
     1751    }
    11021752}
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Reader/Ods/PageSettings.php

    r3463039 r3473263  
    6969            $marginBottom = ($nullsafeVariable8 = $pageLayoutProperties) ? $nullsafeVariable8->getAttributeNS($this->stylesFo, 'margin-bottom') : null;
    7070            $header = $styleSet->getElementsByTagNameNS($this->stylesNs, 'header-style')->item(0);
    71             $headerProperties = ($nullsafeVariable9 = ($nullsafeVariable13 = $header) ? $nullsafeVariable13->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties') : null) ? $nullsafeVariable9->item(0) : null;
    72             $marginHeader = ($nullsafeVariable10 = $headerProperties) ? $nullsafeVariable10->getAttributeNS($this->stylesFo, 'min-height') : null;
     71            $headerProperties = ($nullsafeVariable9 = ($nullsafeVariable10 = $header) ? $nullsafeVariable10->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties') : null) ? $nullsafeVariable9->item(0) : null;
     72            $marginHeader = ($nullsafeVariable11 = $headerProperties) ? $nullsafeVariable11->getAttributeNS($this->stylesFo, 'min-height') : null;
    7373            $footer = $styleSet->getElementsByTagNameNS($this->stylesNs, 'footer-style')->item(0);
    74             $footerProperties = ($nullsafeVariable11 = ($nullsafeVariable14 = $footer) ? $nullsafeVariable14->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties') : null) ? $nullsafeVariable11->item(0) : null;
    75             $marginFooter = ($nullsafeVariable12 = $footerProperties) ? $nullsafeVariable12->getAttributeNS($this->stylesFo, 'min-height') : null;
     74            $footerProperties = ($nullsafeVariable12 = ($nullsafeVariable13 = $footer) ? $nullsafeVariable13->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties') : null) ? $nullsafeVariable12->item(0) : null;
     75            $marginFooter = ($nullsafeVariable14 = $footerProperties) ? $nullsafeVariable14->getAttributeNS($this->stylesFo, 'min-height') : null;
    7676
    7777            $this->pageLayoutStyles[$styleName] = (object) [
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Reader/Xlsx.php

    r3463039 r3473263  
    699699                            $this->styleReader
    700700                                ->readStyle($objStyle, $style);
     701                            if (isset($xfTag->extLst)) {
     702                                foreach ($xfTag->extLst->ext as $extTag) {
     703                                    $attributes = $extTag->attributes();
     704                                    if (isset($attributes['uri'])) {
     705                                        if ((string) $attributes['uri'] === Namespaces::STYLE_CHECKBOX_URI) {
     706                                            $objStyle->setCheckBox(true);
     707                                        }
     708                                    }
     709                                }
     710                            }
    701711                            foreach ($this->styleReader->getFontCharsets() as $fontName => $charset) {
    702712                                $excel->addFontCharset($fontName, $charset);
     
    15991609                                                        if (isset($images[$linkImageKey])) {
    16001610                                                            $url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
    1601                                                             $objDrawing->setPath($url, false, null, $this->allowExternalImages);
     1611                                                            $objDrawing->setPath($url, false, null, $this->allowExternalImages, $this->isWhitelisted);
    16021612                                                        }
    16031613                                                        if ($objDrawing->getPath() === '') {
     
    17031713                                                        if (isset($images[$linkImageKey])) {
    17041714                                                            $url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
    1705                                                             $objDrawing->setPath($url, false, null, $this->allowExternalImages);
     1715                                                            $objDrawing->setPath($url, false, null, $this->allowExternalImages, $this->isWhitelisted);
    17061716                                                        }
    17071717                                                        if ($objDrawing->getPath() === '') {
     
    22332243            if (str_contains($item[1], 'px')) {
    22342244                $item[1] = str_replace('px', '', $item[1]);
    2235             }
    2236             if (str_contains($item[1], 'pt')) {
     2245            } elseif (str_contains($item[1], 'pt')) {
    22372246                $item[1] = str_replace('pt', '', $item[1]);
    2238                 $item[1] = (string) Font::fontSizeToPixels((int) $item[1]);
    2239             }
    2240             if (str_contains($item[1], 'in')) {
     2247                $item[1] = Font::fontSizeToPixels((float) $item[1]);
     2248            } elseif (str_contains($item[1], 'in')) {
    22412249                $item[1] = str_replace('in', '', $item[1]);
    2242                 $item[1] = (string) Font::inchSizeToPixels((int) $item[1]);
    2243             }
    2244             if (str_contains($item[1], 'cm')) {
     2250                $item[1] = (int) Font::inchSizeToPixels((float) $item[1]);
     2251            } elseif (str_contains($item[1], 'cm')) {
    22452252                $item[1] = str_replace('cm', '', $item[1]);
    2246                 $item[1] = (string) Font::centimeterSizeToPixels((int) $item[1]);
     2253                $item[1] = (int) Font::centimeterSizeToPixels((float) $item[1]);
     2254            } elseif (str_contains($item[1], 'mm')) {
     2255                $item[1] = str_replace('mm', '', $item[1]);
     2256                $item[1] = (int) Font::centimeterSizeToPixels((float) $item[1] / 10);
    22472257            }
    22482258
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Reader/Xlsx/Namespaces.php

    r3463039 r3473263  
    130130
    131131    const RELATIONSHIPS_RICH_VALUE_REL = 'http://schemas.microsoft.com/office/2022/10/relationships/richValueRel';
     132
     133    const FEATURE_PROPERTY_BAG = 'http://schemas.microsoft.com/office/spreadsheetml/2022/featurepropertybag';
     134    const RELATIONSHIPS_FEATURE_PROPERTY_BAG = 'http://schemas.microsoft.com/office/2022/11/relationships/FeaturePropertyBag';
     135    const STYLE_CHECKBOX_URI = '{C7286773-470A-42A8-94C5-96B5CB345126}';
    132136}
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Spreadsheet.php

    r3463039 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet;
    44
     5use TablePress\Composer\Pcre\Preg;
    56use JsonSerializable;
    67use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Calculation;
     
    10651066            if ($worksheet !== null) {
    10661067                $wsTitle = StringHelper::strToUpper($worksheet->getTitle());
    1067                 $definedName = (string) preg_replace('/^.*!/', '', $definedName);
     1068                $definedName = Preg::replace('/^.*!/', '', $definedName);
    10681069                foreach ($this->definedNames as $dn) {
    10691070                    $sheet = $dn->getScope() ?? $dn->getWorksheet();
     
    18701871    }
    18711872
     1873    /**
     1874     * Change all 2-digit-year date styles to use 4-digit year;
     1875     * change all dd-mm-yyyy and mm-dd-yyyy styles to yyyy-mm-dd;
     1876     * dd-mmm-yyyy is unambiguous and left unchanged.
     1877     */
     1878    public function disambiguateDateStyles(): void
     1879    {
     1880        foreach ($this->cellXfCollection as $style) {
     1881            $numberFormat = $style->getNumberFormat();
     1882            $oldFormat = (string) $numberFormat->getFormatCode();
     1883            $newFormat = Preg::replace('/\byy\b/i', 'yyyy', $oldFormat);
     1884            $newFormat = Preg::replace(
     1885                '~\bdd?(-|/|"-"|"/")'
     1886                    . 'mm?(-|/|"-"|"/")'
     1887                    . 'yyyy~',
     1888                'yyyy-mm-dd',
     1889                $newFormat
     1890            );
     1891            $newFormat = Preg::replace(
     1892                '~\bmm?(-|/|"-"|"/")'
     1893                    . 'dd?(-|/|"-"|"/")'
     1894                    . 'yyyy~',
     1895                'yyyy-mm-dd',
     1896                $newFormat
     1897            );
     1898            if ($newFormat !== $oldFormat) {
     1899                $numberFormat->setFormatCode($newFormat);
     1900            }
     1901        }
     1902    }
     1903
    18721904    public function returnArrayAsArray(): void
    18731905    {
     
    19041936        return $this->domainWhiteList;
    19051937    }
     1938
     1939    private bool $usesCheckBoxStyle = false;
     1940
     1941    public function getUsesCheckBoxStyle(): bool
     1942    {
     1943        return $this->usesCheckBoxStyle;
     1944    }
     1945
     1946    public function setUsesCheckBoxStyle(): bool
     1947    {
     1948        $this->usesCheckBoxStyle = false;
     1949        foreach ($this->getCellXfCollection() as $cellXf) {
     1950            if ($cellXf->getCheckBox()) {
     1951                $this->usesCheckBoxStyle = true;
     1952
     1953                break;
     1954            }
     1955        }
     1956
     1957        return $this->usesCheckBoxStyle;
     1958    }
    19061959}
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Style/Alignment.php

    r3420898 r3473263  
    366366            }
    367367        } else {
    368             throw new PhpSpreadsheetException('Text rotation should be a value between -90 and 90.');
     368            throw new PhpSpreadsheetException("Text rotation $angleInDegrees should be a value between -90 and 90.");
    369369        }
    370370
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Style/Font.php

    r3350024 r3473263  
    178178     * </code>
    179179     *
    180      * @param array{name?: string, latin?: string, eastAsian?: string, complexScript?: string, bold?: bool, italic?: bool, superscript?: bool, subscript?: bool, underline?: bool|string, strikethrough?: bool, color?: string[], size?: ?int, chartColor?: ChartColor, scheme?: string, cap?: string, autoColor?: bool} $styleArray Array containing style information
     180     * @param array{
     181     *   autoColor?: bool,
     182     *   bold?: bool,
     183     *   cap?: string,
     184     *   chartColor?: ChartColor,
     185     *   color?: string[],
     186     *   complexScript?: string,
     187     *   eastAsian?: string,
     188     *   italic?: bool,
     189     *   latin?: string,
     190     *   name?: string,
     191     *   scheme?: string,
     192     *   size?: null|float|int,
     193     *   strikethrough?: bool,
     194     *   superscript?: bool,
     195     *   subscript?: bool,
     196     *   underline?: bool|string,
     197     * } $styleArray Array containing style information
    181198     *
    182199     * @return $this
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Style/NumberFormat.php

    r3385566 r3473263  
    2424    const FORMAT_DATE_YYYYMMDD = 'yyyy-mm-dd';
    2525    const FORMAT_DATE_DDMMYYYY = 'dd/mm/yyyy';
    26     const FORMAT_DATE_DMYSLASH = 'd/m/yy';
     26    const FORMAT_DATE_DMYSLASH = 'd"/"m"/"yy';
    2727    const FORMAT_DATE_DMYMINUS = 'd-m-yy';
    2828    const FORMAT_DATE_DMMINUS = 'd-m';
     
    3131    const FORMAT_DATE_XLSX14_ACTUAL = 'm/d/yyyy';
    3232    const FORMAT_DATE_XLSX15 = 'd-mmm-yy';
     33    const FORMAT_DATE_XLSX15_YYYY = 'd-mmm-yyyy';
    3334    const FORMAT_DATE_XLSX16 = 'd-mmm';
    3435    const FORMAT_DATE_XLSX17 = 'mmm-yy';
     
    3637    const FORMAT_DATE_XLSX22_ACTUAL = 'm/d/yyyy h:mm';
    3738    const FORMAT_DATE_DATETIME = 'd/m/yy h:mm';
     39    const FORMAT_DATE_DATETIME_BETTER = 'yyyy-mm-dd hh:mm';
    3840    const FORMAT_DATE_TIME1 = 'h:mm AM/PM';
    3941    const FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM';
     
    4446    const FORMAT_DATE_TIME7 = 'i:s.S';
    4547    const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
    46     const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy/mm/dd;@';
     48    const FORMAT_DATE_TIME_INTERVAL_HMS = '[hh]:mm:ss';
     49    const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy"/"mm"/"dd;@';
    4750    const FORMAT_DATE_LONG_DATE = 'dddd, mmmm d, yyyy';
    4851
     
    6265        self::FORMAT_DATE_XLSX22_ACTUAL,
    6366        self::FORMAT_DATE_DATETIME,
     67        self::FORMAT_DATE_DATETIME_BETTER,
    6468        self::FORMAT_DATE_TIME1,
    6569        self::FORMAT_DATE_TIME2,
     
    7074        self::FORMAT_DATE_TIME7,
    7175        self::FORMAT_DATE_TIME8,
     76        self::FORMAT_DATE_TIME_INTERVAL_HMS,
    7277        self::FORMAT_DATE_YYYYMMDDSLASH,
    7378        self::FORMAT_DATE_LONG_DATE,
     
    7681        self::FORMAT_DATE_XLSX22,
    7782        self::FORMAT_DATE_DATETIME,
     83        self::FORMAT_DATE_DATETIME_BETTER,
    7884        self::FORMAT_DATE_TIME1,
    7985        self::FORMAT_DATE_TIME2,
     
    8490        self::FORMAT_DATE_TIME7,
    8591        self::FORMAT_DATE_TIME8,
     92        self::FORMAT_DATE_TIME_INTERVAL_HMS,
    8693    ];
    8794
    88     const FORMAT_CURRENCY_USD_INTEGER = '$#,##0_-';
    89     const FORMAT_CURRENCY_USD = '$#,##0.00_-';
     95    private const FORMAT_CURRENCY_AMOUNT_INTEGER = '#,##0_-';
     96    private const FORMAT_CURRENCY_AMOUNT_FLOAT = '#,##0.00_-';
     97    const FORMAT_CURRENCY_USD_INTEGER = '$' . self::FORMAT_CURRENCY_AMOUNT_INTEGER;
     98    const FORMAT_CURRENCY_USD = '$' . self::FORMAT_CURRENCY_AMOUNT_FLOAT;
     99    const FORMAT_CURRENCY_GBP_INTEGER = '£' . self::FORMAT_CURRENCY_AMOUNT_INTEGER;
     100    const FORMAT_CURRENCY_GBP = '£' . self::FORMAT_CURRENCY_AMOUNT_FLOAT;
     101    const FORMAT_CURRENCY_YEN_YUAN_INTEGER = '¥' . self::FORMAT_CURRENCY_AMOUNT_INTEGER;
     102    const FORMAT_CURRENCY_YEN_YUAN = '¥' . self::FORMAT_CURRENCY_AMOUNT_FLOAT;
    90103    const FORMAT_CURRENCY_EUR_INTEGER = '#,##0_-[$€]';
    91104    const FORMAT_CURRENCY_EUR = '#,##0.00_-[$€]';
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php

    r3420898 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Style\NumberFormat;
    44
     5use TablePress\Composer\Pcre\Preg;
    56use TablePress\PhpOffice\PhpSpreadsheet\Shared\Date;
    67use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
     
    122123                public static function format($value, string $format): string
    123124    {
     125        if ($value < 0 && Preg::isMatch('/^\[?[hms]/i', $format)) {
     126            return '-' . self::format(-$value, $format);
     127        }
    124128        // strip off first part containing e.g. [$-F800] or [$USD-409]
    125129        // general syntax: [$<Currency string>-<language info>]
    126130        // language info is in hexadecimal
    127131        // strip off chinese part like [DBNum1][$-804]
    128         $format = (string) preg_replace('/^(\[DBNum\d\])*(\[\$[^\]]*\])/i', '', $format);
     132        $format = Preg::replace('/^(\[DBNum\d\])*(\[\$[^\]]*\])/i', '', $format);
    129133
    130134        // OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case;
    131135        //    but we don't want to change any quoted strings
    132         /** @var callable $callable */
    133         $callable = [self::class, 'setLowercaseCallback'];
    134         $format = (string) preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', $callable, $format);
     136        $format = Preg::replaceCallback('/(?:^|")([^"]*)(?:$|")/', \Closure::fromCallable([self::class, 'setLowerCaseCallback']), $format);
    135137
    136138        // Only process the non-quoted blocks for date format characters
     
    160162
    161163        // escape any quoted characters so that DateTime format() will render them correctly
    162         /** @var callable $callback */
    163         $callback = [self::class, 'escapeQuotesCallback'];
    164         $format = (string) preg_replace_callback('/"(.*)"/U', $callback, $format);
     164        $format = Preg::replaceCallback('/"(.*)"/U', \Closure::fromCallable([self::class, 'escapeQuotesCallback']), $format);
    165165
    166166        try {
     
    172172        // Excel 2003 XML formats, m will not have been changed to i above.
    173173        // Change it now.
    174         $format = (string) \preg_replace('/\\\:m/', ':i', $format);
     174        $format = Preg::replace('/\\\:m/', ':i', $format);
    175175        $microseconds = (int) $dateObj->format('u');
    176176        if (str_contains($format, ':s.000')) {
     
    208208    }
    209209
    210     /** @param string[] $matches */
     210    /** @param array<?string> $matches */
    211211    private static function setLowercaseCallback(array $matches): string
    212212    {
     213        /** @var string[] $matches */
    213214        return mb_strtolower($matches[0]);
    214215    }
    215216
    216     /** @param string[] $matches */
     217    /** @param array<?string> $matches */
    217218    private static function escapeQuotesCallback(array $matches): string
    218219    {
     220        /** @var string[] $matches */
    219221        return '\\' . implode('\\', mb_str_split($matches[1], 1, 'UTF-8'));
    220222    }
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Style/Style.php

    r3385566 r3473263  
    5252     */
    5353    protected bool $quotePrefix = false;
     54
     55    protected bool $checkBox = false;
    5456
    5557    /**
     
    485487             * numberFormat?: string[],
    486488             * protection?: array{locked?: string, hidden?: string},
     489             * checkBox?: bool,
    487490             * quotePrefix?: bool} $styleArray */
     491            if (isset($styleArray['checkBox'])) {
     492                $this->checkBox = (bool) $styleArray['checkBox'];
     493            }
    488494            if (isset($styleArray['fill'])) {
    489495                $this->getFill()
     
    701707    }
    702708
     709    public function getCheckBox(): bool
     710    {
     711        if ($this->isSupervisor) {
     712            return $this->getSharedComponent()->getCheckBox();
     713        }
     714
     715        return $this->checkBox;
     716    }
     717
     718    /**
     719                 * @return static
     720                 */
     721                public function setCheckBox(bool $checkBox)
     722    {
     723        if ($this->isSupervisor) {
     724            $styleArray = ['checkBox' => $checkBox];
     725            $this->getActiveSheet()
     726                ->getStyle($this->getSelectedCells())
     727                ->applyFromArray($styleArray);
     728        } else {
     729            $this->checkBox = $checkBox;
     730        }
     731
     732        return $this;
     733    }
     734
    703735    /**
    704736     * Get hash code.
     
    716748            . $this->protection->getHashCode()
    717749            . ($this->quotePrefix ? 't' : 'f')
     750            . ($this->checkBox ? 't' : 'f')
    718751            . __CLASS__
    719752        );
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Worksheet/Drawing.php

    r3420898 r3473263  
    9090     * @param bool $verifyFile Verify file
    9191     * @param ?ZipArchive $zip Zip archive instance
     92     * @param null|callable(string):bool $isWhitelisted
    9293     *
    9394     * @return $this
    9495     */
    95     public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip = null, bool $allowExternal = true)
     96    public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip = null, bool $allowExternal = true, ?callable $isWhitelisted = null)
    9697    {
    9798        $this->isUrl = false;
     
    103104
    104105        $this->path = '';
     106        if ($zip instanceof ZipArchive) {
     107            $zipPath = explode('#', $path)[1];
     108            $locate = @$zip->locateName($zipPath);
     109            if ($locate !== false) {
     110                if ($this->isImage($path)) {
     111                    $this->path = $path;
     112                    $this->setSizesAndType($path);
     113                }
     114            }
    105115        // Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
    106         if (filter_var($path, FILTER_VALIDATE_URL) || (preg_match('/^([\w\s\x00-\x1f]+):/u', $path) && !preg_match('/^([\w]+):/u', $path))) {
     116        } elseif (filter_var($path, FILTER_VALIDATE_URL) || (preg_match('/^([\w\s\x00-\x1f]+):/u', $path) && !preg_match('/^([\w]+):/u', $path))) {
    107117            if (!preg_match('/^(http|https|file|ftp|s3):/', $path)) {
    108118                throw new PhpSpreadsheetException('Invalid protocol for linked drawing');
    109119            }
    110120            if (!$allowExternal) {
     121                return $this;
     122            }
     123            if ($isWhitelisted !== null && !$isWhitelisted($path)) {
    111124                return $this;
    112125            }
     
    145158                }
    146159            }
    147         } elseif ($zip instanceof ZipArchive) {
    148             $zipPath = explode('#', $path)[1];
    149             $locate = @$zip->locateName($zipPath);
    150             if ($locate !== false) {
    151                 if ($this->isImage($path)) {
    152                     $this->path = $path;
    153                     $this->setSizesAndType($path);
    154                 }
    155             }
    156160        } else {
    157161            $exists = @file_exists($path);
  • tablepress/tags/3.2.8/libraries/vendor/PhpSpreadsheet/Worksheet/Worksheet.php

    r3463039 r3473263  
    29812981                 * @param bool $formatData Whether to format data according to cell's style.
    29822982                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     2983                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    29832984                 *
    29842985                 * @throws Exception
     
    29862987                 * @return mixed
    29872988                 */
    2988                 protected function cellToArray(Cell $cell, bool $calculateFormulas, bool $formatData, $nullValue, bool $lessFloatPrecision = false)
     2989                protected function cellToArray(Cell $cell, bool $calculateFormulas, bool $formatData, $nullValue, bool $lessFloatPrecision = false, $oldCalculatedValue = false)
    29892990    {
    29902991        $returnValue = $nullValue;
     
    29932994            if ($cell->getValue() instanceof RichText) {
    29942995                $returnValue = $cell->getValue()->getPlainText();
     2996            } elseif ($calculateFormulas) {
     2997                $returnValue = $cell->getCalculatedValue();
     2998            } elseif ($oldCalculatedValue && ($cell->getDataType() === DataType::TYPE_FORMULA)) {
     2999                $returnValue = $cell->getOldCalculatedValue() ?? $cell->getValue();
    29953000            } else {
    2996                 $returnValue = ($calculateFormulas) ? $cell->getCalculatedValue() : $cell->getValue();
     3001                $returnValue = $cell->getValue();
    29973002            }
    29983003
     
    30203025                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    30213026                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3027                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    30223028                 *
    30233029                 * @return mixed[][]
     
    30313037        bool $ignoreHidden = false,
    30323038        bool $reduceArrays = false,
    3033         bool $lessFloatPrecision = false
     3039        bool $lessFloatPrecision = false,
     3040        bool $oldCalculatedValue = false
    30343041    ): array {
    30353042        $returnValue = [];
    30363043
    30373044        // Loop through rows
    3038         foreach ($this->rangeToArrayYieldRows($range, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision) as $rowRef => $rowArray) {
     3045        foreach ($this->rangeToArrayYieldRows($range, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision, $oldCalculatedValue) as $rowRef => $rowArray) {
    30393046            /** @var int $rowRef */
    30403047            $returnValue[$rowRef] = $rowArray;
     
    30573064                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    30583065                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3066                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    30593067                 *
    30603068                 * @return mixed[][]
     
    30683076        bool $ignoreHidden = false,
    30693077        bool $reduceArrays = false,
    3070         bool $lessFloatPrecision = false
     3078        bool $lessFloatPrecision = false,
     3079        bool $oldCalculatedValue = false
    30713080    ): array {
    30723081        $returnValue = [];
     
    30753084        foreach ($parts as $part) {
    30763085            // Loop through rows
    3077             foreach ($this->rangeToArrayYieldRows($part, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision) as $rowRef => $rowArray) {
     3086            foreach ($this->rangeToArrayYieldRows($part, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision, $oldCalculatedValue) as $rowRef => $rowArray) {
    30783087                /** @var int $rowRef */
    30793088                $returnValue[$rowRef] = $rowArray;
     
    30973106                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    30983107                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3108                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    30993109                 *
    31003110                 * @return Generator<array<mixed>>
     
    31083118        bool $ignoreHidden = false,
    31093119        bool $reduceArrays = false,
    3110         bool $lessFloatPrecision = false
     3120        bool $lessFloatPrecision = false,
     3121        bool $oldCalculatedValue = false
    31113122    ) {
    31123123        $range = Validations::validateCellOrCellRange($range);
     
    31743185                        $cell = $this->cellCollection->get("{$col}{$thisRow}");
    31753186                        if ($cell !== null) {
    3176                             $value = $this->cellToArray($cell, $calculateFormulas, $formatData, $nullValue, $lessFloatPrecision);
     3187                            $value = $this->cellToArray($cell, $calculateFormulas, $formatData, $nullValue, $lessFloatPrecision, $oldCalculatedValue);
    31773188                            if ($reduceArrays) {
    31783189                                while (is_array($value)) {
     
    32773288                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    32783289                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3290                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    32793291                 *
    32803292                 * @return mixed[][]
     
    32883300        bool $ignoreHidden = false,
    32893301        bool $reduceArrays = false,
    3290         bool $lessFloatPrecision = false
     3302        bool $lessFloatPrecision = false,
     3303        bool $oldCalculatedValue = false
    32913304    ): array {
    32923305        $retVal = [];
     
    32973310            $workSheet = $namedRange->getWorksheet();
    32983311            if ($workSheet !== null) {
    3299                 $retVal = $workSheet->rangeToArray($cellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision);
     3312                $retVal = $workSheet->rangeToArray($cellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision, $oldCalculatedValue);
    33003313            }
    33013314        }
     
    33163329                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    33173330                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3331                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    33183332                 *
    33193333                 * @return mixed[][]
     
    33263340        bool $ignoreHidden = false,
    33273341        bool $reduceArrays = false,
    3328         bool $lessFloatPrecision = false
     3342        bool $lessFloatPrecision = false,
     3343        bool $oldCalculatedValue = false
    33293344    ): array {
    33303345        // Garbage collect...
     
    33373352
    33383353        // Return
    3339         return $this->rangeToArray("A1:{$maxCol}{$maxRow}", $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision);
     3354        return $this->rangeToArray("A1:{$maxCol}{$maxRow}", $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision, $oldCalculatedValue);
    33403355    }
    33413356
  • tablepress/tags/3.2.8/libraries/vendor/autoload-classmap.php

    r3463039 r3473263  
    263263   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\Database\DCount' => $strauss_src . '/PhpSpreadsheet/Calculation/Database/DCount.php',
    264264   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\Database\DSum' => $strauss_src . '/PhpSpreadsheet/Calculation/Database/DSum.php',
     265   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\CalculationParserOnly' => $strauss_src . '/PhpSpreadsheet/Calculation/CalculationParserOnly.php',
    265266   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\FunctionArray' => $strauss_src . '/PhpSpreadsheet/Calculation/FunctionArray.php',
    266267   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\Info' => $strauss_src . '/PhpSpreadsheet/Calculation/Information/Info.php',
  • tablepress/tags/3.2.8/readme.txt

    r3463039 r3473263  
    66Requires PHP: 7.4
    77Tested up to: 6.9
    8 Stable tag: 3.2.7
     8Stable tag: 3.2.8
    99License: GPLv2
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    108108
    109109Changes in recent versions are shown below. For earlier changes, please see the [changelog history](https://tablepress.org/info/#changelog).
     110
     111= Version 3.2.8 (March 3, 2026) =
     112
     113* Bugfix: Whitespace handling for the “Filter Term Separator” setting in the “Column Filter Dropdowns” feature module now works properly again. (TablePress Pro and Max only.)
     114* Enhancement: The “Column Filter Dropdowns” feature module integration when using “Server-side Processing” is now more reliable. (TablePress Max only.)
     115* Cleaned up and simplified code, for easier future maintenance, to follow WordPress Coding Standards, and to offer helpful inline documentation.
     116* Several external code libraries and build tools have been updated to benefit from enhancements and bug fixes.
     117* Improved support for PHP 8.5.
    110118
    111119= Version 3.2.7 (February 17, 2026) =
     
    212220== Upgrade Notice ==
    213221
    214 = 3.2.7 =
     222= 3.2.8 =
    215223This update is an enhancement, stability, maintenance, and compatibility release. Updating is highly recommended!
    216224
  • tablepress/tags/3.2.8/tablepress.php

    r3463039 r3473263  
    55 * @package TablePress
    66 * @author Tobias Bäthge
    7  * @version 3.2.7
     7 * @version 3.2.8
    88 *
    99 *
     
    1111 * Plugin URI: https://tablepress.org/
    1212 * Description: Embed beautiful and interactive tables into your WordPress website’s posts and pages, without having to write code!
    13  * Version: 3.2.7
     13 * Version: 3.2.8
    1414 * Requires at least: 6.2
    1515 * Requires PHP: 7.4
     
    7070                'has_addons'        => false,
    7171                'has_paid_plans'    => true,
     72                'is_org_compliant'  => true,
    7273                'menu'              => array(
    7374                    'slug'    => 'tablepress',
  • tablepress/trunk/blocks/blocks-manifest.php

    r3463039 r3473263  
    66        'apiVersion' => 3,
    77        'name' => 'tablepress/table',
    8         'version' => '3.2.7',
     8        'version' => '3.2.8',
    99        'title' => 'TablePress table',
    1010        'category' => 'media',
  • tablepress/trunk/blocks/table/block.json

    r3463039 r3473263  
    33    "apiVersion": 3,
    44    "name": "tablepress/table",
    5     "version": "3.2.7",
     5    "version": "3.2.8",
    66    "title": "TablePress table",
    77    "category": "media",
  • tablepress/trunk/classes/class-export.php

    r3463039 r3473263  
    158158        $active_content_triggers = array( '=', '+', '-', '@' );
    159159        if ( in_array( $cell_content[0], $active_content_triggers, true ) ) {
     160            // phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found -- Avoid concatenation of function names to prevent false positives in code scanners.
    160161            $functions_to_escape = array(
    161162                'cmd|',
    162                 'FORFILES|',
    163                 'rundll32',
    164                 'DDE(',
    165                 'IMPORTXML(',
    166                 'IMPORTFEED(',
    167                 'IMPORTHTML(',
    168                 'IMPORTRANGE(',
    169                 'IMPORTDATA(',
     163                'FOR' . 'FILES|',
     164                'rund' . 'll32',
     165                'DD' . 'E(',
     166                'IMPORT' . 'XML(',
     167                'IMPORT' . 'FEED(',
     168                'IMPORT' . 'HTML(',
     169                'IMPORT' . 'RANGE(',
     170                'IMPORT' . 'DATA(',
    170171                'IMAGE(',
    171172                'HYPERLINK(',
    172173                'WEBSERVICE(',
    173174            );
     175            // phpcs:enable
    174176
    175177            $fn_stripos = function_exists( 'mb_stripos' ) ? 'mb_stripos' : 'stripos';
  • tablepress/trunk/classes/class-tablepress.php

    r3463039 r3473263  
    2828     * @const string
    2929     */
    30     public const version = '3.2.7'; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
     30    public const version = '3.2.8'; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
    3131
    3232    /**
     
    3838     * @const int
    3939     */
    40     public const db_version = 121; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
     40    public const db_version = 122; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase
    4141
    4242    /**
  • tablepress/trunk/libraries/evalmath.class.php

    r3381612 r3473263  
    400400
    401401                // Do we now have a function/variable/number?
    402             } elseif ( $ex && ! $expecting_operator ) {
     402            } elseif ( $ex && ! $expecting_operator ) { // @phpstan-ignore booleanNot.alwaysTrue
    403403                $expecting_operator = true;
    404404                $value = $match[1];
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Calculation/Calculation.php

    r3463039 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Calculation;
    44
     5use TablePress\Composer\Pcre\Preg; // many pregs in this program use u modifier, which has side-effects which make it unsuitable for this
    56use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Engine\BranchPruner;
    67use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Engine\CyclicReferenceStack;
     
    3738    //    Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
    3839    const CALCULATION_REGEXP_FUNCTION = '@?(?:_xlfn\.)?(?:_xlws\.)?((?:__xludf\.)?[\p{L}][\p{L}\p{N}\._]*)[\s]*\('; // TablePress: Add _ to allow the deprecated RAND_INT, RAND_FLOAT, NUMBER_FORMAT, and NUMBER_FORMAT_EU functions.
    39     //    Cell reference (cell or range of cells, with or without a sheet reference)
     40    //    Cell reference, with or without a sheet reference)
    4041    const CALCULATION_REGEXP_CELLREF = '((([^\s,!&%^\/\*\+<>=:`-]*)|(\'(?:[^\']|\'[^!])+?\')|(\"(?:[^\"]|\"[^!])+?\"))!)?\$?\b([a-z]{1,3})\$?(\d{1,7})(?![\w.])';
    4142    // Used only to detect spill operator #
     
    9192    private BranchPruner $branchPruner;
    9293
    93     private bool $branchPruningEnabled = true;
     94    protected bool $branchPruningEnabled = true;
    9495
    9596    /**
     
    424425        if (is_string($value)) {
    425426            //    Error values cannot be "wrapped"
    426             if (preg_match('/^' . self::CALCULATION_REGEXP_ERROR . '$/i', $value, $match)) {
     427            if (Preg::isMatch('/^' . self::CALCULATION_REGEXP_ERROR . '$/i', $value, $match)) {
    427428                //    Return Excel errors "as is"
    428429                return $value;
     
    507508            $value = $cell->getValue();
    508509            if (is_string($value) && $cell->getDataType() === DataType::TYPE_FORMULA) {
    509                 $value = preg_replace_callback(
     510                $value = Preg::replaceCallback(
    510511                    self::CALCULATION_REGEXP_CELLREF_SPILL,
    511512                    fn (array $matches) => 'ANCHORARRAY(' . substr($matches[0], 0, -1) . ')',
     
    570571    public function parseFormula(string $formula)
    571572    {
    572         $formula = preg_replace_callback(
     573        $formula = Preg::replaceCallback(
    573574            self::CALCULATION_REGEXP_CELLREF_SPILL,
    574575            fn (array $matches) => 'ANCHORARRAY(' . substr($matches[0], 0, -1) . ')',
    575576            $formula
    576         ) ?? $formula;
     577        );
    577578        //    Basic validation that this is indeed a formula
    578579        //    We return an empty array if not
     
    678679        }
    679680
     681        // https://www.reddit.com/r/excel/comments/chr41y/cmd_formula_stopped_working_since_last_update/
    680682        if (preg_match('/^=\s*cmd\s*\|/miu', $formula) !== 0) {
    681             return self::wrapResult($formula);
     683            return ExcelError::REF(); // returns #BLOCKED in newer versions
    682684        }
    683685
     
    10741076    ];
    10751077
     1078    /** @param string[] $matches */
     1079    private static function unionForComma(array $matches): string
     1080    {
     1081        return $matches[1] . str_replace(',', '∪', $matches[2]);
     1082    }
     1083
     1084    private const CELL_OR_CELLRANGE_OR_DEFINED_NAME
     1085        = '(?:'
     1086        . self::CALCULATION_REGEXP_CELLREF // cell address
     1087        . '(?::' . self::CALCULATION_REGEXP_CELLREF . ')?' // optional range address, non-capturing
     1088        . '|' . self::CALCULATION_REGEXP_DEFINEDNAME
     1089        . ')'
     1090        ;
     1091
     1092    public const UNIONABLE_COMMAS = '/((?:[,(]|^)\s*)' // comma or open paren or start of string, followed by optional whitespace
     1093        . '([(]' // open paren
     1094        . self::CELL_OR_CELLRANGE_OR_DEFINED_NAME // cell address
     1095        . '(?:\s*,\s*' // optioonal whitespace, comma, optional whitespace, non-capturing
     1096        . self::CELL_OR_CELLRANGE_OR_DEFINED_NAME // cell address
     1097        . ')+' // one or more occurrences
     1098        . '\s*[)])/i'; // optional whitespace, end paren
     1099
    10761100    /**
    10771101     * @return array<int, mixed>|false
     
    10811105        if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
    10821106            return false;
     1107        }
     1108
     1109        $oldFormula = $formula;
     1110        $formula = Preg::replaceCallback(self::UNIONABLE_COMMAS, \Closure::fromCallable([self::class, 'unionForComma']), $formula); // @phpstan-ignore-line
     1111        if ($oldFormula !== $formula) {
     1112            $this->debugLog->writeDebugLog('Reformulated as %s', $formula);
    10831113        }
    10841114        $phpSpreadsheetFunctions = &self::getFunctionsAddress();
     
    12011231                        }
    12021232                    } elseif (is_string($expectedArgumentCount) && $expectedArgumentCount !== '*') {
    1203                         if (1 !== preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch)) {
     1233                        if (!Preg::isMatch('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch)) {
    12041234                            $argMatch = ['', '', '', ''];
    12051235                        }
     
    12581288                    // MS Excel allows this if the content is cell references; but doesn't allow actual values,
    12591289                    //    but at this point, we can't differentiate (so allow both)
    1260                     return $this->raiseFormulaError('Formula Error: Unexpected ,');
    1261                     /* The following code may be a better choice, but, with
    1262                        the other changes for this PR, I can no longer come up
    1263                        with a test case that gets here
     1290                    //return $this->raiseFormulaError('Formula Error: Unexpected ,');
     1291
    12641292                    $stack->push('Binary Operator', '∪');
    12651293
     
    12671295                    $expectingOperator = false;
    12681296
    1269                     continue;*/
     1297                    continue;
    12701298                }
    12711299
     
    12931321
    12941322                if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) {
    1295                     $val = (string) preg_replace('/\s/u', '', $val);
     1323                    // $val is known to be valid unicode from statement above, so Preg::replace is okay even with u modifier
     1324                    $val = Preg::replace('/\s/u', '', $val);
    12961325                    if (isset($phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) {    // it's a function
    12971326                        $valToUpper = strtoupper($val);
     
    20112040                    $this->executeNumericBinaryOperation($multiplier, $arg, '*', $stack);
    20122041                }
    2013             } elseif (preg_match('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', StringHelper::convertToString($token ?? ''), $matches)) {
     2042            } elseif (Preg::isMatch('/^' . self::CALCULATION_REGEXP_CELLREF . '$/i', StringHelper::convertToString($token ?? ''), $matches)) {
    20142043                $cellRef = null;
    20152044
    20162045                /* Phpstan says matches[8/9/10] is never set,
    20172046                   and code coverage report seems to confirm.
    2018                    Appease PhpStan for now;
    2019                    probably delete this block later.
     2047                   regex101.com confirms - only 7 capturing groups.
     2048                   My theory is that this code expected regexp to
     2049                   match cell *or* cellRange, but it does not
     2050                   match the latter. Retain the code for now in case
     2051                   we do want to add the range match later.
     2052                   Probably delete this block later.
     2053                   Until delete happens, turn code coverage off.
    20202054                */
    20212055                if (isset($matches[self::$matchIndex8])) {
     2056                    // @codeCoverageIgnoreStart
    20222057                    if ($cell === null) {
    20232058                        // We can't access the range, so return a REF error
     
    20252060                    } else {
    20262061                        $cellRef = $matches[6] . $matches[7] . ':' . $matches[self::$matchIndex9] . $matches[self::$matchIndex10];
     2062                        $matches[2] = (string) $matches[2];
    20272063                        if ($matches[2] > '') {
    20282064                            $matches[2] = trim($matches[2], "\"'");
     
    20492085                        }
    20502086                    }
     2087                    // @codeCoverageIgnoreEnd
    20512088                } else {
    20522089                    if ($cell === null) {
     
    20552092                    } else {
    20562093                        $cellRef = $matches[6] . $matches[7];
     2094                        $matches[2] = (string) $matches[2];
    20572095                        if ($matches[2] > '') {
    20582096                            $matches[2] = trim($matches[2], "\"'");
     
    20962134                    }
    20972135                    if (is_string($cellValue)) {
    2098                         $cellValue = preg_replace('/"/', '""', $cellValue);
     2136                        $cellValue = Preg::replace('/"/', '""', $cellValue);
    20992137                    }
    21002138                    $this->debugLog->writeDebugLog('Scalar Result for cell %s is %s', $cellRef, $this->showTypeDetails($cellValue));
     
    28662904        $definedNameType = $namedRange->isFormula() ? 'Formula' : 'Range';
    28672905        if ($definedNameType === 'Range') {
    2868             if (preg_match('/^(.*!)?(.*)$/', $definedNameValue, $matches) === 1) {
    2869                 $matches2 = trim($matches[2]);
    2870                 $matches2 = preg_replace('/ +/', ' ∩ ', $matches2) ?? $matches2;
    2871                 $matches2 = preg_replace('/,/', ' ∪ ', $matches2) ?? $matches2;
     2906            if (Preg::isMatch('/^(.*!)?(.*)$/', $definedNameValue, $matches)) {
     2907                $matches2 = Preg::replace(
     2908                    ['/ +/', '/,/'],
     2909                    [' ∩ ', ' ∪ '],
     2910                    trim($matches[2])
     2911                );
    28722912                $definedNameValue = $matches[1] . $matches2;
    28732913            }
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Calculation/FormulaParser.php

    r3420898 r3473263  
    2222 * software or the use or other dealings in the software.
    2323 *
     24 * The following links are no longer valid.
    2425 * https://ewbi.blogs.com/develops/2007/03/excel_formula_p.html
    2526 * https://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
     27 *
     28 * @deprecated 5.5.0 No replacement.
    2629 */
    2730class FormulaParser
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Calculation/FormulaToken.php

    r3218835 r3473263  
    2222 * software or the use or other dealings in the software.
    2323 *
     24 * The following links are no longer valid.
    2425 * https://ewbi.blogs.com/develops/2007/03/excel_formula_p.html
    2526 * https://ewbi.blogs.com/develops/2004/12/excel_formula_p.html
     27 *
     28 * @deprecated 5.5.0 No replacement.
    2629 */
    2730class FormulaToken
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Calculation/FunctionArray.php

    r3463039 r3473263  
    406406            'functionCall' => [TextData\Concatenate::class, 'actualCONCATENATE'],
    407407            'argumentCount' => '1+',
     408            'passCellReference' => true,
    408409        ],
    409410        'CONFIDENCE' => [
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Calculation/Functions.php

    r3385566 r3473263  
    44
    55use TablePress\PhpOffice\PhpSpreadsheet\Cell\Cell;
     6use TablePress\PhpOffice\PhpSpreadsheet\Cell\Coordinate;
    67use TablePress\PhpOffice\PhpSpreadsheet\Shared\Date;
    78use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
     
    382383        return $coordinate;
    383384    }
     385
     386    /** @param mixed[] $array */
     387    public static function convertArrayToCellRange(array $array): string
     388    {
     389        $retVal = '';
     390        $lastRow = $lastColumn = $firstRow = $firstColumn = 0;
     391        foreach ($array as $rowkey => $row) {
     392            if (!is_array($row) || !is_int($rowkey) || $rowkey < 1) {
     393                $firstRow = 0;
     394
     395                break;
     396            }
     397            if ($firstRow > $rowkey || $firstRow === 0) {
     398                $firstRow = $rowkey;
     399            }
     400            if ($lastRow < $rowkey) {
     401                $lastRow = $rowkey;
     402            }
     403            foreach ($row as $colkey => $cellValue) {
     404                if (!preg_match('/^[A-Z]{1,3}$/', $colkey)) {
     405                    $firstRow = 0;
     406
     407                    break 2;
     408                }
     409                $column = Coordinate::columnIndexFromString($colkey);
     410                if ($firstColumn > $column || $firstColumn === 0) {
     411                    $firstColumn = $column;
     412                }
     413                if ($lastColumn < $column) {
     414                    $lastColumn = $column;
     415                }
     416            }
     417        }
     418        if ($firstRow > 0 && $firstColumn > 0 && ($firstRow !== $lastRow || $firstColumn !== $lastColumn)) {
     419            $retVal = Coordinate::stringFromColumnIndex($firstColumn)
     420                . $firstRow
     421                . ':'
     422                . Coordinate::stringFromColumnIndex($lastColumn)
     423                . $lastRow;
     424        }
     425
     426        return $retVal;
     427    }
    384428}
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Calculation/MathTrig/Sum.php

    r3463039 r3473263  
    112112        }
    113113
     114        /** @var array<float|int> $wrkArray */
    114115        return array_sum($wrkArray);
    115116    }
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Calculation/TextData/Concatenate.php

    r3350024 r3473263  
    88use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
    99use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
     10use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Internal\ExcelArrayPseudoFunctions;
     11use TablePress\PhpOffice\PhpSpreadsheet\Cell\Cell;
    1012use TablePress\PhpOffice\PhpSpreadsheet\Cell\DataType;
    1113use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
     
    1820     * This implements the CONCAT function, *not* CONCATENATE.
    1921     *
    20      * @param mixed[] $args
     22     * @param mixed $args data to be concatenated
    2123     */
    2224    public static function CONCATENATE(...$args): string
     
    4850     * This implements the CONCATENATE function.
    4951     *
    50      * @param mixed[] $args data to be concatenated
     52     * @param mixed $args data to be concatenated
    5153     *
    5254     * @return array<string>|string
     
    5456    public static function actualCONCATENATE(...$args)
    5557    {
     58        $useSingle = false;
     59        $cell = null;
     60        $count = count($args);
     61        if ($args[$count - 1] instanceof Cell) {
     62            /** @var Cell */
     63            $cell = array_pop($args);
     64            $type = (($nullsafeVariable1 = $cell->getWorksheet()->getParent()) ? $nullsafeVariable1->getCalculationEngine()->getInstanceArrayReturnType() : null) ?? Calculation::getArrayReturnType();
     65            $useSingle = $type === Calculation::RETURN_ARRAY_AS_VALUE;
     66        }
    5667        if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_GNUMERIC) {
    5768            return self::CONCATENATE(...$args);
     
    5970        $result = '';
    6071        foreach ($args as $operand2) {
     72            if ($useSingle && $cell instanceof Cell && is_array($operand2)) {
     73                $temp = Functions::convertArrayToCellRange($operand2);
     74                if ($temp !== '') {
     75                    $operand2 = ExcelArrayPseudoFunctions::single($temp, $cell);
     76                }
     77            }
     78            /** @var null|array<mixed>|bool|float|int|string $operand2 */
    6179            $result = self::concatenate2Args($result, $operand2);
    6280            if (ErrorValue::isError($result, true) === true) {
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Cell/Cell.php

    r3463039 r3473263  
    227227        }
    228228        // Cells?->Worksheet?->Spreadsheet
    229         $binder ??= (($nullsafeVariable3 = ($nullsafeVariable10 = ($nullsafeVariable14 = $this->parent) ? $nullsafeVariable14->getParent() : null) ? $nullsafeVariable10->getParent() : null) ? $nullsafeVariable3->getValueBinder() : null) ?? self::getValueBinder();
     229        $binder ??= (($nullsafeVariable3 = ($nullsafeVariable13 = ($nullsafeVariable17 = $this->parent) ? $nullsafeVariable17->getParent() : null) ? $nullsafeVariable13->getParent() : null) ? $nullsafeVariable3->getValueBinder() : null) ?? self::getValueBinder();
    230230        if (!$binder->bindValue($this, $value)) {
    231231            throw new SpreadsheetException('Value could not be bound to cell.');
     
    291291                $value2 = StringHelper::convertToString($value, true);
    292292                // Cells?->Worksheet?->Spreadsheet
    293                 $binder = ($nullsafeVariable4 = ($nullsafeVariable11 = ($nullsafeVariable15 = $this->parent) ? $nullsafeVariable15->getParent() : null) ? $nullsafeVariable11->getParent() : null) ? $nullsafeVariable4->getValueBinder() : null;
     293                $binder = ($nullsafeVariable4 = ($nullsafeVariable5 = ($nullsafeVariable14 = $this->parent) ? $nullsafeVariable14->getParent() : null) ? $nullsafeVariable5->getParent() : null) ? $nullsafeVariable4->getValueBinder() : null;
    294294                $preserveCr = false;
    295295                if ($binder !== null && method_exists($binder, 'getPreserveCr')) {
     
    341341        $this->updateInCollection();
    342342        $cellCoordinate = $this->getCoordinate();
    343         self::updateIfCellIsTableHeader(($nullsafeVariable5 = $this->getParent()) ? $nullsafeVariable5->getParent() : null, $this, $oldValue, $value);
     343        self::updateIfCellIsTableHeader(($nullsafeVariable6 = $this->getParent()) ? $nullsafeVariable6->getParent() : null, $this, $oldValue, $value);
    344344        $worksheet = $this->getWorksheet();
    345345        $spreadsheet = $worksheet->getParent();
     
    358358        }
    359359
    360         return (($nullsafeVariable6 = $this->getParent()) ? $nullsafeVariable6->get($cellCoordinate) : null) ?? $this;
     360        return (($nullsafeVariable7 = $this->getParent()) ? $nullsafeVariable7->get($cellCoordinate) : null) ?? $this;
    361361    }
    362362    public const CALCULATE_DATE_TIME_ASIS = 0;
     
    437437            try {
    438438                $currentCalendar = SharedDate::getExcelCalendar();
    439                 SharedDate::setExcelCalendar(($nullsafeVariable7 = $this->getWorksheet()->getParent()) ? $nullsafeVariable7->getExcelCalendar() : null);
     439                SharedDate::setExcelCalendar(($nullsafeVariable8 = $this->getWorksheet()->getParent()) ? $nullsafeVariable8->getExcelCalendar() : null);
    440440                $thisworksheet = $this->getWorksheet();
    441441                $index = $thisworksheet->getParentOrThrow()->getActiveSheetIndex();
     
    10021002    public function isLocked(): bool
    10031003    {
    1004         $protected = ($nullsafeVariable8 = ($nullsafeVariable12 = ($nullsafeVariable16 = $this->parent) ? $nullsafeVariable16->getParent() : null) ? $nullsafeVariable12->getProtection() : null) ? $nullsafeVariable8->getSheet() : null;
     1004        $protected = ($nullsafeVariable9 = ($nullsafeVariable10 = ($nullsafeVariable15 = $this->parent) ? $nullsafeVariable15->getParent() : null) ? $nullsafeVariable10->getProtection() : null) ? $nullsafeVariable9->getSheet() : null;
    10051005        if ($protected !== true) {
    10061006            return false;
     
    10151015            return false;
    10161016        }
    1017         $protected = ($nullsafeVariable9 = ($nullsafeVariable13 = ($nullsafeVariable17 = $this->parent) ? $nullsafeVariable17->getParent() : null) ? $nullsafeVariable13->getProtection() : null) ? $nullsafeVariable9->getSheet() : null;
     1017        $protected = ($nullsafeVariable11 = ($nullsafeVariable12 = ($nullsafeVariable16 = $this->parent) ? $nullsafeVariable16->getParent() : null) ? $nullsafeVariable12->getProtection() : null) ? $nullsafeVariable11->getSheet() : null;
    10181018        if ($protected !== true) {
    10191019            return false;
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Cell/DefaultValueBinder.php

    r3463039 r3473263  
    55use TablePress\Composer\Pcre\Preg;
    66use DateTimeInterface;
    7 use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Calculation;
     7use TablePress\PhpOffice\PhpSpreadsheet\Calculation\CalculationParserOnly;
    88use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
    99use TablePress\PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
     
    8888        }
    8989        if (strlen($value) > 1 && $value[0] === '=') {
    90             $calculation = new Calculation();
    91             $calculation->disableBranchPruning();
     90            $calculation = CalculationParserOnly::getParserInstance();
    9291
    9392            try {
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Helper/Downloader.php

    r3283748 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Helper;
    44
     5use DateTimeImmutable;
     6use DateTimeZone;
    57use TablePress\PhpOffice\PhpSpreadsheet\Exception;
    68
     
    9193        // If you're serving to IE over SSL, then the following may be needed
    9294        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
    93         header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
     95        $dt = new DateTimeImmutable('now', new DateTimeZone('UTC'));
     96        header('Last-Modified: ' . $dt->format('D, d M Y H:i:s') . ' GMT'); // always modified
    9497        header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
    9598        header('Pragma: public'); // HTTP/1.0
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Helper/Sample.php

    r3463039 r3473263  
    132132            $writer->save($path);
    133133            $this->logWrite($writer, $path, $callStartTime);
    134             if ($this->isCli() === false) {
    135                 // @codeCoverageIgnoreStart
    136                 echo '<a href="https://plugins.trac.wordpress.org/download.php?type=" . pathinfo($path, PATHINFO_EXTENSION) . '&name=' . basename($path) . '">Download ' . basename($path) . '</a><br />';
    137                 // @codeCoverageIgnoreEnd
    138             }
     134            $this->addDownloadLink($path);
    139135        }
    140136
    141137        $this->logEndingNotes();
     138    }
     139
     140    public function addDownloadLink(string $path): void
     141    {
     142        if ($this->isCli() === false) {
     143            // @codeCoverageIgnoreStart
     144            echo '<a href="https://plugins.trac.wordpress.org/download.php?type=" . pathinfo($path, PATHINFO_EXTENSION) . '&name=' . basename($path) . '">Download ' . basename($path) . '</a><br />';
     145            // @codeCoverageIgnoreEnd
     146        }
    142147    }
    143148
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Reader/BaseReader.php

    r3463039 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Reader;
    44
     5use Closure;
    56use TablePress\PhpOffice\PhpSpreadsheet\Cell\IValueBinder;
    67use TablePress\PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
     
    8182    protected ?IValueBinder $valueBinder = null;
    8283
     84    /** @var null|Closure(string):bool function to return whether image path is okay */
     85    protected ?Closure $isWhitelisted = null;
     86
    8387    public function __construct()
    8488    {
     
    184188
    185189    /**
    186      * Allow external images. Use with caution.
    187      * Improper specification of these within a spreadsheet
     190     * USE WITH CAUTION (and in conjunction with setIsWhiteListed)!
     191     * Allow external images;
     192     * these can be specified within a spreadsheet
     193     * in a way that can subject the caller to security exploits.
     194     */
     195    public function setAllowExternalImages(bool $allowExternalImages): self
     196    {
     197        $this->allowExternalImages = $allowExternalImages;
     198
     199        return $this;
     200    }
     201
     202    public function getAllowExternalImages(): bool
     203    {
     204        return $this->allowExternalImages;
     205    }
     206
     207    /**
     208     * USE WITH CAUTION!
     209     * Supply a callback to determine whether a path should be whitelisted,
     210     * used in conjunction with setAllowExternalImages;
     211     * supplying a method which might return true
    188212     * can subject the caller to security exploits.
    189      */
    190     public function setAllowExternalImages(bool $allowExternalImages): self
    191     {
    192         $this->allowExternalImages = $allowExternalImages;
    193 
    194         return $this;
    195     }
    196 
    197     public function getAllowExternalImages(): bool
    198     {
    199         return $this->allowExternalImages;
     213     *
     214     * @param Closure(string):bool $isWhitelisted
     215     */
     216    public function setIsWhitelisted(Closure $isWhitelisted): self
     217    {
     218        $this->isWhitelisted = $isWhitelisted;
     219
     220        return $this;
    200221    }
    201222
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Reader/Html.php

    r3463039 r3473263  
    291291                 * @param string[] $attributeArray
    292292                 *
    293                  * @param-out string $cellContent In one case, it can be bool
     293                 * @param-out string $cellContentx
    294294                 * @param int|string $row
    295                  * @param mixed $cellContent
     295                 * @param mixed $cellContentx
    296296                 */
    297                 protected function flushCell(Worksheet $sheet, string $column, $row, &$cellContent, array $attributeArray): void
    298     {
     297                protected function flushCell(Worksheet $sheet, string $column, $row, &$cellContentx, array $attributeArray): void
     298    {
     299        $cellContent = $cellContentx;
    299300        if (is_string($cellContent)) {
    300301            //    Simple String content
     
    305306
    306307                // Set cell value explicitly if there is data-type attribute
     308                if (isset($attributeArray['data-checkbox'])) {
     309                    $sheet->getStyle($column . $row)
     310                        ->setCheckBox(true);
     311                }
    307312                if (isset($attributeArray['data-type'])) {
    308313                    $datatype = $attributeArray['data-type'];
     
    317322                    if ($datatype === DataType::TYPE_BOOL) {
    318323                        // This is the case where we can set cellContent to bool rather than string
    319                         $cellContent = self::convertBoolean($cellContent); //* @phpstan-ignore-line
    320                         if (!is_bool($cellContent)) {
    321                             $attributeArray['data-type'] = DataType::TYPE_STRING;
     324                        if ($cellContent === '☑') {
     325                            $cellContent = true;
     326                            $sheet->getStyle($column . $row)
     327                                ->setCheckBox(true);
     328                        } elseif ($cellContent === '☐') {
     329                            $cellContent = false;
     330                            $sheet->getStyle($column . $row)
     331                                ->setCheckBox(true);
     332                        } else {
     333                            $cellContent = self::convertBoolean($cellContent);
     334                            if (!is_bool($cellContent)) {
     335                                $attributeArray['data-type'] = DataType::TYPE_STRING;
     336                            }
    322337                        }
    323338                    }
     
    327342
    328343                    try {
    329                         $sheet->setCellValueExplicit($column . $row, $cellContent, $attributeArray['data-type']);
     344                        if (isset($attributeArray['data-formula'])) {
     345                            $sheet->setCellValueExplicit($column . $row, $attributeArray['data-formula'], DataType::TYPE_FORMULA);
     346                            $sheet->getCell($column . $row)
     347                                ->setCalculatedValue(
     348                                    $cellContent
     349                                );
     350                        } else {
     351                            $sheet->setCellValueExplicit($column . $row, $cellContent, $attributeArray['data-type']);
     352                        }
    330353                    } catch (SpreadsheetException $exception) {
    331354                        $sheet->setCellValue($column . $row, $cellContent);
     
    349372            $this->dataArray[$row][$column] = 'RICH TEXT: ' . StringHelper::convertToString($cellContent); // @codeCoverageIgnore
    350373        }
    351         $cellContent = (string) '';
     374        $cellContentx = '';
    352375    }
    353376
     
    12181241
    12191242        $drawing = new Drawing();
    1220         $drawing->setPath($src, false, null, $this->allowExternalImages);
     1243        $drawing->setPath($src, false, null, $this->allowExternalImages, $this->isWhitelisted);
    12211244        if ($drawing->getPath() === '') {
    12221245            return;
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Reader/Ods.php

    r3463039 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Reader;
    44
     5use Closure;
     6use TablePress\Composer\Pcre\Preg;
     7use DateTime;
     8use DateTimeZone;
    59use DOMAttr;
    610use DOMDocument;
     
    812use DOMNode;
    913use DOMText;
     14use TablePress\PhpOffice\PhpSpreadsheet\Cell\AddressRange;
    1015use TablePress\PhpOffice\PhpSpreadsheet\Cell\Coordinate;
    1116use TablePress\PhpOffice\PhpSpreadsheet\Cell\DataType;
     
    2227use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
    2328use TablePress\PhpOffice\PhpSpreadsheet\Spreadsheet;
     29use TablePress\PhpOffice\PhpSpreadsheet\Style\Alignment;
     30use TablePress\PhpOffice\PhpSpreadsheet\Style\Border;
     31use TablePress\PhpOffice\PhpSpreadsheet\Style\Borders;
     32use TablePress\PhpOffice\PhpSpreadsheet\Style\Fill;
    2433use TablePress\PhpOffice\PhpSpreadsheet\Style\NumberFormat;
     34use TablePress\PhpOffice\PhpSpreadsheet\Style\Protection;
    2535use TablePress\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
    2636use Throwable;
     
    142152     * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
    143153     *
    144      * @return array<int, array{worksheetName: string, lastColumnLetter: string, lastColumnIndex: int, totalRows: int, totalColumns: int, sheetState: string}>
     154     * @return array<int, array{
     155     *   worksheetName: string,
     156     *   lastColumnLetter: string,
     157     *   lastColumnIndex: int,
     158     *   totalRows: int,
     159     *   totalColumns: int,
     160     *   sheetState: string
     161     * }>
    145162     */
    146163    public function listWorksheetInfo(string $filename): array
     
    243260    }
    244261
     262    /** @var array<string,
     263     *  array{
     264     *     font?:array{
     265     *       autoColor?: true,
     266     *       bold?: true,
     267     *       color?: array{rgb: string},
     268     *       italic?: true,
     269     *       name?: non-empty-string,
     270     *       size?: float|int,
     271     *       strikethrough?: true,
     272     *       underline?: 'double'|'single',
     273     *    },
     274     *    fill?:array{
     275     *      fillType?: string,
     276     *      startColor?: array{rgb: string},
     277     *    },
     278     *    alignment?:array{
     279     *      horizontal?: string,
     280     *      readOrder?: int,
     281     *      shrinkToFit?: bool,
     282     *      textRotation?: int,
     283     *      vertical?: string,
     284     *      wrapText?: bool,
     285     *    },
     286     *    protection?:array{
     287     *      locked?: string,
     288     *      hidden?: string,
     289     *    },
     290     *    borders?:array{
     291     *      bottom?: array{borderStyle:string, color:array{rgb: string}},
     292     *      left?: array{borderStyle:string, color:array{rgb: string}},
     293     *      right?: array{borderStyle:string, color:array{rgb: string}},
     294     *      top?: array{borderStyle:string, color:array{rgb: string}},
     295     *      diagonal?: array{borderStyle:string, color:array{rgb: string}},
     296     *      diagonalDirection?: int,
     297     *    },
     298     *  }>
     299     */
     300    private array $allStyles;
     301
     302    private int $highestDataIndex;
     303
    245304    /**
    246305     * Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
     
    270329        // Styles
    271330
     331        $this->allStyles = [];
    272332        $dom = new DOMDocument('1.01', 'UTF-8');
    273333        $dom->loadXML(
     
    275335                ->scan($zip->getFromName('styles.xml'))
    276336        );
     337        $officeNs = (string) $dom->lookupNamespaceUri('office');
     338        $styleNs = (string) $dom->lookupNamespaceUri('style');
     339        $fontNs = (string) $dom->lookupNamespaceUri('fo');
     340
     341        $automaticStyle0 = $this->readDataOnly ? null : $dom->getElementsByTagNameNS($officeNs, 'styles')->item(0);
     342        $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'default-style');
     343        foreach ($automaticStyles as $automaticStyle) {
     344            $styleFamily = $automaticStyle->getAttributeNS($styleNs, 'family');
     345            if ($styleFamily === 'table-cell') {
     346                $fonts = [];
     347                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'text-properties') as $textProperty) {
     348                    $fonts = $this->getFontStyles($textProperty, $styleNs, $fontNs);
     349                }
     350                if (!empty($fonts)) {
     351                    $spreadsheet->getDefaultStyle()
     352                        ->getFont()
     353                        ->applyFromArray($fonts);
     354                }
     355            }
     356        }
     357        $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'style');
     358        foreach ($automaticStyles as $automaticStyle) {
     359            $styleName = $automaticStyle->getAttributeNS($styleNs, 'name');
     360            $styleFamily = $automaticStyle->getAttributeNS($styleNs, 'family');
     361            if ($styleFamily === 'table-cell') {
     362                $fills = $fonts = [];
     363                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'text-properties') as $textProperty) {
     364                    $fonts = $this->getFontStyles($textProperty, $styleNs, $fontNs);
     365                }
     366                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'table-cell-properties') as $tableCellProperty) {
     367                    $fills = $this->getFillStyles($tableCellProperty, $fontNs);
     368                }
     369                if ($styleName !== '') {
     370                    if (!empty($fonts)) {
     371                        $this->allStyles[$styleName]['font'] = $fonts;
     372                        if ($styleName === 'Default') {
     373                            $spreadsheet->getDefaultStyle()
     374                                ->getFont()
     375                                ->applyFromArray($fonts);
     376                        }
     377                    }
     378                    if (!empty($fills)) {
     379                        $this->allStyles[$styleName]['fill'] = $fills;
     380                        if ($styleName === 'Default') {
     381                            $spreadsheet->getDefaultStyle()
     382                                ->getFill()
     383                                ->applyFromArray($fills);
     384                        }
     385                    }
     386                }
     387            }
     388        }
    277389
    278390        $pageSettings = new PageSettings($dom);
     
    286398        );
    287399
    288         $officeNs = (string) $dom->lookupNamespaceUri('office');
    289400        $tableNs = (string) $dom->lookupNamespaceUri('table');
    290401        $textNs = (string) $dom->lookupNamespaceUri('text');
    291402        $xlinkNs = (string) $dom->lookupNamespaceUri('xlink');
    292         $styleNs = (string) $dom->lookupNamespaceUri('style');
    293403
    294404        $pageSettings->readStyleCrossReferences($dom);
     
    297407        $definedNameReader = new DefinedNames($spreadsheet, $tableNs);
    298408        $columnWidths = [];
    299         $automaticStyle0 = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles')->item(0);
     409        $automaticStyle0 = $this->readDataOnly ? null : $dom->getElementsByTagNameNS($officeNs, 'automatic-styles')->item(0);
    300410        $automaticStyles = ($automaticStyle0 === null) ? [] : $automaticStyle0->getElementsByTagNameNS($styleNs, 'style');
    301411        foreach ($automaticStyles as $automaticStyle) {
     
    310420                }
    311421            }
     422            if ($styleFamily === 'table-cell') {
     423                $fonts = $fills = $alignment1 = $alignment2 = $protection = $borders = [];
     424                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'text-properties') as $textProperty) {
     425                    $fonts = $this->getFontStyles($textProperty, $styleNs, $fontNs);
     426                }
     427                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'table-cell-properties') as $tableCellProperty) {
     428                    $fills = $this->getFillStyles($tableCellProperty, $fontNs);
     429                    $borders = $this->getBorderStyles($tableCellProperty, $fontNs, $styleNs);
     430                    $protection = $this->getProtectionStyles($tableCellProperty, $styleNs);
     431                }
     432                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'table-cell-properties') as $tableCellProperty) {
     433                    $alignment1 = $this->getAlignment1Styles($tableCellProperty, $styleNs, $fontNs);
     434                }
     435                foreach ($automaticStyle->getElementsByTagNameNS($styleNs, 'paragraph-properties') as $paragraphProperty) {
     436                    $alignment2 = $this->getAlignment2Styles($paragraphProperty, $styleNs, $fontNs);
     437                }
     438                if ($styleName !== '') {
     439                    if (!empty($fonts)) {
     440                        $this->allStyles[$styleName]['font'] = $fonts;
     441                    }
     442                    if (!empty($fills)) {
     443                        $this->allStyles[$styleName]['fill'] = $fills;
     444                    }
     445                    $alignment = array_merge($alignment1, $alignment2);
     446                    if (!empty($alignment)) {
     447                        $this->allStyles[$styleName]['alignment'] = $alignment;
     448                    }
     449                    if (!empty($protection)) {
     450                        $this->allStyles[$styleName]['protection'] = $protection;
     451                    }
     452                    if (!empty($borders)) {
     453                        $this->allStyles[$styleName]['borders'] = $borders;
     454                    }
     455                }
     456            }
    312457        }
    313458
     
    346491                    // formula cells... during the load, all formulae should be correct, and we're simply
    347492                    // bringing the worksheet name in line with the formula, not the reverse
    348                     $spreadsheet->getActiveSheet()->setTitle((string) $worksheetName, false, false);
     493                    $spreadsheet->getActiveSheet()
     494                        ->setTitle((string) $worksheetName, false, false);
    349495                }
    350496
     
    352498                $rowID = 1;
    353499                $tableColumnIndex = 1;
     500                $this->highestDataIndex = AddressRange::MAX_COLUMN_INT;
    354501                foreach ($worksheetDataSet->childNodes as $childNode) {
    355502                    /** @var DOMElement $childNode */
     
    390537
    391538                            break;
    392                         case 'table-header-columns':
    393                         case 'table-columns':
    394                             $this->processTableHeaderColumns(
    395                                 $childNode,
    396                                 $tableNs,
    397                                 $columnWidths,
    398                                 $tableColumnIndex,
    399                                 $spreadsheet
    400                             );
    401 
    402                             break;
    403                         case 'table-column-group':
    404                             $this->processTableColumnGroup(
    405                                 $childNode,
    406                                 $tableNs,
    407                                 $columnWidths,
    408                                 $tableColumnIndex,
    409                                 $spreadsheet
    410                             );
    411 
    412                             break;
    413                         case 'table-column':
    414                             $this->processTableColumn(
    415                                 $childNode,
    416                                 $tableNs,
    417                                 $columnWidths,
    418                                 $tableColumnIndex,
    419                                 $spreadsheet
    420                             );
    421 
    422                             break;
    423539                        case 'table-row':
    424540                            $this->processTableRow(
     
    434550
    435551                            break;
     552                        case 'table-header-columns':
     553                        case 'table-columns':
     554                            $this->processTableColumnHeader(
     555                                $childNode,
     556                                $tableNs,
     557                                $columnWidths,
     558                                $tableColumnIndex,
     559                                $spreadsheet,
     560                                $this->readEmptyCells,
     561                                true
     562                            );
     563
     564                            break;
     565                        case 'table-column-group':
     566                            $this->processTableColumnGroup(
     567                                $childNode,
     568                                $tableNs,
     569                                $columnWidths,
     570                                $tableColumnIndex,
     571                                $spreadsheet,
     572                                $this->readEmptyCells,
     573                                true
     574                            );
     575
     576                            break;
     577                        case 'table-column':
     578                            $this->processTableColumn(
     579                                $childNode,
     580                                $tableNs,
     581                                $columnWidths,
     582                                $tableColumnIndex,
     583                                $spreadsheet,
     584                                $this->readEmptyCells,
     585                                true
     586                            );
     587
     588                            break;
    436589                    }
    437590                }
     
    449602                $spreadsheet->createSheet();
    450603            }
     604        }
     605
     606        foreach ($spreadsheets as $workbookData) {
     607            /** @var DOMElement $workbookData */
     608            $tables = $workbookData->getElementsByTagNameNS($tableNs, 'table');
     609
     610            $worksheetID = 0;
     611            foreach ($tables as $worksheetDataSet) {
     612                /** @var DOMElement $worksheetDataSet */
     613                $worksheetName = $worksheetDataSet->getAttributeNS($tableNs, 'name');
     614
     615                // Check loadSheetsOnly
     616                if (
     617                    $this->loadSheetsOnly !== null
     618                    && $worksheetName
     619                    && !in_array($worksheetName, $this->loadSheetsOnly)
     620                ) {
     621                    continue;
     622                }
     623
     624                // Create sheet
     625                $spreadsheet->setActiveSheetIndex($worksheetID);
     626                $highestDataColumn = $spreadsheet->getActiveSheet()->getHighestDataColumn();
     627                $this->highestDataIndex = Coordinate::columnIndexFromString($highestDataColumn);
     628
     629                // Go through every child of table element processing column widths
     630                $rowID = 1;
     631                $tableColumnIndex = 1;
     632                foreach ($worksheetDataSet->childNodes as $childNode) {
     633                    /** @var DOMElement $childNode */
     634                    if (empty($columnWidths) || $this->readEmptyCells) {
     635                        break;
     636                    }
     637
     638                    // Filter elements which are not under the "table" ns
     639                    if ($childNode->namespaceURI != $tableNs) {
     640                        continue;
     641                    }
     642
     643                    $key = self::extractNodeName($childNode->nodeName);
     644
     645                    switch ($key) {
     646                        case 'table-header-columns':
     647                        case 'table-columns':
     648                            $this->processTableColumnHeader(
     649                                $childNode,
     650                                $tableNs,
     651                                $columnWidths,
     652                                $tableColumnIndex,
     653                                $spreadsheet,
     654                                true,
     655                                false
     656                            );
     657
     658                            break;
     659                        case 'table-column-group':
     660                            $this->processTableColumnGroup(
     661                                $childNode,
     662                                $tableNs,
     663                                $columnWidths,
     664                                $tableColumnIndex,
     665                                $spreadsheet,
     666                                true,
     667                                false
     668                            );
     669
     670                            break;
     671                        case 'table-column':
     672                            $this->processTableColumn(
     673                                $childNode,
     674                                $tableNs,
     675                                $columnWidths,
     676                                $tableColumnIndex,
     677                                $spreadsheet,
     678                                true,
     679                                false
     680                            );
     681
     682                            break;
     683                    }
     684                }
     685                ++$worksheetID;
     686            }
    451687
    452688            $autoFilterReader->read($workbookData);
    453689            $definedNameReader->read($workbookData);
    454690        }
     691
    455692        $spreadsheet->setActiveSheetIndex(0);
    456693
     
    567804            $rowRepeats = 1;
    568805        }
     806        $worksheet = $spreadsheet->getSheetByName($worksheetName);
    569807
    570808        $columnID = 'A';
     
    574812                continue; // should just be whitespace
    575813            }
     814            if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
     815                $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
     816            } else {
     817                $colRepeats = 1;
     818            }
     819            $styleName = $cellData->getAttributeNS($tableNs, 'style-name');
     820
     821            // When a cell has number-columns-repeated, check if ANY column in the
     822            // repeated range passes the read filter. If not, skip the entire group.
     823            // If some columns pass, we need to fall through to the processing block
     824            // which will handle per-column filtering.
    576825            if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
    577                 if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
    578                     $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
    579                 } else {
    580                     $colRepeats = 1;
    581                 }
    582 
     826                if ($colRepeats <= 1) {
     827                    StringHelper::stringIncrement($columnID);
     828
     829                    continue;
     830                }
     831
     832                // Check if any column within this repeated group passes the filter
     833                $anyColumnPasses = false;
     834                $tempCol = $columnID;
    583835                for ($i = 0; $i < $colRepeats; ++$i) {
    584                     StringHelper::stringIncrement($columnID);
    585                 }
    586 
    587                 continue;
     836                    if ($i > 0) {
     837                        StringHelper::stringIncrement($tempCol);
     838                    }
     839                    if ($this->getReadFilter()->readCell($tempCol, $rowID, $worksheetName)) {
     840                        $anyColumnPasses = true;
     841
     842                        break;
     843                    }
     844                }
     845
     846                if (!$anyColumnPasses) {
     847                    for ($i = 0; $i < $colRepeats; ++$i) {
     848                        StringHelper::stringIncrement($columnID);
     849                    }
     850
     851                    continue;
     852                }
     853                // Fall through to process the cell, with per-column filter checks
     854            }
     855            if ($worksheet !== null && ($cellData->hasChildNodes() || ($cellData->nextSibling !== null)) && isset($this->allStyles[$styleName])) {
     856                $spannedRange = "$columnID$rowID";
     857                // the following is sufficient for ods,
     858                // and does no harm for xlsx/xls.
     859                $worksheet->getStyle($spannedRange)
     860                    ->applyFromArray($this->allStyles[$styleName]);
     861                // the rest of this block is needed for xlsx/xls,
     862                // and does no harm for ods.
     863                if (isset($this->allStyles[$styleName]['borders'])) {
     864                    $spannedRows = $cellData->getAttributeNS($tableNs, 'number-columns-spanned');
     865                    $spannedColumns = $cellData->getAttributeNS($tableNs, 'number-rows-spanned');
     866                    $spannedRows = max((int) $spannedRows, 1);
     867                    $spannedColumns = max((int) $spannedColumns, 1);
     868                    if ($spannedRows > 1 || $spannedColumns > 1) {
     869                        $endRow = $rowID + $spannedRows - 1;
     870                        $endCol = $columnID;
     871                        while ($spannedColumns > 1) {
     872                            StringHelper::stringIncrement($endCol);
     873                            --$spannedColumns;
     874                        }
     875                        $spannedRange .= ":$endCol$endRow";
     876                        $worksheet->getStyle($spannedRange)
     877                            ->getBorders()
     878                            ->applyFromArray(
     879                                $this->allStyles[$styleName]['borders']
     880                            );
     881                    }
     882                }
    588883            }
    589884
     
    658953
    659954            if (count($paragraphs) > 0) {
     955                $dataValue = null;
    660956                // Consolidate if there are multiple p records (maybe with spans as well)
    661957                $dataArray = [];
     
    672968
    673969                $type = $cellData->getAttributeNS($officeNs, 'value-type');
     970                $symbol = '';
     971                $leftHandCurrency = Preg::isMatch('/\$|£|¥/', $allCellDataText, $matches);
     972                if ($leftHandCurrency) {
     973                    $type = str_replace('float', 'currency', $type);
     974                    $symbol = (string) $matches[0];
     975                }
     976                $customFormatting = '';
     977                if ($this->formatCallback !== null) {
     978                    $temp = ($this->formatCallback)($type, $allCellDataText);
     979                    if ($temp !== '') {
     980                        $customFormatting = $temp;
     981                    }
     982                }
    674983
    675984                switch ($type) {
     
    6921001                        break;
    6931002                    case 'percentage':
     1003                        if (!str_contains($allCellDataText, '.')) {
     1004                            $formatting = NumberFormat::FORMAT_PERCENTAGE;
     1005                        } elseif (substr($allCellDataText, -3, 1) === '.') {
     1006                            $formatting = NumberFormat::FORMAT_PERCENTAGE_0;
     1007                        } else {
     1008                            $formatting = NumberFormat::FORMAT_PERCENTAGE_00;
     1009                        }
    6941010                        $type = DataType::TYPE_NUMERIC;
    6951011                        $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
    696 
    697                         // percentage should always be float
    698                         //if (floor($dataValue) == $dataValue) {
    699                         //    $dataValue = (int) $dataValue;
    700                         //}
    701                         $formatting = NumberFormat::FORMAT_PERCENTAGE_00;
    7021012
    7031013                        break;
     
    7061016                        $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
    7071017
    708                         if (floor($dataValue) == $dataValue) {
    709                             $dataValue = (int) $dataValue;
    710                         }
    711                         $formatting = NumberFormat::FORMAT_CURRENCY_USD_INTEGER;
     1018                        $currency = $cellData->getAttributeNS($officeNs, 'currency');
     1019                        if ($leftHandCurrency) {
     1020                            $typeValue = 'currency';
     1021                            $formatting = str_contains($allCellDataText, '.') ? NumberFormat::FORMAT_CURRENCY_USD : NumberFormat::FORMAT_CURRENCY_USD_INTEGER;
     1022                            if ($symbol !== '$') {
     1023                                $formatting = str_replace('$', $symbol, $formatting);
     1024                            }
     1025                        } elseif (str_contains($allCellDataText, '€')) {
     1026                            $typeValue = 'currency';
     1027                            $formatting = str_contains($allCellDataText, '.') ? NumberFormat::FORMAT_CURRENCY_EUR : NumberFormat::FORMAT_CURRENCY_EUR_INTEGER;
     1028                        }
    7121029
    7131030                        break;
     
    7161033                        $dataValue = (float) $cellData->getAttributeNS($officeNs, 'value');
    7171034
     1035                        if ($dataValue !== floor($dataValue)) {
     1036                            // do nothing
     1037                        } elseif (substr($allCellDataText, -2, 1) === '.') {
     1038                            $formatting = NumberFormat::FORMAT_NUMBER_0;
     1039                        } elseif (substr($allCellDataText, -3, 1) === '.') {
     1040                            $formatting = NumberFormat::FORMAT_NUMBER_00;
     1041                        }
    7181042                        if (floor($dataValue) == $dataValue) {
    7191043                            if ($dataValue == (int) $dataValue) {
     
    7281052                        $dataValue = Date::convertIsoDate($value);
    7291053
    730                         if ($dataValue != floor($dataValue)) {
     1054                        if (Preg::isMatch('/^\d\d\d\d-\d\d-\d\d$/', $allCellDataText)) {
     1055                            $formatting = 'yyyy-mm-dd';
     1056                        } elseif (Preg::isMatch('/^\d\d?-[a-zA-Z]+-\d\d\d\d$/', $allCellDataText)) {
     1057                            $formatting = 'd-mmm-yyyy';
     1058                        } elseif ($dataValue != floor($dataValue)) {
    7311059                            $formatting = NumberFormat::FORMAT_DATE_XLSX15
    7321060                                . ' '
     
    7411069
    7421070                        $timeValue = $cellData->getAttributeNS($officeNs, 'time-value');
    743 
    744                         $dataValue = Date::PHPToExcel(
    745                             strtotime(
    746                                 '01-01-1970 ' . implode(':', sscanf($timeValue, 'PT%dH%dM%dS') ?? [])
    747                             )
    748                         );
    749                         $formatting = NumberFormat::FORMAT_DATE_TIME4;
     1071                        $minus = '';
     1072                        if (str_starts_with($timeValue, '-')) {
     1073                            $minus = '-';
     1074                            $timeValue = (string) substr($timeValue, 1);
     1075                        }
     1076                        $timeArray = sscanf($timeValue, 'PT%dH%dM%dS');
     1077                        if (is_array($timeArray)) {
     1078                            /** @var array{int, int, int} $timeArray */
     1079                            $days = intdiv($timeArray[0], 24);
     1080                            $hours = $timeArray[0] % 24;
     1081                            $dt = new DateTime("1899-12-30 $hours:{$timeArray[1]}:{$timeArray[2]}", new DateTimeZone('UTC'));
     1082                            $dt->modify("+$days days");
     1083                            $dataValue = Date::PHPToExcel($dt);
     1084                            if ($minus === '-') {
     1085                                $dataValue *= -1;
     1086                                $formatting = '[hh]:mm:ss';
     1087                            } else {
     1088                                $formatting = NumberFormat::FORMAT_DATE_TIME4;
     1089                            }
     1090                        }
    7501091
    7511092                        break;
    7521093                    default:
    7531094                        $dataValue = null;
     1095                }
     1096                if ($customFormatting !== '') {
     1097                    $formatting = $customFormatting;
    7541098                }
    7551099            } else {
     
    7641108            }
    7651109
    766             if ($cellData->hasAttributeNS($tableNs, 'number-columns-repeated')) {
    767                 $colRepeats = (int) $cellData->getAttributeNS($tableNs, 'number-columns-repeated');
    768             } else {
    769                 $colRepeats = 1;
    770             }
    771 
    772             if ($type !== null) { // @phpstan-ignore-line
    773                 for ($i = 0; $i < $colRepeats; ++$i) {
    774                     if ($i > 0) {
    775                         StringHelper::stringIncrement($columnID);
    776                     }
    777 
    778                     if ($type !== DataType::TYPE_NULL) {
    779                         for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
    780                             $rID = $rowID + $rowAdjust;
    781 
    782                             $cell = $spreadsheet->getActiveSheet()
    783                                 ->getCell($columnID . $rID);
    784 
    785                             // Set value
    786                             if ($hasCalculatedValue) {
    787                                 $cell->setValueExplicit($cellDataFormula, $type);
    788                                 if ($cellDataType === 'array') {
    789                                     $cell->setFormulaAttributes(['t' => 'array', 'ref' => $cellDataRef]);
    790                                 }
    791                             } elseif ($type !== '' || $dataValue !== null) {
    792                                 $cell->setValueExplicit($dataValue, $type);
     1110            for ($i = 0; $i < $colRepeats; ++$i) {
     1111                if ($i > 0) {
     1112                    StringHelper::stringIncrement($columnID);
     1113                }
     1114
     1115                if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
     1116                    continue;
     1117                }
     1118
     1119                if ($type !== DataType::TYPE_NULL) {
     1120                    for ($rowAdjust = 0; $rowAdjust < $rowRepeats; ++$rowAdjust) {
     1121                        $rID = $rowID + $rowAdjust;
     1122
     1123                        $cell = $spreadsheet->getActiveSheet()
     1124                            ->getCell($columnID . $rID);
     1125
     1126                        // Set value
     1127                        if ($hasCalculatedValue) {
     1128                            $cell->setValueExplicit($cellDataFormula, $type);
     1129                            if ($cellDataType === 'array') {
     1130                                $cell->setFormulaAttributes(['t' => 'array', 'ref' => $cellDataRef]);
    7931131                            }
    794 
    795                             if ($hasCalculatedValue) {
    796                                 $cell->setCalculatedValue($dataValue, $type === DataType::TYPE_NUMERIC);
     1132                        } elseif ($type !== '' || $dataValue !== null) {
     1133                            $cell->setValueExplicit($dataValue, $type);
     1134                        }
     1135
     1136                        if ($hasCalculatedValue) {
     1137                            $cell->setCalculatedValue($dataValue, $type === DataType::TYPE_NUMERIC);
     1138                        }
     1139
     1140                        // Set other properties
     1141                        if ($formatting !== null) {
     1142                            $spreadsheet->getActiveSheet()
     1143                                ->getStyle($columnID . $rID)
     1144                                ->getNumberFormat()
     1145                                ->setFormatCode($formatting);
     1146                        } else {
     1147                            $spreadsheet->getActiveSheet()
     1148                                ->getStyle($columnID . $rID)
     1149                                ->getNumberFormat()
     1150                                ->setFormatCode(NumberFormat::FORMAT_GENERAL);
     1151                        }
     1152
     1153                        if ($hyperlink !== null) {
     1154                            if ($hyperlink[0] === '#') {
     1155                                $hyperlink = 'sheet://' . substr($hyperlink, 1);
    7971156                            }
    798 
    799                             // Set other properties
    800                             if ($formatting !== null) {
    801                                 $spreadsheet->getActiveSheet()
    802                                     ->getStyle($columnID . $rID)
    803                                     ->getNumberFormat()
    804                                     ->setFormatCode($formatting);
    805                             } else {
    806                                 $spreadsheet->getActiveSheet()
    807                                     ->getStyle($columnID . $rID)
    808                                     ->getNumberFormat()
    809                                     ->setFormatCode(NumberFormat::FORMAT_GENERAL);
    810                             }
    811 
    812                             if ($hyperlink !== null) {
    813                                 if ($hyperlink[0] === '#') {
    814                                     $hyperlink = 'sheet://' . substr($hyperlink, 1);
    815                                 }
    816                                 $cell->getHyperlink()
    817                                     ->setUrl($hyperlink);
    818                             }
     1157                            $cell->getHyperlink()
     1158                                ->setUrl($hyperlink);
    8191159                        }
    8201160                    }
     
    8441184     * @param string[] $columnWidths
    8451185     */
    846     private function processTableHeaderColumns(
     1186    private function processTableColumnHeader(
    8471187        DOMElement $childNode,
    8481188        string $tableNs,
    8491189        array $columnWidths,
    8501190        int &$tableColumnIndex,
    851         Spreadsheet $spreadsheet
     1191        Spreadsheet $spreadsheet,
     1192        bool $processWidths = true,
     1193        bool $processStyles = true
    8521194    ): void {
    8531195        foreach ($childNode->childNodes as $grandchildNode) {
     
    8611203                        $columnWidths,
    8621204                        $tableColumnIndex,
    863                         $spreadsheet
     1205                        $spreadsheet,
     1206                        $processWidths,
     1207                        $processStyles
    8641208                    );
    8651209
     
    8771221        array $columnWidths,
    8781222        int &$tableColumnIndex,
    879         Spreadsheet $spreadsheet
     1223        Spreadsheet $spreadsheet,
     1224        bool $processWidths = true,
     1225        bool $processStyles = true
    8801226    ): void {
    8811227        foreach ($childNode->childNodes as $grandchildNode) {
     
    8891235                        $columnWidths,
    8901236                        $tableColumnIndex,
    891                         $spreadsheet
     1237                        $spreadsheet,
     1238                        $processWidths,
     1239                        $processStyles
    8921240                    );
    8931241
     
    8951243                case 'table-header-columns':
    8961244                case 'table-columns':
    897                     $this->processTableHeaderColumns(
     1245                    $this->processTableColumnHeader(
    8981246                        $grandchildNode,
    8991247                        $tableNs,
    9001248                        $columnWidths,
    9011249                        $tableColumnIndex,
    902                         $spreadsheet
     1250                        $spreadsheet,
     1251                        $processWidths,
     1252                        $processStyles
    9031253                    );
    9041254
     
    9101260                        $columnWidths,
    9111261                        $tableColumnIndex,
    912                         $spreadsheet
     1262                        $spreadsheet,
     1263                        $processWidths,
     1264                        $processStyles
    9131265                    );
    9141266
     
    9261278        array $columnWidths,
    9271279        int &$tableColumnIndex,
    928         Spreadsheet $spreadsheet
     1280        Spreadsheet $spreadsheet,
     1281        bool $processWidths = true,
     1282        bool $processStyles = true
    9291283    ): void {
    9301284        if ($childNode->hasAttributeNS($tableNs, 'number-columns-repeated')) {
     
    9341288        }
    9351289        $tableStyleName = $childNode->getAttributeNS($tableNs, 'style-name');
    936         if (isset($columnWidths[$tableStyleName])) {
    937             $columnWidth = new HelperDimension($columnWidths[$tableStyleName]);
    938             $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex);
    939             for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0; --$rowRepeats2) {
    940                 /** @var string $tableColumnString */
    941                 $spreadsheet->getActiveSheet()
    942                     ->getColumnDimension($tableColumnString)
    943                     ->setWidth($columnWidth->toUnit('cm'), 'cm');
    944                 StringHelper::stringIncrement($tableColumnString);
     1290        if ($processWidths) {
     1291            if (isset($columnWidths[$tableStyleName])) {
     1292                $columnWidth = new HelperDimension($columnWidths[$tableStyleName]);
     1293                $tableColumnIndex2 = $tableColumnIndex;
     1294                $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex2);
     1295                for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0 && $tableColumnIndex2 <= AddressRange::MAX_COLUMN_INT; --$rowRepeats2) {
     1296                    if (!$this->readEmptyCells && $tableColumnIndex2 > $this->highestDataIndex) {
     1297                        break;
     1298                    }
     1299                    $spreadsheet->getActiveSheet()
     1300                        ->getColumnDimension($tableColumnString)
     1301                        ->setWidth($columnWidth->toUnit('cm'), 'cm');
     1302                    StringHelper::stringIncrement(
     1303                        $tableColumnString
     1304                    );
     1305                    ++$tableColumnIndex2;
     1306                }
     1307            }
     1308        }
     1309        if ($processStyles) {
     1310            $defaultStyleName = $childNode->getAttributeNS($tableNs, 'default-cell-style-name');
     1311            if ($defaultStyleName !== 'Default' && isset($this->allStyles[$defaultStyleName])) {
     1312                $tableColumnIndex2 = $tableColumnIndex;
     1313                $tableColumnString = Coordinate::stringFromColumnIndex($tableColumnIndex2);
     1314                for ($rowRepeats2 = $rowRepeats; $rowRepeats2 > 0 && $tableColumnIndex2 <= AddressRange::MAX_COLUMN_INT; --$rowRepeats2) {
     1315                    $spreadsheet->getActiveSheet()
     1316                        ->getStyle($tableColumnString)
     1317                        ->applyFromArray(
     1318                            $this->allStyles[$defaultStyleName]
     1319                        );
     1320                    StringHelper::stringIncrement(
     1321                        $tableColumnString
     1322                    );
     1323                    ++$tableColumnIndex2;
     1324                }
    9451325            }
    9461326        }
     
    11001480        }
    11011481    }
     1482
     1483    /** @var null|Closure(string, string):string */
     1484    private ?Closure $formatCallback = null;
     1485
     1486    /** @param Closure(string, string):string $formatCallback */
     1487    public function setFormatCallback(Closure $formatCallback): void
     1488    {
     1489        $this->formatCallback = $formatCallback;
     1490    }
     1491
     1492    /** @return array{
     1493     *   autoColor?: true,
     1494     *   bold?: true,
     1495     *   color?: array{rgb: string},
     1496     *   italic?: true,
     1497     *   name?: non-empty-string,
     1498     *   size?: float|int,
     1499     *   strikethrough?: true,
     1500     *   underline?: 'double'|'single',
     1501     * }
     1502     */
     1503    protected function getFontStyles(DOMElement $textProperty, string $styleNs, string $fontNs): array
     1504    {
     1505        $fonts = [];
     1506        $temp = $textProperty->getAttributeNs($styleNs, 'font-name') ?: $textProperty->getAttributeNs($fontNs, 'font-family');
     1507        if ($temp !== '') {
     1508            $fonts['name'] = $temp;
     1509        }
     1510        $temp = $textProperty->getAttributeNs($fontNs, 'font-size');
     1511        if ($temp !== '' && str_ends_with($temp, 'pt')) {
     1512            $fonts['size'] = (float) substr($temp, 0, -2);
     1513        }
     1514        $temp = $textProperty->getAttributeNs($fontNs, 'font-style');
     1515        if ($temp === 'italic') {
     1516            $fonts['italic'] = true;
     1517        }
     1518        $temp = $textProperty->getAttributeNs($fontNs, 'font-weight');
     1519        if ($temp === 'bold') {
     1520            $fonts['bold'] = true;
     1521        }
     1522        $temp = $textProperty->getAttributeNs($fontNs, 'color');
     1523        if (Preg::isMatch('/^#[a-f0-9]{6}$/i', $temp)) {
     1524            $fonts['color'] = ['rgb' => (string) substr($temp, 1)];
     1525        }
     1526        $temp = $textProperty->getAttributeNs($styleNs, 'use-window-font-color');
     1527        if ($temp === 'true') {
     1528            $fonts['autoColor'] = true;
     1529        }
     1530        $temp = $textProperty->getAttributeNs($styleNs, 'text-underline-type');
     1531        if ($temp === '') {
     1532            $temp = $textProperty->getAttributeNs($styleNs, 'text-underline-style');
     1533            if ($temp !== '' && $temp !== 'none') {
     1534                $temp = 'single';
     1535            }
     1536        }
     1537        if ($temp === 'single' || $temp === 'double') {
     1538            $fonts['underline'] = $temp;
     1539        }
     1540        $temp = $textProperty->getAttributeNs($styleNs, 'text-line-through-type');
     1541        if ($temp !== '' && $temp !== 'none') {
     1542            $fonts['strikethrough'] = true;
     1543        }
     1544
     1545        return $fonts;
     1546    }
     1547
     1548    /** @return array{
     1549     *   fillType?: string,
     1550     *   startColor?: array{rgb: string},
     1551     * }
     1552     */
     1553    protected function getFillStyles(DOMElement $tableCellProperties, string $fontNs): array
     1554    {
     1555        $fills = [];
     1556        $temp = $tableCellProperties->getAttributeNs($fontNs, 'background-color');
     1557        if (Preg::isMatch('/^#[a-f0-9]{6}$/i', $temp)) {
     1558            $fills['fillType'] = Fill::FILL_SOLID;
     1559            $fills['startColor'] = ['rgb' => (string) substr($temp, 1)];
     1560        } elseif ($temp === 'transparent') {
     1561            $fills['fillType'] = Fill::FILL_NONE;
     1562        }
     1563
     1564        return $fills;
     1565    }
     1566
     1567    private const MAP_VERTICAL = [
     1568        'top' => Alignment::VERTICAL_TOP,
     1569        'middle' => Alignment::VERTICAL_CENTER,
     1570        'automatic' => Alignment::VERTICAL_JUSTIFY,
     1571        'bottom' => Alignment::VERTICAL_BOTTOM,
     1572    ];
     1573    private const MAP_HORIZONTAL = [
     1574        'center' => Alignment::HORIZONTAL_CENTER,
     1575        'end' => Alignment::HORIZONTAL_RIGHT,
     1576        'justify' => Alignment::HORIZONTAL_FILL,
     1577        'start' => Alignment::HORIZONTAL_LEFT,
     1578    ];
     1579
     1580    /** @return array{
     1581     *   shrinkToFit?: bool,
     1582     *   textRotation?: int,
     1583     *   vertical?: string,
     1584     *   wrapText?: bool,
     1585     * }
     1586     */
     1587    protected function getAlignment1Styles(DOMElement $tableCellProperties, string $styleNs, string $fontNs): array
     1588    {
     1589        $alignment1 = [];
     1590        $temp = $tableCellProperties->getAttributeNs($styleNs, 'rotation-angle');
     1591        if (is_numeric($temp)) {
     1592            $temp2 = (int) $temp;
     1593            if ($temp2 > 90) {
     1594                $temp2 -= 360;
     1595            }
     1596            if ($temp2 >= -90 && $temp2 <= 90) {
     1597                $alignment1['textRotation'] = (int) $temp2;
     1598            }
     1599        }
     1600        $temp = $tableCellProperties->getAttributeNs($styleNs, 'vertical-align');
     1601        $temp2 = self::MAP_VERTICAL[$temp] ?? '';
     1602        if ($temp2 !== '') {
     1603            $alignment1['vertical'] = $temp2;
     1604        }
     1605        $temp = $tableCellProperties->getAttributeNs($fontNs, 'wrap-option');
     1606        if ($temp === 'wrap') {
     1607            $alignment1['wrapText'] = true;
     1608        } elseif ($temp === 'no-wrap') {
     1609            $alignment1['wrapText'] = false;
     1610        }
     1611        $temp = $tableCellProperties->getAttributeNs($styleNs, 'shrink-to-fit');
     1612        if ($temp === 'true' || $temp === 'false') {
     1613            $alignment1['shrinkToFit'] = $temp === 'true';
     1614        }
     1615
     1616        return $alignment1;
     1617    }
     1618
     1619    /** @return array{
     1620     *   horizontal?: string,
     1621     *   readOrder?: int,
     1622     * }
     1623     */
     1624    protected function getAlignment2Styles(DOMElement $paragraphProperties, string $styleNs, string $fontNs): array
     1625    {
     1626        $alignment2 = [];
     1627        $temp = $paragraphProperties->getAttributeNs($fontNs, 'text-align');
     1628        $temp2 = self::MAP_HORIZONTAL[$temp] ?? '';
     1629        if ($temp2 !== '') {
     1630            $alignment2['horizontal'] = $temp2;
     1631        }
     1632        $temp = $paragraphProperties->getAttributeNs($fontNs, 'margin-left') ?: $paragraphProperties->getAttributeNs($fontNs, 'margin-right');
     1633        if (Preg::isMatch('/^\d+([.]\d+)?(cm|in|mm|pt)$/', $temp)) {
     1634            $dimension = new HelperDimension($temp);
     1635            $alignment2['indent'] = (int) round($dimension->toUnit('px') / Alignment::INDENT_UNITS_TO_PIXELS);
     1636        }
     1637
     1638        $temp = $paragraphProperties->getAttributeNs($styleNs, 'writing-mode');
     1639        if ($temp === 'rl-tb') {
     1640            $alignment2['readOrder'] = Alignment::READORDER_RTL;
     1641        } elseif ($temp === 'lr-tb') {
     1642            $alignment2['readOrder'] = Alignment::READORDER_LTR;
     1643        }
     1644
     1645        return $alignment2;
     1646    }
     1647
     1648    /** @return array{
     1649     *   locked?: string,
     1650     *   hidden?: string,
     1651     * }
     1652     */
     1653    protected function getProtectionStyles(DOMElement $tableCellProperties, string $styleNs): array
     1654    {
     1655        $protection = [];
     1656        $temp = $tableCellProperties->getAttributeNs($styleNs, 'cell-protect');
     1657        switch ($temp) {
     1658            case 'protected formula-hidden':
     1659                $protection['locked'] = Protection::PROTECTION_PROTECTED;
     1660                $protection['hidden'] = Protection::PROTECTION_PROTECTED;
     1661
     1662                break;
     1663            case 'formula-hidden':
     1664                $protection['locked'] = Protection::PROTECTION_UNPROTECTED;
     1665                $protection['hidden'] = Protection::PROTECTION_PROTECTED;
     1666
     1667                break;
     1668            case 'protected':
     1669                $protection['locked'] = Protection::PROTECTION_PROTECTED;
     1670                $protection['hidden'] = Protection::PROTECTION_UNPROTECTED;
     1671
     1672                break;
     1673            case 'none':
     1674                $protection['locked'] = Protection::PROTECTION_UNPROTECTED;
     1675                $protection['hidden'] = Protection::PROTECTION_UNPROTECTED;
     1676
     1677                break;
     1678        }
     1679
     1680        return $protection;
     1681    }
     1682
     1683    private const MAP_BORDER_STYLE = [ // default BORDER_THIN
     1684        'none' => Border::BORDER_NONE,
     1685        'hidden' => Border::BORDER_NONE,
     1686        'dotted' => Border::BORDER_DOTTED,
     1687        'dash-dot' => Border::BORDER_DASHDOT,
     1688        'dash-dot-dot' => Border::BORDER_DASHDOTDOT,
     1689        'dashed' => Border::BORDER_DASHED,
     1690        'double' => Border::BORDER_DOUBLE,
     1691    ];
     1692
     1693    private const MAP_BORDER_MEDIUM = [
     1694        Border::BORDER_THIN => Border::BORDER_MEDIUM,
     1695        Border::BORDER_DASHDOT => Border::BORDER_MEDIUMDASHDOT,
     1696        Border::BORDER_DASHDOTDOT => Border::BORDER_MEDIUMDASHDOTDOT,
     1697        Border::BORDER_DASHED => Border::BORDER_MEDIUMDASHED,
     1698    ];
     1699
     1700    private const MAP_BORDER_THICK = [
     1701        Border::BORDER_THIN => Border::BORDER_THICK,
     1702        Border::BORDER_DASHDOT => Border::BORDER_MEDIUMDASHDOT,
     1703        Border::BORDER_DASHDOTDOT => Border::BORDER_MEDIUMDASHDOTDOT,
     1704        Border::BORDER_DASHED => Border::BORDER_MEDIUMDASHED,
     1705    ];
     1706
     1707    /** @return array{
     1708     *   bottom?: array{borderStyle:string, color:array{rgb: string}},
     1709     *   top?: array{borderStyle:string, color:array{rgb: string}},
     1710     *   left?: array{borderStyle:string, color:array{rgb: string}},
     1711     *   right?: array{borderStyle:string, color:array{rgb: string}},
     1712     *   diagonal?: array{borderStyle:string, color:array{rgb: string}},
     1713     *   diagonalDirection?: int,
     1714     * }
     1715     */
     1716    protected function getBorderStyles(DOMElement $tableCellProperties, string $fontNs, string $styleNs): array
     1717    {
     1718        $borders = [];
     1719        $temp = $tableCellProperties->getAttributeNs($fontNs, 'border');
     1720        $diagonalIndex = Borders::DIAGONAL_NONE;
     1721        foreach (['bottom', 'left', 'right', 'top', 'diagonal-tl-br', 'diagonal-bl-tr'] as $direction) {
     1722            if (str_starts_with($direction, 'diagonal')) {
     1723                $directionIndex = 'diagonal';
     1724                $temp = $tableCellProperties->getAttributeNs($styleNs, $direction);
     1725            } else {
     1726                $directionIndex = $direction;
     1727                $temp = $tableCellProperties->getAttributeNs($fontNs, "border-$direction");
     1728            }
     1729            if (Preg::isMatch('/^(\d+(?:[.]\d+)?)pt\s+([-\w]+)\s+#([0-9a-fA-F]{6})$/', $temp, $matches)) {
     1730                $style = self::MAP_BORDER_STYLE[$matches[2]] ?? Border::BORDER_THIN;
     1731                $width = (float) $matches[1];
     1732                if ($width >= 2.5) {
     1733                    $style = self::MAP_BORDER_THICK[$style] ?? $style;
     1734                } elseif ($width >= 1.75) {
     1735                    $style = self::MAP_BORDER_MEDIUM[$style] ?? $style;
     1736                }
     1737                $color = $matches[3];
     1738                $borders[$directionIndex] = ['borderStyle' => $style, 'color' => ['rgb' => $matches[3]]];
     1739                if ($direction === 'diagonal-tl-br') {
     1740                    $diagonalIndex = Borders::DIAGONAL_DOWN;
     1741                } elseif ($direction === 'diagonal-bl-tr') {
     1742                    $diagonalIndex = ($diagonalIndex === Borders::DIAGONAL_NONE) ? Borders::DIAGONAL_UP : Borders::DIAGONAL_BOTH;
     1743                }
     1744            }
     1745        }
     1746        if ($diagonalIndex !== Borders::DIAGONAL_NONE) {
     1747            $borders['diagonalDirection'] = $diagonalIndex;
     1748        }
     1749
     1750        return $borders; // @phpstan-ignore-line
     1751    }
    11021752}
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Reader/Ods/PageSettings.php

    r3463039 r3473263  
    6969            $marginBottom = ($nullsafeVariable8 = $pageLayoutProperties) ? $nullsafeVariable8->getAttributeNS($this->stylesFo, 'margin-bottom') : null;
    7070            $header = $styleSet->getElementsByTagNameNS($this->stylesNs, 'header-style')->item(0);
    71             $headerProperties = ($nullsafeVariable9 = ($nullsafeVariable13 = $header) ? $nullsafeVariable13->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties') : null) ? $nullsafeVariable9->item(0) : null;
    72             $marginHeader = ($nullsafeVariable10 = $headerProperties) ? $nullsafeVariable10->getAttributeNS($this->stylesFo, 'min-height') : null;
     71            $headerProperties = ($nullsafeVariable9 = ($nullsafeVariable10 = $header) ? $nullsafeVariable10->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties') : null) ? $nullsafeVariable9->item(0) : null;
     72            $marginHeader = ($nullsafeVariable11 = $headerProperties) ? $nullsafeVariable11->getAttributeNS($this->stylesFo, 'min-height') : null;
    7373            $footer = $styleSet->getElementsByTagNameNS($this->stylesNs, 'footer-style')->item(0);
    74             $footerProperties = ($nullsafeVariable11 = ($nullsafeVariable14 = $footer) ? $nullsafeVariable14->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties') : null) ? $nullsafeVariable11->item(0) : null;
    75             $marginFooter = ($nullsafeVariable12 = $footerProperties) ? $nullsafeVariable12->getAttributeNS($this->stylesFo, 'min-height') : null;
     74            $footerProperties = ($nullsafeVariable12 = ($nullsafeVariable13 = $footer) ? $nullsafeVariable13->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties') : null) ? $nullsafeVariable12->item(0) : null;
     75            $marginFooter = ($nullsafeVariable14 = $footerProperties) ? $nullsafeVariable14->getAttributeNS($this->stylesFo, 'min-height') : null;
    7676
    7777            $this->pageLayoutStyles[$styleName] = (object) [
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Reader/Xlsx.php

    r3463039 r3473263  
    699699                            $this->styleReader
    700700                                ->readStyle($objStyle, $style);
     701                            if (isset($xfTag->extLst)) {
     702                                foreach ($xfTag->extLst->ext as $extTag) {
     703                                    $attributes = $extTag->attributes();
     704                                    if (isset($attributes['uri'])) {
     705                                        if ((string) $attributes['uri'] === Namespaces::STYLE_CHECKBOX_URI) {
     706                                            $objStyle->setCheckBox(true);
     707                                        }
     708                                    }
     709                                }
     710                            }
    701711                            foreach ($this->styleReader->getFontCharsets() as $fontName => $charset) {
    702712                                $excel->addFontCharset($fontName, $charset);
     
    15991609                                                        if (isset($images[$linkImageKey])) {
    16001610                                                            $url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
    1601                                                             $objDrawing->setPath($url, false, null, $this->allowExternalImages);
     1611                                                            $objDrawing->setPath($url, false, null, $this->allowExternalImages, $this->isWhitelisted);
    16021612                                                        }
    16031613                                                        if ($objDrawing->getPath() === '') {
     
    17031713                                                        if (isset($images[$linkImageKey])) {
    17041714                                                            $url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
    1705                                                             $objDrawing->setPath($url, false, null, $this->allowExternalImages);
     1715                                                            $objDrawing->setPath($url, false, null, $this->allowExternalImages, $this->isWhitelisted);
    17061716                                                        }
    17071717                                                        if ($objDrawing->getPath() === '') {
     
    22332243            if (str_contains($item[1], 'px')) {
    22342244                $item[1] = str_replace('px', '', $item[1]);
    2235             }
    2236             if (str_contains($item[1], 'pt')) {
     2245            } elseif (str_contains($item[1], 'pt')) {
    22372246                $item[1] = str_replace('pt', '', $item[1]);
    2238                 $item[1] = (string) Font::fontSizeToPixels((int) $item[1]);
    2239             }
    2240             if (str_contains($item[1], 'in')) {
     2247                $item[1] = Font::fontSizeToPixels((float) $item[1]);
     2248            } elseif (str_contains($item[1], 'in')) {
    22412249                $item[1] = str_replace('in', '', $item[1]);
    2242                 $item[1] = (string) Font::inchSizeToPixels((int) $item[1]);
    2243             }
    2244             if (str_contains($item[1], 'cm')) {
     2250                $item[1] = (int) Font::inchSizeToPixels((float) $item[1]);
     2251            } elseif (str_contains($item[1], 'cm')) {
    22452252                $item[1] = str_replace('cm', '', $item[1]);
    2246                 $item[1] = (string) Font::centimeterSizeToPixels((int) $item[1]);
     2253                $item[1] = (int) Font::centimeterSizeToPixels((float) $item[1]);
     2254            } elseif (str_contains($item[1], 'mm')) {
     2255                $item[1] = str_replace('mm', '', $item[1]);
     2256                $item[1] = (int) Font::centimeterSizeToPixels((float) $item[1] / 10);
    22472257            }
    22482258
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Reader/Xlsx/Namespaces.php

    r3463039 r3473263  
    130130
    131131    const RELATIONSHIPS_RICH_VALUE_REL = 'http://schemas.microsoft.com/office/2022/10/relationships/richValueRel';
     132
     133    const FEATURE_PROPERTY_BAG = 'http://schemas.microsoft.com/office/spreadsheetml/2022/featurepropertybag';
     134    const RELATIONSHIPS_FEATURE_PROPERTY_BAG = 'http://schemas.microsoft.com/office/2022/11/relationships/FeaturePropertyBag';
     135    const STYLE_CHECKBOX_URI = '{C7286773-470A-42A8-94C5-96B5CB345126}';
    132136}
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Spreadsheet.php

    r3463039 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet;
    44
     5use TablePress\Composer\Pcre\Preg;
    56use JsonSerializable;
    67use TablePress\PhpOffice\PhpSpreadsheet\Calculation\Calculation;
     
    10651066            if ($worksheet !== null) {
    10661067                $wsTitle = StringHelper::strToUpper($worksheet->getTitle());
    1067                 $definedName = (string) preg_replace('/^.*!/', '', $definedName);
     1068                $definedName = Preg::replace('/^.*!/', '', $definedName);
    10681069                foreach ($this->definedNames as $dn) {
    10691070                    $sheet = $dn->getScope() ?? $dn->getWorksheet();
     
    18701871    }
    18711872
     1873    /**
     1874     * Change all 2-digit-year date styles to use 4-digit year;
     1875     * change all dd-mm-yyyy and mm-dd-yyyy styles to yyyy-mm-dd;
     1876     * dd-mmm-yyyy is unambiguous and left unchanged.
     1877     */
     1878    public function disambiguateDateStyles(): void
     1879    {
     1880        foreach ($this->cellXfCollection as $style) {
     1881            $numberFormat = $style->getNumberFormat();
     1882            $oldFormat = (string) $numberFormat->getFormatCode();
     1883            $newFormat = Preg::replace('/\byy\b/i', 'yyyy', $oldFormat);
     1884            $newFormat = Preg::replace(
     1885                '~\bdd?(-|/|"-"|"/")'
     1886                    . 'mm?(-|/|"-"|"/")'
     1887                    . 'yyyy~',
     1888                'yyyy-mm-dd',
     1889                $newFormat
     1890            );
     1891            $newFormat = Preg::replace(
     1892                '~\bmm?(-|/|"-"|"/")'
     1893                    . 'dd?(-|/|"-"|"/")'
     1894                    . 'yyyy~',
     1895                'yyyy-mm-dd',
     1896                $newFormat
     1897            );
     1898            if ($newFormat !== $oldFormat) {
     1899                $numberFormat->setFormatCode($newFormat);
     1900            }
     1901        }
     1902    }
     1903
    18721904    public function returnArrayAsArray(): void
    18731905    {
     
    19041936        return $this->domainWhiteList;
    19051937    }
     1938
     1939    private bool $usesCheckBoxStyle = false;
     1940
     1941    public function getUsesCheckBoxStyle(): bool
     1942    {
     1943        return $this->usesCheckBoxStyle;
     1944    }
     1945
     1946    public function setUsesCheckBoxStyle(): bool
     1947    {
     1948        $this->usesCheckBoxStyle = false;
     1949        foreach ($this->getCellXfCollection() as $cellXf) {
     1950            if ($cellXf->getCheckBox()) {
     1951                $this->usesCheckBoxStyle = true;
     1952
     1953                break;
     1954            }
     1955        }
     1956
     1957        return $this->usesCheckBoxStyle;
     1958    }
    19061959}
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Style/Alignment.php

    r3420898 r3473263  
    366366            }
    367367        } else {
    368             throw new PhpSpreadsheetException('Text rotation should be a value between -90 and 90.');
     368            throw new PhpSpreadsheetException("Text rotation $angleInDegrees should be a value between -90 and 90.");
    369369        }
    370370
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Style/Font.php

    r3350024 r3473263  
    178178     * </code>
    179179     *
    180      * @param array{name?: string, latin?: string, eastAsian?: string, complexScript?: string, bold?: bool, italic?: bool, superscript?: bool, subscript?: bool, underline?: bool|string, strikethrough?: bool, color?: string[], size?: ?int, chartColor?: ChartColor, scheme?: string, cap?: string, autoColor?: bool} $styleArray Array containing style information
     180     * @param array{
     181     *   autoColor?: bool,
     182     *   bold?: bool,
     183     *   cap?: string,
     184     *   chartColor?: ChartColor,
     185     *   color?: string[],
     186     *   complexScript?: string,
     187     *   eastAsian?: string,
     188     *   italic?: bool,
     189     *   latin?: string,
     190     *   name?: string,
     191     *   scheme?: string,
     192     *   size?: null|float|int,
     193     *   strikethrough?: bool,
     194     *   superscript?: bool,
     195     *   subscript?: bool,
     196     *   underline?: bool|string,
     197     * } $styleArray Array containing style information
    181198     *
    182199     * @return $this
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Style/NumberFormat.php

    r3385566 r3473263  
    2424    const FORMAT_DATE_YYYYMMDD = 'yyyy-mm-dd';
    2525    const FORMAT_DATE_DDMMYYYY = 'dd/mm/yyyy';
    26     const FORMAT_DATE_DMYSLASH = 'd/m/yy';
     26    const FORMAT_DATE_DMYSLASH = 'd"/"m"/"yy';
    2727    const FORMAT_DATE_DMYMINUS = 'd-m-yy';
    2828    const FORMAT_DATE_DMMINUS = 'd-m';
     
    3131    const FORMAT_DATE_XLSX14_ACTUAL = 'm/d/yyyy';
    3232    const FORMAT_DATE_XLSX15 = 'd-mmm-yy';
     33    const FORMAT_DATE_XLSX15_YYYY = 'd-mmm-yyyy';
    3334    const FORMAT_DATE_XLSX16 = 'd-mmm';
    3435    const FORMAT_DATE_XLSX17 = 'mmm-yy';
     
    3637    const FORMAT_DATE_XLSX22_ACTUAL = 'm/d/yyyy h:mm';
    3738    const FORMAT_DATE_DATETIME = 'd/m/yy h:mm';
     39    const FORMAT_DATE_DATETIME_BETTER = 'yyyy-mm-dd hh:mm';
    3840    const FORMAT_DATE_TIME1 = 'h:mm AM/PM';
    3941    const FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM';
     
    4446    const FORMAT_DATE_TIME7 = 'i:s.S';
    4547    const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
    46     const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy/mm/dd;@';
     48    const FORMAT_DATE_TIME_INTERVAL_HMS = '[hh]:mm:ss';
     49    const FORMAT_DATE_YYYYMMDDSLASH = 'yyyy"/"mm"/"dd;@';
    4750    const FORMAT_DATE_LONG_DATE = 'dddd, mmmm d, yyyy';
    4851
     
    6265        self::FORMAT_DATE_XLSX22_ACTUAL,
    6366        self::FORMAT_DATE_DATETIME,
     67        self::FORMAT_DATE_DATETIME_BETTER,
    6468        self::FORMAT_DATE_TIME1,
    6569        self::FORMAT_DATE_TIME2,
     
    7074        self::FORMAT_DATE_TIME7,
    7175        self::FORMAT_DATE_TIME8,
     76        self::FORMAT_DATE_TIME_INTERVAL_HMS,
    7277        self::FORMAT_DATE_YYYYMMDDSLASH,
    7378        self::FORMAT_DATE_LONG_DATE,
     
    7681        self::FORMAT_DATE_XLSX22,
    7782        self::FORMAT_DATE_DATETIME,
     83        self::FORMAT_DATE_DATETIME_BETTER,
    7884        self::FORMAT_DATE_TIME1,
    7985        self::FORMAT_DATE_TIME2,
     
    8490        self::FORMAT_DATE_TIME7,
    8591        self::FORMAT_DATE_TIME8,
     92        self::FORMAT_DATE_TIME_INTERVAL_HMS,
    8693    ];
    8794
    88     const FORMAT_CURRENCY_USD_INTEGER = '$#,##0_-';
    89     const FORMAT_CURRENCY_USD = '$#,##0.00_-';
     95    private const FORMAT_CURRENCY_AMOUNT_INTEGER = '#,##0_-';
     96    private const FORMAT_CURRENCY_AMOUNT_FLOAT = '#,##0.00_-';
     97    const FORMAT_CURRENCY_USD_INTEGER = '$' . self::FORMAT_CURRENCY_AMOUNT_INTEGER;
     98    const FORMAT_CURRENCY_USD = '$' . self::FORMAT_CURRENCY_AMOUNT_FLOAT;
     99    const FORMAT_CURRENCY_GBP_INTEGER = '£' . self::FORMAT_CURRENCY_AMOUNT_INTEGER;
     100    const FORMAT_CURRENCY_GBP = '£' . self::FORMAT_CURRENCY_AMOUNT_FLOAT;
     101    const FORMAT_CURRENCY_YEN_YUAN_INTEGER = '¥' . self::FORMAT_CURRENCY_AMOUNT_INTEGER;
     102    const FORMAT_CURRENCY_YEN_YUAN = '¥' . self::FORMAT_CURRENCY_AMOUNT_FLOAT;
    90103    const FORMAT_CURRENCY_EUR_INTEGER = '#,##0_-[$€]';
    91104    const FORMAT_CURRENCY_EUR = '#,##0.00_-[$€]';
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php

    r3420898 r3473263  
    33namespace TablePress\PhpOffice\PhpSpreadsheet\Style\NumberFormat;
    44
     5use TablePress\Composer\Pcre\Preg;
    56use TablePress\PhpOffice\PhpSpreadsheet\Shared\Date;
    67use TablePress\PhpOffice\PhpSpreadsheet\Shared\StringHelper;
     
    122123                public static function format($value, string $format): string
    123124    {
     125        if ($value < 0 && Preg::isMatch('/^\[?[hms]/i', $format)) {
     126            return '-' . self::format(-$value, $format);
     127        }
    124128        // strip off first part containing e.g. [$-F800] or [$USD-409]
    125129        // general syntax: [$<Currency string>-<language info>]
    126130        // language info is in hexadecimal
    127131        // strip off chinese part like [DBNum1][$-804]
    128         $format = (string) preg_replace('/^(\[DBNum\d\])*(\[\$[^\]]*\])/i', '', $format);
     132        $format = Preg::replace('/^(\[DBNum\d\])*(\[\$[^\]]*\])/i', '', $format);
    129133
    130134        // OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case;
    131135        //    but we don't want to change any quoted strings
    132         /** @var callable $callable */
    133         $callable = [self::class, 'setLowercaseCallback'];
    134         $format = (string) preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', $callable, $format);
     136        $format = Preg::replaceCallback('/(?:^|")([^"]*)(?:$|")/', \Closure::fromCallable([self::class, 'setLowerCaseCallback']), $format);
    135137
    136138        // Only process the non-quoted blocks for date format characters
     
    160162
    161163        // escape any quoted characters so that DateTime format() will render them correctly
    162         /** @var callable $callback */
    163         $callback = [self::class, 'escapeQuotesCallback'];
    164         $format = (string) preg_replace_callback('/"(.*)"/U', $callback, $format);
     164        $format = Preg::replaceCallback('/"(.*)"/U', \Closure::fromCallable([self::class, 'escapeQuotesCallback']), $format);
    165165
    166166        try {
     
    172172        // Excel 2003 XML formats, m will not have been changed to i above.
    173173        // Change it now.
    174         $format = (string) \preg_replace('/\\\:m/', ':i', $format);
     174        $format = Preg::replace('/\\\:m/', ':i', $format);
    175175        $microseconds = (int) $dateObj->format('u');
    176176        if (str_contains($format, ':s.000')) {
     
    208208    }
    209209
    210     /** @param string[] $matches */
     210    /** @param array<?string> $matches */
    211211    private static function setLowercaseCallback(array $matches): string
    212212    {
     213        /** @var string[] $matches */
    213214        return mb_strtolower($matches[0]);
    214215    }
    215216
    216     /** @param string[] $matches */
     217    /** @param array<?string> $matches */
    217218    private static function escapeQuotesCallback(array $matches): string
    218219    {
     220        /** @var string[] $matches */
    219221        return '\\' . implode('\\', mb_str_split($matches[1], 1, 'UTF-8'));
    220222    }
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Style/Style.php

    r3385566 r3473263  
    5252     */
    5353    protected bool $quotePrefix = false;
     54
     55    protected bool $checkBox = false;
    5456
    5557    /**
     
    485487             * numberFormat?: string[],
    486488             * protection?: array{locked?: string, hidden?: string},
     489             * checkBox?: bool,
    487490             * quotePrefix?: bool} $styleArray */
     491            if (isset($styleArray['checkBox'])) {
     492                $this->checkBox = (bool) $styleArray['checkBox'];
     493            }
    488494            if (isset($styleArray['fill'])) {
    489495                $this->getFill()
     
    701707    }
    702708
     709    public function getCheckBox(): bool
     710    {
     711        if ($this->isSupervisor) {
     712            return $this->getSharedComponent()->getCheckBox();
     713        }
     714
     715        return $this->checkBox;
     716    }
     717
     718    /**
     719                 * @return static
     720                 */
     721                public function setCheckBox(bool $checkBox)
     722    {
     723        if ($this->isSupervisor) {
     724            $styleArray = ['checkBox' => $checkBox];
     725            $this->getActiveSheet()
     726                ->getStyle($this->getSelectedCells())
     727                ->applyFromArray($styleArray);
     728        } else {
     729            $this->checkBox = $checkBox;
     730        }
     731
     732        return $this;
     733    }
     734
    703735    /**
    704736     * Get hash code.
     
    716748            . $this->protection->getHashCode()
    717749            . ($this->quotePrefix ? 't' : 'f')
     750            . ($this->checkBox ? 't' : 'f')
    718751            . __CLASS__
    719752        );
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Worksheet/Drawing.php

    r3420898 r3473263  
    9090     * @param bool $verifyFile Verify file
    9191     * @param ?ZipArchive $zip Zip archive instance
     92     * @param null|callable(string):bool $isWhitelisted
    9293     *
    9394     * @return $this
    9495     */
    95     public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip = null, bool $allowExternal = true)
     96    public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip = null, bool $allowExternal = true, ?callable $isWhitelisted = null)
    9697    {
    9798        $this->isUrl = false;
     
    103104
    104105        $this->path = '';
     106        if ($zip instanceof ZipArchive) {
     107            $zipPath = explode('#', $path)[1];
     108            $locate = @$zip->locateName($zipPath);
     109            if ($locate !== false) {
     110                if ($this->isImage($path)) {
     111                    $this->path = $path;
     112                    $this->setSizesAndType($path);
     113                }
     114            }
    105115        // Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
    106         if (filter_var($path, FILTER_VALIDATE_URL) || (preg_match('/^([\w\s\x00-\x1f]+):/u', $path) && !preg_match('/^([\w]+):/u', $path))) {
     116        } elseif (filter_var($path, FILTER_VALIDATE_URL) || (preg_match('/^([\w\s\x00-\x1f]+):/u', $path) && !preg_match('/^([\w]+):/u', $path))) {
    107117            if (!preg_match('/^(http|https|file|ftp|s3):/', $path)) {
    108118                throw new PhpSpreadsheetException('Invalid protocol for linked drawing');
    109119            }
    110120            if (!$allowExternal) {
     121                return $this;
     122            }
     123            if ($isWhitelisted !== null && !$isWhitelisted($path)) {
    111124                return $this;
    112125            }
     
    145158                }
    146159            }
    147         } elseif ($zip instanceof ZipArchive) {
    148             $zipPath = explode('#', $path)[1];
    149             $locate = @$zip->locateName($zipPath);
    150             if ($locate !== false) {
    151                 if ($this->isImage($path)) {
    152                     $this->path = $path;
    153                     $this->setSizesAndType($path);
    154                 }
    155             }
    156160        } else {
    157161            $exists = @file_exists($path);
  • tablepress/trunk/libraries/vendor/PhpSpreadsheet/Worksheet/Worksheet.php

    r3463039 r3473263  
    29812981                 * @param bool $formatData Whether to format data according to cell's style.
    29822982                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     2983                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    29832984                 *
    29842985                 * @throws Exception
     
    29862987                 * @return mixed
    29872988                 */
    2988                 protected function cellToArray(Cell $cell, bool $calculateFormulas, bool $formatData, $nullValue, bool $lessFloatPrecision = false)
     2989                protected function cellToArray(Cell $cell, bool $calculateFormulas, bool $formatData, $nullValue, bool $lessFloatPrecision = false, $oldCalculatedValue = false)
    29892990    {
    29902991        $returnValue = $nullValue;
     
    29932994            if ($cell->getValue() instanceof RichText) {
    29942995                $returnValue = $cell->getValue()->getPlainText();
     2996            } elseif ($calculateFormulas) {
     2997                $returnValue = $cell->getCalculatedValue();
     2998            } elseif ($oldCalculatedValue && ($cell->getDataType() === DataType::TYPE_FORMULA)) {
     2999                $returnValue = $cell->getOldCalculatedValue() ?? $cell->getValue();
    29953000            } else {
    2996                 $returnValue = ($calculateFormulas) ? $cell->getCalculatedValue() : $cell->getValue();
     3001                $returnValue = $cell->getValue();
    29973002            }
    29983003
     
    30203025                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    30213026                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3027                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    30223028                 *
    30233029                 * @return mixed[][]
     
    30313037        bool $ignoreHidden = false,
    30323038        bool $reduceArrays = false,
    3033         bool $lessFloatPrecision = false
     3039        bool $lessFloatPrecision = false,
     3040        bool $oldCalculatedValue = false
    30343041    ): array {
    30353042        $returnValue = [];
    30363043
    30373044        // Loop through rows
    3038         foreach ($this->rangeToArrayYieldRows($range, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision) as $rowRef => $rowArray) {
     3045        foreach ($this->rangeToArrayYieldRows($range, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision, $oldCalculatedValue) as $rowRef => $rowArray) {
    30393046            /** @var int $rowRef */
    30403047            $returnValue[$rowRef] = $rowArray;
     
    30573064                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    30583065                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3066                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    30593067                 *
    30603068                 * @return mixed[][]
     
    30683076        bool $ignoreHidden = false,
    30693077        bool $reduceArrays = false,
    3070         bool $lessFloatPrecision = false
     3078        bool $lessFloatPrecision = false,
     3079        bool $oldCalculatedValue = false
    30713080    ): array {
    30723081        $returnValue = [];
     
    30753084        foreach ($parts as $part) {
    30763085            // Loop through rows
    3077             foreach ($this->rangeToArrayYieldRows($part, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision) as $rowRef => $rowArray) {
     3086            foreach ($this->rangeToArrayYieldRows($part, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision, $oldCalculatedValue) as $rowRef => $rowArray) {
    30783087                /** @var int $rowRef */
    30793088                $returnValue[$rowRef] = $rowArray;
     
    30973106                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    30983107                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3108                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    30993109                 *
    31003110                 * @return Generator<array<mixed>>
     
    31083118        bool $ignoreHidden = false,
    31093119        bool $reduceArrays = false,
    3110         bool $lessFloatPrecision = false
     3120        bool $lessFloatPrecision = false,
     3121        bool $oldCalculatedValue = false
    31113122    ) {
    31123123        $range = Validations::validateCellOrCellRange($range);
     
    31743185                        $cell = $this->cellCollection->get("{$col}{$thisRow}");
    31753186                        if ($cell !== null) {
    3176                             $value = $this->cellToArray($cell, $calculateFormulas, $formatData, $nullValue, $lessFloatPrecision);
     3187                            $value = $this->cellToArray($cell, $calculateFormulas, $formatData, $nullValue, $lessFloatPrecision, $oldCalculatedValue);
    31773188                            if ($reduceArrays) {
    31783189                                while (is_array($value)) {
     
    32773288                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    32783289                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3290                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    32793291                 *
    32803292                 * @return mixed[][]
     
    32883300        bool $ignoreHidden = false,
    32893301        bool $reduceArrays = false,
    3290         bool $lessFloatPrecision = false
     3302        bool $lessFloatPrecision = false,
     3303        bool $oldCalculatedValue = false
    32913304    ): array {
    32923305        $retVal = [];
     
    32973310            $workSheet = $namedRange->getWorksheet();
    32983311            if ($workSheet !== null) {
    3299                 $retVal = $workSheet->rangeToArray($cellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision);
     3312                $retVal = $workSheet->rangeToArray($cellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision, $oldCalculatedValue);
    33003313            }
    33013314        }
     
    33163329                 * @param bool $reduceArrays If true and result is a formula which evaluates to an array, reduce it to the top leftmost value.
    33173330                 * @param bool $lessFloatPrecision If true, formatting unstyled floats will convert them to a more human-friendly but less computationally accurate value
     3331                 * @param bool $oldCalculatedValue If calculateFormulas is false and this is true, use oldCalculatedFormula instead.
    33183332                 *
    33193333                 * @return mixed[][]
     
    33263340        bool $ignoreHidden = false,
    33273341        bool $reduceArrays = false,
    3328         bool $lessFloatPrecision = false
     3342        bool $lessFloatPrecision = false,
     3343        bool $oldCalculatedValue = false
    33293344    ): array {
    33303345        // Garbage collect...
     
    33373352
    33383353        // Return
    3339         return $this->rangeToArray("A1:{$maxCol}{$maxRow}", $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision);
     3354        return $this->rangeToArray("A1:{$maxCol}{$maxRow}", $nullValue, $calculateFormulas, $formatData, $returnCellRef, $ignoreHidden, $reduceArrays, $lessFloatPrecision, $oldCalculatedValue);
    33403355    }
    33413356
  • tablepress/trunk/libraries/vendor/autoload-classmap.php

    r3463039 r3473263  
    263263   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\Database\DCount' => $strauss_src . '/PhpSpreadsheet/Calculation/Database/DCount.php',
    264264   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\Database\DSum' => $strauss_src . '/PhpSpreadsheet/Calculation/Database/DSum.php',
     265   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\CalculationParserOnly' => $strauss_src . '/PhpSpreadsheet/Calculation/CalculationParserOnly.php',
    265266   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\FunctionArray' => $strauss_src . '/PhpSpreadsheet/Calculation/FunctionArray.php',
    266267   'TablePress\PhpOffice\PhpSpreadsheet\Calculation\Information\Info' => $strauss_src . '/PhpSpreadsheet/Calculation/Information/Info.php',
  • tablepress/trunk/readme.txt

    r3463039 r3473263  
    66Requires PHP: 7.4
    77Tested up to: 6.9
    8 Stable tag: 3.2.7
     8Stable tag: 3.2.8
    99License: GPLv2
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    108108
    109109Changes in recent versions are shown below. For earlier changes, please see the [changelog history](https://tablepress.org/info/#changelog).
     110
     111= Version 3.2.8 (March 3, 2026) =
     112
     113* Bugfix: Whitespace handling for the “Filter Term Separator” setting in the “Column Filter Dropdowns” feature module now works properly again. (TablePress Pro and Max only.)
     114* Enhancement: The “Column Filter Dropdowns” feature module integration when using “Server-side Processing” is now more reliable. (TablePress Max only.)
     115* Cleaned up and simplified code, for easier future maintenance, to follow WordPress Coding Standards, and to offer helpful inline documentation.
     116* Several external code libraries and build tools have been updated to benefit from enhancements and bug fixes.
     117* Improved support for PHP 8.5.
    110118
    111119= Version 3.2.7 (February 17, 2026) =
     
    212220== Upgrade Notice ==
    213221
    214 = 3.2.7 =
     222= 3.2.8 =
    215223This update is an enhancement, stability, maintenance, and compatibility release. Updating is highly recommended!
    216224
  • tablepress/trunk/tablepress.php

    r3463039 r3473263  
    55 * @package TablePress
    66 * @author Tobias Bäthge
    7  * @version 3.2.7
     7 * @version 3.2.8
    88 *
    99 *
     
    1111 * Plugin URI: https://tablepress.org/
    1212 * Description: Embed beautiful and interactive tables into your WordPress website’s posts and pages, without having to write code!
    13  * Version: 3.2.7
     13 * Version: 3.2.8
    1414 * Requires at least: 6.2
    1515 * Requires PHP: 7.4
     
    7070                'has_addons'        => false,
    7171                'has_paid_plans'    => true,
     72                'is_org_compliant'  => true,
    7273                'menu'              => array(
    7374                    'slug'    => 'tablepress',
Note: See TracChangeset for help on using the changeset viewer.