From d5791213211e90d9eea88e317771747f5fb2151b Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Thu, 22 Mar 2018 17:02:27 +0200 Subject: [PATCH 1/4] put in xlsx unsupported loaded data + load some supported data - load workbookProtection attributes - save loaded pageSetup[r:id] - save loaded sheet's AlternateContent - save loaded unparsed VmlDrawings - save loaded drawing files `rId` - save loaded draw's AlternateContent - save loaded control properties - save loaded printer settings - save loaded unparsed override content types (for ctrlProp,..) --- src/PhpSpreadsheet/Reader/Xlsx.php | 154 ++++++++++++++++++ src/PhpSpreadsheet/Spreadsheet.php | 8 + src/PhpSpreadsheet/Writer/Xlsx.php | 20 +++ .../Writer/Xlsx/ContentTypes.php | 18 +- src/PhpSpreadsheet/Writer/Xlsx/Drawing.php | 7 + src/PhpSpreadsheet/Writer/Xlsx/Rels.php | 45 ++++- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 37 ++++- 7 files changed, 284 insertions(+), 5 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index b54622cd37..1c8e66bce2 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -655,6 +655,32 @@ public function load($pFilename) } } + // Set protection + if ($xmlWorkbook->workbookProtection) { + if ($xmlWorkbook->workbookProtection['lockRevision']) { + $excel->getSecurity()->setLockRevision((bool) $xmlWorkbook->workbookProtection['lockRevision']); + } + if ($xmlWorkbook->workbookProtection['lockStructure']) { + $excel->getSecurity()->setLockStructure((bool) $xmlWorkbook->workbookProtection['lockStructure']); + } + if ($xmlWorkbook->workbookProtection['lockWindows']) { + $excel->getSecurity()->setLockWindows((bool) $xmlWorkbook->workbookProtection['lockWindows']); + } + if ($xmlWorkbook->workbookProtection['revisionsPassword']) { + $excel->getSecurity()->setRevisionPassword((string) $xmlWorkbook->workbookProtection['revisionsPassword'], true); + } + if ($xmlWorkbook->workbookProtection['workbookPassword']) { + $excel->getSecurity()->setWorkbookPassword((string) $xmlWorkbook->workbookProtection['workbookPassword'], true); + } + } + + // Set workbookView + /* + if ($xmlWorkbook->bookViews && $xmlWorkbook->bookViews->workbookView) { + // activeTab will be setup later + } + */ + $sheetId = 0; // keep track of new sheet id in final workbook $oldSheetId = -1; // keep track of old sheet id in final workbook $countSkippedSheets = 0; // keep track of number of skipped sheets @@ -1185,6 +1211,11 @@ public function load($pFilename) 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'])) { + $excel->unparsedLoadedData['sheets'][$docSheet->getCodeName()]['pageSetupRelId'] = (string) $relAttributes['id']; + } } if ($xmlSheet && $xmlSheet->headerFooter && !$this->readDataOnly) { @@ -1268,6 +1299,16 @@ public function load($pFilename) } } + // unparsed sheet AlternateContent + if ($xmlSheet && !$this->readDataOnly) { + $mc = $xmlSheet->children('http://schemas.openxmlformats.org/markup-compatibility/2006'); + if ($mc->AlternateContent) { + foreach ($mc->AlternateContent as $alternateContent) { + $excel->unparsedLoadedData['sheets'][$docSheet->getCodeName()]['AlternateContents'][] = $alternateContent->asXML(); + } + } + } + // Add hyperlinks $hyperlinks = []; if (!$this->readDataOnly) { @@ -1367,6 +1408,9 @@ public function load($pFilename) } } + // later we will remove from it real vmlComments + $unparsedVmlDrawings = $vmlComments; + // Loop through VML comments foreach ($vmlComments as $relName => $relPath) { // Load VML comments file @@ -1431,11 +1475,26 @@ public function load($pFilename) $comment->setVisible($stylePair[1] == 'visible'); } } + + unset($unparsedVmlDrawings[$relName]); } } } } + // unparsed vmlDrawing + if ($unparsedVmlDrawings) { + foreach ($unparsedVmlDrawings as $rId => $relPath) { + $rId = substr($rId, 3); // rIdXXX + $unparsedVmlDrawing = &$excel->unparsedLoadedData['sheets'][$docSheet->getCodeName()]['vmlDrawings']; + $unparsedVmlDrawing[$rId] = []; + $unparsedVmlDrawing[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $relPath); + $unparsedVmlDrawing[$rId]['relFilePath'] = $relPath; + $unparsedVmlDrawing[$rId]['content'] = $this->securityScan($this->getFromZipArchive($zip, $unparsedVmlDrawing[$rId]['filePath'])); + unset($unparsedVmlDrawing); + } + } + // Header/footer images if ($xmlSheet && $xmlSheet->legacyDrawingHF && !$this->readDataOnly) { if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) { @@ -1680,9 +1739,86 @@ public function load($pFilename) } } } + + // store original rId of drawing files + $excel->unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'] = []; + foreach ($relsWorksheet->Relationship as $ele) { + if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing') { + $excel->unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingOriginalIds'][(string) $ele['Target']] = (string) $ele['Id']; + } + } + + // unparsed drawing AlternateContent + $xmlAltDrawing = simplexml_load_string( + $this->securityScan($this->getFromZipArchive($zip, $fileDrawing)), + 'SimpleXMLElement', + Settings::getLibXmlLoaderOptions() + )->children('http://schemas.openxmlformats.org/markup-compatibility/2006'); + + if ($xmlAltDrawing->AlternateContent) { + foreach ($xmlAltDrawing->AlternateContent as $alternateContent) { + $excel->unparsedLoadedData['sheets'][$docSheet->getCodeName()]['drawingAlternateContents'][] = $alternateContent->asXML(); + } + } } } + // form control properties + if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) { + $relsWorksheet = simplexml_load_string( + //~ http://schemas.openxmlformats.org/package/2006/relationships" + $this->securityScan( + $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels') + ), + 'SimpleXMLElement', + Settings::getLibXmlLoaderOptions() + ); + $ctrlProps = []; + foreach ($relsWorksheet->Relationship as $ele) { + if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp') { + $ctrlProps[(string) $ele['Id']] = $ele; + } + } + + $unparsedCtrlProps = &$excel->unparsedLoadedData['sheets'][$docSheet->getCodeName()]['ctrlProps']; + foreach ($ctrlProps as $rId => $ctrlProp) { + $rId = substr($rId, 3); // rIdXXX + $unparsedCtrlProps[$rId] = []; + $unparsedCtrlProps[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $ctrlProp['Target']); + $unparsedCtrlProps[$rId]['relFilePath'] = (string) $ctrlProp['Target']; + $unparsedCtrlProps[$rId]['content'] = $this->securityScan($this->getFromZipArchive($zip, $unparsedCtrlProps[$rId]['filePath'])); + } + unset($unparsedCtrlProps); + } + + // printer settings + if ($zip->locateName(dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels')) { + $relsWorksheet = simplexml_load_string( + //~ http://schemas.openxmlformats.org/package/2006/relationships" + $this->securityScan( + $this->getFromZipArchive($zip, dirname("$dir/$fileWorksheet") . '/_rels/' . basename($fileWorksheet) . '.rels') + ), + 'SimpleXMLElement', + Settings::getLibXmlLoaderOptions() + ); + $sheetPrinterSettings = []; + foreach ($relsWorksheet->Relationship as $ele) { + if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings') { + $sheetPrinterSettings[(string) $ele['Id']] = $ele; + } + } + + $unparsedPrinterSettings = &$excel->unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings']; + foreach ($sheetPrinterSettings as $rId => $printerSettings) { + $rId = substr($rId, 3); // rIdXXX + $unparsedPrinterSettings[$rId] = []; + $unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']); + $unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target']; + $unparsedPrinterSettings[$rId]['content'] = $this->securityScan($this->getFromZipArchive($zip, $unparsedPrinterSettings[$rId]['filePath'])); + } + unset($unparsedPrinterSettings); + } + // Loop through definedNames if ($xmlWorkbook->definedNames) { foreach ($xmlWorkbook->definedNames->definedName as $definedName) { @@ -1852,6 +1988,16 @@ public function load($pFilename) 'SimpleXMLElement', Settings::getLibXmlLoaderOptions() ); + // Default content types + foreach ($contentTypes->Default as $contentType) { + switch ($contentType['ContentType']) { + case 'application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings': + $excel->unparsedLoadedData['default_content_types'][(string) $contentType['Extension']] = (string) $contentType['ContentType']; + + break; + } + } + // Override content types foreach ($contentTypes->Override as $contentType) { switch ($contentType['ContentType']) { case 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml': @@ -1876,6 +2022,14 @@ public function load($pFilename) } } } + + break; + + // unparsed + case 'application/vnd.ms-excel.controlproperties+xml': + $excel->unparsedLoadedData['override_content_types'][(string) $contentType['PartName']] = (string) $contentType['ContentType']; + + break; } } } diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index 659b3100d7..eda7951a44 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -115,6 +115,14 @@ class Spreadsheet */ private $ribbonBinObjects; + /** + * unparsedLoadedData : list of unparsed loaded data for export to same format with better compatibility + * has to be minimized, when the library start to support currently unparsed data. + * + * @var array + */ + public $unparsedLoadedData = []; + /** * The workbook has macros ? * diff --git a/src/PhpSpreadsheet/Writer/Xlsx.php b/src/PhpSpreadsheet/Writer/Xlsx.php index 0982c06826..ed75e39d63 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx.php +++ b/src/PhpSpreadsheet/Writer/Xlsx.php @@ -296,6 +296,19 @@ public function save($pFilename) // Add relationships $zip->addFromString('xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts)); + // Add unparsedLoadedData + $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName(); + if (isset($this->getSpreadsheet()->unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) { + foreach ($this->getSpreadsheet()->unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) { + $zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']); + } + } + if (isset($this->getSpreadsheet()->unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) { + foreach ($this->getSpreadsheet()->unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) { + $zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']); + } + } + $drawings = $this->spreadSheet->getSheet($i)->getDrawingCollection(); $drawingCount = count($drawings); if ($this->includeCharts) { @@ -320,6 +333,13 @@ public function save($pFilename) $zip->addFromString('xl/comments' . ($i + 1) . '.xml', $this->getWriterPart('Comments')->writeComments($this->spreadSheet->getSheet($i))); } + // Add unparsed relationship parts + if (isset($this->spreadSheet->unparsedLoadedData['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['vmlDrawings'])) { + foreach ($this->spreadSheet->unparsedLoadedData['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['vmlDrawings'] as $vmlDrawing) { + $zip->addFromString($vmlDrawing['filePath'], $vmlDrawing['content']); + } + } + // Add header/footer relationship parts if (count($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) { // VML Drawings diff --git a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php index a34d34656b..6315776cf5 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php @@ -57,7 +57,9 @@ public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = fal // Yes : not standard content but "macroEnabled" $this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.ms-excel.sheet.macroEnabled.main+xml'); //... and define a new type for the VBA project - $this->writeDefaultContentType($objWriter, 'bin', 'application/vnd.ms-office.vbaProject'); + //$this->writeDefaultContentType($objWriter, 'bin', 'application/vnd.ms-office.vbaProject'); + // Better use Override, because we can use 'bin' also for xl\printerSettings\printerSettings1.bin + $this->writeOverrideContentType($objWriter, '/xl/vbaProject.bin', 'application/vnd.ms-office.vbaProject'); if ($spreadsheet->hasMacrosCertificate()) { // signed macros ? // Yes : add needed information @@ -160,6 +162,20 @@ public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = fal } } + // unparsed defaults + if (isset($spreadsheet->unparsedLoadedData['default_content_types'])) { + foreach ($spreadsheet->unparsedLoadedData['default_content_types'] as $extName => $contentType) { + $this->writeDefaultContentType($objWriter, $extName, $contentType); + } + } + + // unparsed overrides + if (isset($spreadsheet->unparsedLoadedData['override_content_types'])) { + foreach ($spreadsheet->unparsedLoadedData['override_content_types'] as $partName => $overrideType) { + $this->writeOverrideContentType($objWriter, $partName, $overrideType); + } + } + $objWriter->endElement(); // Return diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php b/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php index a8bd8c04ef..d3cc2c9caa 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Drawing.php @@ -59,6 +59,13 @@ public function writeDrawings(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $pWo } } + // unparsed AlternateContent + if (isset($pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingAlternateContents'])) { + foreach ($pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingAlternateContents'] as $drawingAlternateContent) { + $objWriter->writeRaw($drawingAlternateContent); + } + } + $objWriter->endElement(); // Return diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php index e5d2aa531d..6545280447 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php @@ -195,6 +195,10 @@ public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\ // Write drawing relationships? $d = 0; + $drawingOriginalIds = []; + if (isset($pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'])) { + $drawingOriginalIds = $pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds']; + } if ($includeCharts) { $charts = $pWorksheet->getChartCollection(); } else { @@ -202,11 +206,16 @@ public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\ } if (($pWorksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0)) { + $relPath = '../drawings/drawing' . $pWorksheetId . '.xml'; + $rId = ++$d; + if (isset($drawingOriginalIds[$relPath])) { + $rId = substr($drawingOriginalIds[$relPath], 3); + } $this->writeRelationship( $objWriter, - ++$d, + $rId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing', - '../drawings/drawing' . $pWorksheetId . '.xml' + $relPath ); } @@ -255,6 +264,38 @@ public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\ ); } + // Write unparsed relationship? + if (isset($pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['ctrlProps'])) { + foreach ($pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['ctrlProps'] as $rId => $ctrlProp) { + $this->writeRelationship( + $objWriter, + $rId, + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp', + $ctrlProp['relFilePath'] + ); + } + } + if (isset($pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['vmlDrawings'])) { + foreach ($pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['vmlDrawings'] as $rId => $vmlDrawing) { + $this->writeRelationship( + $objWriter, + $rId, + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing', + $vmlDrawing['relFilePath'] + ); + } + } + if (isset($pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['printerSettings'])) { + foreach ($pWorksheet->getParent()->unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['printerSettings'] as $rId => $printerSettings) { + $this->writeRelationship( + $objWriter, + $rId, + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings', + $printerSettings['relFilePath'] + ); + } + } + $objWriter->endElement(); return $objWriter->getData(); diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index f551c60d73..b814e3786c 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -51,6 +51,12 @@ public function writeWorksheet(PhpspreadsheetWorksheet $pSheet, $pStringTable = $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('xmlns:xdr', 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing'); + $objWriter->writeAttribute('xmlns:x14', 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main'); + $objWriter->writeAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006'); + $objWriter->writeAttribute('mc:Ignorable', 'x14ac'); + $objWriter->writeAttribute('xmlns:x14ac', 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac'); + // sheetPr $this->writeSheetPr($objWriter, $pSheet); @@ -114,6 +120,9 @@ public function writeWorksheet(PhpspreadsheetWorksheet $pSheet, $pStringTable = // LegacyDrawingHF $this->writeLegacyDrawingHF($objWriter, $pSheet); + // AlternateContent + $this->writeAlternateContent($objWriter, $pSheet); + $objWriter->endElement(); // Return @@ -283,7 +292,7 @@ private function writeSheetViews(XMLWriter $objWriter, PhpspreadsheetWorksheet $ $objWriter->writeAttribute('pane', $pane); } $objWriter->writeAttribute('activeCell', $activeCell); - $objWriter->writeAttribute('sqref', $activeCell); + $objWriter->writeAttribute('sqref', $pSheet->getSelectedCells()); $objWriter->endElement(); $objWriter->endElement(); @@ -843,6 +852,10 @@ private function writePageSetup(XMLWriter $objWriter, PhpspreadsheetWorksheet $p $objWriter->writeAttribute('useFirstPageNumber', '1'); } + if (isset($pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['pageSetupRelId'])) { + $objWriter->writeAttribute('r:id', $pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['pageSetupRelId']); + } + $objWriter->endElement(); } @@ -1149,7 +1162,16 @@ private function writeDrawings(XMLWriter $objWriter = null, PhpspreadsheetWorksh if (($pSheet->getDrawingCollection()->count() > 0) || ($chartCount > 0)) { $objWriter->startElement('drawing'); - $objWriter->writeAttribute('r:id', 'rId1'); + + $rId = 'rId1'; + $drawingOriginalIds = []; + if (isset($pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'])) { + $drawingOriginalIds = $pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds']; + // take first. In future can be overriten + $rId = reset($drawingOriginalIds); + } + + $objWriter->writeAttribute('r:id', $rId); $objWriter->endElement(); } } @@ -1185,4 +1207,15 @@ private function writeLegacyDrawingHF(XMLWriter $objWriter, PhpspreadsheetWorksh $objWriter->endElement(); } } + + private function writeAlternateContent(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet) + { + if (empty($pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['AlternateContents'])) { + return; + } + + foreach ($pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['AlternateContents'] as $alternateContent) { + $objWriter->writeRaw($alternateContent); + } + } } From 4af397f080a5ee4dfeee27b54b0db38068fcb91a Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 23 Mar 2018 11:07:38 +0200 Subject: [PATCH 2/4] test case --- src/PhpSpreadsheet/Writer/Xlsx.php | 11 +- .../Writer/Xlsx/UnparsedDataTest.php | 104 ++++++++++++++++++ tests/data/Writer/XLSX/form_pass_print.xlsm | Bin 0 -> 17469 bytes 3 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Writer/Xlsx/UnparsedDataTest.php create mode 100644 tests/data/Writer/XLSX/form_pass_print.xlsm diff --git a/src/PhpSpreadsheet/Writer/Xlsx.php b/src/PhpSpreadsheet/Writer/Xlsx.php index ed75e39d63..37d2967690 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx.php +++ b/src/PhpSpreadsheet/Writer/Xlsx.php @@ -298,13 +298,13 @@ public function save($pFilename) // Add unparsedLoadedData $sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName(); - if (isset($this->getSpreadsheet()->unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) { - foreach ($this->getSpreadsheet()->unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) { + if (isset($this->spreadSheet->unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'])) { + foreach ($this->spreadSheet->unparsedLoadedData['sheets'][$sheetCodeName]['ctrlProps'] as $ctrlProp) { $zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']); } } - if (isset($this->getSpreadsheet()->unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) { - foreach ($this->getSpreadsheet()->unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) { + if (isset($this->spreadSheet->unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'])) { + foreach ($this->spreadSheet->unparsedLoadedData['sheets'][$sheetCodeName]['printerSettings'] as $ctrlProp) { $zip->addFromString($ctrlProp['filePath'], $ctrlProp['content']); } } @@ -320,6 +320,9 @@ public function save($pFilename) // Drawing relationships $zip->addFromString('xl/drawings/_rels/drawing' . ($i + 1) . '.xml.rels', $this->getWriterPart('Rels')->writeDrawingRelationships($this->spreadSheet->getSheet($i), $chartRef1, $this->includeCharts)); + // Drawings + $zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts)); + } elseif (isset($this->spreadSheet->unparsedLoadedData['sheets'][$sheetCodeName]['drawingAlternateContents'])) { // Drawings $zip->addFromString('xl/drawings/drawing' . ($i + 1) . '.xml', $this->getWriterPart('Drawing')->writeDrawings($this->spreadSheet->getSheet($i), $this->includeCharts)); } diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/UnparsedDataTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/UnparsedDataTest.php new file mode 100644 index 0000000000..be6a91d625 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/UnparsedDataTest.php @@ -0,0 +1,104 @@ +load($sampleFilename); + + $excel->getSheet(1)->setCellValue('B1', '222'); + + $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($excel); + $writer->save($resultFilename); + if (!file_exists($resultFilename)) { + $this->fail("Result file not found! ($resultFilename)"); + + return; + } + + $resultZip = new \ZipArchive(); + $resultZip->open($resultFilename); + $resultContentTypesRaw = $resultZip->getFromName('[Content_Types].xml'); + $resultControlPropRaw = $resultZip->getFromName('xl/ctrlProps/ctrlProp1.xml'); + $resultDrawingRaw = $resultZip->getFromName('xl/drawings/drawing1.xml'); + $resultVmlDrawingRaw = $resultZip->getFromName('xl/drawings/vmlDrawing1.vml'); + $resultPrinterSettingsRaw = $resultZip->getFromName('xl/printerSettings/printerSettings1.bin'); + $resultVbaProjectRaw = $resultZip->getFromName('xl/vbaProject.bin'); + $resultWorkbookRaw = $resultZip->getFromName('xl/workbook.xml'); + $resultSheet1RelsRaw = $resultZip->getFromName('xl/worksheets/_rels/sheet1.xml.rels'); + $resultSheet1Raw = $resultZip->getFromName('xl/worksheets/sheet1.xml'); + $resultSheet2Raw = $resultZip->getFromName('xl/worksheets/sheet2.xml'); + if (false === $resultZip->close()) { + throw new \Exception("Could not close zip file \"{$resultFilename}\"."); + } + unlink($resultFilename); + + // [Content_Types].xml + $this->assertTrue(strpos($resultContentTypesRaw, 'application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings') > 0, 'Content type for printerSettings not found!'); + $this->assertTrue(strpos($resultContentTypesRaw, 'application/vnd.ms-office.vbaProject') > 0, 'Content type for VbaProject not found!'); + $this->assertTrue(strpos($resultContentTypesRaw, 'application/vnd.ms-excel.controlproperties+xml') > 0, 'Content type for ctrlProp not found!'); + + // xl/ctrlProps/ctrlProp1.xml + $this->assertTrue(!empty($resultControlPropRaw), 'ctrlProp not found!'); + + // xl/drawings/drawing1.xml + $this->assertTrue(strpos($resultDrawingRaw, ' 0, 'AlternateContent at drawing.xml not found!'); + + // xl/drawings/vmlDrawing1.vml + $this->assertTrue(!empty($resultVmlDrawingRaw), 'vmlDrawing not found!'); + + // xl/printerSettings/printerSettings1.bin + $this->assertTrue(!empty($resultPrinterSettingsRaw), 'printerSettings.bin not found!'); + + // xl/vbaProject.bin + $this->assertTrue(!empty($resultVbaProjectRaw), 'vbaProject.bin not found!'); + + // xl/workbook.xml + $xmlWorkbook = simplexml_load_string($resultWorkbookRaw, 'SimpleXMLElement', Settings::getLibXmlLoaderOptions()); + if (!$xmlWorkbook->workbookProtection) { + $this->fail('workbook.xml/workbookProtection not found!'); + } else { + $this->assertEquals($xmlWorkbook->workbookProtection['workbookPassword'], 'CBEB', 'workbook.xml/workbookProtection[workbookPassword] is wrong!'); + $this->assertEquals($xmlWorkbook->workbookProtection['lockStructure'], 'true', 'workbook.xml/workbookProtection[lockStructure] is wrong!'); + + $this->assertEquals($xmlWorkbook->sheets->sheet[0]['state'], '', 'workbook.xml/sheets/sheet[0][state] is wrong!'); + $this->assertEquals($xmlWorkbook->sheets->sheet[1]['state'], 'hidden', 'workbook.xml/sheets/sheet[1][state] is wrong!'); + } + unset($xmlWorkbook); + + // xl/worksheets/_rels/sheet1.xml.rels + $this->assertTrue(strpos($resultSheet1RelsRaw, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings') > 0, 'Sheet relation with printerSettings not found!'); + $this->assertTrue(strpos($resultSheet1RelsRaw, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing') > 0, 'Sheet relation with vmlDrawing not found!'); + $this->assertTrue(strpos($resultSheet1RelsRaw, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp') > 0, 'Sheet relation with ctrlProp not found!'); + + // xl/worksheets/sheet1.xml + $this->assertTrue(strpos($resultSheet1Raw, ' 0, 'AlternateContent at sheet1.xml not found!'); + $xmlWorksheet = simplexml_load_string($resultSheet1Raw, 'SimpleXMLElement', Settings::getLibXmlLoaderOptions()); + $pageSetupAttributes = $xmlWorksheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $this->assertTrue(!empty($pageSetupAttributes['id']), 'sheet1.xml/pageSetup[r:id] not found!'); + if (!$xmlWorksheet->sheetProtection) { + $this->fail('sheet1.xml/sheetProtection not found!'); + } else { + $this->assertEquals($xmlWorksheet->sheetProtection['password'], 'CBEB', 'sheet1.xml/sheetProtection[password] is wrong!'); + $this->assertEquals($xmlWorksheet->sheetProtection['sheet'], 'true', 'sheet1.xml/sheetProtection[sheet] is wrong!'); + $this->assertEquals($xmlWorksheet->sheetProtection['objects'], 'true', 'sheet1.xml/sheetProtection[objects] is wrong!'); + $this->assertEquals($xmlWorksheet->sheetProtection['scenarios'], 'true', 'sheet1.xml/sheetProtection[scenarios] is wrong!'); + } + unset($xmlWorksheet); + + // xl/worksheets/sheet2.xml + $this->assertTrue(!empty($resultSheet2Raw), 'sheet2.xml not found!'); + } +} diff --git a/tests/data/Writer/XLSX/form_pass_print.xlsm b/tests/data/Writer/XLSX/form_pass_print.xlsm new file mode 100644 index 0000000000000000000000000000000000000000..9a593a9cb2b4ec144ab354ab4cbbd6c9a55c3ec1 GIT binary patch literal 17469 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 literal 0 HcmV?d00001 From 0503ffd0b5b618f4a6d58ef7092361f3b576f955 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 23 Mar 2018 13:03:54 +0200 Subject: [PATCH 3/4] code clean up --- src/PhpSpreadsheet/Writer/Xlsx.php | 2 +- .../Writer/Xlsx/ContentTypes.php | 1 - src/PhpSpreadsheet/Writer/Xlsx/Rels.php | 2 +- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 28 +++++++++---------- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Xlsx.php b/src/PhpSpreadsheet/Writer/Xlsx.php index 37d2967690..f0072918e6 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx.php +++ b/src/PhpSpreadsheet/Writer/Xlsx.php @@ -290,7 +290,7 @@ public function save($pFilename) } } - $chartRef1 = $chartRef2 = 0; + $chartRef1 = 0; // Add worksheet relationships (drawings, ...) for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) { // Add relationships diff --git a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php index 6315776cf5..f8d52b29f0 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php @@ -57,7 +57,6 @@ public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = fal // Yes : not standard content but "macroEnabled" $this->writeOverrideContentType($objWriter, '/xl/workbook.xml', 'application/vnd.ms-excel.sheet.macroEnabled.main+xml'); //... and define a new type for the VBA project - //$this->writeDefaultContentType($objWriter, 'bin', 'application/vnd.ms-office.vbaProject'); // Better use Override, because we can use 'bin' also for xl\printerSettings\printerSettings1.bin $this->writeOverrideContentType($objWriter, '/xl/vbaProject.bin', 'application/vnd.ms-office.vbaProject'); if ($spreadsheet->hasMacrosCertificate()) { diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php index 6545280447..1feac6c3f6 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php @@ -209,7 +209,7 @@ public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\ $relPath = '../drawings/drawing' . $pWorksheetId . '.xml'; $rId = ++$d; if (isset($drawingOriginalIds[$relPath])) { - $rId = substr($drawingOriginalIds[$relPath], 3); + $rId = (int) (substr($drawingOriginalIds[$relPath], 3)); } $this->writeRelationship( $objWriter, diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index b814e3786c..3f23703f81 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1155,25 +1155,25 @@ private function writeCell(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet * @param PhpspreadsheetWorksheet $pSheet Worksheet * @param bool $includeCharts Flag indicating if we should include drawing details for charts */ - private function writeDrawings(XMLWriter $objWriter = null, PhpspreadsheetWorksheet $pSheet = null, $includeCharts = false) + private function writeDrawings(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, $includeCharts = false) { $chartCount = ($includeCharts) ? $pSheet->getChartCollection()->count() : 0; + if ($chartCount == 0 && $pSheet->getDrawingCollection()->count() == 0) { + return; + } + // If sheet contains drawings, add the relationships - if (($pSheet->getDrawingCollection()->count() > 0) || - ($chartCount > 0)) { - $objWriter->startElement('drawing'); - - $rId = 'rId1'; - $drawingOriginalIds = []; - if (isset($pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'])) { - $drawingOriginalIds = $pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds']; - // take first. In future can be overriten - $rId = reset($drawingOriginalIds); - } + $objWriter->startElement('drawing'); - $objWriter->writeAttribute('r:id', $rId); - $objWriter->endElement(); + $rId = 'rId1'; + if (isset($pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'])) { + $drawingOriginalIds = $pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds']; + // take first. In future can be overriten + $rId = reset($drawingOriginalIds); } + + $objWriter->writeAttribute('r:id', $rId); + $objWriter->endElement(); } /** From f82bb37ab716a8e65de2169be550157a7a89d262 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Tue, 27 Mar 2018 14:59:24 +0300 Subject: [PATCH 4/4] fix Excel crash result of using unparsed data for cases when source file has drawing-file, but has not draw-elements --- src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php | 3 ++- src/PhpSpreadsheet/Writer/Xlsx/Rels.php | 3 ++- src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php index f8d52b29f0..358ceeef5c 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php @@ -94,9 +94,10 @@ public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = fal $drawings = $spreadsheet->getSheet($i)->getDrawingCollection(); $drawingCount = count($drawings); $chartCount = ($includeCharts) ? $spreadsheet->getSheet($i)->getChartCount() : 0; + $hasUnparsedDrawing = isset($spreadsheet->unparsedLoadedData['sheets'][$spreadsheet->getSheet($i)->getCodeName()]['drawingOriginalIds']); // We need a drawing relationship for the worksheet if we have either drawings or charts - if (($drawingCount > 0) || ($chartCount > 0)) { + if (($drawingCount > 0) || ($chartCount > 0) || $hasUnparsedDrawing) { $this->writeOverrideContentType($objWriter, '/xl/drawings/drawing' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.drawing+xml'); } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php index 1feac6c3f6..c98b6c0cbe 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php @@ -205,7 +205,8 @@ public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\ $charts = []; } if (($pWorksheet->getDrawingCollection()->count() > 0) || - (count($charts) > 0)) { + (count($charts) > 0) || + $drawingOriginalIds) { $relPath = '../drawings/drawing' . $pWorksheetId . '.xml'; $rId = ++$d; if (isset($drawingOriginalIds[$relPath])) { diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 3f23703f81..4522fb850b 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1157,8 +1157,9 @@ private function writeCell(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet */ private function writeDrawings(XMLWriter $objWriter, PhpspreadsheetWorksheet $pSheet, $includeCharts = false) { + $hasUnparsedDrawing = isset($pSheet->getParent()->unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds']); $chartCount = ($includeCharts) ? $pSheet->getChartCollection()->count() : 0; - if ($chartCount == 0 && $pSheet->getDrawingCollection()->count() == 0) { + if ($chartCount == 0 && $pSheet->getDrawingCollection()->count() == 0 && !$hasUnparsedDrawing) { return; }