From 75160b12821f7f4299cce7f0b69c83c1502ae071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Mon, 27 May 2024 13:08:29 +0200 Subject: 2024-02-19 upstream --- .../src/PhpSpreadsheet/Reader/Xml.php | 897 +++++++++++++++++++++ 1 file changed, 897 insertions(+) create mode 100644 vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php (limited to 'vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php') diff --git a/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php new file mode 100644 index 0000000..5f909d0 --- /dev/null +++ b/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php @@ -0,0 +1,897 @@ +securityScanner = XmlScanner::getInstance($this); + } + + private $fileContents = ''; + + private static $mappings = [ + 'borderStyle' => [ + '1continuous' => Border::BORDER_THIN, + '1dash' => Border::BORDER_DASHED, + '1dashdot' => Border::BORDER_DASHDOT, + '1dashdotdot' => Border::BORDER_DASHDOTDOT, + '1dot' => Border::BORDER_DOTTED, + '1double' => Border::BORDER_DOUBLE, + '2continuous' => Border::BORDER_MEDIUM, + '2dash' => Border::BORDER_MEDIUMDASHED, + '2dashdot' => Border::BORDER_MEDIUMDASHDOT, + '2dashdotdot' => Border::BORDER_MEDIUMDASHDOTDOT, + '2dot' => Border::BORDER_DOTTED, + '2double' => Border::BORDER_DOUBLE, + '3continuous' => Border::BORDER_THICK, + '3dash' => Border::BORDER_MEDIUMDASHED, + '3dashdot' => Border::BORDER_MEDIUMDASHDOT, + '3dashdotdot' => Border::BORDER_MEDIUMDASHDOTDOT, + '3dot' => Border::BORDER_DOTTED, + '3double' => Border::BORDER_DOUBLE, + ], + 'fillType' => [ + 'solid' => Fill::FILL_SOLID, + 'gray75' => Fill::FILL_PATTERN_DARKGRAY, + 'gray50' => Fill::FILL_PATTERN_MEDIUMGRAY, + 'gray25' => Fill::FILL_PATTERN_LIGHTGRAY, + 'gray125' => Fill::FILL_PATTERN_GRAY125, + 'gray0625' => Fill::FILL_PATTERN_GRAY0625, + 'horzstripe' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe + 'vertstripe' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe + 'reversediagstripe' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe + 'diagstripe' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe + 'diagcross' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch + 'thickdiagcross' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch + 'thinhorzstripe' => Fill::FILL_PATTERN_LIGHTHORIZONTAL, + 'thinvertstripe' => Fill::FILL_PATTERN_LIGHTVERTICAL, + 'thinreversediagstripe' => Fill::FILL_PATTERN_LIGHTUP, + 'thindiagstripe' => Fill::FILL_PATTERN_LIGHTDOWN, + 'thinhorzcross' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch + 'thindiagcross' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch + ], + ]; + + public static function xmlMappings(): array + { + return self::$mappings; + } + + /** + * Can the current IReader read the file? + * + * @param string $pFilename + * + * @return bool + */ + public function canRead($pFilename) + { + // Office xmlns:o="urn:schemas-microsoft-com:office:office" + // Excel xmlns:x="urn:schemas-microsoft-com:office:excel" + // XML Spreadsheet xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" + // Spreadsheet component xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet" + // XML schema xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" + // XML data type xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" + // MS-persist recordset xmlns:rs="urn:schemas-microsoft-com:rowset" + // Rowset xmlns:z="#RowsetSchema" + // + + $signature = [ + '', + ]; + + // Open file + $data = file_get_contents($pFilename); + + // Why? + //$data = str_replace("'", '"', $data); // fix headers with single quote + + $valid = true; + foreach ($signature as $match) { + // every part of the signature must be present + if (strpos($data, $match) === false) { + $valid = false; + + break; + } + } + + // Retrieve charset encoding + if (preg_match('//m', $data, $matches)) { + $charSet = strtoupper($matches[1]); + if (1 == preg_match('/^ISO-8859-\d[\dL]?$/i', $charSet)) { + $data = StringHelper::convertEncoding($data, 'UTF-8', $charSet); + $data = preg_replace('/()/um', '$1' . 'UTF-8' . '$2', $data, 1); + } + } + $this->fileContents = $data; + + return $valid; + } + + /** + * Check if the file is a valid SimpleXML. + * + * @param string $pFilename + * + * @return false|SimpleXMLElement + */ + public function trySimpleXMLLoadString($pFilename) + { + try { + $xml = simplexml_load_string( + $this->securityScanner->scan($this->fileContents ?: file_get_contents($pFilename)), + 'SimpleXMLElement', + Settings::getLibXmlLoaderOptions() + ); + } catch (\Exception $e) { + throw new Exception('Cannot load invalid XML file: ' . $pFilename, 0, $e); + } + $this->fileContents = ''; + + return $xml; + } + + /** + * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object. + * + * @param string $pFilename + * + * @return array + */ + public function listWorksheetNames($pFilename) + { + File::assertFile($pFilename); + if (!$this->canRead($pFilename)) { + throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); + } + + $worksheetNames = []; + + $xml = $this->trySimpleXMLLoadString($pFilename); + + $namespaces = $xml->getNamespaces(true); + + $xml_ss = $xml->children($namespaces['ss']); + foreach ($xml_ss->Worksheet as $worksheet) { + $worksheet_ss = $worksheet->attributes($namespaces['ss']); + $worksheetNames[] = (string) $worksheet_ss['Name']; + } + + return $worksheetNames; + } + + /** + * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns). + * + * @param string $pFilename + * + * @return array + */ + public function listWorksheetInfo($pFilename) + { + File::assertFile($pFilename); + if (!$this->canRead($pFilename)) { + throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); + } + + $worksheetInfo = []; + + $xml = $this->trySimpleXMLLoadString($pFilename); + + $namespaces = $xml->getNamespaces(true); + + $worksheetID = 1; + $xml_ss = $xml->children($namespaces['ss']); + foreach ($xml_ss->Worksheet as $worksheet) { + $worksheet_ss = $worksheet->attributes($namespaces['ss']); + + $tmpInfo = []; + $tmpInfo['worksheetName'] = ''; + $tmpInfo['lastColumnLetter'] = 'A'; + $tmpInfo['lastColumnIndex'] = 0; + $tmpInfo['totalRows'] = 0; + $tmpInfo['totalColumns'] = 0; + + $tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}"; + if (isset($worksheet_ss['Name'])) { + $tmpInfo['worksheetName'] = (string) $worksheet_ss['Name']; + } + + if (isset($worksheet->Table->Row)) { + $rowIndex = 0; + + foreach ($worksheet->Table->Row as $rowData) { + $columnIndex = 0; + $rowHasData = false; + + foreach ($rowData->Cell as $cell) { + if (isset($cell->Data)) { + $tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex); + $rowHasData = true; + } + + ++$columnIndex; + } + + ++$rowIndex; + + if ($rowHasData) { + $tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex); + } + } + } + + $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1); + $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1; + + $worksheetInfo[] = $tmpInfo; + ++$worksheetID; + } + + return $worksheetInfo; + } + + /** + * Loads Spreadsheet from file. + * + * @param string $pFilename + * + * @return Spreadsheet + */ + public function load($pFilename) + { + // Create new Spreadsheet + $spreadsheet = new Spreadsheet(); + $spreadsheet->removeSheetByIndex(0); + + // Load into this instance + return $this->loadIntoExisting($pFilename, $spreadsheet); + } + + private static function identifyFixedStyleValue($styleList, &$styleAttributeValue) + { + $returnValue = false; + $styleAttributeValue = strtolower($styleAttributeValue); + foreach ($styleList as $style) { + if ($styleAttributeValue == strtolower($style)) { + $styleAttributeValue = $style; + $returnValue = true; + + break; + } + } + + return $returnValue; + } + + protected static function hex2str($hex) + { + return mb_chr((int) hexdec($hex[1]), 'UTF-8'); + } + + /** + * Loads from file into Spreadsheet instance. + * + * @param string $pFilename + * + * @return Spreadsheet + */ + public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet) + { + File::assertFile($pFilename); + if (!$this->canRead($pFilename)) { + throw new Exception($pFilename . ' is an Invalid Spreadsheet file.'); + } + + $xml = $this->trySimpleXMLLoadString($pFilename); + + $namespaces = $xml->getNamespaces(true); + + $docProps = $spreadsheet->getProperties(); + if (isset($xml->DocumentProperties[0])) { + foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) { + $stringValue = (string) $propertyValue; + switch ($propertyName) { + case 'Title': + $docProps->setTitle($stringValue); + + break; + case 'Subject': + $docProps->setSubject($stringValue); + + break; + case 'Author': + $docProps->setCreator($stringValue); + + break; + case 'Created': + $creationDate = strtotime($stringValue); + $docProps->setCreated($creationDate); + + break; + case 'LastAuthor': + $docProps->setLastModifiedBy($stringValue); + + break; + case 'LastSaved': + $lastSaveDate = strtotime($stringValue); + $docProps->setModified($lastSaveDate); + + break; + case 'Company': + $docProps->setCompany($stringValue); + + break; + case 'Category': + $docProps->setCategory($stringValue); + + break; + case 'Manager': + $docProps->setManager($stringValue); + + break; + case 'Keywords': + $docProps->setKeywords($stringValue); + + break; + case 'Description': + $docProps->setDescription($stringValue); + + break; + } + } + } + if (isset($xml->CustomDocumentProperties)) { + foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) { + $propertyAttributes = $propertyValue->attributes($namespaces['dt']); + $propertyName = preg_replace_callback('/_x([0-9a-f]{4})_/i', ['self', 'hex2str'], $propertyName); + $propertyType = Properties::PROPERTY_TYPE_UNKNOWN; + switch ((string) $propertyAttributes) { + case 'string': + $propertyType = Properties::PROPERTY_TYPE_STRING; + $propertyValue = trim($propertyValue); + + break; + case 'boolean': + $propertyType = Properties::PROPERTY_TYPE_BOOLEAN; + $propertyValue = (bool) $propertyValue; + + break; + case 'integer': + $propertyType = Properties::PROPERTY_TYPE_INTEGER; + $propertyValue = (int) $propertyValue; + + break; + case 'float': + $propertyType = Properties::PROPERTY_TYPE_FLOAT; + $propertyValue = (float) $propertyValue; + + break; + case 'dateTime.tz': + $propertyType = Properties::PROPERTY_TYPE_DATE; + $propertyValue = strtotime(trim($propertyValue)); + + break; + } + $docProps->setCustomProperty($propertyName, $propertyValue, $propertyType); + } + } + + $this->parseStyles($xml, $namespaces); + + $worksheetID = 0; + $xml_ss = $xml->children($namespaces['ss']); + + foreach ($xml_ss->Worksheet as $worksheet) { + $worksheet_ss = $worksheet->attributes($namespaces['ss']); + + if ( + (isset($this->loadSheetsOnly)) && (isset($worksheet_ss['Name'])) && + (!in_array($worksheet_ss['Name'], $this->loadSheetsOnly)) + ) { + continue; + } + + // Create new Worksheet + $spreadsheet->createSheet(); + $spreadsheet->setActiveSheetIndex($worksheetID); + if (isset($worksheet_ss['Name'])) { + $worksheetName = (string) $worksheet_ss['Name']; + // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in + // formula cells... during the load, all formulae should be correct, and we're simply bringing + // the worksheet name in line with the formula, not the reverse + $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); + } + + // locally scoped defined names + if (isset($worksheet->Names[0])) { + foreach ($worksheet->Names[0] as $definedName) { + $definedName_ss = $definedName->attributes($namespaces['ss']); + $name = (string) $definedName_ss['Name']; + $definedValue = (string) $definedName_ss['RefersTo']; + $convertedValue = AddressHelper::convertFormulaToA1($definedValue); + if ($convertedValue[0] === '=') { + $convertedValue = substr($convertedValue, 1); + } + $spreadsheet->addDefinedName(DefinedName::createInstance($name, $spreadsheet->getActiveSheet(), $convertedValue, true)); + } + } + + $columnID = 'A'; + if (isset($worksheet->Table->Column)) { + foreach ($worksheet->Table->Column as $columnData) { + $columnData_ss = $columnData->attributes($namespaces['ss']); + if (isset($columnData_ss['Index'])) { + $columnID = Coordinate::stringFromColumnIndex((int) $columnData_ss['Index']); + } + if (isset($columnData_ss['Width'])) { + $columnWidth = $columnData_ss['Width']; + $spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4); + } + ++$columnID; + } + } + + $rowID = 1; + if (isset($worksheet->Table->Row)) { + $additionalMergedCells = 0; + foreach ($worksheet->Table->Row as $rowData) { + $rowHasData = false; + $row_ss = $rowData->attributes($namespaces['ss']); + if (isset($row_ss['Index'])) { + $rowID = (int) $row_ss['Index']; + } + + $columnID = 'A'; + foreach ($rowData->Cell as $cell) { + $cell_ss = $cell->attributes($namespaces['ss']); + if (isset($cell_ss['Index'])) { + $columnID = Coordinate::stringFromColumnIndex((int) $cell_ss['Index']); + } + $cellRange = $columnID . $rowID; + + if ($this->getReadFilter() !== null) { + if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) { + ++$columnID; + + continue; + } + } + + if (isset($cell_ss['HRef'])) { + $spreadsheet->getActiveSheet()->getCell($cellRange)->getHyperlink()->setUrl((string) $cell_ss['HRef']); + } + + if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) { + $columnTo = $columnID; + if (isset($cell_ss['MergeAcross'])) { + $additionalMergedCells += (int) $cell_ss['MergeAcross']; + $columnTo = Coordinate::stringFromColumnIndex(Coordinate::columnIndexFromString($columnID) + $cell_ss['MergeAcross']); + } + $rowTo = $rowID; + if (isset($cell_ss['MergeDown'])) { + $rowTo = $rowTo + $cell_ss['MergeDown']; + } + $cellRange .= ':' . $columnTo . $rowTo; + $spreadsheet->getActiveSheet()->mergeCells($cellRange); + } + + $hasCalculatedValue = false; + $cellDataFormula = ''; + if (isset($cell_ss['Formula'])) { + $cellDataFormula = $cell_ss['Formula']; + $hasCalculatedValue = true; + } + if (isset($cell->Data)) { + $cellData = $cell->Data; + $cellValue = (string) $cellData; + $type = DataType::TYPE_NULL; + $cellData_ss = $cellData->attributes($namespaces['ss']); + if (isset($cellData_ss['Type'])) { + $cellDataType = $cellData_ss['Type']; + switch ($cellDataType) { + /* + const TYPE_STRING = 's'; + const TYPE_FORMULA = 'f'; + const TYPE_NUMERIC = 'n'; + const TYPE_BOOL = 'b'; + const TYPE_NULL = 'null'; + const TYPE_INLINE = 'inlineStr'; + const TYPE_ERROR = 'e'; + */ + case 'String': + $type = DataType::TYPE_STRING; + + break; + case 'Number': + $type = DataType::TYPE_NUMERIC; + $cellValue = (float) $cellValue; + if (floor($cellValue) == $cellValue) { + $cellValue = (int) $cellValue; + } + + break; + case 'Boolean': + $type = DataType::TYPE_BOOL; + $cellValue = ($cellValue != 0); + + break; + case 'DateTime': + $type = DataType::TYPE_NUMERIC; + $cellValue = Date::PHPToExcel(strtotime($cellValue . ' UTC')); + + break; + case 'Error': + $type = DataType::TYPE_ERROR; + $hasCalculatedValue = false; + + break; + } + } + + if ($hasCalculatedValue) { + $type = DataType::TYPE_FORMULA; + $columnNumber = Coordinate::columnIndexFromString($columnID); + $cellDataFormula = AddressHelper::convertFormulaToA1($cellDataFormula, $rowID, $columnNumber); + } + + $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type); + if ($hasCalculatedValue) { + $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue); + } + $rowHasData = true; + } + + if (isset($cell->Comment)) { + $commentAttributes = $cell->Comment->attributes($namespaces['ss']); + $author = 'unknown'; + if (isset($commentAttributes->Author)) { + $author = (string) $commentAttributes->Author; + } + $node = $cell->Comment->Data->asXML(); + $annotation = strip_tags($node); + $spreadsheet->getActiveSheet()->getComment($columnID . $rowID)->setAuthor($author)->setText($this->parseRichText($annotation)); + } + + if (isset($cell_ss['StyleID'])) { + $style = (string) $cell_ss['StyleID']; + if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) { + //if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) { + // $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null); + //} + $spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styles[$style]); + } + } + ++$columnID; + while ($additionalMergedCells > 0) { + ++$columnID; + --$additionalMergedCells; + } + } + + if ($rowHasData) { + if (isset($row_ss['Height'])) { + $rowHeight = $row_ss['Height']; + $spreadsheet->getActiveSheet()->getRowDimension($rowID)->setRowHeight($rowHeight); + } + } + + ++$rowID; + } + + $xmlX = $worksheet->children($namespaces['x']); + if (isset($xmlX->WorksheetOptions)) { + (new PageSettings($xmlX, $namespaces))->loadPageSettings($spreadsheet); + } + } + ++$worksheetID; + } + + // Globally scoped defined names + $activeWorksheet = $spreadsheet->setActiveSheetIndex(0); + if (isset($xml->Names[0])) { + foreach ($xml->Names[0] as $definedName) { + $definedName_ss = $definedName->attributes($namespaces['ss']); + $name = (string) $definedName_ss['Name']; + $definedValue = (string) $definedName_ss['RefersTo']; + $convertedValue = AddressHelper::convertFormulaToA1($definedValue); + if ($convertedValue[0] === '=') { + $convertedValue = substr($convertedValue, 1); + } + $spreadsheet->addDefinedName(DefinedName::createInstance($name, $activeWorksheet, $convertedValue)); + } + } + + // Return + return $spreadsheet; + } + + protected function parseRichText($is) + { + $value = new RichText(); + + $value->createText($is); + + return $value; + } + + private function parseStyles(SimpleXMLElement $xml, array $namespaces): void + { + if (!isset($xml->Styles)) { + return; + } + + foreach ($xml->Styles[0] as $style) { + $style_ss = $style->attributes($namespaces['ss']); + $styleID = (string) $style_ss['ID']; + $this->styles[$styleID] = (isset($this->styles['Default'])) ? $this->styles['Default'] : []; + foreach ($style as $styleType => $styleData) { + $styleAttributes = $styleData->attributes($namespaces['ss']); + switch ($styleType) { + case 'Alignment': + $this->parseStyleAlignment($styleID, $styleAttributes); + + break; + case 'Borders': + $this->parseStyleBorders($styleID, $styleData, $namespaces); + + break; + case 'Font': + $this->parseStyleFont($styleID, $styleAttributes); + + break; + case 'Interior': + $this->parseStyleInterior($styleID, $styleAttributes); + + break; + case 'NumberFormat': + $this->parseStyleNumberFormat($styleID, $styleAttributes); + + break; + } + } + } + } + + /** + * @param string $styleID + */ + private function parseStyleAlignment($styleID, SimpleXMLElement $styleAttributes): void + { + $verticalAlignmentStyles = [ + Alignment::VERTICAL_BOTTOM, + Alignment::VERTICAL_TOP, + Alignment::VERTICAL_CENTER, + Alignment::VERTICAL_JUSTIFY, + ]; + $horizontalAlignmentStyles = [ + Alignment::HORIZONTAL_GENERAL, + Alignment::HORIZONTAL_LEFT, + Alignment::HORIZONTAL_RIGHT, + Alignment::HORIZONTAL_CENTER, + Alignment::HORIZONTAL_CENTER_CONTINUOUS, + Alignment::HORIZONTAL_JUSTIFY, + ]; + + foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) { + $styleAttributeValue = (string) $styleAttributeValue; + switch ($styleAttributeKey) { + case 'Vertical': + if (self::identifyFixedStyleValue($verticalAlignmentStyles, $styleAttributeValue)) { + $this->styles[$styleID]['alignment']['vertical'] = $styleAttributeValue; + } + + break; + case 'Horizontal': + if (self::identifyFixedStyleValue($horizontalAlignmentStyles, $styleAttributeValue)) { + $this->styles[$styleID]['alignment']['horizontal'] = $styleAttributeValue; + } + + break; + case 'WrapText': + $this->styles[$styleID]['alignment']['wrapText'] = true; + + break; + case 'Rotate': + $this->styles[$styleID]['alignment']['textRotation'] = $styleAttributeValue; + + break; + } + } + } + + private static $borderPositions = ['top', 'left', 'bottom', 'right']; + + /** + * @param $styleID + */ + private function parseStyleBorders($styleID, SimpleXMLElement $styleData, array $namespaces): void + { + $diagonalDirection = ''; + $borderPosition = ''; + foreach ($styleData->Border as $borderStyle) { + $borderAttributes = $borderStyle->attributes($namespaces['ss']); + $thisBorder = []; + $style = (string) $borderAttributes->Weight; + $style .= strtolower((string) $borderAttributes->LineStyle); + $thisBorder['borderStyle'] = self::$mappings['borderStyle'][$style] ?? Border::BORDER_NONE; + foreach ($borderAttributes as $borderStyleKey => $borderStyleValue) { + switch ($borderStyleKey) { + case 'Position': + $borderStyleValue = strtolower((string) $borderStyleValue); + if (in_array($borderStyleValue, self::$borderPositions)) { + $borderPosition = $borderStyleValue; + } elseif ($borderStyleValue == 'diagonalleft') { + $diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_DOWN; + } elseif ($borderStyleValue == 'diagonalright') { + $diagonalDirection = $diagonalDirection ? Borders::DIAGONAL_BOTH : Borders::DIAGONAL_UP; + } + + break; + case 'Color': + $borderColour = substr($borderStyleValue, 1); + $thisBorder['color']['rgb'] = $borderColour; + + break; + } + } + if ($borderPosition) { + $this->styles[$styleID]['borders'][$borderPosition] = $thisBorder; + } elseif ($diagonalDirection) { + $this->styles[$styleID]['borders']['diagonalDirection'] = $diagonalDirection; + $this->styles[$styleID]['borders']['diagonal'] = $thisBorder; + } + } + } + + private static $underlineStyles = [ + Font::UNDERLINE_NONE, + Font::UNDERLINE_DOUBLE, + Font::UNDERLINE_DOUBLEACCOUNTING, + Font::UNDERLINE_SINGLE, + Font::UNDERLINE_SINGLEACCOUNTING, + ]; + + private function parseStyleFontUnderline(string $styleID, string $styleAttributeValue): void + { + if (self::identifyFixedStyleValue(self::$underlineStyles, $styleAttributeValue)) { + $this->styles[$styleID]['font']['underline'] = $styleAttributeValue; + } + } + + private function parseStyleFontVerticalAlign(string $styleID, string $styleAttributeValue): void + { + if ($styleAttributeValue == 'Superscript') { + $this->styles[$styleID]['font']['superscript'] = true; + } + if ($styleAttributeValue == 'Subscript') { + $this->styles[$styleID]['font']['subscript'] = true; + } + } + + /** + * @param $styleID + */ + private function parseStyleFont(string $styleID, SimpleXMLElement $styleAttributes): void + { + foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) { + $styleAttributeValue = (string) $styleAttributeValue; + switch ($styleAttributeKey) { + case 'FontName': + $this->styles[$styleID]['font']['name'] = $styleAttributeValue; + + break; + case 'Size': + $this->styles[$styleID]['font']['size'] = $styleAttributeValue; + + break; + case 'Color': + $this->styles[$styleID]['font']['color']['rgb'] = substr($styleAttributeValue, 1); + + break; + case 'Bold': + $this->styles[$styleID]['font']['bold'] = true; + + break; + case 'Italic': + $this->styles[$styleID]['font']['italic'] = true; + + break; + case 'Underline': + $this->parseStyleFontUnderline($styleID, $styleAttributeValue); + + break; + case 'VerticalAlign': + $this->parseStyleFontVerticalAlign($styleID, $styleAttributeValue); + + break; + } + } + } + + /** + * @param $styleID + */ + private function parseStyleInterior($styleID, SimpleXMLElement $styleAttributes): void + { + foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) { + switch ($styleAttributeKey) { + case 'Color': + $this->styles[$styleID]['fill']['endColor']['rgb'] = substr($styleAttributeValue, 1); + $this->styles[$styleID]['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1); + + break; + case 'PatternColor': + $this->styles[$styleID]['fill']['startColor']['rgb'] = substr($styleAttributeValue, 1); + + break; + case 'Pattern': + $lcStyleAttributeValue = strtolower((string) $styleAttributeValue); + $this->styles[$styleID]['fill']['fillType'] = self::$mappings['fillType'][$lcStyleAttributeValue] ?? Fill::FILL_NONE; + + break; + } + } + } + + /** + * @param $styleID + */ + private function parseStyleNumberFormat($styleID, SimpleXMLElement $styleAttributes): void + { + $fromFormats = ['\-', '\ ']; + $toFormats = ['-', ' ']; + + foreach ($styleAttributes as $styleAttributeKey => $styleAttributeValue) { + $styleAttributeValue = str_replace($fromFormats, $toFormats, $styleAttributeValue); + switch ($styleAttributeValue) { + case 'Short Date': + $styleAttributeValue = 'dd/mm/yyyy'; + + break; + } + + if ($styleAttributeValue > '') { + $this->styles[$styleID]['numberFormat']['formatCode'] = $styleAttributeValue; + } + } + } +} -- cgit v1.2.3