Browse Source

Update to phpspreadsheet 1.8.2

Laurent Destailleur 4 years ago
parent
commit
76c3229c97
61 changed files with 2555 additions and 1124 deletions
  1. 1 1
      COPYRIGHT
  2. 57 0
      composer.lock
  3. 19 6
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php
  4. 54 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php
  5. 23 11
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php
  6. 3 2
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php
  7. 1 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt
  8. 2 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php
  9. 1 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php
  10. 1 3
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php
  11. 2 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php
  12. 31 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php
  13. 2 2
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php
  14. 2 2
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php
  15. 8 8
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php
  16. 3 3
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php
  17. 1 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
  18. 6 85
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php
  19. 13 13
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php
  20. 1 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php
  21. 309 19
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php
  22. 106 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php
  23. 16 100
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php
  24. 136 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
  25. 60 31
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
  26. 1 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
  27. 1 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
  28. 113 688
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
  29. 144 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php
  30. 19 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
  31. 204 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php
  32. 92 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
  33. 50 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
  34. 58 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php
  35. 150 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php
  36. 91 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
  37. 124 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
  38. 127 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
  39. 265 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
  40. 1 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
  41. 13 13
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
  42. 42 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
  43. 13 12
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php
  44. 21 15
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php
  45. 3 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php
  46. 3 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php
  47. 3 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php
  48. 3 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
  49. 3 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
  50. 1 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php
  51. 19 1
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php
  52. 2 2
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php
  53. 10 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php
  54. 2 2
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php
  55. 0 55
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php
  56. 69 0
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php
  57. 16 17
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php
  58. 4 6
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php
  59. 13 2
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php
  60. 2 2
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php
  61. 15 11
      htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

+ 1 - 1
COPYRIGHT

@@ -23,7 +23,7 @@ PEAR Mail_MIME         1.8.9         BSD                         Yes
 ParseDown              1.6           MIT License                 Yes             Markdown parser
 PCLZip                 2.8.4         LGPL-3+                     Yes             Library to zip/unzip files             
 PHPDebugBar            1.15.1        MIT License	             Yes             Used only by the module "debugbar" for developers
-PHPSpreadSheet         ?             LGPL-2.1+                   Yes             Read/Write XLS files, read ODS files
+PHPSpreadSheet         1.8.2         LGPL-2.1+                   Yes             Read/Write XLS files, read ODS files
 php-iban               1.4.7         LGPL-3+                     Yes             Parse and validate IBAN (and IIBAN) bank account information in PHP
 PHPoAuthLib            0.8.2         MIT License                 Yes             Library to provide oauth1 and oauth2 to different service
 PHPPrintIPP            1.3           GPL-2+                      Yes             Library to send print IPP requests

+ 57 - 0
composer.lock

@@ -328,6 +328,63 @@
             },
             "time": "2020-03-23T09:12:05+00:00"
         },
+ 		{
+            "name": "phpoffice/phpexcel",
+            "version": "1.8.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHPOffice/PHPExcel.git",
+                "reference": "1441011fb7ecdd8cc689878f54f8b58a6805f870"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/1441011fb7ecdd8cc689878f54f8b58a6805f870",
+                "reference": "1441011fb7ecdd8cc689878f54f8b58a6805f870",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "ext-xml": "*",
+                "ext-xmlwriter": "*",
+                "php": "^5.2|^7.0"
+            },
+            "require-dev": {
+                "squizlabs/php_codesniffer": "2.*"
+            },
+            "type": "library",
+            "autoload": {
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL-2.1"
+            ],
+            "authors": [
+                {
+                    "name": "Maarten Balliauw",
+                    "homepage": "http://blog.maartenballiauw.be"
+                },
+                {
+                    "name": "Erik Tilt"
+                },
+                {
+                    "name": "Franck Lefevre",
+                    "homepage": "http://rootslabs.net"
+                },
+                {
+                    "name": "Mark Baker",
+                    "homepage": "http://markbakeruk.net"
+                }
+            ],
+            "description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
+            "homepage": "https://github.com/PHPOffice/PHPExcel",
+            "keywords": [
+                "OpenXML",
+                "excel",
+                "xlsx"
+            ],
+            "abandoned": "phpoffice/phpspreadsheet",
+            "time": "2018-11-22T23:07:24+00:00"
+        },        
         {
             "name": "restler/framework",
             "version": "3.0.0-RC6",

+ 19 - 6
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php

@@ -117,7 +117,7 @@ class Calculation
     /**
      * An array of the nested cell references accessed by the calculation engine, used for the debug log.
      *
-     * @var array of string
+     * @var CyclicReferenceStack
      */
     private $cyclicReferenceStack;
 
@@ -1945,6 +1945,11 @@ class Calculation
             'functionCall' => [MathTrig::class, 'SUMXMY2'],
             'argumentCount' => '2',
         ],
+        'SWITCH' => [
+            'category' => Category::CATEGORY_LOGICAL,
+            'functionCall' => [Logical::class, 'statementSwitch'],
+            'argumentCount' => '3+',
+        ],
         'SYD' => [
             'category' => Category::CATEGORY_FINANCIAL,
             'functionCall' => [Financial::class, 'SYD'],
@@ -2207,8 +2212,8 @@ class Calculation
     private static function loadLocales()
     {
         $localeFileDirectory = __DIR__ . '/locale/';
-        foreach (glob($localeFileDirectory . '/*', GLOB_ONLYDIR) as $filename) {
-            $filename = substr($filename, strlen($localeFileDirectory) + 1);
+        foreach (glob($localeFileDirectory . '*', GLOB_ONLYDIR) as $filename) {
+            $filename = substr($filename, strlen($localeFileDirectory));
             if ($filename != 'en') {
                 self::$validLocaleLanguages[] = $filename;
             }
@@ -2413,7 +2418,6 @@ class Calculation
         if (strpos($locale, '_') !== false) {
             list($language) = explode('_', $locale);
         }
-
         if (count(self::$validLocaleLanguages) == 1) {
             self::loadLocales();
         }
@@ -2704,7 +2708,7 @@ class Calculation
      * @param Cell $pCell Cell to calculate
      * @param bool $resetLog Flag indicating whether the debug log should be reset or not
      *
-     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
      *
      * @return mixed
      */
@@ -2808,7 +2812,7 @@ class Calculation
      * @param string $cellID Address of the cell to calculate
      * @param Cell $pCell Cell to calculate
      *
-     * @throws Exception
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
      *
      * @return mixed
      */
@@ -2892,6 +2896,15 @@ class Calculation
     {
         $cellValue = null;
 
+        //  Quote-Prefixed cell values cannot be formulae, but are treated as strings
+        if ($pCell !== null && $pCell->getStyle()->getQuotePrefix() === true) {
+            return self::wrapResult((string) $formula);
+        }
+
+        if (preg_match('/^=\s*cmd\s*\|/miu', $formula) !== 0) {
+            return self::wrapResult($formula);
+        }
+
         //    Basic validation that this is indeed a formula
         //    We simply return the cell value if not
         $formula = trim($formula);

+ 54 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Logical.php

@@ -273,6 +273,60 @@ class Logical
         return ($condition) ? $returnIfTrue : $returnIfFalse;
     }
 
+    /**
+     * STATEMENT_SWITCH.
+     *
+     * Returns corresponding with first match (any data type such as a string, numeric, date, etc).
+     *
+     * Excel Function:
+     *        =SWITCH (expression, value1, result1, value2, result2, ... value_n, result_n [, default])
+     *
+     *        Expression
+     *              The expression to compare to a list of values.
+     *        value1, value2, ... value_n
+     *              A list of values that are compared to expression. The SWITCH function is looking for the first value that matches the expression.
+     *        result1, result2, ... result_n
+     *              A list of results. The SWITCH function returns the corresponding result when a value matches expression.
+     *         default
+     *              Optional. It is the default to return if expression does not match any of the values (value1, value2, ... value_n).
+     *
+     * @category Logical Functions
+     *
+     * @param mixed $arguments Statement arguments
+     *
+     * @return mixed The value of matched expression
+     */
+    public static function statementSwitch(...$arguments)
+    {
+        $result = Functions::VALUE();
+
+        if (count($arguments) > 0) {
+            $targetValue = Functions::flattenSingleValue($arguments[0]);
+            $argc = count($arguments) - 1;
+            $switchCount = floor($argc / 2);
+            $switchSatisfied = false;
+            $hasDefaultClause = $argc % 2 !== 0;
+            $defaultClause = $argc % 2 === 0 ? null : $arguments[count($arguments) - 1];
+
+            if ($switchCount) {
+                for ($index = 0; $index < $switchCount; ++$index) {
+                    if ($targetValue == $arguments[$index * 2 + 1]) {
+                        $result = $arguments[$index * 2 + 2];
+                        $switchSatisfied = true;
+
+                        break;
+                    }
+                }
+            }
+
+            if (!$switchSatisfied) {
+                $result = $hasDefaultClause ? $defaultClause : Functions::NA();
+            }
+        }
+
+        return $result;
+    }
+
     /**
      * IFERROR.
      *

+ 23 - 11
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef.php

@@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation;
 
 use PhpOffice\PhpSpreadsheet\Cell\Cell;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 class LookupRef
@@ -473,8 +474,9 @@ class LookupRef
         $lookupValue = Functions::flattenSingleValue($lookupValue);
         $matchType = ($matchType === null) ? 1 : (int) Functions::flattenSingleValue($matchType);
 
+        $initialLookupValue = $lookupValue;
         // MATCH is not case sensitive
-        $lookupValue = strtolower($lookupValue);
+        $lookupValue = StringHelper::strToLower($lookupValue);
 
         // Lookup_value type has to be number, text, or logical values
         if ((!is_numeric($lookupValue)) && (!is_string($lookupValue)) && (!is_bool($lookupValue))) {
@@ -502,7 +504,7 @@ class LookupRef
             }
             // Convert strings to lowercase for case-insensitive testing
             if (is_string($lookupArrayValue)) {
-                $lookupArray[$i] = strtolower($lookupArrayValue);
+                $lookupArray[$i] = StringHelper::strToLower($lookupArrayValue);
             }
             if (($lookupArrayValue === null) && (($matchType == 1) || ($matchType == -1))) {
                 $lookupArray = array_slice($lookupArray, 0, $i - 1);
@@ -522,9 +524,13 @@ class LookupRef
 
         if ($matchType == 0 || $matchType == 1) {
             foreach ($lookupArray as $i => $lookupArrayValue) {
-                if (($matchType == 0) && ($lookupArrayValue == $lookupValue)) {
+                $onlyNumeric = is_numeric($lookupArrayValue) && is_numeric($lookupValue);
+                $onlyNumericExactMatch = $onlyNumeric && $lookupArrayValue == $lookupValue;
+                $nonOnlyNumericExactMatch = !$onlyNumeric && $lookupArrayValue === $lookupValue;
+                $exactMatch = $onlyNumericExactMatch || $nonOnlyNumericExactMatch;
+                if (($matchType == 0) && $exactMatch) {
                     //    exact match
-                    return ++$i;
+                    return $i + 1;
                 } elseif (($matchType == 1) && ($lookupArrayValue <= $lookupValue)) {
                     $i = array_search($i, $keySet);
 
@@ -661,7 +667,9 @@ class LookupRef
     {
         reset($a);
         $firstColumn = key($a);
-        if (($aLower = strtolower($a[$firstColumn])) == ($bLower = strtolower($b[$firstColumn]))) {
+        $aLower = StringHelper::strToLower($a[$firstColumn]);
+        $bLower = StringHelper::strToLower($b[$firstColumn]);
+        if ($aLower == $bLower) {
             return 0;
         }
 
@@ -707,11 +715,14 @@ class LookupRef
             uasort($lookup_array, ['self', 'vlookupSort']);
         }
 
+        $lookupLower = StringHelper::strToLower($lookup_value);
         $rowNumber = $rowValue = false;
         foreach ($lookup_array as $rowKey => $rowData) {
+            $firstLower = StringHelper::strToLower($rowData[$firstColumn]);
+
             // break if we have passed possible keys
             if ((is_numeric($lookup_value) && is_numeric($rowData[$firstColumn]) && ($rowData[$firstColumn] > $lookup_value)) ||
-                (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && (strtolower($rowData[$firstColumn]) > strtolower($lookup_value)))) {
+                (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]) && ($firstLower > $lookupLower))) {
                 break;
             }
             // remember the last key, but only if datatypes match
@@ -719,17 +730,15 @@ class LookupRef
                 (!is_numeric($lookup_value) && !is_numeric($rowData[$firstColumn]))) {
                 if ($not_exact_match) {
                     $rowNumber = $rowKey;
-                    $rowValue = $rowData[$firstColumn];
 
                     continue;
-                } elseif ((strtolower($rowData[$firstColumn]) == strtolower($lookup_value))
+                } elseif (($firstLower == $lookupLower)
                     // Spreadsheets software returns first exact match,
                     // we have sorted and we might have broken key orders
                     // we want the first one (by its initial index)
                     && (($rowNumber == false) || ($rowKey < $rowNumber))
                 ) {
                     $rowNumber = $rowKey;
-                    $rowValue = $rowData[$firstColumn];
                 }
             }
         }
@@ -782,8 +791,11 @@ class LookupRef
             // break if we have passed possible keys
             $bothNumeric = is_numeric($lookup_value) && is_numeric($rowData);
             $bothNotNumeric = !is_numeric($lookup_value) && !is_numeric($rowData);
+            $lookupLower = StringHelper::strToLower($lookup_value);
+            $rowDataLower = StringHelper::strToLower($rowData);
+
             if (($bothNumeric && $rowData > $lookup_value) ||
-                ($bothNotNumeric && strtolower($rowData) > strtolower($lookup_value))) {
+                ($bothNotNumeric && $rowDataLower > $lookupLower)) {
                 break;
             }
 
@@ -793,7 +805,7 @@ class LookupRef
                     $rowNumber = $rowKey;
 
                     continue;
-                } elseif (strtolower($rowData) === strtolower($lookup_value)
+                } elseif ($rowDataLower === $lookupLower
                     && ($rowNumber === null || $rowKey < $rowNumber)
                 ) {
                     $rowNumber = $rowKey;

+ 3 - 2
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig.php

@@ -1224,11 +1224,12 @@ class MathTrig
             }
 
             $testCondition = '=' . $arg . $condition;
+            $sumValue = array_key_exists($key, $sumArgs) ? $sumArgs[$key] : 0;
 
-            if (is_numeric($sumArgs[$key]) &&
+            if (is_numeric($sumValue) &&
                 Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
                 // Is it a value within our criteria and only numeric can be added to the result
-                $returnValue += $sumArgs[$key];
+                $returnValue += $sumValue;
             }
         }
 

+ 1 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/functionlist.txt

@@ -337,6 +337,7 @@ SUMSQ
 SUMX2MY2
 SUMX2PY2
 SUMXMY2
+SWITCH
 SYD
 T
 TAN

+ 2 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/AdvancedValueBinder.php

@@ -16,6 +16,8 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
      * @param Cell $cell Cell to bind value to
      * @param mixed $value Value to bind in cell
      *
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     *
      * @return bool
      */
     public function bindValue(Cell $cell, $value = null)

+ 1 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Cell.php

@@ -523,7 +523,7 @@ class Cell
     /**
      * If this cell is in a merge range, then return the range.
      *
-     * @return string
+     * @return false|string
      */
     public function getMergeRange()
     {

+ 1 - 3
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/Coordinate.php

@@ -157,14 +157,12 @@ abstract class Coordinate
         }
 
         // Build range
-        $imploded = [];
         $counter = count($pRange);
         for ($i = 0; $i < $counter; ++$i) {
             $pRange[$i] = implode(':', $pRange[$i]);
         }
-        $imploded = implode(',', $pRange);
 
-        return $imploded;
+        return implode(',', $pRange);
     }
 
     /**

+ 2 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/DefaultValueBinder.php

@@ -14,6 +14,8 @@ class DefaultValueBinder implements IValueBinder
      * @param Cell $cell Cell to bind value to
      * @param mixed $value Value to bind in cell
      *
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     *
      * @return bool
      */
     public function bindValue(Cell $cell, $value)

+ 31 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Cell;
+
+use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
+
+class StringValueBinder implements IValueBinder
+{
+    /**
+     * Bind value to a cell.
+     *
+     * @param Cell $cell Cell to bind value to
+     * @param mixed $value Value to bind in cell
+     *
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     *
+     * @return bool
+     */
+    public function bindValue(Cell $cell, $value)
+    {
+        // sanitize UTF-8 strings
+        if (is_string($value)) {
+            $value = StringHelper::sanitizeUTF8($value);
+        }
+
+        $cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
+
+        // Done!
+        return true;
+    }
+}

+ 2 - 2
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Axis.php

@@ -365,10 +365,10 @@ class Axis extends Properties
     }
 
     /**
-     * Set Shadow Properties from Maped Values.
+     * Set Shadow Properties from Mapped Values.
      *
      * @param array $properties_map
-     * @param * $reference
+     * @param mixed &$reference
      *
      * @return Axis
      */

+ 2 - 2
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/GridLines.php

@@ -319,7 +319,7 @@ class GridLines extends Properties
      * Set Shadow Properties Values.
      *
      * @param array $properties_map
-     * @param * $reference
+     * @param mixed &$reference
      *
      * @return GridLines
      */
@@ -439,7 +439,7 @@ class GridLines extends Properties
     {
         if ($size !== null) {
             $this->activateObject();
-            $softEdges['size'] = (string) $this->getExcelPointsWidth($size);
+            $this->softEdges['size'] = (string) $this->getExcelPointsWidth($size);
         }
     }
 

+ 8 - 8
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Layout.php

@@ -175,13 +175,13 @@ class Layout
     /**
      * Set X-Mode.
      *
-     * @param X-Mode $value
+     * @param string $value
      *
      * @return Layout
      */
     public function setXMode($value)
     {
-        $this->xMode = $value;
+        $this->xMode = (string) $value;
 
         return $this;
     }
@@ -199,13 +199,13 @@ class Layout
     /**
      * Set Y-Mode.
      *
-     * @param Y-Mode $value
+     * @param string $value
      *
      * @return Layout
      */
     public function setYMode($value)
     {
-        $this->yMode = $value;
+        $this->yMode = (string) $value;
 
         return $this;
     }
@@ -223,13 +223,13 @@ class Layout
     /**
      * Set X-Position.
      *
-     * @param X-Position $value
+     * @param float $value
      *
      * @return Layout
      */
     public function setXPosition($value)
     {
-        $this->xPos = $value;
+        $this->xPos = (float) $value;
 
         return $this;
     }
@@ -247,13 +247,13 @@ class Layout
     /**
      * Set Y-Position.
      *
-     * @param Y-Position $value
+     * @param float $value
      *
      * @return Layout
      */
     public function setYPosition($value)
     {
-        $this->yPos = $value;
+        $this->yPos = (float) $value;
 
         return $this;
     }

+ 3 - 3
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Collection/Cells.php

@@ -241,7 +241,7 @@ class Cells
      */
     public function getHighestColumn($row = null)
     {
-        if ($row == null) {
+        if ($row === null) {
             $colRow = $this->getHighestRowAndColumn();
 
             return $colRow['column'];
@@ -259,7 +259,7 @@ class Cells
             $columnList[] = Coordinate::columnIndexFromString($c);
         }
 
-        return Coordinate::stringFromColumnIndex(max($columnList) + 1);
+        return Coordinate::stringFromColumnIndex(max($columnList));
     }
 
     /**
@@ -272,7 +272,7 @@ class Cells
      */
     public function getHighestRow($column = null)
     {
-        if ($column == null) {
+        if ($column === null) {
             $colRow = $this->getHighestRowAndColumn();
 
             return $colRow['row'];

+ 1 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php

@@ -418,7 +418,7 @@ class Properties
      *
      * @param string $propertyName
      *
-     * @return string
+     * @return mixed
      */
     public function getCustomPropertyValue($propertyName)
     {

+ 6 - 85
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/BaseReader.php

@@ -55,27 +55,16 @@ abstract class BaseReader implements IReader
      */
     protected $securityScanner;
 
-    /**
-     * Read data only?
-     *        If this is true, then the Reader will only read data values for cells, it will not read any formatting information.
-     *        If false (the default) it will read data and formatting.
-     *
-     * @return bool
-     */
+    public function __construct()
+    {
+        $this->readFilter = new DefaultReadFilter();
+    }
+
     public function getReadDataOnly()
     {
         return $this->readDataOnly;
     }
 
-    /**
-     * Set read data only
-     *        Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting information.
-     *        Set to false (the default) to advise the Reader to read both data and formatting for cells.
-     *
-     * @param bool $pValue
-     *
-     * @return IReader
-     */
     public function setReadDataOnly($pValue)
     {
         $this->readDataOnly = (bool) $pValue;
@@ -83,27 +72,11 @@ abstract class BaseReader implements IReader
         return $this;
     }
 
-    /**
-     * Read empty cells?
-     *        If this is true (the default), then the Reader will read data values for all cells, irrespective of value.
-     *        If false it will not read data for cells containing a null value or an empty string.
-     *
-     * @return bool
-     */
     public function getReadEmptyCells()
     {
         return $this->readEmptyCells;
     }
 
-    /**
-     * Set read empty cells
-     *        Set to true (the default) to advise the Reader read data values for all cells, irrespective of value.
-     *        Set to false to advise the Reader to ignore cells containing a null value or an empty string.
-     *
-     * @param bool $pValue
-     *
-     * @return IReader
-     */
     public function setReadEmptyCells($pValue)
     {
         $this->readEmptyCells = (bool) $pValue;
@@ -111,29 +84,11 @@ abstract class BaseReader implements IReader
         return $this;
     }
 
-    /**
-     * Read charts in workbook?
-     *        If this is true, then the Reader will include any charts that exist in the workbook.
-     *      Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
-     *        If false (the default) it will ignore any charts defined in the workbook file.
-     *
-     * @return bool
-     */
     public function getIncludeCharts()
     {
         return $this->includeCharts;
     }
 
-    /**
-     * Set read charts in workbook
-     *        Set to true, to advise the Reader to include any charts that exist in the workbook.
-     *      Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
-     *        Set to false (the default) to discard charts.
-     *
-     * @param bool $pValue
-     *
-     * @return IReader
-     */
     public function setIncludeCharts($pValue)
     {
         $this->includeCharts = (bool) $pValue;
@@ -141,27 +96,11 @@ abstract class BaseReader implements IReader
         return $this;
     }
 
-    /**
-     * Get which sheets to load
-     * Returns either an array of worksheet names (the list of worksheets that should be loaded), or a null
-     *        indicating that all worksheets in the workbook should be loaded.
-     *
-     * @return mixed
-     */
     public function getLoadSheetsOnly()
     {
         return $this->loadSheetsOnly;
     }
 
-    /**
-     * Set which sheets to load.
-     *
-     * @param mixed $value
-     *        This should be either an array of worksheet names to be loaded, or a string containing a single worksheet name.
-     *        If NULL, then it tells the Reader to read all worksheets in the workbook
-     *
-     * @return IReader
-     */
     public function setLoadSheetsOnly($value)
     {
         if ($value === null) {
@@ -173,12 +112,6 @@ abstract class BaseReader implements IReader
         return $this;
     }
 
-    /**
-     * Set all sheets to load
-     *        Tells the Reader to load all worksheets from the workbook.
-     *
-     * @return IReader
-     */
     public function setLoadAllSheets()
     {
         $this->loadSheetsOnly = null;
@@ -186,23 +119,11 @@ abstract class BaseReader implements IReader
         return $this;
     }
 
-    /**
-     * Read filter.
-     *
-     * @return IReadFilter
-     */
     public function getReadFilter()
     {
         return $this->readFilter;
     }
 
-    /**
-     * Set read filter.
-     *
-     * @param IReadFilter $pValue
-     *
-     * @return IReader
-     */
     public function setReadFilter(IReadFilter $pValue)
     {
         $this->readFilter = $pValue;
@@ -210,7 +131,7 @@ abstract class BaseReader implements IReader
         return $this;
     }
 
-    public function getSecuritySCanner()
+    public function getSecurityScanner()
     {
         if (property_exists($this, 'securityScanner')) {
             return $this->securityScanner;

+ 13 - 13
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Csv.php

@@ -62,7 +62,7 @@ class Csv extends BaseReader
      */
     public function __construct()
     {
-        $this->readFilter = new DefaultReadFilter();
+        parent::__construct();
     }
 
     /**
@@ -143,7 +143,7 @@ class Csv extends BaseReader
             return;
         }
 
-        return $this->skipBOM();
+        $this->skipBOM();
     }
 
     /**
@@ -155,7 +155,7 @@ class Csv extends BaseReader
             return;
         }
 
-        $potentialDelimiters = [',', ';', "\t", '|', ':', ' '];
+        $potentialDelimiters = [',', ';', "\t", '|', ':', ' ', '~'];
         $counts = [];
         foreach ($potentialDelimiters as $delimiter) {
             $counts[$delimiter] = [];
@@ -184,8 +184,9 @@ class Csv extends BaseReader
         // If number of lines is 0, nothing to infer : fall back to the default
         if ($numberLines === 0) {
             $this->delimiter = reset($potentialDelimiters);
+            $this->skipBOM();
 
-            return $this->skipBOM();
+            return;
         }
 
         // Calculate the mean square deviations for each delimiter (ignoring delimiters that haven't been found consistently)
@@ -230,7 +231,7 @@ class Csv extends BaseReader
             $this->delimiter = reset($potentialDelimiters);
         }
 
-        return $this->skipBOM();
+        $this->skipBOM();
     }
 
     /**
@@ -254,15 +255,13 @@ class Csv extends BaseReader
         $line = $line . $newLine;
 
         // Drop everything that is enclosed to avoid counting false positives in enclosures
-        $enclosure = preg_quote($this->enclosure, '/');
-        $line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/U', '', $line);
+        $enclosure = '(?<!' . preg_quote($this->escapeCharacter, '/') . ')'
+            . preg_quote($this->enclosure, '/');
+        $line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line);
 
         // See if we have any enclosures left in the line
-        $matches = [];
-        preg_match('/(' . $enclosure . ')/', $line, $matches);
-
-        // if we still have an enclosure then we need to read the next line aswell
-        if (count($matches) > 0) {
+        // if we still have an enclosure then we need to read the next line as well
+        if (preg_match('/(' . $enclosure . ')/', $line) > 0) {
             $line = $this->getNextLine($line);
         }
 
@@ -547,7 +546,8 @@ class Csv extends BaseReader
         fclose($this->fileHandle);
 
         // Trust file extension if any
-        if (strtolower(pathinfo($pFilename, PATHINFO_EXTENSION)) === 'csv') {
+        $extension = strtolower(pathinfo($pFilename, PATHINFO_EXTENSION));
+        if (in_array($extension, ['csv', 'tsv'])) {
             return true;
         }
 

+ 1 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric.php

@@ -36,7 +36,7 @@ class Gnumeric extends BaseReader
      */
     public function __construct()
     {
-        $this->readFilter = new DefaultReadFilter();
+        parent::__construct();
         $this->referenceHelper = ReferenceHelper::getInstance();
         $this->securityScanner = XmlScanner::getInstance($this);
     }

+ 309 - 19
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Html.php

@@ -12,6 +12,9 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Style\Border;
 use PhpOffice\PhpSpreadsheet\Style\Color;
 use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 
 /** PhpSpreadsheet root directory */
@@ -96,6 +99,26 @@ class Html extends BaseReader
                 ],
             ],
         ], //    Bottom border
+        'strong' => [
+            'font' => [
+                'bold' => true,
+            ],
+        ], //    Bold
+        'b' => [
+            'font' => [
+                'bold' => true,
+            ],
+        ], //    Bold
+        'i' => [
+            'font' => [
+                'italic' => true,
+            ],
+        ], //    Italic
+        'em' => [
+            'font' => [
+                'italic' => true,
+            ],
+        ], //    Italic
     ];
 
     protected $rowspan = [];
@@ -105,7 +128,7 @@ class Html extends BaseReader
      */
     public function __construct()
     {
-        $this->readFilter = new DefaultReadFilter();
+        parent::__construct();
         $this->securityScanner = XmlScanner::getInstance($this);
     }
 
@@ -295,11 +318,9 @@ class Html extends BaseReader
                 switch ($child->nodeName) {
                     case 'meta':
                         foreach ($attributeArray as $attributeName => $attributeValue) {
-                            switch ($attributeName) {
-                                case 'content':
-                                    //    TODO
-                                    //    Extract character set, so we can convert to UTF-8 if required
-                                    break;
+                            // Extract character set, so we can convert to UTF-8 if required
+                            if ($attributeName === 'charset') {
+                                $this->setInputEncoding($attributeValue);
                             }
                         }
                         $this->processDomElement($child, $sheet, $row, $column, $cellContent);
@@ -334,6 +355,10 @@ class Html extends BaseReader
                             $cellContent .= ' ';
                         }
 
+                        if (isset($this->formats[$child->nodeName])) {
+                            $sheet->getStyle($column . $row)->applyFromArray($this->formats[$child->nodeName]);
+                        }
+
                         break;
                     case 'hr':
                         $this->flushCell($sheet, $column, $row, $cellContent);
@@ -423,6 +448,10 @@ class Html extends BaseReader
                             $column = 'A';
                         }
 
+                        break;
+                    case 'img':
+                        $this->insertImage($sheet, $column, $row, $attributeArray);
+
                         break;
                     case 'table':
                         $this->flushCell($sheet, $column, $row, $cellContent);
@@ -448,6 +477,11 @@ class Html extends BaseReader
                         $column = $this->getTableStartColumn();
                         $cellContent = '';
                         $this->processDomElement($child, $sheet, $row, $column, $cellContent);
+
+                        if (isset($attributeArray['height'])) {
+                            $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
+                        }
+
                         ++$row;
 
                         break;
@@ -501,6 +535,27 @@ class Html extends BaseReader
                                 ]
                             );
                         }
+
+                        if (isset($attributeArray['width'])) {
+                            $sheet->getColumnDimension($column)->setWidth($attributeArray['width']);
+                        }
+
+                        if (isset($attributeArray['height'])) {
+                            $sheet->getRowDimension($row)->setRowHeight($attributeArray['height']);
+                        }
+
+                        if (isset($attributeArray['align'])) {
+                            $sheet->getStyle($column . $row)->getAlignment()->setHorizontal($attributeArray['align']);
+                        }
+
+                        if (isset($attributeArray['valign'])) {
+                            $sheet->getStyle($column . $row)->getAlignment()->setVertical($attributeArray['valign']);
+                        }
+
+                        if (isset($attributeArray['data-format'])) {
+                            $sheet->getStyle($column . $row)->getNumberFormat()->setFormatCode($attributeArray['data-format']);
+                        }
+
                         ++$column;
 
                         break;
@@ -608,36 +663,271 @@ class Html extends BaseReader
             return;
         }
 
-        $supported_styles = ['background-color', 'color'];
+        $cellStyle = $sheet->getStyle($column . $row);
 
         // add color styles (background & text) from dom element,currently support : td & th, using ONLY inline css style with RGB color
         $styles = explode(';', $attributeArray['style']);
         foreach ($styles as $st) {
             $value = explode(':', $st);
+            $styleName = isset($value[0]) ? trim($value[0]) : null;
+            $styleValue = isset($value[1]) ? trim($value[1]) : null;
 
-            if (empty(trim($value[0])) || !in_array(trim($value[0]), $supported_styles)) {
+            if (!$styleName) {
                 continue;
             }
 
-            //check if has #, so we can get clean hex
-            if (substr(trim($value[1]), 0, 1) == '#') {
-                $style_color = substr(trim($value[1]), 1);
-            }
+            switch ($styleName) {
+                case 'background':
+                case 'background-color':
+                    $styleColor = $this->getStyleColor($styleValue);
 
-            if (empty($style_color)) {
-                continue;
-            }
+                    if (!$styleColor) {
+                        continue 2;
+                    }
 
-            switch (trim($value[0])) {
-                case 'background-color':
-                    $sheet->getStyle($column . $row)->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => "{$style_color}"]]]);
+                    $cellStyle->applyFromArray(['fill' => ['fillType' => Fill::FILL_SOLID, 'color' => ['rgb' => $styleColor]]]);
 
                     break;
                 case 'color':
-                    $sheet->getStyle($column . $row)->applyFromArray(['font' => ['color' => ['rgb' => "{$style_color}"]]]);
+                    $styleColor = $this->getStyleColor($styleValue);
+
+                    if (!$styleColor) {
+                        continue 2;
+                    }
+
+                    $cellStyle->applyFromArray(['font' => ['color' => ['rgb' => $styleColor]]]);
+
+                    break;
+
+                case 'border':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'allBorders');
+
+                    break;
+
+                case 'border-top':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'top');
+
+                    break;
+
+                case 'border-bottom':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'bottom');
+
+                    break;
+
+                case 'border-left':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'left');
+
+                    break;
+
+                case 'border-right':
+                    $this->setBorderStyle($cellStyle, $styleValue, 'right');
+
+                    break;
+
+                case 'font-size':
+                    $cellStyle->getFont()->setSize(
+                        (float) $styleValue
+                    );
+
+                    break;
+
+                case 'font-weight':
+                    if ($styleValue === 'bold' || $styleValue >= 500) {
+                        $cellStyle->getFont()->setBold(true);
+                    }
+
+                    break;
+
+                case 'font-style':
+                    if ($styleValue === 'italic') {
+                        $cellStyle->getFont()->setItalic(true);
+                    }
+
+                    break;
+
+                case 'font-family':
+                    $cellStyle->getFont()->setName(str_replace('\'', '', $styleValue));
+
+                    break;
+
+                case 'text-decoration':
+                    switch ($styleValue) {
+                        case 'underline':
+                            $cellStyle->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
+
+                            break;
+                        case 'line-through':
+                            $cellStyle->getFont()->setStrikethrough(true);
+
+                            break;
+                    }
+
+                    break;
+
+                case 'text-align':
+                    $cellStyle->getAlignment()->setHorizontal($styleValue);
+
+                    break;
+
+                case 'vertical-align':
+                    $cellStyle->getAlignment()->setVertical($styleValue);
+
+                    break;
+
+                case 'width':
+                    $sheet->getColumnDimension($column)->setWidth(
+                        str_replace('px', '', $styleValue)
+                    );
+
+                    break;
+
+                case 'height':
+                    $sheet->getRowDimension($row)->setRowHeight(
+                        str_replace('px', '', $styleValue)
+                    );
+
+                    break;
+
+                case 'word-wrap':
+                    $cellStyle->getAlignment()->setWrapText(
+                        $styleValue === 'break-word'
+                    );
+
+                    break;
+
+                case 'text-indent':
+                    $cellStyle->getAlignment()->setIndent(
+                        (int) str_replace(['px'], '', $styleValue)
+                    );
 
                     break;
             }
         }
     }
+
+    /**
+     * Check if has #, so we can get clean hex.
+     *
+     * @param $value
+     *
+     * @return null|string
+     */
+    public function getStyleColor($value)
+    {
+        if (strpos($value, '#') === 0) {
+            return substr($value, 1);
+        }
+
+        return null;
+    }
+
+    /**
+     * @param Worksheet $sheet
+     * @param string    $column
+     * @param int       $row
+     * @param array     $attributes
+     *
+     * @throws \PhpOffice\PhpSpreadsheet\Exception
+     */
+    private function insertImage(Worksheet $sheet, $column, $row, array $attributes)
+    {
+        if (!isset($attributes['src'])) {
+            return;
+        }
+
+        $src = urldecode($attributes['src']);
+        $width = isset($attributes['width']) ? (float) $attributes['width'] : null;
+        $height = isset($attributes['height']) ? (float) $attributes['height'] : null;
+        $name = isset($attributes['alt']) ? (float) $attributes['alt'] : null;
+
+        $drawing = new Drawing();
+        $drawing->setPath($src);
+        $drawing->setWorksheet($sheet);
+        $drawing->setCoordinates($column . $row);
+        $drawing->setOffsetX(0);
+        $drawing->setOffsetY(10);
+        $drawing->setResizeProportional(true);
+
+        if ($name) {
+            $drawing->setName($name);
+        }
+
+        if ($width) {
+            $drawing->setWidth((int) $width);
+        }
+
+        if ($height) {
+            $drawing->setHeight((int) $height);
+        }
+
+        $sheet->getColumnDimension($column)->setWidth(
+            $drawing->getWidth() / 6
+        );
+
+        $sheet->getRowDimension($row)->setRowHeight(
+            $drawing->getHeight() * 0.9
+        );
+    }
+
+    /**
+     * Map html border style to PhpSpreadsheet border style.
+     *
+     * @param  string $style
+     *
+     * @return null|string
+     */
+    public function getBorderStyle($style)
+    {
+        switch ($style) {
+            case 'solid':
+                return Border::BORDER_THIN;
+            case 'dashed':
+                return Border::BORDER_DASHED;
+            case 'dotted':
+                return Border::BORDER_DOTTED;
+            case 'medium':
+                return Border::BORDER_MEDIUM;
+            case 'thick':
+                return Border::BORDER_THICK;
+            case 'none':
+                return Border::BORDER_NONE;
+            case 'dash-dot':
+                return Border::BORDER_DASHDOT;
+            case 'dash-dot-dot':
+                return Border::BORDER_DASHDOTDOT;
+            case 'double':
+                return Border::BORDER_DOUBLE;
+            case 'hair':
+                return Border::BORDER_HAIR;
+            case 'medium-dash-dot':
+                return Border::BORDER_MEDIUMDASHDOT;
+            case 'medium-dash-dot-dot':
+                return Border::BORDER_MEDIUMDASHDOTDOT;
+            case 'medium-dashed':
+                return Border::BORDER_MEDIUMDASHED;
+            case 'slant-dash-dot':
+                return Border::BORDER_SLANTDASHDOT;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param Style  $cellStyle
+     * @param string $styleValue
+     * @param string $type
+     */
+    private function setBorderStyle(Style $cellStyle, $styleValue, $type)
+    {
+        list(, $borderStyle, $color) = explode(' ', $styleValue);
+
+        $cellStyle->applyFromArray([
+            'borders' => [
+                $type => [
+                    'borderStyle' => $this->getBorderStyle($borderStyle),
+                    'color' => ['rgb' => $this->getStyleColor($color)],
+                ],
+            ],
+        ]);
+    }
 }

+ 106 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/IReader.php

@@ -18,6 +18,112 @@ interface IReader
      */
     public function canRead($pFilename);
 
+    /**
+     * Read data only?
+     *        If this is true, then the Reader will only read data values for cells, it will not read any formatting information.
+     *        If false (the default) it will read data and formatting.
+     *
+     * @return bool
+     */
+    public function getReadDataOnly();
+
+    /**
+     * Set read data only
+     *        Set to true, to advise the Reader only to read data values for cells, and to ignore any formatting information.
+     *        Set to false (the default) to advise the Reader to read both data and formatting for cells.
+     *
+     * @param bool $pValue
+     *
+     * @return IReader
+     */
+    public function setReadDataOnly($pValue);
+
+    /**
+     * Read empty cells?
+     *        If this is true (the default), then the Reader will read data values for all cells, irrespective of value.
+     *        If false it will not read data for cells containing a null value or an empty string.
+     *
+     * @return bool
+     */
+    public function getReadEmptyCells();
+
+    /**
+     * Set read empty cells
+     *        Set to true (the default) to advise the Reader read data values for all cells, irrespective of value.
+     *        Set to false to advise the Reader to ignore cells containing a null value or an empty string.
+     *
+     * @param bool $pValue
+     *
+     * @return IReader
+     */
+    public function setReadEmptyCells($pValue);
+
+    /**
+     * Read charts in workbook?
+     *        If this is true, then the Reader will include any charts that exist in the workbook.
+     *      Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
+     *        If false (the default) it will ignore any charts defined in the workbook file.
+     *
+     * @return bool
+     */
+    public function getIncludeCharts();
+
+    /**
+     * Set read charts in workbook
+     *        Set to true, to advise the Reader to include any charts that exist in the workbook.
+     *      Note that a ReadDataOnly value of false overrides, and charts won't be read regardless of the IncludeCharts value.
+     *        Set to false (the default) to discard charts.
+     *
+     * @param bool $pValue
+     *
+     * @return IReader
+     */
+    public function setIncludeCharts($pValue);
+
+    /**
+     * Get which sheets to load
+     * Returns either an array of worksheet names (the list of worksheets that should be loaded), or a null
+     *        indicating that all worksheets in the workbook should be loaded.
+     *
+     * @return mixed
+     */
+    public function getLoadSheetsOnly();
+
+    /**
+     * Set which sheets to load.
+     *
+     * @param mixed $value
+     *        This should be either an array of worksheet names to be loaded, or a string containing a single worksheet name.
+     *        If NULL, then it tells the Reader to read all worksheets in the workbook
+     *
+     * @return IReader
+     */
+    public function setLoadSheetsOnly($value);
+
+    /**
+     * Set all sheets to load
+     *        Tells the Reader to load all worksheets from the workbook.
+     *
+     * @return IReader
+     */
+    public function setLoadAllSheets();
+
+    /**
+     * Read filter.
+     *
+     * @return IReadFilter
+     */
+    public function getReadFilter();
+
+    /**
+     * Set read filter.
+     *
+     * @param IReadFilter $pValue
+     *
+     * @return IReader
+     */
+    public function setReadFilter(IReadFilter $pValue);
+
     /**
      * Loads PhpSpreadsheet from file.
      *

+ 16 - 100
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods.php

@@ -7,7 +7,7 @@ use DateTimeZone;
 use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\DataType;
-use PhpOffice\PhpSpreadsheet\Document\Properties;
+use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties;
 use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Settings;
@@ -25,7 +25,7 @@ class Ods extends BaseReader
      */
     public function __construct()
     {
-        $this->readFilter = new DefaultReadFilter();
+        parent::__construct();
         $this->securityScanner = XmlScanner::getInstance($this);
     }
 
@@ -52,7 +52,7 @@ class Ods extends BaseReader
             $stat = $zip->statName('mimetype');
             if ($stat && ($stat['size'] <= 255)) {
                 $mimeType = $zip->getFromName($stat['name']);
-            } elseif ($stat = $zip->statName('META-INF/manifest.xml')) {
+            } elseif ($zip->statName('META-INF/manifest.xml')) {
                 $xml = simplexml_load_string(
                     $this->securityScanner->scan($zip->getFromName('META-INF/manifest.xml')),
                     'SimpleXMLElement',
@@ -265,7 +265,7 @@ class Ods extends BaseReader
 
         $zip = new ZipArchive();
         if (!$zip->open($pFilename)) {
-            throw new Exception('Could not open ' . $pFilename . ' for reading! Error opening file.');
+            throw new Exception("Could not open {$pFilename} for reading! Error opening file.");
         }
 
         // Meta
@@ -275,97 +275,13 @@ class Ods extends BaseReader
             'SimpleXMLElement',
             Settings::getLibXmlLoaderOptions()
         );
-        $namespacesMeta = $xml->getNamespaces(true);
+        if ($xml === false) {
+            throw new Exception('Unable to read data from {$pFilename}');
+        }
 
-        $docProps = $spreadsheet->getProperties();
-        $officeProperty = $xml->children($namespacesMeta['office']);
-        foreach ($officeProperty as $officePropertyData) {
-            $officePropertyDC = [];
-            if (isset($namespacesMeta['dc'])) {
-                $officePropertyDC = $officePropertyData->children($namespacesMeta['dc']);
-            }
-            foreach ($officePropertyDC as $propertyName => $propertyValue) {
-                $propertyValue = (string) $propertyValue;
-                switch ($propertyName) {
-                    case 'title':
-                        $docProps->setTitle($propertyValue);
-
-                        break;
-                    case 'subject':
-                        $docProps->setSubject($propertyValue);
-
-                        break;
-                    case 'creator':
-                        $docProps->setCreator($propertyValue);
-                        $docProps->setLastModifiedBy($propertyValue);
-
-                        break;
-                    case 'date':
-                        $creationDate = strtotime($propertyValue);
-                        $docProps->setCreated($creationDate);
-                        $docProps->setModified($creationDate);
-
-                        break;
-                    case 'description':
-                        $docProps->setDescription($propertyValue);
-
-                        break;
-                }
-            }
-            $officePropertyMeta = [];
-            if (isset($namespacesMeta['dc'])) {
-                $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
-            }
-            foreach ($officePropertyMeta as $propertyName => $propertyValue) {
-                $propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']);
-                $propertyValue = (string) $propertyValue;
-                switch ($propertyName) {
-                    case 'initial-creator':
-                        $docProps->setCreator($propertyValue);
-
-                        break;
-                    case 'keyword':
-                        $docProps->setKeywords($propertyValue);
-
-                        break;
-                    case 'creation-date':
-                        $creationDate = strtotime($propertyValue);
-                        $docProps->setCreated($creationDate);
-
-                        break;
-                    case 'user-defined':
-                        $propertyValueType = Properties::PROPERTY_TYPE_STRING;
-                        foreach ($propertyValueAttributes as $key => $value) {
-                            if ($key == 'name') {
-                                $propertyValueName = (string) $value;
-                            } elseif ($key == 'value-type') {
-                                switch ($value) {
-                                    case 'date':
-                                        $propertyValue = Properties::convertProperty($propertyValue, 'date');
-                                        $propertyValueType = Properties::PROPERTY_TYPE_DATE;
-
-                                        break;
-                                    case 'boolean':
-                                        $propertyValue = Properties::convertProperty($propertyValue, 'bool');
-                                        $propertyValueType = Properties::PROPERTY_TYPE_BOOLEAN;
-
-                                        break;
-                                    case 'float':
-                                        $propertyValue = Properties::convertProperty($propertyValue, 'r4');
-                                        $propertyValueType = Properties::PROPERTY_TYPE_FLOAT;
-
-                                        break;
-                                    default:
-                                        $propertyValueType = Properties::PROPERTY_TYPE_STRING;
-                                }
-                            }
-                        }
-                        $docProps->setCustomProperty($propertyValueName, $propertyValue, $propertyValueType);
+        $namespacesMeta = $xml->getNamespaces(true);
 
-                        break;
-                }
-            }
-        }
+        (new DocumentProperties($spreadsheet))->load($xml, $namespacesMeta);
 
         // Content
 
@@ -513,7 +429,7 @@ class Ods extends BaseReader
                                     foreach ($paragraphs as $pData) {
                                         $dataArray[] = $this->scanElementForText($pData);
                                     }
-                                    $allCellDataText = implode($dataArray, "\n");
+                                    $allCellDataText = implode("\n", $dataArray);
 
                                     $type = $cellData->getAttributeNS($officeNs, 'value-type');
 
@@ -580,12 +496,12 @@ class Ods extends BaseReader
                                             );
 
                                             $dataValue = Date::formattedPHPToExcel(
-                                                $year,
-                                                $month,
-                                                $day,
-                                                $hour,
-                                                $minute,
-                                                $second
+                                                (int) $year,
+                                                (int) $month,
+                                                (int) $day,
+                                                (int) $hour,
+                                                (int) $minute,
+                                                (int) $second
                                             );
 
                                             if ($dataValue != floor($dataValue)) {

+ 136 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php

@@ -0,0 +1,136 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Ods;
+
+use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+class Properties
+{
+    private $spreadsheet;
+
+    public function __construct(Spreadsheet $spreadsheet)
+    {
+        $this->spreadsheet = $spreadsheet;
+    }
+
+    public function load(\SimpleXMLElement $xml, $namespacesMeta)
+    {
+        $docProps = $this->spreadsheet->getProperties();
+        $officeProperty = $xml->children($namespacesMeta['office']);
+        foreach ($officeProperty as $officePropertyData) {
+            /** @var \SimpleXMLElement $officePropertyData */
+            $officePropertiesDC = (object) [];
+            if (isset($namespacesMeta['dc'])) {
+                $officePropertiesDC = $officePropertyData->children($namespacesMeta['dc']);
+            }
+            $this->setCoreProperties($docProps, $officePropertiesDC);
+
+            $officePropertyMeta = (object) [];
+            if (isset($namespacesMeta['dc'])) {
+                $officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
+            }
+            foreach ($officePropertyMeta as $propertyName => $propertyValue) {
+                $this->setMetaProperties($namespacesMeta, $propertyValue, $propertyName, $docProps);
+            }
+        }
+    }
+
+    private function setCoreProperties(DocumentProperties $docProps, \SimpleXMLElement $officePropertyDC)
+    {
+        foreach ($officePropertyDC as $propertyName => $propertyValue) {
+            $propertyValue = (string) $propertyValue;
+            switch ($propertyName) {
+                case 'title':
+                    $docProps->setTitle($propertyValue);
+
+                    break;
+                case 'subject':
+                    $docProps->setSubject($propertyValue);
+
+                    break;
+                case 'creator':
+                    $docProps->setCreator($propertyValue);
+                    $docProps->setLastModifiedBy($propertyValue);
+
+                    break;
+                case 'creation-date':
+                    $creationDate = strtotime($propertyValue);
+                    $docProps->setCreated($creationDate);
+                    $docProps->setModified($creationDate);
+
+                    break;
+                case 'keyword':
+                    $docProps->setKeywords($propertyValue);
+
+                    break;
+                case 'description':
+                    $docProps->setDescription($propertyValue);
+
+                    break;
+            }
+        }
+    }
+
+    private function setMetaProperties(
+        $namespacesMeta,
+        \SimpleXMLElement $propertyValue,
+        $propertyName,
+        DocumentProperties $docProps
+    ) {
+        $propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']);
+        $propertyValue = (string) $propertyValue;
+        switch ($propertyName) {
+            case 'initial-creator':
+                $docProps->setCreator($propertyValue);
+
+                break;
+            case 'keyword':
+                $docProps->setKeywords($propertyValue);
+
+                break;
+            case 'creation-date':
+                $creationDate = strtotime($propertyValue);
+                $docProps->setCreated($creationDate);
+
+                break;
+            case 'user-defined':
+                $this->setUserDefinedProperty($propertyValueAttributes, $propertyValue, $docProps);
+
+                break;
+        }
+    }
+
+    private function setUserDefinedProperty($propertyValueAttributes, $propertyValue, DocumentProperties $docProps)
+    {
+        $propertyValueName = '';
+        $propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
+        foreach ($propertyValueAttributes as $key => $value) {
+            if ($key == 'name') {
+                $propertyValueName = (string) $value;
+            } elseif ($key == 'value-type') {
+                switch ($value) {
+                    case 'date':
+                        $propertyValue = DocumentProperties::convertProperty($propertyValue, 'date');
+                        $propertyValueType = DocumentProperties::PROPERTY_TYPE_DATE;
+
+                        break;
+                    case 'boolean':
+                        $propertyValue = DocumentProperties::convertProperty($propertyValue, 'bool');
+                        $propertyValueType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
+
+                        break;
+                    case 'float':
+                        $propertyValue = DocumentProperties::convertProperty($propertyValue, 'r4');
+                        $propertyValueType = DocumentProperties::PROPERTY_TYPE_FLOAT;
+
+                        break;
+                    default:
+                        $propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
+                }
+            }
+        }
+
+        $docProps->setCustomProperty($propertyValueName, $propertyValue, $propertyValueType);
+    }
+}

+ 60 - 31
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php

@@ -3,16 +3,10 @@
 namespace PhpOffice\PhpSpreadsheet\Reader\Security;
 
 use PhpOffice\PhpSpreadsheet\Reader;
+use PhpOffice\PhpSpreadsheet\Settings;
 
 class XmlScanner
 {
-    /**
-     * Identifies whether the thread-safe libxmlDisableEntityLoader() function is available.
-     *
-     * @var bool
-     */
-    private $libxmlDisableEntityLoader = false;
-
     /**
      * String used to identify risky xml elements.
      *
@@ -22,10 +16,16 @@ class XmlScanner
 
     private $callback;
 
-    private function __construct($pattern = '<!DOCTYPE')
+    private static $libxmlDisableEntityLoaderValue;
+
+    public function __construct($pattern = '<!DOCTYPE')
     {
         $this->pattern = $pattern;
-        $this->libxmlDisableEntityLoader = $this->identifyLibxmlDisableEntityLoaderAvailability();
+
+        $this->disableEntityLoaderCheck();
+
+        // A fatal error will bypass the destructor, so we register a shutdown here
+        register_shutdown_function([__CLASS__, 'shutdown']);
     }
 
     public static function getInstance(Reader\IReader $reader)
@@ -43,7 +43,7 @@ class XmlScanner
         }
     }
 
-    private function identifyLibxmlDisableEntityLoaderAvailability()
+    public static function threadSafeLibxmlDisableEntityLoaderAvailability()
     {
         if (PHP_MAJOR_VERSION == 7) {
             switch (PHP_MINOR_VERSION) {
@@ -61,11 +61,54 @@ class XmlScanner
         return false;
     }
 
+    private function disableEntityLoaderCheck()
+    {
+        if (Settings::getLibXmlDisableEntityLoader()) {
+            $libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
+
+            if (self::$libxmlDisableEntityLoaderValue === null) {
+                self::$libxmlDisableEntityLoaderValue = $libxmlDisableEntityLoaderValue;
+            }
+        }
+    }
+
+    public static function shutdown()
+    {
+        if (self::$libxmlDisableEntityLoaderValue !== null) {
+            libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue);
+            self::$libxmlDisableEntityLoaderValue = null;
+        }
+    }
+
+    public function __destruct()
+    {
+        self::shutdown();
+    }
+
     public function setAdditionalCallback(callable $callback)
     {
         $this->callback = $callback;
     }
 
+    private function toUtf8($xml)
+    {
+        $pattern = '/encoding="(.*?)"/';
+        $result = preg_match($pattern, $xml, $matches);
+        $charset = strtoupper($result ? $matches[1] : 'UTF-8');
+
+        if ($charset !== 'UTF-8') {
+            $xml = mb_convert_encoding($xml, 'UTF-8', $charset);
+
+            $result = preg_match($pattern, $xml, $matches);
+            $charset = strtoupper($result ? $matches[1] : 'UTF-8');
+            if ($charset !== 'UTF-8') {
+                throw new Reader\Exception('Suspicious Double-encoded XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
+            }
+        }
+
+        return $xml;
+    }
+
     /**
      * Scan the XML for use of <!ENTITY to prevent XXE/XEE attacks.
      *
@@ -77,33 +120,19 @@ class XmlScanner
      */
     public function scan($xml)
     {
-        if ($this->libxmlDisableEntityLoader) {
-            $previousLibxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
-        }
+        $this->disableEntityLoaderCheck();
 
-        $pattern = '/encoding="(.*?)"/';
-        $result = preg_match($pattern, $xml, $matches);
-        $charset = $result ? $matches[1] : 'UTF-8';
-
-        if ($charset !== 'UTF-8') {
-            $xml = mb_convert_encoding($xml, 'UTF-8', $charset);
-        }
+        $xml = $this->toUtf8($xml);
 
         // Don't rely purely on libxml_disable_entity_loader()
         $pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/';
 
-        try {
-            if (preg_match($pattern, $xml)) {
-                throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
-            }
+        if (preg_match($pattern, $xml)) {
+            throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
+        }
 
-            if ($this->callback !== null && is_callable($this->callback)) {
-                $xml = call_user_func($this->callback, $xml);
-            }
-        } finally {
-            if (isset($previousLibxmlDisableEntityLoaderValue)) {
-                libxml_disable_entity_loader($previousLibxmlDisableEntityLoaderValue);
-            }
+        if ($this->callback !== null && is_callable($this->callback)) {
+            $xml = call_user_func($this->callback, $xml);
         }
 
         return $xml;

+ 1 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php

@@ -43,7 +43,7 @@ class Slk extends BaseReader
      */
     public function __construct()
     {
-        $this->readFilter = new DefaultReadFilter();
+        parent::__construct();
     }
 
     /**

+ 1 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php

@@ -412,7 +412,7 @@ class Xls extends BaseReader
      */
     public function __construct()
     {
-        $this->readFilter = new DefaultReadFilter();
+        parent::__construct();
     }
 
     /**

+ 113 - 688
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php

@@ -4,10 +4,19 @@ namespace PhpOffice\PhpSpreadsheet\Reader;
 
 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
 use PhpOffice\PhpSpreadsheet\Cell\Hyperlink;
-use PhpOffice\PhpSpreadsheet\Document\Properties;
 use PhpOffice\PhpSpreadsheet\NamedRange;
 use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\AutoFilter;
 use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Chart;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\ColumnAndRowAttributes;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\ConditionalStyles;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\DataValidations;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Hyperlinks;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\PageSetup;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Properties as PropertyReader;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViewOptions;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViews;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Styles;
 use PhpOffice\PhpSpreadsheet\ReferenceHelper;
 use PhpOffice\PhpSpreadsheet\RichText\RichText;
 use PhpOffice\PhpSpreadsheet\Settings;
@@ -20,11 +29,9 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
 use PhpOffice\PhpSpreadsheet\Style\Border;
 use PhpOffice\PhpSpreadsheet\Style\Borders;
 use PhpOffice\PhpSpreadsheet\Style\Color;
-use PhpOffice\PhpSpreadsheet\Style\Conditional;
 use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
 use PhpOffice\PhpSpreadsheet\Style\Protection;
 use PhpOffice\PhpSpreadsheet\Style\Style;
-use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
 use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
 use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
 use SimpleXMLElement;
@@ -52,7 +59,7 @@ class Xlsx extends BaseReader
      */
     public function __construct()
     {
-        $this->readFilter = new DefaultReadFilter();
+        parent::__construct();
         $this->referenceHelper = ReferenceHelper::getInstance();
         $this->securityScanner = XmlScanner::getInstance($this);
     }
@@ -323,60 +330,6 @@ class Xlsx extends BaseReader
         return $contents;
     }
 
-    /**
-     * Set Worksheet column attributes by attributes array passed.
-     *
-     * @param Worksheet $docSheet
-     * @param string $column A, B, ... DX, ...
-     * @param array $columnAttributes array of attributes (indexes are attribute name, values are value)
-     *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ?
-     */
-    private function setColumnAttributes(Worksheet $docSheet, $column, array $columnAttributes)
-    {
-        if (isset($columnAttributes['xfIndex'])) {
-            $docSheet->getColumnDimension($column)->setXfIndex($columnAttributes['xfIndex']);
-        }
-        if (isset($columnAttributes['visible'])) {
-            $docSheet->getColumnDimension($column)->setVisible($columnAttributes['visible']);
-        }
-        if (isset($columnAttributes['collapsed'])) {
-            $docSheet->getColumnDimension($column)->setCollapsed($columnAttributes['collapsed']);
-        }
-        if (isset($columnAttributes['outlineLevel'])) {
-            $docSheet->getColumnDimension($column)->setOutlineLevel($columnAttributes['outlineLevel']);
-        }
-        if (isset($columnAttributes['width'])) {
-            $docSheet->getColumnDimension($column)->setWidth($columnAttributes['width']);
-        }
-    }
-
-    /**
-     * Set Worksheet row attributes by attributes array passed.
-     *
-     * @param Worksheet $docSheet
-     * @param int $row 1, 2, 3, ... 99, ...
-     * @param array $rowAttributes array of attributes (indexes are attribute name, values are value)
-     *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
-     */
-    private function setRowAttributes(Worksheet $docSheet, $row, array $rowAttributes)
-    {
-        if (isset($rowAttributes['xfIndex'])) {
-            $docSheet->getRowDimension($row)->setXfIndex($rowAttributes['xfIndex']);
-        }
-        if (isset($rowAttributes['visible'])) {
-            $docSheet->getRowDimension($row)->setVisible($rowAttributes['visible']);
-        }
-        if (isset($rowAttributes['collapsed'])) {
-            $docSheet->getRowDimension($row)->setCollapsed($rowAttributes['collapsed']);
-        }
-        if (isset($rowAttributes['outlineLevel'])) {
-            $docSheet->getRowDimension($row)->setOutlineLevel($rowAttributes['outlineLevel']);
-        }
-        if (isset($rowAttributes['rowHeight'])) {
-            $docSheet->getRowDimension($row)->setRowHeight($rowAttributes['rowHeight']);
-        }
-    }
-
     /**
      * Loads Spreadsheet from file.
      *
@@ -456,70 +409,20 @@ class Xlsx extends BaseReader
             'SimpleXMLElement',
             Settings::getLibXmlLoaderOptions()
         );
+
+        $propertyReader = new PropertyReader($this->securityScanner, $excel->getProperties());
         foreach ($rels->Relationship as $rel) {
             switch ($rel['Type']) {
                 case 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties':
-                    $xmlCore = simplexml_load_string(
-                        $this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}")),
-                        'SimpleXMLElement',
-                        Settings::getLibXmlLoaderOptions()
-                    );
-                    if (is_object($xmlCore)) {
-                        $xmlCore->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
-                        $xmlCore->registerXPathNamespace('dcterms', 'http://purl.org/dc/terms/');
-                        $xmlCore->registerXPathNamespace('cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
-                        $docProps = $excel->getProperties();
-                        $docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
-                        $docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
-                        $docProps->setCreated(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:created')))); //! respect xsi:type
-                        $docProps->setModified(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:modified')))); //! respect xsi:type
-                        $docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
-                        $docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
-                        $docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
-                        $docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
-                        $docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
-                    }
+                    $propertyReader->readCoreProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
 
                     break;
                 case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties':
-                    $xmlCore = simplexml_load_string(
-                        $this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}")),
-                        'SimpleXMLElement',
-                        Settings::getLibXmlLoaderOptions()
-                    );
-                    if (is_object($xmlCore)) {
-                        $docProps = $excel->getProperties();
-                        if (isset($xmlCore->Company)) {
-                            $docProps->setCompany((string) $xmlCore->Company);
-                        }
-                        if (isset($xmlCore->Manager)) {
-                            $docProps->setManager((string) $xmlCore->Manager);
-                        }
-                    }
+                    $propertyReader->readExtendedProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
 
                     break;
                 case 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties':
-                    $xmlCore = simplexml_load_string(
-                        $this->securityScanner->scan($this->getFromZipArchive($zip, "{$rel['Target']}")),
-                        'SimpleXMLElement',
-                        Settings::getLibXmlLoaderOptions()
-                    );
-                    if (is_object($xmlCore)) {
-                        $docProps = $excel->getProperties();
-                        /** @var SimpleXMLElement $xmlProperty */
-                        foreach ($xmlCore as $xmlProperty) {
-                            $cellDataOfficeAttributes = $xmlProperty->attributes();
-                            if (isset($cellDataOfficeAttributes['name'])) {
-                                $propertyName = (string) $cellDataOfficeAttributes['name'];
-                                $cellDataOfficeChildren = $xmlProperty->children('http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
-                                $attributeType = $cellDataOfficeChildren->getName();
-                                $attributeValue = (string) $cellDataOfficeChildren->{$attributeType};
-                                $attributeValue = Properties::convertProperty($attributeValue, $attributeType);
-                                $attributeType = Properties::convertPropertyType($attributeType);
-                                $docProps->setCustomProperty($propertyName, $attributeValue, $attributeType);
-                            }
-                        }
-                    }
+                    $propertyReader->readCustomProperties($this->getFromZipArchive($zip, "{$rel['Target']}"));
 
                     break;
                 //Ribbon
@@ -586,8 +489,7 @@ class Xlsx extends BaseReader
                             }
                         }
                     }
-                    $styles = [];
-                    $cellStyles = [];
+
                     $xpath = self::getArrayItem($relsWorkbook->xpath("rel:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles']"));
                     //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
                     $xmlStyles = simplexml_load_string(
@@ -595,6 +497,9 @@ class Xlsx extends BaseReader
                         'SimpleXMLElement',
                         Settings::getLibXmlLoaderOptions()
                     );
+
+                    $styles = [];
+                    $cellStyles = [];
                     $numFmts = null;
                     if ($xmlStyles && $xmlStyles->numFmts[0]) {
                         $numFmts = $xmlStyles->numFmts[0];
@@ -674,31 +579,10 @@ class Xlsx extends BaseReader
                         }
                     }
 
-                    $dxfs = [];
-                    if (!$this->readDataOnly && $xmlStyles) {
-                        //    Conditional Styles
-                        if ($xmlStyles->dxfs) {
-                            foreach ($xmlStyles->dxfs->dxf as $dxf) {
-                                $style = new Style(false, true);
-                                self::readStyle($style, $dxf);
-                                $dxfs[] = $style;
-                            }
-                        }
-                        //    Cell Styles
-                        if ($xmlStyles->cellStyles) {
-                            foreach ($xmlStyles->cellStyles->cellStyle as $cellStyle) {
-                                if ((int) ($cellStyle['builtinId']) == 0) {
-                                    if (isset($cellStyles[(int) ($cellStyle['xfId'])])) {
-                                        // Set default style
-                                        $style = new Style();
-                                        self::readStyle($style, $cellStyles[(int) ($cellStyle['xfId'])]);
-
-                                        // normal style, currently not using it for anything
-                                    }
-                                }
-                            }
-                        }
-                    }
+                    $styleReader = new Styles($xmlStyles);
+                    $styleReader->setStyleBaseData(self::$theme, $styles, $cellStyles);
+                    $dxfs = $styleReader->dxfs($this->readDataOnly);
+                    $styles = $styleReader->styles();
 
                     //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main"
                     $xmlWorkbook = simplexml_load_string(
@@ -765,134 +649,19 @@ class Xlsx extends BaseReader
                                 $docSheet->setSheetState((string) $eleSheet['state']);
                             }
 
-                            if (isset($xmlSheet->sheetViews, $xmlSheet->sheetViews->sheetView)) {
-                                if (isset($xmlSheet->sheetViews->sheetView['zoomScale'])) {
-                                    $zoomScale = (int) ($xmlSheet->sheetViews->sheetView['zoomScale']);
-                                    if ($zoomScale <= 0) {
-                                        // setZoomScale will throw an Exception if the scale is less than or equals 0
-                                        // that is OK when manually creating documents, but we should be able to read all documents
-                                        $zoomScale = 100;
-                                    }
-
-                                    $docSheet->getSheetView()->setZoomScale($zoomScale);
-                                }
-                                if (isset($xmlSheet->sheetViews->sheetView['zoomScaleNormal'])) {
-                                    $zoomScaleNormal = (int) ($xmlSheet->sheetViews->sheetView['zoomScaleNormal']);
-                                    if ($zoomScaleNormal <= 0) {
-                                        // setZoomScaleNormal will throw an Exception if the scale is less than or equals 0
-                                        // that is OK when manually creating documents, but we should be able to read all documents
-                                        $zoomScaleNormal = 100;
-                                    }
-
-                                    $docSheet->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
-                                }
-                                if (isset($xmlSheet->sheetViews->sheetView['view'])) {
-                                    $docSheet->getSheetView()->setView((string) $xmlSheet->sheetViews->sheetView['view']);
-                                }
-                                if (isset($xmlSheet->sheetViews->sheetView['showGridLines'])) {
-                                    $docSheet->setShowGridLines(self::boolean((string) $xmlSheet->sheetViews->sheetView['showGridLines']));
-                                }
-                                if (isset($xmlSheet->sheetViews->sheetView['showRowColHeaders'])) {
-                                    $docSheet->setShowRowColHeaders(self::boolean((string) $xmlSheet->sheetViews->sheetView['showRowColHeaders']));
-                                }
-                                if (isset($xmlSheet->sheetViews->sheetView['rightToLeft'])) {
-                                    $docSheet->setRightToLeft(self::boolean((string) $xmlSheet->sheetViews->sheetView['rightToLeft']));
-                                }
-                                if (isset($xmlSheet->sheetViews->sheetView->pane)) {
-                                    $xSplit = 0;
-                                    $ySplit = 0;
-                                    $topLeftCell = null;
-
-                                    if (isset($xmlSheet->sheetViews->sheetView->pane['xSplit'])) {
-                                        $xSplit = (int) ($xmlSheet->sheetViews->sheetView->pane['xSplit']);
-                                    }
-
-                                    if (isset($xmlSheet->sheetViews->sheetView->pane['ySplit'])) {
-                                        $ySplit = (int) ($xmlSheet->sheetViews->sheetView->pane['ySplit']);
-                                    }
-
-                                    if (isset($xmlSheet->sheetViews->sheetView->pane['topLeftCell'])) {
-                                        $topLeftCell = (string) $xmlSheet->sheetViews->sheetView->pane['topLeftCell'];
-                                    }
-
-                                    $docSheet->freezePane(Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1), $topLeftCell);
-                                }
-
-                                if (isset($xmlSheet->sheetViews->sheetView->selection)) {
-                                    if (isset($xmlSheet->sheetViews->sheetView->selection['sqref'])) {
-                                        $sqref = (string) $xmlSheet->sheetViews->sheetView->selection['sqref'];
-                                        $sqref = explode(' ', $sqref);
-                                        $sqref = $sqref[0];
-                                        $docSheet->setSelectedCells($sqref);
-                                    }
-                                }
-                            }
-
-                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->tabColor)) {
-                                if (isset($xmlSheet->sheetPr->tabColor['rgb'])) {
-                                    $docSheet->getTabColor()->setARGB((string) $xmlSheet->sheetPr->tabColor['rgb']);
-                                }
-                            }
-                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr['codeName'])) {
-                                $docSheet->setCodeName((string) $xmlSheet->sheetPr['codeName'], false);
-                            }
-                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->outlinePr)) {
-                                if (isset($xmlSheet->sheetPr->outlinePr['summaryRight']) &&
-                                    !self::boolean((string) $xmlSheet->sheetPr->outlinePr['summaryRight'])) {
-                                    $docSheet->setShowSummaryRight(false);
-                                } else {
-                                    $docSheet->setShowSummaryRight(true);
+                            if ($xmlSheet) {
+                                if (isset($xmlSheet->sheetViews, $xmlSheet->sheetViews->sheetView)) {
+                                    $sheetViews = new SheetViews($xmlSheet->sheetViews->sheetView, $docSheet);
+                                    $sheetViews->load();
                                 }
 
-                                if (isset($xmlSheet->sheetPr->outlinePr['summaryBelow']) &&
-                                    !self::boolean((string) $xmlSheet->sheetPr->outlinePr['summaryBelow'])) {
-                                    $docSheet->setShowSummaryBelow(false);
-                                } else {
-                                    $docSheet->setShowSummaryBelow(true);
-                                }
-                            }
+                                $sheetViewOptions = new SheetViewOptions($docSheet, $xmlSheet);
+                                $sheetViewOptions->load($this->getReadDataOnly());
 
-                            if (isset($xmlSheet->sheetPr, $xmlSheet->sheetPr->pageSetUpPr)) {
-                                if (isset($xmlSheet->sheetPr->pageSetUpPr['fitToPage']) &&
-                                    !self::boolean((string) $xmlSheet->sheetPr->pageSetUpPr['fitToPage'])) {
-                                    $docSheet->getPageSetup()->setFitToPage(false);
-                                } else {
-                                    $docSheet->getPageSetup()->setFitToPage(true);
-                                }
+                                (new ColumnAndRowAttributes($docSheet, $xmlSheet))
+                                    ->load($this->getReadFilter(), $this->getReadDataOnly());
                             }
 
-                            if (isset($xmlSheet->sheetFormatPr)) {
-                                if (isset($xmlSheet->sheetFormatPr['customHeight']) &&
-                                    self::boolean((string) $xmlSheet->sheetFormatPr['customHeight']) &&
-                                    isset($xmlSheet->sheetFormatPr['defaultRowHeight'])) {
-                                    $docSheet->getDefaultRowDimension()->setRowHeight((float) $xmlSheet->sheetFormatPr['defaultRowHeight']);
-                                }
-                                if (isset($xmlSheet->sheetFormatPr['defaultColWidth'])) {
-                                    $docSheet->getDefaultColumnDimension()->setWidth((float) $xmlSheet->sheetFormatPr['defaultColWidth']);
-                                }
-                                if (isset($xmlSheet->sheetFormatPr['zeroHeight']) &&
-                                    ((string) $xmlSheet->sheetFormatPr['zeroHeight'] == '1')) {
-                                    $docSheet->getDefaultRowDimension()->setZeroHeight(true);
-                                }
-                            }
-
-                            if (isset($xmlSheet->printOptions) && !$this->readDataOnly) {
-                                if (self::boolean((string) $xmlSheet->printOptions['gridLinesSet'])) {
-                                    $docSheet->setShowGridlines(true);
-                                }
-                                if (self::boolean((string) $xmlSheet->printOptions['gridLines'])) {
-                                    $docSheet->setPrintGridlines(true);
-                                }
-                                if (self::boolean((string) $xmlSheet->printOptions['horizontalCentered'])) {
-                                    $docSheet->getPageSetup()->setHorizontalCentered(true);
-                                }
-                                if (self::boolean((string) $xmlSheet->printOptions['verticalCentered'])) {
-                                    $docSheet->getPageSetup()->setVerticalCentered(true);
-                                }
-                            }
-
-                            $this->readColumnsAndRowsAttributes($xmlSheet, $docSheet);
-
                             if ($xmlSheet && $xmlSheet->sheetData && $xmlSheet->sheetData->row) {
                                 $cIndex = 1; // Cell Start from 1
                                 foreach ($xmlSheet->sheetData->row as $row) {
@@ -972,38 +741,39 @@ class Xlsx extends BaseReader
                                                 break;
                                         }
 
-                                        // Check for numeric values
-                                        if (is_numeric($value) && $cellDataType != 's') {
-                                            if ($value == (int) $value) {
-                                                $value = (int) $value;
-                                            } elseif ($value == (float) $value) {
-                                                $value = (float) $value;
-                                            } elseif ($value == (float) $value) {
-                                                $value = (float) $value;
+                                        // read empty cells or the cells are not empty
+                                        if ($this->readEmptyCells || ($value !== null && $value !== '')) {
+                                            // Check for numeric values
+                                            if (is_numeric($value) && $cellDataType != 's') {
+                                                if ($value == (int) $value) {
+                                                    $value = (int) $value;
+                                                } elseif ($value == (float) $value) {
+                                                    $value = (float) $value;
+                                                }
                                             }
-                                        }
 
-                                        // Rich text?
-                                        if ($value instanceof RichText && $this->readDataOnly) {
-                                            $value = $value->getPlainText();
-                                        }
+                                            // Rich text?
+                                            if ($value instanceof RichText && $this->readDataOnly) {
+                                                $value = $value->getPlainText();
+                                            }
 
-                                        $cell = $docSheet->getCell($r);
-                                        // Assign value
-                                        if ($cellDataType != '') {
-                                            $cell->setValueExplicit($value, $cellDataType);
-                                        } else {
-                                            $cell->setValue($value);
-                                        }
-                                        if ($calculatedValue !== null) {
-                                            $cell->setCalculatedValue($calculatedValue);
-                                        }
+                                            $cell = $docSheet->getCell($r);
+                                            // Assign value
+                                            if ($cellDataType != '') {
+                                                $cell->setValueExplicit($value, $cellDataType);
+                                            } else {
+                                                $cell->setValue($value);
+                                            }
+                                            if ($calculatedValue !== null) {
+                                                $cell->setCalculatedValue($calculatedValue);
+                                            }
 
-                                        // Style information?
-                                        if ($c['s'] && !$this->readDataOnly) {
-                                            // no style index means 0, it seems
-                                            $cell->setXfIndex(isset($styles[(int) ($c['s'])]) ?
-                                                (int) ($c['s']) : 0);
+                                            // Style information?
+                                            if ($c['s'] && !$this->readDataOnly) {
+                                                // no style index means 0, it seems
+                                                $cell->setXfIndex(isset($styles[(int) ($c['s'])]) ?
+                                                    (int) ($c['s']) : 0);
+                                            }
                                         }
                                         $rowIndex += 1;
                                     }
@@ -1011,49 +781,8 @@ class Xlsx extends BaseReader
                                 }
                             }
 
-                            $conditionals = [];
                             if (!$this->readDataOnly && $xmlSheet && $xmlSheet->conditionalFormatting) {
-                                foreach ($xmlSheet->conditionalFormatting as $conditional) {
-                                    foreach ($conditional->cfRule as $cfRule) {
-                                        if (((string) $cfRule['type'] == Conditional::CONDITION_NONE || (string) $cfRule['type'] == Conditional::CONDITION_CELLIS || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION) && isset($dxfs[(int) ($cfRule['dxfId'])])) {
-                                            $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
-                                        }
-                                    }
-                                }
-
-                                foreach ($conditionals as $ref => $cfRules) {
-                                    ksort($cfRules);
-                                    $conditionalStyles = [];
-                                    foreach ($cfRules as $cfRule) {
-                                        $objConditional = new Conditional();
-                                        $objConditional->setConditionType((string) $cfRule['type']);
-                                        $objConditional->setOperatorType((string) $cfRule['operator']);
-
-                                        if ((string) $cfRule['text'] != '') {
-                                            $objConditional->setText((string) $cfRule['text']);
-                                        }
-
-                                        if (isset($cfRule['stopIfTrue']) && (int) $cfRule['stopIfTrue'] === 1) {
-                                            $objConditional->setStopIfTrue(true);
-                                        }
-
-                                        if (count($cfRule->formula) > 1) {
-                                            foreach ($cfRule->formula as $formula) {
-                                                $objConditional->addCondition((string) $formula);
-                                            }
-                                        } else {
-                                            $objConditional->addCondition((string) $cfRule->formula);
-                                        }
-                                        $objConditional->setStyle(clone $dxfs[(int) ($cfRule['dxfId'])]);
-                                        $conditionalStyles[] = $objConditional;
-                                    }
-
-                                    // Extract all cell references in $ref
-                                    $cellBlocks = explode(' ', str_replace('$', '', strtoupper($ref)));
-                                    foreach ($cellBlocks as $cellBlock) {
-                                        $docSheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
-                                    }
-                                }
+                                (new ConditionalStyles($docSheet, $xmlSheet, $dxfs))->load();
                             }
 
                             $aKeys = ['sheet', 'objects', 'scenarios', 'formatCells', 'formatColumns', 'formatRows', 'insertColumns', 'insertRows', 'insertHyperlinks', 'deleteColumns', 'deleteRows', 'selectLockedCells', 'sort', 'autoFilter', 'pivotTables', 'selectUnlockedCells'];
@@ -1074,103 +803,7 @@ class Xlsx extends BaseReader
                             }
 
                             if ($xmlSheet && $xmlSheet->autoFilter && !$this->readDataOnly) {
-                                $autoFilterRange = (string) $xmlSheet->autoFilter['ref'];
-                                if (strpos($autoFilterRange, ':') !== false) {
-                                    $autoFilter = $docSheet->getAutoFilter();
-                                    $autoFilter->setRange($autoFilterRange);
-
-                                    foreach ($xmlSheet->autoFilter->filterColumn as $filterColumn) {
-                                        $column = $autoFilter->getColumnByOffset((int) $filterColumn['colId']);
-                                        //    Check for standard filters
-                                        if ($filterColumn->filters) {
-                                            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
-                                            $filters = $filterColumn->filters;
-                                            if ((isset($filters['blank'])) && ($filters['blank'] == 1)) {
-                                                //    Operator is undefined, but always treated as EQUAL
-                                                $column->createRule()->setRule(null, '')->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_FILTER);
-                                            }
-                                            //    Standard filters are always an OR join, so no join rule needs to be set
-                                            //    Entries can be either filter elements
-                                            foreach ($filters->filter as $filterRule) {
-                                                //    Operator is undefined, but always treated as EQUAL
-                                                $column->createRule()->setRule(null, (string) $filterRule['val'])->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_FILTER);
-                                            }
-                                            //    Or Date Group elements
-                                            foreach ($filters->dateGroupItem as $dateGroupItem) {
-                                                //    Operator is undefined, but always treated as EQUAL
-                                                $column->createRule()->setRule(
-                                                    null,
-                                                    [
-                                                        'year' => (string) $dateGroupItem['year'],
-                                                        'month' => (string) $dateGroupItem['month'],
-                                                        'day' => (string) $dateGroupItem['day'],
-                                                        'hour' => (string) $dateGroupItem['hour'],
-                                                        'minute' => (string) $dateGroupItem['minute'],
-                                                        'second' => (string) $dateGroupItem['second'],
-                                                    ],
-                                                    (string) $dateGroupItem['dateTimeGrouping']
-                                                )
-                                                    ->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP);
-                                            }
-                                        }
-                                        //    Check for custom filters
-                                        if ($filterColumn->customFilters) {
-                                            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
-                                            $customFilters = $filterColumn->customFilters;
-                                            //    Custom filters can an AND or an OR join;
-                                            //        and there should only ever be one or two entries
-                                            if ((isset($customFilters['and'])) && ($customFilters['and'] == 1)) {
-                                                $column->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
-                                            }
-                                            foreach ($customFilters->customFilter as $filterRule) {
-                                                $column->createRule()->setRule(
-                                                    (string) $filterRule['operator'],
-                                                    (string) $filterRule['val']
-                                                )
-                                                    ->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
-                                            }
-                                        }
-                                        //    Check for dynamic filters
-                                        if ($filterColumn->dynamicFilter) {
-                                            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
-                                            //    We should only ever have one dynamic filter
-                                            foreach ($filterColumn->dynamicFilter as $filterRule) {
-                                                //    Operator is undefined, but always treated as EQUAL
-                                                $column->createRule()->setRule(
-                                                    null,
-                                                    (string) $filterRule['val'],
-                                                    (string) $filterRule['type']
-                                                )
-                                                    ->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
-                                                if (isset($filterRule['val'])) {
-                                                    $column->setAttribute('val', (string) $filterRule['val']);
-                                                }
-                                                if (isset($filterRule['maxVal'])) {
-                                                    $column->setAttribute('maxVal', (string) $filterRule['maxVal']);
-                                                }
-                                            }
-                                        }
-                                        //    Check for dynamic filters
-                                        if ($filterColumn->top10) {
-                                            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER);
-                                            //    We should only ever have one top10 filter
-                                            foreach ($filterColumn->top10 as $filterRule) {
-                                                $column->createRule()->setRule(
-                                                    (((isset($filterRule['percent'])) && ($filterRule['percent'] == 1))
-                                                        ? Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
-                                                        : Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
-                                                    ),
-                                                    (string) $filterRule['val'],
-                                                    (((isset($filterRule['top'])) && ($filterRule['top'] == 1))
-                                                        ? Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
-                                                        : Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
-                                                    )
-                                                )
-                                                    ->setRuleType(Column\Rule::AUTOFILTER_RULETYPE_TOPTENFILTER);
-                                            }
-                                        }
-                                    }
-                                }
+                                (new AutoFilter($docSheet, $xmlSheet))->load();
                             }
 
                             if ($xmlSheet && $xmlSheet->mergeCells && $xmlSheet->mergeCells->mergeCell && !$this->readDataOnly) {
@@ -1182,124 +815,12 @@ class Xlsx extends BaseReader
                                 }
                             }
 
-                            if ($xmlSheet && $xmlSheet->pageMargins && !$this->readDataOnly) {
-                                $docPageMargins = $docSheet->getPageMargins();
-                                $docPageMargins->setLeft((float) ($xmlSheet->pageMargins['left']));
-                                $docPageMargins->setRight((float) ($xmlSheet->pageMargins['right']));
-                                $docPageMargins->setTop((float) ($xmlSheet->pageMargins['top']));
-                                $docPageMargins->setBottom((float) ($xmlSheet->pageMargins['bottom']));
-                                $docPageMargins->setHeader((float) ($xmlSheet->pageMargins['header']));
-                                $docPageMargins->setFooter((float) ($xmlSheet->pageMargins['footer']));
-                            }
-
-                            if ($xmlSheet && $xmlSheet->pageSetup && !$this->readDataOnly) {
-                                $docPageSetup = $docSheet->getPageSetup();
-
-                                if (isset($xmlSheet->pageSetup['orientation'])) {
-                                    $docPageSetup->setOrientation((string) $xmlSheet->pageSetup['orientation']);
-                                }
-                                if (isset($xmlSheet->pageSetup['paperSize'])) {
-                                    $docPageSetup->setPaperSize((int) ($xmlSheet->pageSetup['paperSize']));
-                                }
-                                if (isset($xmlSheet->pageSetup['scale'])) {
-                                    $docPageSetup->setScale((int) ($xmlSheet->pageSetup['scale']), false);
-                                }
-                                if (isset($xmlSheet->pageSetup['fitToHeight']) && (int) ($xmlSheet->pageSetup['fitToHeight']) >= 0) {
-                                    $docPageSetup->setFitToHeight((int) ($xmlSheet->pageSetup['fitToHeight']), false);
-                                }
-                                if (isset($xmlSheet->pageSetup['fitToWidth']) && (int) ($xmlSheet->pageSetup['fitToWidth']) >= 0) {
-                                    $docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
-                                }
-                                if (isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) &&
-                                    self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) {
-                                    $docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
-                                }
-
-                                $relAttributes = $xmlSheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
-                                if (isset($relAttributes['id'])) {
-                                    $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['pageSetupRelId'] = (string) $relAttributes['id'];
-                                }
-                            }
-
-                            if ($xmlSheet && $xmlSheet->headerFooter && !$this->readDataOnly) {
-                                $docHeaderFooter = $docSheet->getHeaderFooter();
-
-                                if (isset($xmlSheet->headerFooter['differentOddEven']) &&
-                                    self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])) {
-                                    $docHeaderFooter->setDifferentOddEven(true);
-                                } else {
-                                    $docHeaderFooter->setDifferentOddEven(false);
-                                }
-                                if (isset($xmlSheet->headerFooter['differentFirst']) &&
-                                    self::boolean((string) $xmlSheet->headerFooter['differentFirst'])) {
-                                    $docHeaderFooter->setDifferentFirst(true);
-                                } else {
-                                    $docHeaderFooter->setDifferentFirst(false);
-                                }
-                                if (isset($xmlSheet->headerFooter['scaleWithDoc']) &&
-                                    !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])) {
-                                    $docHeaderFooter->setScaleWithDocument(false);
-                                } else {
-                                    $docHeaderFooter->setScaleWithDocument(true);
-                                }
-                                if (isset($xmlSheet->headerFooter['alignWithMargins']) &&
-                                    !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])) {
-                                    $docHeaderFooter->setAlignWithMargins(false);
-                                } else {
-                                    $docHeaderFooter->setAlignWithMargins(true);
-                                }
-
-                                $docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
-                                $docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
-                                $docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
-                                $docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
-                                $docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
-                                $docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
-                            }
-
-                            if ($xmlSheet && $xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk && !$this->readDataOnly) {
-                                foreach ($xmlSheet->rowBreaks->brk as $brk) {
-                                    if ($brk['man']) {
-                                        $docSheet->setBreak("A$brk[id]", Worksheet::BREAK_ROW);
-                                    }
-                                }
-                            }
-                            if ($xmlSheet && $xmlSheet->colBreaks && $xmlSheet->colBreaks->brk && !$this->readDataOnly) {
-                                foreach ($xmlSheet->colBreaks->brk as $brk) {
-                                    if ($brk['man']) {
-                                        $docSheet->setBreak(Coordinate::stringFromColumnIndex((string) $brk['id'] + 1) . '1', Worksheet::BREAK_COLUMN);
-                                    }
-                                }
+                            if ($xmlSheet && !$this->readDataOnly) {
+                                $unparsedLoadedData = (new PageSetup($docSheet, $xmlSheet))->load($unparsedLoadedData);
                             }
 
                             if ($xmlSheet && $xmlSheet->dataValidations && !$this->readDataOnly) {
-                                foreach ($xmlSheet->dataValidations->dataValidation as $dataValidation) {
-                                    // Uppercase coordinate
-                                    $range = strtoupper($dataValidation['sqref']);
-                                    $rangeSet = explode(' ', $range);
-                                    foreach ($rangeSet as $range) {
-                                        $stRange = $docSheet->shrinkRangeToFit($range);
-
-                                        // Extract all cell references in $range
-                                        foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $reference) {
-                                            // Create validation
-                                            $docValidation = $docSheet->getCell($reference)->getDataValidation();
-                                            $docValidation->setType((string) $dataValidation['type']);
-                                            $docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
-                                            $docValidation->setOperator((string) $dataValidation['operator']);
-                                            $docValidation->setAllowBlank($dataValidation['allowBlank'] != 0);
-                                            $docValidation->setShowDropDown($dataValidation['showDropDown'] == 0);
-                                            $docValidation->setShowInputMessage($dataValidation['showInputMessage'] != 0);
-                                            $docValidation->setShowErrorMessage($dataValidation['showErrorMessage'] != 0);
-                                            $docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
-                                            $docValidation->setError((string) $dataValidation['error']);
-                                            $docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
-                                            $docValidation->setPrompt((string) $dataValidation['prompt']);
-                                            $docValidation->setFormula1((string) $dataValidation->formula1);
-                                            $docValidation->setFormula2((string) $dataValidation->formula2);
-                                        }
-                                    }
-                                }
+                                (new DataValidations($docSheet, $xmlSheet))->load();
                             }
 
                             // unparsed sheet AlternateContent
@@ -1313,50 +834,25 @@ class Xlsx extends BaseReader
                             }
 
                             // Add hyperlinks
-                            $hyperlinks = [];
                             if (!$this->readDataOnly) {
+                                $hyperlinkReader = new Hyperlinks($docSheet);
                                 // Locate hyperlink relations
-                                if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) {
+                                $relationsFileName = dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels';
+                                if ($zip->locateName($relationsFileName)) {
                                     //~ http://schemas.openxmlformats.org/package/2006/relationships"
                                     $relsWorksheet = simplexml_load_string(
                                         $this->securityScanner->scan(
-                                            $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')
+                                            $this->getFromZipArchive($zip, $relationsFileName)
                                         ),
                                         'SimpleXMLElement',
                                         Settings::getLibXmlLoaderOptions()
                                     );
-                                    foreach ($relsWorksheet->Relationship as $ele) {
-                                        if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
-                                            $hyperlinks[(string) $ele['Id']] = (string) $ele['Target'];
-                                        }
-                                    }
+                                    $hyperlinkReader->readHyperlinks($relsWorksheet);
                                 }
 
                                 // Loop through hyperlinks
                                 if ($xmlSheet && $xmlSheet->hyperlinks) {
-                                    /** @var SimpleXMLElement $hyperlink */
-                                    foreach ($xmlSheet->hyperlinks->hyperlink as $hyperlink) {
-                                        // Link url
-                                        $linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
-
-                                        foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
-                                            $cell = $docSheet->getCell($cellReference);
-                                            if (isset($linkRel['id'])) {
-                                                $hyperlinkUrl = $hyperlinks[(string) $linkRel['id']];
-                                                if (isset($hyperlink['location'])) {
-                                                    $hyperlinkUrl .= '#' . (string) $hyperlink['location'];
-                                                }
-                                                $cell->getHyperlink()->setUrl($hyperlinkUrl);
-                                            } elseif (isset($hyperlink['location'])) {
-                                                $cell->getHyperlink()->setUrl('sheet://' . (string) $hyperlink['location']);
-                                            }
-
-                                            // Tooltip
-                                            if (isset($hyperlink['tooltip'])) {
-                                                $cell->getHyperlink()->setTooltip((string) $hyperlink['tooltip']);
-                                            }
-                                        }
-                                    }
+                                    $hyperlinkReader->setHyperlinks($xmlSheet->hyperlinks);
                                 }
                             }
 
@@ -1418,12 +914,18 @@ class Xlsx extends BaseReader
                                 foreach ($vmlComments as $relName => $relPath) {
                                     // Load VML comments file
                                     $relPath = File::realpath(dirname("$dir/$fileWorksheet") . '/' . $relPath);
-                                    $vmlCommentsFile = simplexml_load_string(
-                                        $this->securityScanner->scan($this->getFromZipArchive($zip, $relPath)),
-                                        'SimpleXMLElement',
-                                        Settings::getLibXmlLoaderOptions()
-                                    );
-                                    $vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+
+                                    try {
+                                        $vmlCommentsFile = simplexml_load_string(
+                                            $this->securityScanner->scan($this->getFromZipArchive($zip, $relPath)),
+                                            'SimpleXMLElement',
+                                            Settings::getLibXmlLoaderOptions()
+                                        );
+                                        $vmlCommentsFile->registerXPathNamespace('v', 'urn:schemas-microsoft-com:vml');
+                                    } catch (\Throwable $ex) {
+                                        //Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData
+                                        continue;
+                                    }
 
                                     $shapes = $vmlCommentsFile->xpath('//v:shape');
                                     foreach ($shapes as $shape) {
@@ -1597,8 +1099,10 @@ class Xlsx extends BaseReader
                                     }
                                 }
                                 if ($xmlSheet->drawing && !$this->readDataOnly) {
+                                    $unparsedDrawings = [];
                                     foreach ($xmlSheet->drawing as $drawing) {
-                                        $fileDrawing = $drawings[(string) self::getArrayItem($drawing->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id')];
+                                        $drawingRelId = (string) self::getArrayItem($drawing->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'), 'id');
+                                        $fileDrawing = $drawings[$drawingRelId];
                                         //~ http://schemas.openxmlformats.org/package/2006/relationships"
                                         $relsDrawing = simplexml_load_string(
                                             $this->securityScanner->scan(
@@ -1630,10 +1134,11 @@ class Xlsx extends BaseReader
                                             $this->securityScanner->scan($this->getFromZipArchive($zip, $fileDrawing)),
                                             'SimpleXMLElement',
                                             Settings::getLibXmlLoaderOptions()
-                                        )->children('http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
+                                        );
+                                        $xmlDrawingChildren = $xmlDrawing->children('http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing');
 
-                                        if ($xmlDrawing->oneCellAnchor) {
-                                            foreach ($xmlDrawing->oneCellAnchor as $oneCellAnchor) {
+                                        if ($xmlDrawingChildren->oneCellAnchor) {
+                                            foreach ($xmlDrawingChildren->oneCellAnchor as $oneCellAnchor) {
                                                 if ($oneCellAnchor->pic->blipFill) {
                                                     /** @var SimpleXMLElement $blip */
                                                     $blip = $oneCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
@@ -1667,12 +1172,13 @@ class Xlsx extends BaseReader
                                                     if ($outerShdw) {
                                                         $shadow = $objDrawing->getShadow();
                                                         $shadow->setVisible(true);
-                                                        $shadow->setBlurRadius(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
-                                                        $shadow->setDistance(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
+                                                        $shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
+                                                        $shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
                                                         $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
                                                         $shadow->setAlignment((string) self::getArrayItem($outerShdw->attributes(), 'algn'));
-                                                        $shadow->getColor()->setRGB(self::getArrayItem($outerShdw->srgbClr->attributes(), 'val'));
-                                                        $shadow->setAlpha(self::getArrayItem($outerShdw->srgbClr->alpha->attributes(), 'val') / 1000);
+                                                        $clr = isset($outerShdw->srgbClr) ? $outerShdw->srgbClr : $outerShdw->prstClr;
+                                                        $shadow->getColor()->setRGB(self::getArrayItem($clr->attributes(), 'val'));
+                                                        $shadow->setAlpha(self::getArrayItem($clr->alpha->attributes(), 'val') / 1000);
                                                     }
 
                                                     $this->readHyperLinkDrawing($objDrawing, $oneCellAnchor, $hyperlinks);
@@ -1688,8 +1194,8 @@ class Xlsx extends BaseReader
                                                 }
                                             }
                                         }
-                                        if ($xmlDrawing->twoCellAnchor) {
-                                            foreach ($xmlDrawing->twoCellAnchor as $twoCellAnchor) {
+                                        if ($xmlDrawingChildren->twoCellAnchor) {
+                                            foreach ($xmlDrawingChildren->twoCellAnchor as $twoCellAnchor) {
                                                 if ($twoCellAnchor->pic->blipFill) {
                                                     $blip = $twoCellAnchor->pic->blipFill->children('http://schemas.openxmlformats.org/drawingml/2006/main')->blip;
                                                     $xfrm = $twoCellAnchor->pic->spPr->children('http://schemas.openxmlformats.org/drawingml/2006/main')->xfrm;
@@ -1719,12 +1225,13 @@ class Xlsx extends BaseReader
                                                     if ($outerShdw) {
                                                         $shadow = $objDrawing->getShadow();
                                                         $shadow->setVisible(true);
-                                                        $shadow->setBlurRadius(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
-                                                        $shadow->setDistance(Drawing::EMUTopixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
+                                                        $shadow->setBlurRadius(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'blurRad')));
+                                                        $shadow->setDistance(Drawing::EMUToPixels(self::getArrayItem($outerShdw->attributes(), 'dist')));
                                                         $shadow->setDirection(Drawing::angleToDegrees(self::getArrayItem($outerShdw->attributes(), 'dir')));
                                                         $shadow->setAlignment((string) self::getArrayItem($outerShdw->attributes(), 'algn'));
-                                                        $shadow->getColor()->setRGB(self::getArrayItem($outerShdw->srgbClr->attributes(), 'val'));
-                                                        $shadow->setAlpha(self::getArrayItem($outerShdw->srgbClr->alpha->attributes(), 'val') / 1000);
+                                                        $clr = isset($outerShdw->srgbClr) ? $outerShdw->srgbClr : $outerShdw->prstClr;
+                                                        $shadow->getColor()->setRGB(self::getArrayItem($clr->attributes(), 'val'));
+                                                        $shadow->setAlpha(self::getArrayItem($clr->alpha->attributes(), 'val') / 1000);
                                                     }
 
                                                     $this->readHyperLinkDrawing($objDrawing, $twoCellAnchor, $hyperlinks);
@@ -1754,13 +1261,21 @@ class Xlsx extends BaseReader
                                                 }
                                             }
                                         }
+                                        if ($relsDrawing === false && $xmlDrawing->count() == 0) {
+                                            // Save Drawing without rels and children as unparsed
+                                            $unparsedDrawings[$drawingRelId] = $xmlDrawing->asXML();
+                                        }
                                     }
 
                                     // store original rId of drawing files
                                     $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'] = [];
                                     foreach ($relsWorksheet->Relationship as $ele) {
                                         if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing') {
-                                            $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = (string) $ele['Id'];
+                                            $drawingRelId = (string) $ele['Id'];
+                                            $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = $drawingRelId;
+                                            if (isset($unparsedDrawings[$drawingRelId])) {
+                                                $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['Drawings'][$drawingRelId] = $unparsedDrawings[$drawingRelId];
+                                            }
                                         }
                                     }
 
@@ -1905,7 +1420,7 @@ class Xlsx extends BaseReader
                                     if (strpos((string) $definedName, '!') !== false) {
                                         // Extract sheet name
                                         $extractedSheetName = Worksheet::extractSheetTitle((string) $definedName, true);
-                                        $extractedSheetName = $extractedSheetName[0];
+                                        $extractedSheetName = trim($extractedSheetName[0], "'");
 
                                         // Locate sheet
                                         $locatedSheet = $excel->getSheetByName($extractedSheetName);
@@ -2511,94 +2026,4 @@ class Xlsx extends BaseReader
 
         return (bool) $xsdBoolean;
     }
-
-    /**
-     * Read columns and rows attributes from XML and set them on the worksheet.
-     *
-     * @param SimpleXMLElement $xmlSheet
-     * @param Worksheet $docSheet
-     */
-    private function readColumnsAndRowsAttributes(SimpleXMLElement $xmlSheet, Worksheet $docSheet)
-    {
-        $columnsAttributes = [];
-        $rowsAttributes = [];
-        if (isset($xmlSheet->cols) && !$this->readDataOnly) {
-            foreach ($xmlSheet->cols->col as $col) {
-                for ($i = (int) ($col['min']); $i <= (int) ($col['max']); ++$i) {
-                    if ($col['style'] && !$this->readDataOnly) {
-                        $columnsAttributes[Coordinate::stringFromColumnIndex($i)]['xfIndex'] = (int) $col['style'];
-                    }
-                    if (self::boolean($col['hidden'])) {
-                        $columnsAttributes[Coordinate::stringFromColumnIndex($i)]['visible'] = false;
-                    }
-                    if (self::boolean($col['collapsed'])) {
-                        $columnsAttributes[Coordinate::stringFromColumnIndex($i)]['collapsed'] = true;
-                    }
-                    if ($col['outlineLevel'] > 0) {
-                        $columnsAttributes[Coordinate::stringFromColumnIndex($i)]['outlineLevel'] = (int) $col['outlineLevel'];
-                    }
-                    $columnsAttributes[Coordinate::stringFromColumnIndex($i)]['width'] = (float) $col['width'];
-
-                    if ((int) ($col['max']) == 16384) {
-                        break;
-                    }
-                }
-            }
-        }
-
-        if ($xmlSheet && $xmlSheet->sheetData && $xmlSheet->sheetData->row) {
-            foreach ($xmlSheet->sheetData->row as $row) {
-                if ($row['ht'] && !$this->readDataOnly) {
-                    $rowsAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
-                }
-                if (self::boolean($row['hidden']) && !$this->readDataOnly) {
-                    $rowsAttributes[(int) $row['r']]['visible'] = false;
-                }
-                if (self::boolean($row['collapsed'])) {
-                    $rowsAttributes[(int) $row['r']]['collapsed'] = true;
-                }
-                if ($row['outlineLevel'] > 0) {
-                    $rowsAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel'];
-                }
-                if ($row['s'] && !$this->readDataOnly) {
-                    $rowsAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s'];
-                }
-            }
-        }
-
-        $readFilter = (\get_class($this->getReadFilter()) !== DefaultReadFilter::class ? $this->getReadFilter() : null);
-
-        // set columns/rows attributes
-        $columnsAttributesSet = [];
-        $rowsAttributesSet = [];
-        foreach ($columnsAttributes as $coordColumn => $columnAttributes) {
-            if ($readFilter !== null) {
-                foreach ($rowsAttributes as $coordRow => $rowAttributes) {
-                    if (!$readFilter->readCell($coordColumn, $coordRow, $docSheet->getTitle())) {
-                        continue 2;
-                    }
-                }
-            }
-
-            if (!isset($columnsAttributesSet[$coordColumn])) {
-                $this->setColumnAttributes($docSheet, $coordColumn, $columnAttributes);
-                $columnsAttributesSet[$coordColumn] = true;
-            }
-        }
-
-        foreach ($rowsAttributes as $coordRow => $rowAttributes) {
-            if ($readFilter !== null) {
-                foreach ($columnsAttributes as $coordColumn => $columnAttributes) {
-                    if (!$readFilter->readCell($coordColumn, $coordRow, $docSheet->getTitle())) {
-                        continue 2;
-                    }
-                }
-            }
-
-            if (!isset($rowsAttributesSet[$coordRow])) {
-                $this->setRowAttributes($docSheet, $coordRow, $rowAttributes);
-                $rowsAttributesSet[$coordRow] = true;
-            }
-        }
-    }
 }

+ 144 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/AutoFilter.php

@@ -0,0 +1,144 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
+use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class AutoFilter
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    public function load()
+    {
+        $autoFilterRange = (string) $this->worksheetXml->autoFilter['ref'];
+        if (strpos($autoFilterRange, ':') !== false) {
+            $this->readAutoFilter($autoFilterRange, $this->worksheetXml);
+        }
+    }
+
+    private function readAutoFilter($autoFilterRange, $xmlSheet)
+    {
+        $autoFilter = $this->worksheet->getAutoFilter();
+        $autoFilter->setRange($autoFilterRange);
+
+        foreach ($xmlSheet->autoFilter->filterColumn as $filterColumn) {
+            $column = $autoFilter->getColumnByOffset((int) $filterColumn['colId']);
+            //    Check for standard filters
+            if ($filterColumn->filters) {
+                $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
+                $filters = $filterColumn->filters;
+                if ((isset($filters['blank'])) && ($filters['blank'] == 1)) {
+                    //    Operator is undefined, but always treated as EQUAL
+                    $column->createRule()->setRule(null, '')->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
+                }
+                //    Standard filters are always an OR join, so no join rule needs to be set
+                //    Entries can be either filter elements
+                foreach ($filters->filter as $filterRule) {
+                    //    Operator is undefined, but always treated as EQUAL
+                    $column->createRule()->setRule(null, (string) $filterRule['val'])->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
+                }
+
+                //    Or Date Group elements
+                $this->readDateRangeAutoFilter($filters, $column);
+            }
+
+            //    Check for custom filters
+            $this->readCustomAutoFilter($filterColumn, $column);
+            //    Check for dynamic filters
+            $this->readDynamicAutoFilter($filterColumn, $column);
+            //    Check for dynamic filters
+            $this->readTopTenAutoFilter($filterColumn, $column);
+        }
+    }
+
+    private function readDateRangeAutoFilter(\SimpleXMLElement $filters, Column $column)
+    {
+        foreach ($filters->dateGroupItem as $dateGroupItem) {
+            //    Operator is undefined, but always treated as EQUAL
+            $column->createRule()->setRule(
+                null,
+                [
+                    'year' => (string) $dateGroupItem['year'],
+                    'month' => (string) $dateGroupItem['month'],
+                    'day' => (string) $dateGroupItem['day'],
+                    'hour' => (string) $dateGroupItem['hour'],
+                    'minute' => (string) $dateGroupItem['minute'],
+                    'second' => (string) $dateGroupItem['second'],
+                ],
+                (string) $dateGroupItem['dateTimeGrouping']
+            )->setRuleType(Rule::AUTOFILTER_RULETYPE_DATEGROUP);
+        }
+    }
+
+    private function readCustomAutoFilter(\SimpleXMLElement $filterColumn, Column $column)
+    {
+        if ($filterColumn->customFilters) {
+            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
+            $customFilters = $filterColumn->customFilters;
+            //    Custom filters can an AND or an OR join;
+            //        and there should only ever be one or two entries
+            if ((isset($customFilters['and'])) && ($customFilters['and'] == 1)) {
+                $column->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
+            }
+            foreach ($customFilters->customFilter as $filterRule) {
+                $column->createRule()->setRule(
+                    (string) $filterRule['operator'],
+                    (string) $filterRule['val']
+                )->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
+            }
+        }
+    }
+
+    private function readDynamicAutoFilter(\SimpleXMLElement $filterColumn, Column $column)
+    {
+        if ($filterColumn->dynamicFilter) {
+            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
+            //    We should only ever have one dynamic filter
+            foreach ($filterColumn->dynamicFilter as $filterRule) {
+                //    Operator is undefined, but always treated as EQUAL
+                $column->createRule()->setRule(
+                    null,
+                    (string) $filterRule['val'],
+                    (string) $filterRule['type']
+                )->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
+                if (isset($filterRule['val'])) {
+                    $column->setAttribute('val', (string) $filterRule['val']);
+                }
+                if (isset($filterRule['maxVal'])) {
+                    $column->setAttribute('maxVal', (string) $filterRule['maxVal']);
+                }
+            }
+        }
+    }
+
+    private function readTopTenAutoFilter(\SimpleXMLElement $filterColumn, Column $column)
+    {
+        if ($filterColumn->top10) {
+            $column->setFilterType(Column::AUTOFILTER_FILTERTYPE_TOPTENFILTER);
+            //    We should only ever have one top10 filter
+            foreach ($filterColumn->top10 as $filterRule) {
+                $column->createRule()->setRule(
+                    (((isset($filterRule['percent'])) && ($filterRule['percent'] == 1))
+                        ? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT
+                        : Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BY_VALUE
+                    ),
+                    (string) $filterRule['val'],
+                    (((isset($filterRule['top'])) && ($filterRule['top'] == 1))
+                        ? Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP
+                        : Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM
+                    )
+                )->setRuleType(Rule::AUTOFILTER_RULETYPE_TOPTENFILTER);
+            }
+        }
+    }
+}

+ 19 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+class BaseParserClass
+{
+    protected static function boolean($value)
+    {
+        if (is_object($value)) {
+            $value = (string) $value;
+        }
+
+        if (is_numeric($value)) {
+            return (bool) $value;
+        }
+
+        return $value === strtolower('true');
+    }
+}

+ 204 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ColumnAndRowAttributes.php

@@ -0,0 +1,204 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class ColumnAndRowAttributes extends BaseParserClass
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml = null)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    /**
+     * Set Worksheet column attributes by attributes array passed.
+     *
+     * @param string $columnAddress A, B, ... DX, ...
+     * @param array $columnAttributes array of attributes (indexes are attribute name, values are value)
+     *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ?
+     */
+    private function setColumnAttributes($columnAddress, array $columnAttributes)
+    {
+        if (isset($columnAttributes['xfIndex'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setXfIndex($columnAttributes['xfIndex']);
+        }
+        if (isset($columnAttributes['visible'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setVisible($columnAttributes['visible']);
+        }
+        if (isset($columnAttributes['collapsed'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setCollapsed($columnAttributes['collapsed']);
+        }
+        if (isset($columnAttributes['outlineLevel'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setOutlineLevel($columnAttributes['outlineLevel']);
+        }
+        if (isset($columnAttributes['width'])) {
+            $this->worksheet->getColumnDimension($columnAddress)->setWidth($columnAttributes['width']);
+        }
+    }
+
+    /**
+     * Set Worksheet row attributes by attributes array passed.
+     *
+     * @param int $rowNumber 1, 2, 3, ... 99, ...
+     * @param array $rowAttributes array of attributes (indexes are attribute name, values are value)
+     *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
+     */
+    private function setRowAttributes($rowNumber, array $rowAttributes)
+    {
+        if (isset($rowAttributes['xfIndex'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setXfIndex($rowAttributes['xfIndex']);
+        }
+        if (isset($rowAttributes['visible'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setVisible($rowAttributes['visible']);
+        }
+        if (isset($rowAttributes['collapsed'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setCollapsed($rowAttributes['collapsed']);
+        }
+        if (isset($rowAttributes['outlineLevel'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setOutlineLevel($rowAttributes['outlineLevel']);
+        }
+        if (isset($rowAttributes['rowHeight'])) {
+            $this->worksheet->getRowDimension($rowNumber)->setRowHeight($rowAttributes['rowHeight']);
+        }
+    }
+
+    /**
+     * @param IReadFilter $readFilter
+     * @param bool $readDataOnly
+     */
+    public function load(IReadFilter $readFilter = null, $readDataOnly = false)
+    {
+        if ($this->worksheetXml === null) {
+            return;
+        }
+
+        $columnsAttributes = [];
+        $rowsAttributes = [];
+        if (isset($this->worksheetXml->cols)) {
+            $columnsAttributes = $this->readColumnAttributes($this->worksheetXml->cols, $readDataOnly);
+        }
+
+        if ($this->worksheetXml->sheetData && $this->worksheetXml->sheetData->row) {
+            $rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly);
+        }
+
+        // set columns/rows attributes
+        $columnsAttributesAreSet = [];
+        foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
+            if ($readFilter === null ||
+                !$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes)) {
+                if (!isset($columnsAttributesAreSet[$columnCoordinate])) {
+                    $this->setColumnAttributes($columnCoordinate, $columnAttributes);
+                    $columnsAttributesAreSet[$columnCoordinate] = true;
+                }
+            }
+        }
+
+        $rowsAttributesAreSet = [];
+        foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
+            if ($readFilter === null ||
+                !$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes)) {
+                if (!isset($rowsAttributesAreSet[$rowCoordinate])) {
+                    $this->setRowAttributes($rowCoordinate, $rowAttributes);
+                    $rowsAttributesAreSet[$rowCoordinate] = true;
+                }
+            }
+        }
+    }
+
+    private function isFilteredColumn(IReadFilter $readFilter, $columnCoordinate, array $rowsAttributes)
+    {
+        foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
+            if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private function readColumnAttributes(\SimpleXMLElement $worksheetCols, $readDataOnly)
+    {
+        $columnAttributes = [];
+
+        foreach ($worksheetCols->col as $column) {
+            $startColumn = Coordinate::stringFromColumnIndex((int) $column['min']);
+            $endColumn = Coordinate::stringFromColumnIndex((int) $column['max']);
+            ++$endColumn;
+            for ($columnAddress = $startColumn; $columnAddress !== $endColumn; ++$columnAddress) {
+                $columnAttributes[$columnAddress] = $this->readColumnRangeAttributes($column, $readDataOnly);
+
+                if ((int) ($column['max']) == 16384) {
+                    break;
+                }
+            }
+        }
+
+        return $columnAttributes;
+    }
+
+    private function readColumnRangeAttributes(\SimpleXMLElement $column, $readDataOnly)
+    {
+        $columnAttributes = [];
+
+        if ($column['style'] && !$readDataOnly) {
+            $columnAttributes['xfIndex'] = (int) $column['style'];
+        }
+        if (self::boolean($column['hidden'])) {
+            $columnAttributes['visible'] = false;
+        }
+        if (self::boolean($column['collapsed'])) {
+            $columnAttributes['collapsed'] = true;
+        }
+        if (((int) $column['outlineLevel']) > 0) {
+            $columnAttributes['outlineLevel'] = (int) $column['outlineLevel'];
+        }
+        $columnAttributes['width'] = (float) $column['width'];
+
+        return $columnAttributes;
+    }
+
+    private function isFilteredRow(IReadFilter $readFilter, $rowCoordinate, array $columnsAttributes)
+    {
+        foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
+            if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private function readRowAttributes(\SimpleXMLElement $worksheetRow, $readDataOnly)
+    {
+        $rowAttributes = [];
+
+        foreach ($worksheetRow as $row) {
+            if ($row['ht'] && !$readDataOnly) {
+                $rowAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
+            }
+            if (self::boolean($row['hidden'])) {
+                $rowAttributes[(int) $row['r']]['visible'] = false;
+            }
+            if (self::boolean($row['collapsed'])) {
+                $rowAttributes[(int) $row['r']]['collapsed'] = true;
+            }
+            if ((int) $row['outlineLevel'] > 0) {
+                $rowAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel'];
+            }
+            if ($row['s'] && !$readDataOnly) {
+                $rowAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s'];
+            }
+        }
+
+        return $rowAttributes;
+    }
+}

+ 92 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php

@@ -0,0 +1,92 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Style\Conditional;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class ConditionalStyles
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    private $dxfs;
+
+    public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml, array $dxfs = [])
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+        $this->dxfs = $dxfs;
+    }
+
+    public function load()
+    {
+        $this->setConditionalStyles(
+            $this->worksheet,
+            $this->readConditionalStyles($this->worksheetXml)
+        );
+    }
+
+    private function readConditionalStyles($xmlSheet)
+    {
+        $conditionals = [];
+        foreach ($xmlSheet->conditionalFormatting as $conditional) {
+            foreach ($conditional->cfRule as $cfRule) {
+                if (((string) $cfRule['type'] == Conditional::CONDITION_NONE
+                    || (string) $cfRule['type'] == Conditional::CONDITION_CELLIS
+                    || (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT
+                    || (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION)
+                    && isset($this->dxfs[(int) ($cfRule['dxfId'])])) {
+                    $conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
+                }
+            }
+        }
+
+        return $conditionals;
+    }
+
+    private function setConditionalStyles(Worksheet $worksheet, array $conditionals)
+    {
+        foreach ($conditionals as $ref => $cfRules) {
+            ksort($cfRules);
+            $conditionalStyles = $this->readStyleRules($cfRules);
+
+            // Extract all cell references in $ref
+            $cellBlocks = explode(' ', str_replace('$', '', strtoupper($ref)));
+            foreach ($cellBlocks as $cellBlock) {
+                $worksheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
+            }
+        }
+    }
+
+    private function readStyleRules($cfRules)
+    {
+        $conditionalStyles = [];
+        foreach ($cfRules as $cfRule) {
+            $objConditional = new Conditional();
+            $objConditional->setConditionType((string) $cfRule['type']);
+            $objConditional->setOperatorType((string) $cfRule['operator']);
+
+            if ((string) $cfRule['text'] != '') {
+                $objConditional->setText((string) $cfRule['text']);
+            }
+
+            if (isset($cfRule['stopIfTrue']) && (int) $cfRule['stopIfTrue'] === 1) {
+                $objConditional->setStopIfTrue(true);
+            }
+
+            if (count($cfRule->formula) > 1) {
+                foreach ($cfRule->formula as $formula) {
+                    $objConditional->addCondition((string) $formula);
+                }
+            } else {
+                $objConditional->addCondition((string) $cfRule->formula);
+            }
+            $objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);
+            $conditionalStyles[] = $objConditional;
+        }
+
+        return $conditionalStyles;
+    }
+}

+ 50 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class DataValidations
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    public function load()
+    {
+        foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
+            // Uppercase coordinate
+            $range = strtoupper($dataValidation['sqref']);
+            $rangeSet = explode(' ', $range);
+            foreach ($rangeSet as $range) {
+                $stRange = $this->worksheet->shrinkRangeToFit($range);
+
+                // Extract all cell references in $range
+                foreach (Coordinate::extractAllCellReferencesInRange($stRange) as $reference) {
+                    // Create validation
+                    $docValidation = $this->worksheet->getCell($reference)->getDataValidation();
+                    $docValidation->setType((string) $dataValidation['type']);
+                    $docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
+                    $docValidation->setOperator((string) $dataValidation['operator']);
+                    $docValidation->setAllowBlank($dataValidation['allowBlank'] != 0);
+                    $docValidation->setShowDropDown($dataValidation['showDropDown'] == 0);
+                    $docValidation->setShowInputMessage($dataValidation['showInputMessage'] != 0);
+                    $docValidation->setShowErrorMessage($dataValidation['showErrorMessage'] != 0);
+                    $docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
+                    $docValidation->setError((string) $dataValidation['error']);
+                    $docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
+                    $docValidation->setPrompt((string) $dataValidation['prompt']);
+                    $docValidation->setFormula1((string) $dataValidation->formula1);
+                    $docValidation->setFormula2((string) $dataValidation->formula2);
+                }
+            }
+        }
+    }
+}

+ 58 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Hyperlinks.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class Hyperlinks
+{
+    private $worksheet;
+
+    private $hyperlinks = [];
+
+    public function __construct(Worksheet $workSheet)
+    {
+        $this->worksheet = $workSheet;
+    }
+
+    public function readHyperlinks(\SimpleXMLElement $relsWorksheet)
+    {
+        foreach ($relsWorksheet->Relationship as $element) {
+            if ($element['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink') {
+                $this->hyperlinks[(string) $element['Id']] = (string) $element['Target'];
+            }
+        }
+    }
+
+    public function setHyperlinks(\SimpleXMLElement $worksheetXml)
+    {
+        foreach ($worksheetXml->hyperlink as $hyperlink) {
+            $this->setHyperlink($hyperlink, $this->worksheet);
+        }
+    }
+
+    private function setHyperlink(\SimpleXMLElement $hyperlink, Worksheet $worksheet)
+    {
+        // Link url
+        $linkRel = $hyperlink->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+
+        foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
+            $cell = $worksheet->getCell($cellReference);
+            if (isset($linkRel['id'])) {
+                $hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']];
+                if (isset($hyperlink['location'])) {
+                    $hyperlinkUrl .= '#' . (string) $hyperlink['location'];
+                }
+                $cell->getHyperlink()->setUrl($hyperlinkUrl);
+            } elseif (isset($hyperlink['location'])) {
+                $cell->getHyperlink()->setUrl('sheet://' . (string) $hyperlink['location']);
+            }
+
+            // Tooltip
+            if (isset($hyperlink['tooltip'])) {
+                $cell->getHyperlink()->setTooltip((string) $hyperlink['tooltip']);
+            }
+        }
+    }
+}

+ 150 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php

@@ -0,0 +1,150 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class PageSetup extends BaseParserClass
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml = null)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    public function load(array $unparsedLoadedData)
+    {
+        if (!$this->worksheetXml) {
+            return $unparsedLoadedData;
+        }
+
+        $this->margins($this->worksheetXml, $this->worksheet);
+        $unparsedLoadedData = $this->pageSetup($this->worksheetXml, $this->worksheet, $unparsedLoadedData);
+        $this->headerFooter($this->worksheetXml, $this->worksheet);
+        $this->pageBreaks($this->worksheetXml, $this->worksheet);
+
+        return $unparsedLoadedData;
+    }
+
+    private function margins(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
+    {
+        if ($xmlSheet->pageMargins) {
+            $docPageMargins = $worksheet->getPageMargins();
+            $docPageMargins->setLeft((float) ($xmlSheet->pageMargins['left']));
+            $docPageMargins->setRight((float) ($xmlSheet->pageMargins['right']));
+            $docPageMargins->setTop((float) ($xmlSheet->pageMargins['top']));
+            $docPageMargins->setBottom((float) ($xmlSheet->pageMargins['bottom']));
+            $docPageMargins->setHeader((float) ($xmlSheet->pageMargins['header']));
+            $docPageMargins->setFooter((float) ($xmlSheet->pageMargins['footer']));
+        }
+    }
+
+    private function pageSetup(\SimpleXMLElement $xmlSheet, Worksheet $worksheet, array $unparsedLoadedData)
+    {
+        if ($xmlSheet->pageSetup) {
+            $docPageSetup = $worksheet->getPageSetup();
+
+            if (isset($xmlSheet->pageSetup['orientation'])) {
+                $docPageSetup->setOrientation((string) $xmlSheet->pageSetup['orientation']);
+            }
+            if (isset($xmlSheet->pageSetup['paperSize'])) {
+                $docPageSetup->setPaperSize((int) ($xmlSheet->pageSetup['paperSize']));
+            }
+            if (isset($xmlSheet->pageSetup['scale'])) {
+                $docPageSetup->setScale((int) ($xmlSheet->pageSetup['scale']), false);
+            }
+            if (isset($xmlSheet->pageSetup['fitToHeight']) && (int) ($xmlSheet->pageSetup['fitToHeight']) >= 0) {
+                $docPageSetup->setFitToHeight((int) ($xmlSheet->pageSetup['fitToHeight']), false);
+            }
+            if (isset($xmlSheet->pageSetup['fitToWidth']) && (int) ($xmlSheet->pageSetup['fitToWidth']) >= 0) {
+                $docPageSetup->setFitToWidth((int) ($xmlSheet->pageSetup['fitToWidth']), false);
+            }
+            if (isset($xmlSheet->pageSetup['firstPageNumber'], $xmlSheet->pageSetup['useFirstPageNumber']) &&
+                self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) {
+                $docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber']));
+            }
+
+            $relAttributes = $xmlSheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships');
+            if (isset($relAttributes['id'])) {
+                $unparsedLoadedData['sheets'][$worksheet->getCodeName()]['pageSetupRelId'] = (string) $relAttributes['id'];
+            }
+        }
+
+        return $unparsedLoadedData;
+    }
+
+    private function headerFooter(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
+    {
+        if ($xmlSheet->headerFooter) {
+            $docHeaderFooter = $worksheet->getHeaderFooter();
+
+            if (isset($xmlSheet->headerFooter['differentOddEven']) &&
+                self::boolean((string) $xmlSheet->headerFooter['differentOddEven'])) {
+                $docHeaderFooter->setDifferentOddEven(true);
+            } else {
+                $docHeaderFooter->setDifferentOddEven(false);
+            }
+            if (isset($xmlSheet->headerFooter['differentFirst']) &&
+                self::boolean((string) $xmlSheet->headerFooter['differentFirst'])) {
+                $docHeaderFooter->setDifferentFirst(true);
+            } else {
+                $docHeaderFooter->setDifferentFirst(false);
+            }
+            if (isset($xmlSheet->headerFooter['scaleWithDoc']) &&
+                !self::boolean((string) $xmlSheet->headerFooter['scaleWithDoc'])) {
+                $docHeaderFooter->setScaleWithDocument(false);
+            } else {
+                $docHeaderFooter->setScaleWithDocument(true);
+            }
+            if (isset($xmlSheet->headerFooter['alignWithMargins']) &&
+                !self::boolean((string) $xmlSheet->headerFooter['alignWithMargins'])) {
+                $docHeaderFooter->setAlignWithMargins(false);
+            } else {
+                $docHeaderFooter->setAlignWithMargins(true);
+            }
+
+            $docHeaderFooter->setOddHeader((string) $xmlSheet->headerFooter->oddHeader);
+            $docHeaderFooter->setOddFooter((string) $xmlSheet->headerFooter->oddFooter);
+            $docHeaderFooter->setEvenHeader((string) $xmlSheet->headerFooter->evenHeader);
+            $docHeaderFooter->setEvenFooter((string) $xmlSheet->headerFooter->evenFooter);
+            $docHeaderFooter->setFirstHeader((string) $xmlSheet->headerFooter->firstHeader);
+            $docHeaderFooter->setFirstFooter((string) $xmlSheet->headerFooter->firstFooter);
+        }
+    }
+
+    private function pageBreaks(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
+    {
+        if ($xmlSheet->rowBreaks && $xmlSheet->rowBreaks->brk) {
+            $this->rowBreaks($xmlSheet, $worksheet);
+        }
+        if ($xmlSheet->colBreaks && $xmlSheet->colBreaks->brk) {
+            $this->columnBreaks($xmlSheet, $worksheet);
+        }
+    }
+
+    private function rowBreaks(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
+    {
+        foreach ($xmlSheet->rowBreaks->brk as $brk) {
+            if ($brk['man']) {
+                $worksheet->setBreak("A{$brk['id']}", Worksheet::BREAK_ROW);
+            }
+        }
+    }
+
+    private function columnBreaks(\SimpleXMLElement $xmlSheet, Worksheet $worksheet)
+    {
+        foreach ($xmlSheet->colBreaks->brk as $brk) {
+            if ($brk['man']) {
+                $worksheet->setBreak(
+                    Coordinate::stringFromColumnIndex(((int) $brk['id']) + 1) . '1',
+                    Worksheet::BREAK_COLUMN
+                );
+            }
+        }
+    }
+}

+ 91 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php

@@ -0,0 +1,91 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
+use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
+use PhpOffice\PhpSpreadsheet\Settings;
+
+class Properties
+{
+    private $securityScanner;
+
+    private $docProps;
+
+    public function __construct(XmlScanner $securityScanner, DocumentProperties $docProps)
+    {
+        $this->securityScanner = $securityScanner;
+        $this->docProps = $docProps;
+    }
+
+    private function extractPropertyData($propertyData)
+    {
+        return simplexml_load_string(
+            $this->securityScanner->scan($propertyData),
+            'SimpleXMLElement',
+            Settings::getLibXmlLoaderOptions()
+        );
+    }
+
+    public function readCoreProperties($propertyData)
+    {
+        $xmlCore = $this->extractPropertyData($propertyData);
+
+        if (is_object($xmlCore)) {
+            $xmlCore->registerXPathNamespace('dc', 'http://purl.org/dc/elements/1.1/');
+            $xmlCore->registerXPathNamespace('dcterms', 'http://purl.org/dc/terms/');
+            $xmlCore->registerXPathNamespace('cp', 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties');
+
+            $this->docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
+            $this->docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
+            $this->docProps->setCreated(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:created')))); //! respect xsi:type
+            $this->docProps->setModified(strtotime(self::getArrayItem($xmlCore->xpath('dcterms:modified')))); //! respect xsi:type
+            $this->docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
+            $this->docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
+            $this->docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
+            $this->docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
+            $this->docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
+        }
+    }
+
+    public function readExtendedProperties($propertyData)
+    {
+        $xmlCore = $this->extractPropertyData($propertyData);
+
+        if (is_object($xmlCore)) {
+            if (isset($xmlCore->Company)) {
+                $this->docProps->setCompany((string) $xmlCore->Company);
+            }
+            if (isset($xmlCore->Manager)) {
+                $this->docProps->setManager((string) $xmlCore->Manager);
+            }
+        }
+    }
+
+    public function readCustomProperties($propertyData)
+    {
+        $xmlCore = $this->extractPropertyData($propertyData);
+
+        if (is_object($xmlCore)) {
+            foreach ($xmlCore as $xmlProperty) {
+                /** @var \SimpleXMLElement $xmlProperty */
+                $cellDataOfficeAttributes = $xmlProperty->attributes();
+                if (isset($cellDataOfficeAttributes['name'])) {
+                    $propertyName = (string) $cellDataOfficeAttributes['name'];
+                    $cellDataOfficeChildren = $xmlProperty->children('http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
+
+                    $attributeType = $cellDataOfficeChildren->getName();
+                    $attributeValue = (string) $cellDataOfficeChildren->{$attributeType};
+                    $attributeValue = DocumentProperties::convertProperty($attributeValue, $attributeType);
+                    $attributeType = DocumentProperties::convertPropertyType($attributeType);
+                    $this->docProps->setCustomProperty($propertyName, $attributeValue, $attributeType);
+                }
+            }
+        }
+    }
+
+    private static function getArrayItem(array $array, $key = 0)
+    {
+        return isset($array[$key]) ? $array[$key] : null;
+    }
+}

+ 124 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php

@@ -0,0 +1,124 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class SheetViewOptions extends BaseParserClass
+{
+    private $worksheet;
+
+    private $worksheetXml;
+
+    public function __construct(Worksheet $workSheet, \SimpleXMLElement $worksheetXml = null)
+    {
+        $this->worksheet = $workSheet;
+        $this->worksheetXml = $worksheetXml;
+    }
+
+    /**
+     * @param bool $readDataOnly
+     */
+    public function load($readDataOnly = false)
+    {
+        if ($this->worksheetXml === null) {
+            return;
+        }
+
+        if (isset($this->worksheetXml->sheetPr)) {
+            $this->tabColor($this->worksheetXml->sheetPr);
+            $this->codeName($this->worksheetXml->sheetPr);
+            $this->outlines($this->worksheetXml->sheetPr);
+            $this->pageSetup($this->worksheetXml->sheetPr);
+        }
+
+        if (isset($this->worksheetXml->sheetFormatPr)) {
+            $this->sheetFormat($this->worksheetXml->sheetFormatPr);
+        }
+
+        if (!$readDataOnly && isset($this->worksheetXml->printOptions)) {
+            $this->printOptions($this->worksheetXml->printOptions);
+        }
+    }
+
+    private function tabColor(\SimpleXMLElement $sheetPr)
+    {
+        if (isset($sheetPr->tabColor, $sheetPr->tabColor['rgb'])) {
+            $this->worksheet->getTabColor()->setARGB((string) $sheetPr->tabColor['rgb']);
+        }
+    }
+
+    private function codeName(\SimpleXMLElement $sheetPr)
+    {
+        if (isset($sheetPr['codeName'])) {
+            $this->worksheet->setCodeName((string) $sheetPr['codeName'], false);
+        }
+    }
+
+    private function outlines(\SimpleXMLElement $sheetPr)
+    {
+        if (isset($sheetPr->outlinePr)) {
+            if (isset($sheetPr->outlinePr['summaryRight']) &&
+                !self::boolean((string) $sheetPr->outlinePr['summaryRight'])) {
+                $this->worksheet->setShowSummaryRight(false);
+            } else {
+                $this->worksheet->setShowSummaryRight(true);
+            }
+
+            if (isset($sheetPr->outlinePr['summaryBelow']) &&
+                !self::boolean((string) $sheetPr->outlinePr['summaryBelow'])) {
+                $this->worksheet->setShowSummaryBelow(false);
+            } else {
+                $this->worksheet->setShowSummaryBelow(true);
+            }
+        }
+    }
+
+    private function pageSetup(\SimpleXMLElement $sheetPr)
+    {
+        if (isset($sheetPr->pageSetUpPr)) {
+            if (isset($sheetPr->pageSetUpPr['fitToPage']) &&
+                !self::boolean((string) $sheetPr->pageSetUpPr['fitToPage'])) {
+                $this->worksheet->getPageSetup()->setFitToPage(false);
+            } else {
+                $this->worksheet->getPageSetup()->setFitToPage(true);
+            }
+        }
+    }
+
+    private function sheetFormat(\SimpleXMLElement $sheetFormatPr)
+    {
+        if (isset($sheetFormatPr['customHeight']) &&
+            self::boolean((string) $sheetFormatPr['customHeight']) &&
+            isset($sheetFormatPr['defaultRowHeight'])) {
+            $this->worksheet->getDefaultRowDimension()
+                ->setRowHeight((float) $sheetFormatPr['defaultRowHeight']);
+        }
+
+        if (isset($sheetFormatPr['defaultColWidth'])) {
+            $this->worksheet->getDefaultColumnDimension()
+                ->setWidth((float) $sheetFormatPr['defaultColWidth']);
+        }
+
+        if (isset($sheetFormatPr['zeroHeight']) &&
+            ((string) $sheetFormatPr['zeroHeight'] === '1')) {
+            $this->worksheet->getDefaultRowDimension()->setZeroHeight(true);
+        }
+    }
+
+    private function printOptions(\SimpleXMLElement $printOptions)
+    {
+        if (self::boolean((string) $printOptions['gridLinesSet'])) {
+            $this->worksheet->setShowGridlines(true);
+        }
+        if (self::boolean((string) $printOptions['gridLines'])) {
+            $this->worksheet->setPrintGridlines(true);
+        }
+        if (self::boolean((string) $printOptions['horizontalCentered'])) {
+            $this->worksheet->getPageSetup()->setHorizontalCentered(true);
+        }
+        if (self::boolean((string) $printOptions['verticalCentered'])) {
+            $this->worksheet->getPageSetup()->setVerticalCentered(true);
+        }
+    }
+}

+ 127 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php

@@ -0,0 +1,127 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
+use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
+
+class SheetViews extends BaseParserClass
+{
+    private $sheetViewXml;
+
+    private $worksheet;
+
+    public function __construct(\SimpleXMLElement $sheetViewXml, Worksheet $workSheet)
+    {
+        $this->sheetViewXml = $sheetViewXml;
+        $this->worksheet = $workSheet;
+    }
+
+    public function load()
+    {
+        $this->zoomScale();
+        $this->view();
+        $this->gridLines();
+        $this->headers();
+        $this->direction();
+
+        if (isset($this->sheetViewXml->pane)) {
+            $this->pane();
+        }
+        if (isset($this->sheetViewXml->selection, $this->sheetViewXml->selection['sqref'])) {
+            $this->selection();
+        }
+    }
+
+    private function zoomScale()
+    {
+        if (isset($this->sheetViewXml['zoomScale'])) {
+            $zoomScale = (int) ($this->sheetViewXml['zoomScale']);
+            if ($zoomScale <= 0) {
+                // setZoomScale will throw an Exception if the scale is less than or equals 0
+                // that is OK when manually creating documents, but we should be able to read all documents
+                $zoomScale = 100;
+            }
+
+            $this->worksheet->getSheetView()->setZoomScale($zoomScale);
+        }
+
+        if (isset($this->sheetViewXml['zoomScaleNormal'])) {
+            $zoomScaleNormal = (int) ($this->sheetViewXml['zoomScaleNormal']);
+            if ($zoomScaleNormal <= 0) {
+                // setZoomScaleNormal will throw an Exception if the scale is less than or equals 0
+                // that is OK when manually creating documents, but we should be able to read all documents
+                $zoomScaleNormal = 100;
+            }
+
+            $this->worksheet->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
+        }
+    }
+
+    private function view()
+    {
+        if (isset($this->sheetViewXml['view'])) {
+            $this->worksheet->getSheetView()->setView((string) $this->sheetViewXml['view']);
+        }
+    }
+
+    private function gridLines()
+    {
+        if (isset($this->sheetViewXml['showGridLines'])) {
+            $this->worksheet->setShowGridLines(
+                self::boolean((string) $this->sheetViewXml['showGridLines'])
+            );
+        }
+    }
+
+    private function headers()
+    {
+        if (isset($this->sheetViewXml['showRowColHeaders'])) {
+            $this->worksheet->setShowRowColHeaders(
+                self::boolean((string) $this->sheetViewXml['showRowColHeaders'])
+            );
+        }
+    }
+
+    private function direction()
+    {
+        if (isset($this->sheetViewXml['rightToLeft'])) {
+            $this->worksheet->setRightToLeft(
+                self::boolean((string) $this->sheetViewXml['rightToLeft'])
+            );
+        }
+    }
+
+    private function pane()
+    {
+        $xSplit = 0;
+        $ySplit = 0;
+        $topLeftCell = null;
+
+        if (isset($this->sheetViewXml->pane['xSplit'])) {
+            $xSplit = (int) ($this->sheetViewXml->pane['xSplit']);
+        }
+
+        if (isset($this->sheetViewXml->pane['ySplit'])) {
+            $ySplit = (int) ($this->sheetViewXml->pane['ySplit']);
+        }
+
+        if (isset($this->sheetViewXml->pane['topLeftCell'])) {
+            $topLeftCell = (string) $this->sheetViewXml->pane['topLeftCell'];
+        }
+
+        $this->worksheet->freezePane(
+            Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1),
+            $topLeftCell
+        );
+    }
+
+    private function selection()
+    {
+        $sqref = (string) $this->sheetViewXml->selection['sqref'];
+        $sqref = explode(' ', $sqref);
+        $sqref = $sqref[0];
+
+        $this->worksheet->setSelectedCells($sqref);
+    }
+}

+ 265 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php

@@ -0,0 +1,265 @@
+<?php
+
+namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
+
+use PhpOffice\PhpSpreadsheet\Style\Alignment;
+use PhpOffice\PhpSpreadsheet\Style\Border;
+use PhpOffice\PhpSpreadsheet\Style\Borders;
+use PhpOffice\PhpSpreadsheet\Style\Color;
+use PhpOffice\PhpSpreadsheet\Style\Fill;
+use PhpOffice\PhpSpreadsheet\Style\Font;
+use PhpOffice\PhpSpreadsheet\Style\Protection;
+use PhpOffice\PhpSpreadsheet\Style\Style;
+
+class Styles extends BaseParserClass
+{
+    /**
+     * Theme instance.
+     *
+     * @var Theme
+     */
+    private static $theme = null;
+
+    private $styles = [];
+
+    private $cellStyles = [];
+
+    private $styleXml;
+
+    public function __construct(\SimpleXMLElement $styleXml)
+    {
+        $this->styleXml = $styleXml;
+    }
+
+    public function setStyleBaseData(Theme $theme = null, $styles = [], $cellStyles = [])
+    {
+        self::$theme = $theme;
+        $this->styles = $styles;
+        $this->cellStyles = $cellStyles;
+    }
+
+    private static function readFontStyle(Font $fontStyle, \SimpleXMLElement $fontStyleXml)
+    {
+        $fontStyle->setName((string) $fontStyleXml->name['val']);
+        $fontStyle->setSize((float) $fontStyleXml->sz['val']);
+
+        if (isset($fontStyleXml->b)) {
+            $fontStyle->setBold(!isset($fontStyleXml->b['val']) || self::boolean((string) $fontStyleXml->b['val']));
+        }
+        if (isset($fontStyleXml->i)) {
+            $fontStyle->setItalic(!isset($fontStyleXml->i['val']) || self::boolean((string) $fontStyleXml->i['val']));
+        }
+        if (isset($fontStyleXml->strike)) {
+            $fontStyle->setStrikethrough(!isset($fontStyleXml->strike['val']) || self::boolean((string) $fontStyleXml->strike['val']));
+        }
+        $fontStyle->getColor()->setARGB(self::readColor($fontStyleXml->color));
+
+        if (isset($fontStyleXml->u) && !isset($fontStyleXml->u['val'])) {
+            $fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
+        } elseif (isset($fontStyleXml->u, $fontStyleXml->u['val'])) {
+            $fontStyle->setUnderline((string) $fontStyleXml->u['val']);
+        }
+
+        if (isset($fontStyleXml->vertAlign, $fontStyleXml->vertAlign['val'])) {
+            $verticalAlign = strtolower((string) $fontStyleXml->vertAlign['val']);
+            if ($verticalAlign === 'superscript') {
+                $fontStyle->setSuperscript(true);
+            }
+            if ($verticalAlign === 'subscript') {
+                $fontStyle->setSubscript(true);
+            }
+        }
+    }
+
+    private static function readFillStyle(Fill $fillStyle, \SimpleXMLElement $fillStyleXml)
+    {
+        if ($fillStyleXml->gradientFill) {
+            /** @var \SimpleXMLElement $gradientFill */
+            $gradientFill = $fillStyleXml->gradientFill[0];
+            if (!empty($gradientFill['type'])) {
+                $fillStyle->setFillType((string) $gradientFill['type']);
+            }
+            $fillStyle->setRotation((float) ($gradientFill['degree']));
+            $gradientFill->registerXPathNamespace('sml', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
+            $fillStyle->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
+            $fillStyle->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
+        } elseif ($fillStyleXml->patternFill) {
+            $patternType = (string) $fillStyleXml->patternFill['patternType'] != '' ? (string) $fillStyleXml->patternFill['patternType'] : 'solid';
+            $fillStyle->setFillType($patternType);
+            if ($fillStyleXml->patternFill->fgColor) {
+                $fillStyle->getStartColor()->setARGB(self::readColor($fillStyleXml->patternFill->fgColor, true));
+            } else {
+                $fillStyle->getStartColor()->setARGB('FF000000');
+            }
+            if ($fillStyleXml->patternFill->bgColor) {
+                $fillStyle->getEndColor()->setARGB(self::readColor($fillStyleXml->patternFill->bgColor, true));
+            }
+        }
+    }
+
+    private static function readBorderStyle(Borders $borderStyle, \SimpleXMLElement $borderStyleXml)
+    {
+        $diagonalUp = self::boolean((string) $borderStyleXml['diagonalUp']);
+        $diagonalDown = self::boolean((string) $borderStyleXml['diagonalDown']);
+        if (!$diagonalUp && !$diagonalDown) {
+            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
+        } elseif ($diagonalUp && !$diagonalDown) {
+            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
+        } elseif (!$diagonalUp && $diagonalDown) {
+            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
+        } else {
+            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
+        }
+
+        self::readBorder($borderStyle->getLeft(), $borderStyleXml->left);
+        self::readBorder($borderStyle->getRight(), $borderStyleXml->right);
+        self::readBorder($borderStyle->getTop(), $borderStyleXml->top);
+        self::readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
+        self::readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
+    }
+
+    private static function readBorder(Border $border, \SimpleXMLElement $borderXml)
+    {
+        if (isset($borderXml['style'])) {
+            $border->setBorderStyle((string) $borderXml['style']);
+        }
+        if (isset($borderXml->color)) {
+            $border->getColor()->setARGB(self::readColor($borderXml->color));
+        }
+    }
+
+    private static function readAlignmentStyle(Alignment $alignment, \SimpleXMLElement $alignmentXml)
+    {
+        $alignment->setHorizontal((string) $alignmentXml->alignment['horizontal']);
+        $alignment->setVertical((string) $alignmentXml->alignment['vertical']);
+
+        $textRotation = 0;
+        if ((int) $alignmentXml->alignment['textRotation'] <= 90) {
+            $textRotation = (int) $alignmentXml->alignment['textRotation'];
+        } elseif ((int) $alignmentXml->alignment['textRotation'] > 90) {
+            $textRotation = 90 - (int) $alignmentXml->alignment['textRotation'];
+        }
+
+        $alignment->setTextRotation((int) $textRotation);
+        $alignment->setWrapText(self::boolean((string) $alignmentXml->alignment['wrapText']));
+        $alignment->setShrinkToFit(self::boolean((string) $alignmentXml->alignment['shrinkToFit']));
+        $alignment->setIndent((int) ((string) $alignmentXml->alignment['indent']) > 0 ? (int) ((string) $alignmentXml->alignment['indent']) : 0);
+        $alignment->setReadOrder((int) ((string) $alignmentXml->alignment['readingOrder']) > 0 ? (int) ((string) $alignmentXml->alignment['readingOrder']) : 0);
+    }
+
+    private function readStyle(Style $docStyle, $style)
+    {
+        $docStyle->getNumberFormat()->setFormatCode($style->numFmt);
+
+        if (isset($style->font)) {
+            self::readFontStyle($docStyle->getFont(), $style->font);
+        }
+
+        if (isset($style->fill)) {
+            self::readFillStyle($docStyle->getFill(), $style->fill);
+        }
+
+        if (isset($style->border)) {
+            self::readBorderStyle($docStyle->getBorders(), $style->border);
+        }
+
+        if (isset($style->alignment)) {
+            self::readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
+        }
+
+        // protection
+        if (isset($style->protection)) {
+            $this->readProtectionLocked($docStyle, $style);
+            $this->readProtectionHidden($docStyle, $style);
+        }
+
+        // top-level style settings
+        if (isset($style->quotePrefix)) {
+            $docStyle->setQuotePrefix(true);
+        }
+    }
+
+    private function readProtectionLocked(Style $docStyle, $style)
+    {
+        if (isset($style->protection['locked'])) {
+            if (self::boolean((string) $style->protection['locked'])) {
+                $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
+            } else {
+                $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
+            }
+        }
+    }
+
+    private function readProtectionHidden(Style $docStyle, $style)
+    {
+        if (isset($style->protection['hidden'])) {
+            if (self::boolean((string) $style->protection['hidden'])) {
+                $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
+            } else {
+                $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
+            }
+        }
+    }
+
+    private static function readColor($color, $background = false)
+    {
+        if (isset($color['rgb'])) {
+            return (string) $color['rgb'];
+        } elseif (isset($color['indexed'])) {
+            return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
+        } elseif (isset($color['theme'])) {
+            if (self::$theme !== null) {
+                $returnColour = self::$theme->getColourByIndex((int) $color['theme']);
+                if (isset($color['tint'])) {
+                    $tintAdjust = (float) $color['tint'];
+                    $returnColour = Color::changeBrightness($returnColour, $tintAdjust);
+                }
+
+                return 'FF' . $returnColour;
+            }
+        }
+
+        return ($background) ? 'FFFFFFFF' : 'FF000000';
+    }
+
+    public function dxfs($readDataOnly = false)
+    {
+        $dxfs = [];
+        if (!$readDataOnly && $this->styleXml) {
+            //    Conditional Styles
+            if ($this->styleXml->dxfs) {
+                foreach ($this->styleXml->dxfs->dxf as $dxf) {
+                    $style = new Style(false, true);
+                    $this->readStyle($style, $dxf);
+                    $dxfs[] = $style;
+                }
+            }
+            //    Cell Styles
+            if ($this->styleXml->cellStyles) {
+                foreach ($this->styleXml->cellStyles->cellStyle as $cellStyle) {
+                    if ((int) ($cellStyle['builtinId']) == 0) {
+                        if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
+                            // Set default style
+                            $style = new Style();
+                            $this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
+
+                            // normal style, currently not using it for anything
+                        }
+                    }
+                }
+            }
+        }
+
+        return $dxfs;
+    }
+
+    public function styles()
+    {
+        return $this->styles;
+    }
+
+    private static function getArrayItem($array, $key = 0)
+    {
+        return isset($array[$key]) ? $array[$key] : null;
+    }
+}

+ 1 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php

@@ -41,7 +41,7 @@ class Xml extends BaseReader
      */
     public function __construct()
     {
-        $this->readFilter = new DefaultReadFilter();
+        parent::__construct();
         $this->securityScanner = XmlScanner::getInstance($this);
     }
 

+ 13 - 13
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php

@@ -82,13 +82,13 @@ class ReferenceHelper
      */
     public static function cellSort($a, $b)
     {
-        $ac = $bc = '';
-        $ar = $br = 0;
+        // TODO Scrutinizer doesn't like sscanf($a, '%[A-Z]%d', $ac, $ar), and we can't use short list() syntax
+        //      [$ac, $ar] = sscanf($a, '%[A-Z]%d') while retaining PHP 5.6 support.
+        //      Switch when we drop support for 5.6
+        list($ac, $ar) = sscanf($a, '%[A-Z]%d');
+        list($bc, $br) = sscanf($b, '%[A-Z]%d');
 
-        sscanf($a, '%[A-Z]%d', $ac, $ar);
-        sscanf($b, '%[A-Z]%d', $bc, $br);
-
-        if ($ar == $br) {
+        if ($ar === $br) {
             return strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
         }
 
@@ -106,13 +106,13 @@ class ReferenceHelper
      */
     public static function cellReverseSort($a, $b)
     {
-        $ac = $bc = '';
-        $ar = $br = 0;
-
-        sscanf($a, '%[A-Z]%d', $ac, $ar);
-        sscanf($b, '%[A-Z]%d', $bc, $br);
+        // TODO Scrutinizer doesn't like sscanf($a, '%[A-Z]%d', $ac, $ar), and we can't use short list() syntax
+        //      [$ac, $ar] = sscanf($a, '%[A-Z]%d') while retaining PHP 5.6 support.
+        //      Switch when we drop support for 5.6
+        list($ac, $ar) = sscanf($a, '%[A-Z]%d');
+        list($bc, $br) = sscanf($b, '%[A-Z]%d');
 
-        if ($ar == $br) {
+        if ($ar === $br) {
             return 1 - strcasecmp(strlen($ac) . $ac, strlen($bc) . $bc);
         }
 
@@ -625,7 +625,7 @@ class ReferenceHelper
      * Update references within formulas.
      *
      * @param string $pFormula Formula to update
-     * @param int $pBefore Insert before this one
+     * @param string $pBefore Insert before this one
      * @param int $pNumCols Number of columns to insert
      * @param int $pNumRows Number of rows to insert
      * @param string $sheetName Worksheet name/title

+ 42 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php

@@ -24,6 +24,20 @@ class Settings
      */
     private static $libXmlLoaderOptions = null;
 
+    /**
+     * Allow/disallow libxml_disable_entity_loader() call when not thread safe.
+     * Default behaviour is to do the check, but if you're running PHP versions
+     *      7.2 < 7.2.1
+     *      7.1 < 7.1.13
+     *      7.0 < 7.0.27
+     *      5.6 ANY
+     * then you may need to disable this check to prevent unwanted behaviour in other threads
+     * SECURITY WARNING: Changing this flag is not recommended.
+     *
+     * @var bool
+     */
+    private static $libXmlDisableEntityLoader = true;
+
     /**
      * The cache implementation to be used for cell collection.
      *
@@ -101,6 +115,34 @@ class Settings
         return self::$libXmlLoaderOptions;
     }
 
+    /**
+     * Enable/Disable the entity loader for libxml loader.
+     * Allow/disallow libxml_disable_entity_loader() call when not thread safe.
+     * Default behaviour is to do the check, but if you're running PHP versions
+     *      7.2 < 7.2.1
+     *      7.1 < 7.1.13
+     *      7.0 < 7.0.27
+     *      5.6 ANY
+     * then you may need to disable this check to prevent unwanted behaviour in other threads
+     * SECURITY WARNING: Changing this flag to false is not recommended.
+     *
+     * @param bool $state
+     */
+    public static function setLibXmlDisableEntityLoader($state)
+    {
+        self::$libXmlDisableEntityLoader = (bool) $state;
+    }
+
+    /**
+     * Return the state of the entity loader (disabled/enabled) for libxml loader.
+     *
+     * @return bool $state
+     */
+    public static function getLibXmlDisableEntityLoader()
+    {
+        return self::$libXmlDisableEntityLoader;
+    }
+
     /**
      * Sets the implementation of cache that should be used for cell collection.
      *

+ 13 - 12
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Date.php

@@ -192,7 +192,7 @@ class Date
         $interval = $days . ' days';
 
         return $baseDate->modify($interval)
-            ->setTime($hours, $minutes, $seconds);
+            ->setTime((int) $hours, (int) $minutes, (int) $seconds);
     }
 
     /**
@@ -244,12 +244,12 @@ class Date
     public static function dateTimeToExcel(DateTimeInterface $dateValue)
     {
         return self::formattedPHPToExcel(
-            $dateValue->format('Y'),
-            $dateValue->format('m'),
-            $dateValue->format('d'),
-            $dateValue->format('H'),
-            $dateValue->format('i'),
-            $dateValue->format('s')
+            (int) $dateValue->format('Y'),
+            (int) $dateValue->format('m'),
+            (int) $dateValue->format('d'),
+            (int) $dateValue->format('H'),
+            (int) $dateValue->format('i'),
+            (int) $dateValue->format('s')
         );
     }
 
@@ -325,11 +325,12 @@ class Date
      */
     public static function isDateTime(Cell $pCell)
     {
-        return self::isDateTimeFormat(
-            $pCell->getWorksheet()->getStyle(
-                $pCell->getCoordinate()
-            )->getNumberFormat()
-        );
+        return is_numeric($pCell->getValue()) &&
+            self::isDateTimeFormat(
+                $pCell->getWorksheet()->getStyle(
+                    $pCell->getCoordinate()
+                )->getNumberFormat()
+            );
     }
 
     /**

+ 21 - 15
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE.php

@@ -537,31 +537,37 @@ class OLE
     /**
      * Returns a timestamp from an OLE container's date.
      *
-     * @param int $string A binary string with the encoded date
+     * @param string $oleTimestamp A binary string with the encoded date
      *
-     * @return string The timestamp corresponding to the string
+     * @throws ReaderException
+     *
+     * @return int The Unix timestamp corresponding to the string
      */
-    public static function OLE2LocalDate($string)
+    public static function OLE2LocalDate($oleTimestamp)
     {
-        if (strlen($string) != 8) {
+        if (strlen($oleTimestamp) != 8) {
             throw new ReaderException('Expecting 8 byte string');
         }
 
-        // factor used for separating numbers into 4 bytes parts
-        $factor = pow(2, 32);
-        list(, $high_part) = unpack('V', substr($string, 4, 4));
-        list(, $low_part) = unpack('V', substr($string, 0, 4));
+        // convert to units of 100 ns since 1601:
+        $unpackedTimestamp = unpack('v4', $oleTimestamp);
+        $timestampHigh = (float) $unpackedTimestamp[4] * 65536 + (float) $unpackedTimestamp[3];
+        $timestampLow = (float) $unpackedTimestamp[2] * 65536 + (float) $unpackedTimestamp[1];
 
-        $big_date = ($high_part * $factor) + $low_part;
-        // translate to seconds
-        $big_date /= 10000000;
+        // translate to seconds since 1601:
+        $timestampHigh /= 10000000;
+        $timestampLow /= 10000000;
 
-        // days from 1-1-1601 until the beggining of UNIX era
+        // days from 1601 to 1970:
         $days = 134774;
 
-        // translate to seconds from beggining of UNIX era
-        $big_date -= $days * 24 * 3600;
+        // translate to seconds since 1970:
+        $unixTimestamp = floor(65536.0 * 65536.0 * $timestampHigh + $timestampLow - $days * 24 * 3600 + 0.5);
+
+        if ((int) $unixTimestamp == $unixTimestamp) {
+            return (int) $unixTimestamp;
+        }
 
-        return floor($big_date);
+        return $unixTimestamp >= 0.0 ? PHP_INT_MAX : PHP_INT_MIN;
     }
 }

+ 3 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/ExponentialBestFit.php

@@ -113,7 +113,9 @@ class ExponentialBestFit extends BestFit
      */
     public function __construct($yValues, $xValues = [], $const = true)
     {
-        if (parent::__construct($yValues, $xValues) !== false) {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
             $this->exponentialRegression($yValues, $xValues, $const);
         }
     }

+ 3 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LinearBestFit.php

@@ -72,7 +72,9 @@ class LinearBestFit extends BestFit
      */
     public function __construct($yValues, $xValues = [], $const = true)
     {
-        if (parent::__construct($yValues, $xValues) !== false) {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
             $this->linearRegression($yValues, $xValues, $const);
         }
     }

+ 3 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/LogarithmicBestFit.php

@@ -81,7 +81,9 @@ class LogarithmicBestFit extends BestFit
      */
     public function __construct($yValues, $xValues = [], $const = true)
     {
-        if (parent::__construct($yValues, $xValues) !== false) {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
             $this->logarithmicRegression($yValues, $xValues, $const);
         }
     }

+ 3 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php

@@ -182,7 +182,9 @@ class PolynomialBestFit extends BestFit
      */
     public function __construct($order, $yValues, $xValues = [], $const = true)
     {
-        if (parent::__construct($yValues, $xValues) !== false) {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
             if ($order < $this->valueCount) {
                 $this->bestFitType .= '_' . $order;
                 $this->order = $order;

+ 3 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php

@@ -105,7 +105,9 @@ class PowerBestFit extends BestFit
      */
     public function __construct($yValues, $xValues = [], $const = true)
     {
-        if (parent::__construct($yValues, $xValues) !== false) {
+        parent::__construct($yValues, $xValues);
+
+        if (!$this->error) {
             $this->powerRegression($yValues, $xValues, $const);
         }
     }

+ 1 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Spreadsheet.php

@@ -715,7 +715,7 @@ class Spreadsheet
      *
      * @param string $pName Sheet name
      *
-     * @return Worksheet
+     * @return null|Worksheet
      */
     public function getSheetByName($pName)
     {

+ 19 - 1
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Style/NumberFormat.php

@@ -49,6 +49,8 @@ class NumberFormat extends Supervisor
     const FORMAT_CURRENCY_USD = '$#,##0_-';
     const FORMAT_CURRENCY_EUR_SIMPLE = '#,##0.00_-"€"';
     const FORMAT_CURRENCY_EUR = '#,##0_-"€"';
+    const FORMAT_ACCOUNTING_USD = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
+    const FORMAT_ACCOUNTING_EUR = '_("€"* #,##0.00_);_("€"* \(#,##0.00\);_("€"* "-"??_);_(@_)';
 
     /**
      * Excel built-in number formats.
@@ -308,6 +310,22 @@ class NumberFormat extends Supervisor
             self::$builtInFormats[69] = 't# ?/?';
             self::$builtInFormats[70] = 't# ??/??';
 
+            // JPN
+            self::$builtInFormats[28] = '[$-411]ggge"年"m"月"d"日"';
+            self::$builtInFormats[29] = '[$-411]ggge"年"m"月"d"日"';
+            self::$builtInFormats[31] = 'yyyy"年"m"月"d"日"';
+            self::$builtInFormats[32] = 'h"時"mm"分"';
+            self::$builtInFormats[33] = 'h"時"mm"分"ss"秒"';
+            self::$builtInFormats[34] = 'yyyy"年"m"月"';
+            self::$builtInFormats[35] = 'm"月"d"日"';
+            self::$builtInFormats[51] = '[$-411]ggge"年"m"月"d"日"';
+            self::$builtInFormats[52] = 'yyyy"年"m"月"';
+            self::$builtInFormats[53] = 'm"月"d"日"';
+            self::$builtInFormats[54] = '[$-411]ggge"年"m"月"d"日"';
+            self::$builtInFormats[55] = 'yyyy"年"m"月"';
+            self::$builtInFormats[56] = 'm"月"d"日"';
+            self::$builtInFormats[58] = '[$-411]ggge"年"m"月"d"日"';
+
             // Flip array (for faster lookups)
             self::$flippedBuiltInFormats = array_flip(self::$builtInFormats);
         }
@@ -596,7 +614,7 @@ class NumberFormat extends Supervisor
         }
 
         // Convert any other escaped characters to quoted strings, e.g. (\T to "T")
-        $format = preg_replace('/(\\\([^ ]))(?=(?:[^"]|"[^"]*")*$)/u', '"${2}"', $format);
+        $format = preg_replace('/(\\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/u', '"${2}"', $format);
 
         // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal)
         $sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format);

+ 2 - 2
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Dimension.php

@@ -64,7 +64,7 @@ abstract class Dimension
      */
     public function setVisible($pValue)
     {
-        $this->visible = $pValue;
+        $this->visible = (bool) $pValue;
 
         return $this;
     }
@@ -119,7 +119,7 @@ abstract class Dimension
      */
     public function setCollapsed($pValue)
     {
-        $this->collapsed = $pValue;
+        $this->collapsed = (bool) $pValue;
 
         return $this;
     }

+ 10 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/RowCellIterator.php

@@ -170,6 +170,16 @@ class RowCellIterator extends CellIterator
         return $this->currentColumnIndex <= $this->endColumnIndex && $this->currentColumnIndex >= $this->startColumnIndex;
     }
 
+    /**
+     * Return the current iterator position.
+     *
+     * @return int
+     */
+    public function getCurrentColumnIndex()
+    {
+        return $this->currentColumnIndex;
+    }
+
     /**
      * Validate start/end values for "IterateOnlyExistingCells" mode, and adjust if necessary.
      *

+ 2 - 2
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Worksheet/Worksheet.php

@@ -1096,7 +1096,7 @@ class Worksheet implements IComparable
      * @param string $column Return the highest data row for the specified column,
      *                                     or the highest data row of any column if no column letter is passed
      *
-     * @return string Highest row number that contains data
+     * @return int Highest row number that contains data
      */
     public function getHighestDataRow($column = null)
     {
@@ -2064,7 +2064,7 @@ class Worksheet implements IComparable
     /**
      * Insert a new column, updating all possible related data.
      *
-     * @param int $pBefore Insert before this one, eg: 'A'
+     * @param string $pBefore Insert before this one, eg: 'A'
      * @param int $pNumCols Number of columns to insert
      *
      * @throws Exception

+ 0 - 55
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/BaseWriter.php

@@ -35,27 +35,11 @@ abstract class BaseWriter implements IWriter
      */
     private $diskCachingDirectory = './';
 
-    /**
-     * Write charts in workbook?
-     *        If this is true, then the Writer will write definitions for any charts that exist in the PhpSpreadsheet object.
-     *        If false (the default) it will ignore any charts defined in the PhpSpreadsheet object.
-     *
-     * @return bool
-     */
     public function getIncludeCharts()
     {
         return $this->includeCharts;
     }
 
-    /**
-     * Set write charts in workbook
-     *        Set to true, to advise the Writer to include any charts that exist in the PhpSpreadsheet object.
-     *        Set to false (the default) to ignore charts.
-     *
-     * @param bool $pValue
-     *
-     * @return IWriter
-     */
     public function setIncludeCharts($pValue)
     {
         $this->includeCharts = (bool) $pValue;
@@ -63,30 +47,11 @@ abstract class BaseWriter implements IWriter
         return $this;
     }
 
-    /**
-     * Get Pre-Calculate Formulas flag
-     *     If this is true (the default), then the writer will recalculate all formulae in a workbook when saving,
-     *        so that the pre-calculated values are immediately available to MS Excel or other office spreadsheet
-     *        viewer when opening the file
-     *     If false, then formulae are not calculated on save. This is faster for saving in PhpSpreadsheet, but slower
-     *        when opening the resulting file in MS Excel, because Excel has to recalculate the formulae itself.
-     *
-     * @return bool
-     */
     public function getPreCalculateFormulas()
     {
         return $this->preCalculateFormulas;
     }
 
-    /**
-     * Set Pre-Calculate Formulas
-     *        Set to true (the default) to advise the Writer to calculate all formulae on save
-     *        Set to false to prevent precalculation of formulae on save.
-     *
-     * @param bool $pValue Pre-Calculate Formulas?
-     *
-     * @return IWriter
-     */
     public function setPreCalculateFormulas($pValue)
     {
         $this->preCalculateFormulas = (bool) $pValue;
@@ -94,26 +59,11 @@ abstract class BaseWriter implements IWriter
         return $this;
     }
 
-    /**
-     * Get use disk caching where possible?
-     *
-     * @return bool
-     */
     public function getUseDiskCaching()
     {
         return $this->useDiskCaching;
     }
 
-    /**
-     * Set use disk caching where possible?
-     *
-     * @param bool $pValue
-     * @param string $pDirectory Disk caching directory
-     *
-     * @throws Exception when directory does not exist
-     *
-     * @return IWriter
-     */
     public function setUseDiskCaching($pValue, $pDirectory = null)
     {
         $this->useDiskCaching = $pValue;
@@ -129,11 +79,6 @@ abstract class BaseWriter implements IWriter
         return $this;
     }
 
-    /**
-     * Get disk caching directory.
-     *
-     * @return string
-     */
     public function getDiskCachingDirectory()
     {
         return $this->diskCachingDirectory;

+ 69 - 0
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/IWriter.php

@@ -13,6 +13,49 @@ interface IWriter
      */
     public function __construct(Spreadsheet $spreadsheet);
 
+    /**
+     * Write charts in workbook?
+     *        If this is true, then the Writer will write definitions for any charts that exist in the PhpSpreadsheet object.
+     *        If false (the default) it will ignore any charts defined in the PhpSpreadsheet object.
+     *
+     * @return bool
+     */
+    public function getIncludeCharts();
+
+    /**
+     * Set write charts in workbook
+     *        Set to true, to advise the Writer to include any charts that exist in the PhpSpreadsheet object.
+     *        Set to false (the default) to ignore charts.
+     *
+     * @param bool $pValue
+     *
+     * @return IWriter
+     */
+    public function setIncludeCharts($pValue);
+
+    /**
+     * Get Pre-Calculate Formulas flag
+     *     If this is true (the default), then the writer will recalculate all formulae in a workbook when saving,
+     *        so that the pre-calculated values are immediately available to MS Excel or other office spreadsheet
+     *        viewer when opening the file
+     *     If false, then formulae are not calculated on save. This is faster for saving in PhpSpreadsheet, but slower
+     *        when opening the resulting file in MS Excel, because Excel has to recalculate the formulae itself.
+     *
+     * @return bool
+     */
+    public function getPreCalculateFormulas();
+
+    /**
+     * Set Pre-Calculate Formulas
+     *        Set to true (the default) to advise the Writer to calculate all formulae on save
+     *        Set to false to prevent precalculation of formulae on save.
+     *
+     * @param bool $pValue Pre-Calculate Formulas?
+     *
+     * @return IWriter
+     */
+    public function setPreCalculateFormulas($pValue);
+
     /**
      * Save PhpSpreadsheet to file.
      *
@@ -21,4 +64,30 @@ interface IWriter
      * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
      */
     public function save($pFilename);
+
+    /**
+     * Get use disk caching where possible?
+     *
+     * @return bool
+     */
+    public function getUseDiskCaching();
+
+    /**
+     * Set use disk caching where possible?
+     *
+     * @param bool $pValue
+     * @param string $pDirectory Disk caching directory
+     *
+     * @throws Exception when directory does not exist
+     *
+     * @return IWriter
+     */
+    public function setUseDiskCaching($pValue, $pDirectory = null);
+
+    /**
+     * Get disk caching directory.
+     *
+     * @return string
+     */
+    public function getDiskCachingDirectory();
 }

+ 16 - 17
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Parser.php

@@ -1009,7 +1009,7 @@ class Parser
 
                 break;
             case '>':
-                if ($this->lookAhead == '=') { // it's a GE token
+                if ($this->lookAhead === '=') { // it's a GE token
                     break;
                 }
 
@@ -1018,7 +1018,7 @@ class Parser
                 break;
             case '<':
                 // it's a LE or a NE token
-                if (($this->lookAhead == '=') or ($this->lookAhead == '>')) {
+                if (($this->lookAhead === '=') or ($this->lookAhead === '>')) {
                     break;
                 }
 
@@ -1027,12 +1027,12 @@ class Parser
                 break;
             default:
                 // if it's a reference A1 or $A$1 or $A1 or A$1
-                if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead != ':') and ($this->lookAhead != '.') and ($this->lookAhead != '!')) {
+                if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead !== ':') and ($this->lookAhead !== '.') and ($this->lookAhead !== '!')) {
                     return $token;
-                } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead != ':') and ($this->lookAhead != '.')) {
+                } elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead !== ':') and ($this->lookAhead !== '.')) {
                     // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
                     return $token;
-                } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead != ':') and ($this->lookAhead != '.')) {
+                } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) and !preg_match('/\d/', $this->lookAhead) and ($this->lookAhead !== ':') and ($this->lookAhead !== '.')) {
                     // If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
                     return $token;
                 } elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead)) {
@@ -1044,19 +1044,19 @@ class Parser
                 } elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $token) and !preg_match('/\d/', $this->lookAhead)) {
                     // If it's an external range like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
                     return $token;
-                } elseif (is_numeric($token) and (!is_numeric($token . $this->lookAhead) or ($this->lookAhead == '')) and ($this->lookAhead != '!') and ($this->lookAhead != ':')) {
+                } elseif (is_numeric($token) and (!is_numeric($token . $this->lookAhead) or ($this->lookAhead == '')) and ($this->lookAhead !== '!') and ($this->lookAhead !== ':')) {
                     // If it's a number (check that it's not a sheet name or range)
                     return $token;
-                } elseif (preg_match('/"([^"]|""){0,255}"/', $token) and $this->lookAhead != '"' and (substr_count($token, '"') % 2 == 0)) {
+                } elseif (preg_match('/"([^"]|""){0,255}"/', $token) and $this->lookAhead !== '"' and (substr_count($token, '"') % 2 == 0)) {
                     // If it's a string (of maximum 255 characters)
                     return $token;
-                } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) or $token == '#N/A') {
+                } elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) or $token === '#N/A') {
                     // If it's an error code
                     return $token;
-                } elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) and ($this->lookAhead == '(')) {
+                } elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) and ($this->lookAhead === '(')) {
                     // if it's a function call
                     return $token;
-                } elseif (substr($token, -1) == ')') {
+                } elseif (substr($token, -1) === ')') {
                     //    It's an argument of some description (e.g. a named range),
                     //        precise nature yet to be determined
                     return $token;
@@ -1078,8 +1078,7 @@ class Parser
     {
         $this->currentCharacter = 0;
         $this->formula = $formula;
-        $this->lookAhead = isset($formula[1]) ? $formula[1]
-        : '';
+        $this->lookAhead = isset($formula[1]) ? $formula[1] : '';
         $this->advance();
         $this->parseTree = $this->condition();
 
@@ -1248,10 +1247,10 @@ class Parser
      */
     private function fact()
     {
-        if ($this->currentToken == '(') {
+        if ($this->currentToken === '(') {
             $this->advance(); // eat the "("
             $result = $this->parenthesizedExpression();
-            if ($this->currentToken != ')') {
+            if ($this->currentToken !== ')') {
                 throw new WriterException("')' token expected.");
             }
             $this->advance(); // eat the ")"
@@ -1299,7 +1298,7 @@ class Parser
             return $result;
         } elseif (is_numeric($this->currentToken)) {
             // If it's a number or a percent
-            if ($this->lookAhead == '%') {
+            if ($this->lookAhead === '%') {
                 $result = $this->createTree('ptgPercent', $this->currentToken, '');
                 $this->advance(); // Skip the percentage operator once we've pre-built that tree
             } else {
@@ -1331,9 +1330,9 @@ class Parser
         $result = ''; // initialize result
         $this->advance();
         $this->advance(); // eat the "("
-        while ($this->currentToken != ')') {
+        while ($this->currentToken !== ')') {
             if ($num_args > 0) {
-                if ($this->currentToken == ',' || $this->currentToken == ';') {
+                if ($this->currentToken === ',' || $this->currentToken === ';') {
                     $this->advance(); // eat the "," or ";"
                 } else {
                     throw new WriterException("Syntax error: comma expected in function $function, arg #{$num_args}");

+ 4 - 6
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xls/Workbook.php

@@ -267,9 +267,7 @@ class Workbook extends BIFFwriter
 
         $this->xfWriters[] = $xfWriter;
 
-        $xfIndex = count($this->xfWriters) - 1;
-
-        return $xfIndex;
+        return count($this->xfWriters) - 1;
     }
 
     /**
@@ -319,7 +317,7 @@ class Workbook extends BIFFwriter
             if ($colorIndex) {
                 $this->colors[$rgb] = $colorIndex;
             } else {
-                if (count($this->colors) == 0) {
+                if (count($this->colors) === 0) {
                     $lastColor = 7;
                 } else {
                     $lastColor = end($this->colors);
@@ -437,7 +435,7 @@ class Workbook extends BIFFwriter
 
         // Prepare part 3 of the workbook global stream, what goes after the SHEET records
         $part3 = '';
-        if ($this->countryCode != -1) {
+        if ($this->countryCode !== -1) {
             $part3 .= $this->writeCountry();
         }
         $part3 .= $this->writeRecalcId();
@@ -918,7 +916,7 @@ class Workbook extends BIFFwriter
         $record = 0x0022; // Record identifier
         $length = 0x0002; // Bytes to follow
 
-        $f1904 = (Date::getExcelCalendar() == Date::CALENDAR_MAC_1904)
+        $f1904 = (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904)
             ? 1
             : 0; // Flag for 1904 date system
 

+ 13 - 2
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx.php

@@ -328,6 +328,17 @@ class Xlsx extends BaseWriter
                     $zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts));
                 }
 
+                // Add unparsed drawings
+                if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'])) {
+                    foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['Drawings'] as $relId => $drawingXml) {
+                        $drawingFile = array_search($relId, $unparsedLoadedData['sheets'][$sheetCodeName]['drawingOriginalIds']);
+                        if ($drawingFile !== false) {
+                            $drawingFile = ltrim($drawingFile, '.');
+                            $zip->addFromString('xl' . $drawingFile, $drawingXml);
+                        }
+                    }
+                }
+
                 // Add comment relationship parts
                 if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
                     // VML Comments
@@ -338,8 +349,8 @@ class Xlsx extends BaseWriter
                 }
 
                 // Add unparsed relationship parts
-                if (isset($unparsedLoadedData['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['vmlDrawings'])) {
-                    foreach ($unparsedLoadedData['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['vmlDrawings'] as $vmlDrawing) {
+                if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) {
+                    foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) {
                         $zip->addFromString($vmlDrawing['filePath'], $vmlDrawing['content']);
                     }
                 }

+ 2 - 2
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php

@@ -93,7 +93,7 @@ class Workbook extends WriterPart
     {
         $objWriter->startElement('workbookPr');
 
-        if (Date::getExcelCalendar() == Date::CALENDAR_MAC_1904) {
+        if (Date::getExcelCalendar() === Date::CALENDAR_MAC_1904) {
             $objWriter->writeAttribute('date1904', '1');
         }
 
@@ -225,7 +225,7 @@ class Workbook extends WriterPart
             $objWriter->startElement('sheet');
             $objWriter->writeAttribute('name', $pSheetname);
             $objWriter->writeAttribute('sheetId', $pSheetId);
-            if ($sheetState != 'visible' && $sheetState != '') {
+            if ($sheetState !== 'visible' && $sheetState != '') {
                 $objWriter->writeAttribute('state', $sheetState);
             }
             $objWriter->writeAttribute('r:id', 'rId' . $pRelId);

+ 15 - 11
htdocs/includes/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

@@ -141,7 +141,7 @@ class Worksheet extends WriterPart
         $objWriter->startElement('sheetPr');
         if ($pSheet->getParent()->hasMacros()) {
             //if the workbook have macros, we need to have codeName for the sheet
-            if ($pSheet->hasCodeName() == false) {
+            if (!$pSheet->hasCodeName()) {
                 $pSheet->setCodeName($pSheet->getTitle());
             }
             $objWriter->writeAttribute('codeName', $pSheet->getCodeName());
@@ -322,7 +322,7 @@ class Worksheet extends WriterPart
         }
 
         // Set Zero Height row
-        if ((string) $pSheet->getDefaultRowDimension()->getZeroHeight() == '1' ||
+        if ((string) $pSheet->getDefaultRowDimension()->getZeroHeight() === '1' ||
             strtolower((string) $pSheet->getDefaultRowDimension()->getZeroHeight()) == 'true') {
             $objWriter->writeAttribute('zeroHeight', '1');
         }
@@ -383,7 +383,7 @@ class Worksheet extends WriterPart
                 }
 
                 // Column visibility
-                if ($colDimension->getVisible() == false) {
+                if ($colDimension->getVisible() === false) {
                     $objWriter->writeAttribute('hidden', 'true');
                 }
 
@@ -398,7 +398,7 @@ class Worksheet extends WriterPart
                 }
 
                 // Collapsed
-                if ($colDimension->getCollapsed() == true) {
+                if ($colDimension->getCollapsed() === true) {
                     $objWriter->writeAttribute('collapsed', 'true');
                 }
 
@@ -428,7 +428,7 @@ class Worksheet extends WriterPart
         // sheetProtection
         $objWriter->startElement('sheetProtection');
 
-        if ($pSheet->getProtection()->getPassword() != '') {
+        if ($pSheet->getProtection()->getPassword() !== '') {
             $objWriter->writeAttribute('password', $pSheet->getProtection()->getPassword());
         }
 
@@ -519,6 +519,9 @@ class Worksheet extends WriterPart
                             // Formula
                             $objWriter->writeElement('formula', $formula);
                         }
+                    } elseif ($conditional->getConditionType() == Conditional::CONDITION_CONTAINSBLANKS) {
+                        // formula copied from ms xlsx xml source file
+                        $objWriter->writeElement('formula', 'LEN(TRIM(' . $cellCoordinate . '))=0');
                     }
 
                     $objWriter->endElement();
@@ -624,8 +627,9 @@ class Worksheet extends WriterPart
                     $objWriter->writeAttribute('location', str_replace('sheet://', '', $hyperlink->getUrl()));
                 }
 
-                if ($hyperlink->getTooltip() != '') {
+                if ($hyperlink->getTooltip() !== '') {
                     $objWriter->writeAttribute('tooltip', $hyperlink->getTooltip());
+                    $objWriter->writeAttribute('display', $hyperlink->getTooltip());
                 }
 
                 $objWriter->endElement();
@@ -991,12 +995,12 @@ class Worksheet extends WriterPart
                 }
 
                 // Row visibility
-                if ($rowDimension->getVisible() == false) {
+                if (!$rowDimension->getVisible() === true) {
                     $objWriter->writeAttribute('hidden', 'true');
                 }
 
                 // Collapsed
-                if ($rowDimension->getCollapsed() == true) {
+                if ($rowDimension->getCollapsed() === true) {
                     $objWriter->writeAttribute('collapsed', 'true');
                 }
 
@@ -1101,7 +1105,7 @@ class Worksheet extends WriterPart
                     break;
                 case 'f':            // Formula
                     $attributes = $pCell->getFormulaAttributes();
-                    if ($attributes['t'] == 'array') {
+                    if ($attributes['t'] === 'array') {
                         $objWriter->startElement('f');
                         $objWriter->writeAttribute('t', 'array');
                         $objWriter->writeAttribute('ref', $pCellAddress);
@@ -1114,7 +1118,7 @@ class Worksheet extends WriterPart
                     }
                     if ($this->getParentWriter()->getOffice2003Compatibility() === false) {
                         if ($this->getParentWriter()->getPreCalculateFormulas()) {
-                            if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) != '#') {
+                            if (!is_array($calculatedValue) && substr($calculatedValue, 0, 1) !== '#') {
                                 $objWriter->writeElement('v', StringHelper::formatNumber($calculatedValue));
                             } else {
                                 $objWriter->writeElement('v', '0');
@@ -1135,7 +1139,7 @@ class Worksheet extends WriterPart
 
                     break;
                 case 'e':            // Error
-                    if (substr($cellValue, 0, 1) == '=') {
+                    if (substr($cellValue, 0, 1) === '=') {
                         $objWriter->writeElement('f', substr($cellValue, 1));
                         $objWriter->writeElement('v', substr($cellValue, 1));
                     } else {