objWriter = $objWriter; $this->spreadsheet = $spreadsheet; } public function write(): void { // Write defined names $this->objWriter->startElement('definedNames'); // Named ranges if (count($this->spreadsheet->getDefinedNames()) > 0) { // Named ranges $this->writeNamedRangesAndFormulae(); } // Other defined names $sheetCount = $this->spreadsheet->getSheetCount(); for ($i = 0; $i < $sheetCount; ++$i) { // NamedRange for autoFilter $this->writeNamedRangeForAutofilter($this->spreadsheet->getSheet($i), $i); // NamedRange for Print_Titles $this->writeNamedRangeForPrintTitles($this->spreadsheet->getSheet($i), $i); // NamedRange for Print_Area $this->writeNamedRangeForPrintArea($this->spreadsheet->getSheet($i), $i); } $this->objWriter->endElement(); } /** * Write defined names. */ private function writeNamedRangesAndFormulae(): void { // Loop named ranges $definedNames = $this->spreadsheet->getDefinedNames(); foreach ($definedNames as $definedName) { $this->writeDefinedName($definedName); } } /** * Write Defined Name for named range. */ private function writeDefinedName(DefinedName $pDefinedName): void { // definedName for named range $this->objWriter->startElement('definedName'); $this->objWriter->writeAttribute('name', $pDefinedName->getName()); if ($pDefinedName->getLocalOnly() && $pDefinedName->getScope() !== null) { $this->objWriter->writeAttribute('localSheetId', $pDefinedName->getScope()->getParent()->getIndex($pDefinedName->getScope())); } $definedRange = $pDefinedName->getValue(); $splitCount = preg_match_all( '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/mui', $definedRange, $splitRanges, PREG_OFFSET_CAPTURE ); $lengths = array_map('strlen', array_column($splitRanges[0], 0)); $offsets = array_column($splitRanges[0], 1); $worksheets = $splitRanges[2]; $columns = $splitRanges[6]; $rows = $splitRanges[7]; while ($splitCount > 0) { --$splitCount; $length = $lengths[$splitCount]; $offset = $offsets[$splitCount]; $worksheet = $worksheets[$splitCount][0]; $column = $columns[$splitCount][0]; $row = $rows[$splitCount][0]; $newRange = ''; if (empty($worksheet)) { if (($offset === 0) || ($definedRange[$offset - 1] !== ':')) { // We should have a worksheet $worksheet = $pDefinedName->getWorksheet()->getTitle(); } } else { $worksheet = str_replace("''", "'", trim($worksheet, "'")); } if (!empty($worksheet)) { $newRange = "'" . str_replace("'", "''", $worksheet) . "'!"; } if (!empty($column)) { $newRange .= $column; } if (!empty($row)) { $newRange .= $row; } $definedRange = substr($definedRange, 0, $offset) . $newRange . substr($definedRange, $offset + $length); } if (substr($definedRange, 0, 1) === '=') { $definedRange = substr($definedRange, 1); } $this->objWriter->writeRawData($definedRange); $this->objWriter->endElement(); } /** * Write Defined Name for autoFilter. */ private function writeNamedRangeForAutofilter(Worksheet $pSheet, int $pSheetId = 0): void { // NamedRange for autoFilter $autoFilterRange = $pSheet->getAutoFilter()->getRange(); if (!empty($autoFilterRange)) { $this->objWriter->startElement('definedName'); $this->objWriter->writeAttribute('name', '_xlnm._FilterDatabase'); $this->objWriter->writeAttribute('localSheetId', $pSheetId); $this->objWriter->writeAttribute('hidden', '1'); // Create absolute coordinate and write as raw text $range = Coordinate::splitRange($autoFilterRange); $range = $range[0]; // Strip any worksheet ref so we can make the cell ref absolute [$ws, $range[0]] = Worksheet::extractSheetTitle($range[0], true); $range[0] = Coordinate::absoluteCoordinate($range[0]); $range[1] = Coordinate::absoluteCoordinate($range[1]); $range = implode(':', $range); $this->objWriter->writeRawData('\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . $range); $this->objWriter->endElement(); } } /** * Write Defined Name for PrintTitles. */ private function writeNamedRangeForPrintTitles(Worksheet $pSheet, int $pSheetId = 0): void { // NamedRange for PrintTitles if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) { $this->objWriter->startElement('definedName'); $this->objWriter->writeAttribute('name', '_xlnm.Print_Titles'); $this->objWriter->writeAttribute('localSheetId', $pSheetId); // Setting string $settingString = ''; // Columns to repeat if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) { $repeat = $pSheet->getPageSetup()->getColumnsToRepeatAtLeft(); $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1]; } // Rows to repeat if ($pSheet->getPageSetup()->isRowsToRepeatAtTopSet()) { if ($pSheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) { $settingString .= ','; } $repeat = $pSheet->getPageSetup()->getRowsToRepeatAtTop(); $settingString .= '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1]; } $this->objWriter->writeRawData($settingString); $this->objWriter->endElement(); } } /** * Write Defined Name for PrintTitles. */ private function writeNamedRangeForPrintArea(Worksheet $pSheet, int $pSheetId = 0): void { // NamedRange for PrintArea if ($pSheet->getPageSetup()->isPrintAreaSet()) { $this->objWriter->startElement('definedName'); $this->objWriter->writeAttribute('name', '_xlnm.Print_Area'); $this->objWriter->writeAttribute('localSheetId', $pSheetId); // Print area $printArea = Coordinate::splitRange($pSheet->getPageSetup()->getPrintArea()); $chunks = []; foreach ($printArea as $printAreaRect) { $printAreaRect[0] = Coordinate::absoluteReference($printAreaRect[0]); $printAreaRect[1] = Coordinate::absoluteReference($printAreaRect[1]); $chunks[] = '\'' . str_replace("'", "''", $pSheet->getTitle()) . '\'!' . implode(':', $printAreaRect); } $this->objWriter->writeRawData(implode(',', $chunks)); $this->objWriter->endElement(); } } }