AutorÃa | Ultima modificación | Ver Log |
<?phpnamespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;use Composer\Pcre\Preg;use Exception;use PhpOffice\PhpSpreadsheet\Calculation\Calculation;use PhpOffice\PhpSpreadsheet\Cell\Coordinate;use PhpOffice\PhpSpreadsheet\DefinedName;use PhpOffice\PhpSpreadsheet\Shared\StringHelper;use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;use PhpOffice\PhpSpreadsheet\Spreadsheet;use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet as ActualWorksheet;class DefinedNames{private XMLWriter $objWriter;private Spreadsheet $spreadsheet;public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet){$this->objWriter = $objWriter;$this->spreadsheet = $spreadsheet;}public function write(): void{// Write defined names$this->objWriter->startElement('definedNames');// Named rangesif (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 $definedName): void{// definedName for named range$local = -1;if ($definedName->getLocalOnly() && $definedName->getScope() !== null) {try {$local = $definedName->getScope()->getParentOrThrow()->getIndex($definedName->getScope());} catch (Exception) {// See issue 2266 - deleting sheet which contains// defined names will cause Exception above.return;}}$this->objWriter->startElement('definedName');$this->objWriter->writeAttribute('name', $definedName->getName());if ($local >= 0) {$this->objWriter->writeAttribute('localSheetId',"$local");}$definedRange = $this->getDefinedRange($definedName);$this->objWriter->writeRawData($definedRange);$this->objWriter->endElement();}/*** Write Defined Name for autoFilter.*/private function writeNamedRangeForAutofilter(ActualWorksheet $worksheet, int $worksheetId = 0): void{// NamedRange for autoFilter$autoFilterRange = $worksheet->getAutoFilter()->getRange();if (!empty($autoFilterRange)) {$this->objWriter->startElement('definedName');$this->objWriter->writeAttribute('name', '_xlnm._FilterDatabase');$this->objWriter->writeAttribute('localSheetId', "$worksheetId");$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[, $range[0]] = ActualWorksheet::extractSheetTitle($range[0], true);$range[0] = Coordinate::absoluteCoordinate($range[0] ?? '');if (count($range) > 1) {$range[1] = Coordinate::absoluteCoordinate($range[1]);}$range = implode(':', $range);$this->objWriter->writeRawData('\'' . str_replace("'", "''", $worksheet->getTitle()) . '\'!' . $range);$this->objWriter->endElement();}}/*** Write Defined Name for PrintTitles.*/private function writeNamedRangeForPrintTitles(ActualWorksheet $worksheet, int $worksheetId = 0): void{// NamedRange for PrintTitlesif ($worksheet->getPageSetup()->isColumnsToRepeatAtLeftSet() || $worksheet->getPageSetup()->isRowsToRepeatAtTopSet()) {$this->objWriter->startElement('definedName');$this->objWriter->writeAttribute('name', '_xlnm.Print_Titles');$this->objWriter->writeAttribute('localSheetId', "$worksheetId");// Setting string$settingString = '';// Columns to repeatif ($worksheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {$repeat = $worksheet->getPageSetup()->getColumnsToRepeatAtLeft();$settingString .= '\'' . str_replace("'", "''", $worksheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];}// Rows to repeatif ($worksheet->getPageSetup()->isRowsToRepeatAtTopSet()) {if ($worksheet->getPageSetup()->isColumnsToRepeatAtLeftSet()) {$settingString .= ',';}$repeat = $worksheet->getPageSetup()->getRowsToRepeatAtTop();$settingString .= '\'' . str_replace("'", "''", $worksheet->getTitle()) . '\'!$' . $repeat[0] . ':$' . $repeat[1];}$this->objWriter->writeRawData($settingString);$this->objWriter->endElement();}}/*** Write Defined Name for PrintTitles.*/private function writeNamedRangeForPrintArea(ActualWorksheet $worksheet, int $worksheetId = 0): void{// NamedRange for PrintAreaif ($worksheet->getPageSetup()->isPrintAreaSet()) {$this->objWriter->startElement('definedName');$this->objWriter->writeAttribute('name', '_xlnm.Print_Area');$this->objWriter->writeAttribute('localSheetId', "$worksheetId");// Print area$printArea = Coordinate::splitRange($worksheet->getPageSetup()->getPrintArea());$chunks = [];foreach ($printArea as $printAreaRect) {$printAreaRect[0] = Coordinate::absoluteReference($printAreaRect[0]);$printAreaRect[1] = Coordinate::absoluteReference($printAreaRect[1]);$chunks[] = '\'' . str_replace("'", "''", $worksheet->getTitle()) . '\'!' . implode(':', $printAreaRect);}$this->objWriter->writeRawData(implode(',', $chunks));$this->objWriter->endElement();}}private function getDefinedRange(DefinedName $definedName): string{$definedRange = $definedName->getValue();$splitCount = Preg::matchAllWithOffsets('/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/mui',$definedRange,$splitRanges);$lengths = array_map([StringHelper::class, 'strlenAllowNull'], 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$ws = $definedName->getWorksheet();$worksheet = ($ws === null) ? null : $ws->getTitle();}} else {$worksheet = str_replace("''", "'", trim($worksheet, "'"));}if (!empty($worksheet)) {$newRange = "'" . str_replace("'", "''", $worksheet) . "'!";}$newRange = "{$newRange}{$column}{$row}";$definedRange = substr($definedRange, 0, $offset) . $newRange . substr($definedRange, $offset + $length);}if (str_starts_with($definedRange, '=')) {$definedRange = substr($definedRange, 1);}return $definedRange;}}