diff --git a/src/PhpSpreadsheet/Reader/Xml.php b/src/PhpSpreadsheet/Reader/Xml.php index 9cce7003fb..7b8697aff2 100644 --- a/src/PhpSpreadsheet/Reader/Xml.php +++ b/src/PhpSpreadsheet/Reader/Xml.php @@ -231,6 +231,19 @@ public function listWorksheetInfo($filename) return $worksheetInfo; } + /** + * Loads Spreadsheet from string. + */ + public function loadSpreadsheetFromString(string $contents): Spreadsheet + { + // Create new Spreadsheet + $spreadsheet = new Spreadsheet(); + $spreadsheet->removeSheetByIndex(0); + + // Load into this instance + return $this->loadIntoExisting($contents, $spreadsheet, true); + } + /** * Loads Spreadsheet from file. */ @@ -245,17 +258,19 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet } /** - * Loads from file into Spreadsheet instance. - * - * @param string $filename + * Loads from file or contents into Spreadsheet instance. * - * @return Spreadsheet + * @param string $filename file name if useContents is false else file contents */ - public function loadIntoExisting($filename, Spreadsheet $spreadsheet) + public function loadIntoExisting(string $filename, Spreadsheet $spreadsheet, bool $useContents = false): Spreadsheet { - File::assertFile($filename); - if (!$this->canRead($filename)) { - throw new Exception($filename . ' is an Invalid Spreadsheet file.'); + if ($useContents) { + $this->fileContents = $filename; + } else { + File::assertFile($filename); + if (!$this->canRead($filename)) { + throw new Exception($filename . ' is an Invalid Spreadsheet file.'); + } } $xml = $this->trySimpleXMLLoadString($filename); @@ -268,6 +283,9 @@ public function loadIntoExisting($filename, Spreadsheet $spreadsheet) (new Properties($spreadsheet))->readProperties($xml, $namespaces); $this->styles = (new Style())->parseStyles($xml, $namespaces); + if (isset($this->styles['Default'])) { + $spreadsheet->getCellXfCollection()[0]->applyFromArray($this->styles['Default']); + } $worksheetID = 0; $xml_ss = $xml->children($namespaces['ss']); @@ -295,6 +313,10 @@ public function loadIntoExisting($filename, Spreadsheet $spreadsheet) // the worksheet name in line with the formula, not the reverse $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); } + if (isset($worksheet_ss['Protected'])) { + $protection = (string) $worksheet_ss['Protected'] === '1'; + $spreadsheet->getActiveSheet()->getProtection()->setSheet($protection); + } // locally scoped defined names if (isset($worksheet->Names[0])) { @@ -314,14 +336,34 @@ public function loadIntoExisting($filename, Spreadsheet $spreadsheet) if (isset($worksheet->Table->Column)) { foreach ($worksheet->Table->Column as $columnData) { $columnData_ss = self::getAttributes($columnData, $namespaces['ss']); + $colspan = 0; + if (isset($columnData_ss['Span'])) { + $spanAttr = (string) $columnData_ss['Span']; + if (is_numeric($spanAttr)) { + $colspan = max(0, (int) $spanAttr); + } + } if (isset($columnData_ss['Index'])) { $columnID = Coordinate::stringFromColumnIndex((int) $columnData_ss['Index']); } + $columnWidth = null; if (isset($columnData_ss['Width'])) { $columnWidth = $columnData_ss['Width']; - $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4); } - ++$columnID; + $columnVisible = null; + if (isset($columnData_ss['Hidden'])) { + $columnVisible = ((string) $columnData_ss['Hidden']) !== '1'; + } + while ($colspan >= 0) { + if (isset($columnWidth)) { + $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4); + } + if (isset($columnVisible)) { + $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setVisible($columnVisible); + } + ++$columnID; + --$colspan; + } } } @@ -334,6 +376,10 @@ public function loadIntoExisting($filename, Spreadsheet $spreadsheet) if (isset($row_ss['Index'])) { $rowID = (int) $row_ss['Index']; } + if (isset($row_ss['Hidden'])) { + $rowVisible = ((string) $row_ss['Hidden']) !== '1'; + $spreadsheet->getActiveSheet()->getRowDimension($rowID)->setVisible($rowVisible); + } $columnID = 'A'; foreach ($rowData->Cell as $cell) { @@ -471,6 +517,42 @@ public function loadIntoExisting($filename, Spreadsheet $spreadsheet) $xmlX = $worksheet->children($namespaces['x']); if (isset($xmlX->WorksheetOptions)) { (new PageSettings($xmlX, $namespaces))->loadPageSettings($spreadsheet); + if (isset($xmlX->WorksheetOptions->TopRowVisible, $xmlX->WorksheetOptions->LeftColumnVisible)) { + $leftTopRow = (string) $xmlX->WorksheetOptions->TopRowVisible; + $leftTopColumn = (string) $xmlX->WorksheetOptions->LeftColumnVisible; + if (is_numeric($leftTopRow) && is_numeric($leftTopColumn)) { + $leftTopCoordinate = Coordinate::stringFromColumnIndex((int) $leftTopColumn + 1) . (string) ($leftTopRow + 1); + $spreadsheet->getActiveSheet()->setTopLeftCell($leftTopCoordinate); + } + } + $rangeCalculated = false; + if (isset($xmlX->WorksheetOptions->Panes->Pane->RangeSelection)) { + if (1 === preg_match('/^R(\d+)C(\d+):R(\d+)C(\d+)$/', (string) $xmlX->WorksheetOptions->Panes->Pane->RangeSelection, $selectionMatches)) { + $selectedCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2]) + . $selectionMatches[1] + . ':' + . Coordinate::stringFromColumnIndex((int) $selectionMatches[4]) + . $selectionMatches[3]; + $spreadsheet->getActiveSheet()->setSelectedCells($selectedCell); + $rangeCalculated = true; + } + } + if (!$rangeCalculated) { + if (isset($xmlX->WorksheetOptions->Panes->Pane->ActiveRow)) { + $activeRow = (string) $xmlX->WorksheetOptions->Panes->Pane->ActiveRow; + } else { + $activeRow = 0; + } + if (isset($xmlX->WorksheetOptions->Panes->Pane->ActiveCol)) { + $activeColumn = (string) $xmlX->WorksheetOptions->Panes->Pane->ActiveCol; + } else { + $activeColumn = 0; + } + if (is_numeric($activeRow) && is_numeric($activeColumn)) { + $selectedCell = Coordinate::stringFromColumnIndex((int) $activeColumn + 1) . (string) ($activeRow + 1); + $spreadsheet->getActiveSheet()->setSelectedCells($selectedCell); + } + } } } } @@ -478,7 +560,11 @@ public function loadIntoExisting($filename, Spreadsheet $spreadsheet) } // Globally scoped defined names - $activeWorksheet = $spreadsheet->setActiveSheetIndex(0); + $activeSheetIndex = 0; + if (isset($xml->ExcelWorkbook->ActiveSheet)) { + $activeSheetIndex = (int) (string) $xml->ExcelWorkbook->ActiveSheet; + } + $activeWorksheet = $spreadsheet->setActiveSheetIndex($activeSheetIndex); if (isset($xml->Names[0])) { foreach ($xml->Names[0] as $definedName) { $definedName_ss = self::getAttributes($definedName, $namespaces['ss']); diff --git a/src/PhpSpreadsheet/Reader/Xml/Style.php b/src/PhpSpreadsheet/Reader/Xml/Style.php index 774fffe8b9..698acf6ac0 100644 --- a/src/PhpSpreadsheet/Reader/Xml/Style.php +++ b/src/PhpSpreadsheet/Reader/Xml/Style.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xml; +use PhpOffice\PhpSpreadsheet\Style\Protection; use SimpleXMLElement; class Style @@ -30,7 +31,7 @@ public function parseStyles(SimpleXMLElement $xml, array $namespaces): array $styleID = (string) $style_ss['ID']; $this->styles[$styleID] = $this->styles['Default'] ?? []; - $alignment = $border = $font = $fill = $numberFormat = []; + $alignment = $border = $font = $fill = $numberFormat = $protection = []; foreach ($style as $styleType => $styleDatax) { $styleData = self::getSxml($styleDatax); @@ -64,11 +65,31 @@ public function parseStyles(SimpleXMLElement $xml, array $namespaces): array $numberFormat = $numberFormatStyleParser->parseStyle($styleAttributes); } + break; + case 'Protection': + $locked = $hidden = null; + $styleAttributesP = $styleData->attributes($namespaces['x']); + if (isset($styleAttributes['Protected'])) { + $locked = ((bool) (string) $styleAttributes['Protected']) ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED; + } + if (isset($styleAttributesP['HideFormula'])) { + $hidden = ((bool) (string) $styleAttributesP['HideFormula']) ? Protection::PROTECTION_PROTECTED : Protection::PROTECTION_UNPROTECTED; + } + if ($locked !== null || $hidden !== null) { + $protection['protection'] = []; + if ($locked !== null) { + $protection['protection']['locked'] = $locked; + } + if ($hidden !== null) { + $protection['protection']['hidden'] = $hidden; + } + } + break; } } - $this->styles[$styleID] = array_merge($alignment, $border, $font, $fill, $numberFormat); + $this->styles[$styleID] = array_merge($alignment, $border, $font, $fill, $numberFormat, $protection); } return $this->styles; diff --git a/src/PhpSpreadsheet/Reader/Xml/Style/Font.php b/src/PhpSpreadsheet/Reader/Xml/Style/Font.php index 16ab44d80d..5f824889a5 100644 --- a/src/PhpSpreadsheet/Reader/Xml/Style/Font.php +++ b/src/PhpSpreadsheet/Reader/Xml/Style/Font.php @@ -56,11 +56,11 @@ public function parseStyle(SimpleXMLElement $styleAttributes): array break; case 'Bold': - $style['font']['bold'] = true; + $style['font']['bold'] = $styleAttributeValue === '1'; break; case 'Italic': - $style['font']['italic'] = true; + $style['font']['italic'] = $styleAttributeValue === '1'; break; case 'Underline': diff --git a/tests/PhpSpreadsheetTests/Reader/Xml/XmlActiveSheetTest.php b/tests/PhpSpreadsheetTests/Reader/Xml/XmlActiveSheetTest.php new file mode 100644 index 0000000000..5df4feaa78 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xml/XmlActiveSheetTest.php @@ -0,0 +1,116 @@ + + + + + 16.00 + + + + + + 6820 + 19200 + 32767 + 32767 + 1 + False + False + + + + + + + + Sheet 1 + +
+ + False + False + +
+ + + + Sheet 2 + +
+ + + + + 3 + 2 + 2 + + + False + False + +
+ + + + Sheet 3 + +
+ + +
+