diff --git a/samples/Reader/22_Reader_formscomments.php b/samples/Reader/22_Reader_formscomments.php new file mode 100644 index 0000000000..3f1dd3c5cb --- /dev/null +++ b/samples/Reader/22_Reader_formscomments.php @@ -0,0 +1,22 @@ +log('Start'); + +$inputFileType = 'Xlsx'; +$inputFileName = __DIR__ . '/sampleData/formscomments.xlsx'; + +$helper->log('Loading file ' . $inputFileName . ' using IOFactory with a defined reader type of ' . $inputFileType); +$reader = IOFactory::createReader($inputFileType); +$helper->log('Loading all WorkSheets'); +$reader->setLoadAllSheets(); +$spreadsheet = $reader->load($inputFileName); + +// Save +$helper->write($spreadsheet, __FILE__, ['Xlsx']); +$spreadsheet->disconnectWorksheets(); + +$helper->log('end'); diff --git a/samples/Reader/22_Reader_issue1767.php b/samples/Reader/22_Reader_issue1767.php index 2c1ce93143..10caef1216 100644 --- a/samples/Reader/22_Reader_issue1767.php +++ b/samples/Reader/22_Reader_issue1767.php @@ -17,5 +17,6 @@ // Save $helper->write($spreadsheet, __FILE__); +$spreadsheet->disconnectWorksheets(); $helper->log('end'); diff --git a/samples/Reader/sampleData/formscomments.xlsx b/samples/Reader/sampleData/formscomments.xlsx new file mode 100644 index 0000000000..b63b00c1ca Binary files /dev/null and b/samples/Reader/sampleData/formscomments.xlsx differ diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 8ae685037b..599a558142 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -122,9 +122,12 @@ public static function falseToArray($value): array return is_array($value) ? $value : []; } - private function loadZip(string $filename, string $ns = ''): SimpleXMLElement + private function loadZip(string $filename, string $ns = '', bool $replaceUnclosedBr = false): SimpleXMLElement { $contents = $this->getFromZipArchive($this->zip, $filename); + if ($replaceUnclosedBr) { + $contents = str_replace('
', '
', $contents); + } $rels = simplexml_load_string( $this->securityScanner->scan($contents), 'SimpleXMLElement', @@ -1024,6 +1027,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet // later we will remove from it real vmlComments $unparsedVmlDrawings = $vmlComments; + $vmlDrawingContents = []; // Loop through VML comments foreach ($vmlComments as $relName => $relPath) { @@ -1032,7 +1036,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet try { // no namespace okay - processed with Xpath - $vmlCommentsFile = $this->loadZip($relPath, ''); + $vmlCommentsFile = $this->loadZip($relPath, '', true); $vmlCommentsFile->registerXPathNamespace('v', Namespaces::URN_VML); } catch (Throwable $ex) { //Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData @@ -1042,6 +1046,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet // Locate VML drawings image relations $drowingImages = []; $VMLDrawingsRelations = dirname($relPath) . '/_rels/' . basename($relPath) . '.rels'; + $vmlDrawingContents[$relName] = $this->securityScanner->scan($this->getFromZipArchive($zip, $relPath)); if ($zip->locateName($VMLDrawingsRelations)) { $relsVMLDrawing = $this->loadZip($VMLDrawingsRelations, Namespaces::RELATIONSHIPS); foreach ($relsVMLDrawing->Relationship as $elex) { @@ -1495,6 +1500,14 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet } } } + if ($xmlSheet->legacyDrawing && !$this->readDataOnly) { + foreach ($xmlSheet->legacyDrawing as $drawing) { + $drawingRelId = (string) self::getArrayItem(self::getAttributes($drawing, $xmlNamespaceBase), 'id'); + if (isset($vmlDrawingContents[$drawingRelId])) { + $unparsedLoadedData['sheets'][$docSheet->getCodeName()]['legacyDrawing'] = $vmlDrawingContents[$drawingRelId]; + } + } + } // unparsed drawing AlternateContent $xmlAltDrawing = $this->loadZip((string) $fileDrawing, Namespaces::COMPATIBILITY); diff --git a/src/PhpSpreadsheet/Writer/Xlsx.php b/src/PhpSpreadsheet/Writer/Xlsx.php index c9446e7077..5e7d688ae4 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx.php +++ b/src/PhpSpreadsheet/Writer/Xlsx.php @@ -455,14 +455,17 @@ public function save($filename, int $flags = 0): void } // Add comment relationship parts - if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) { + $legacy = $unparsedLoadedData['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['legacyDrawing'] ?? null; + if (count($this->spreadSheet->getSheet($i)->getComments()) > 0 || $legacy !== null) { // VML Comments relationships $zipContent['xl/drawings/_rels/vmlDrawing' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeVMLDrawingRelationships($this->spreadSheet->getSheet($i)); // VML Comments - $zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i)); + $zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $legacy ?? $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i)); + } - // Comments + // Comments + if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) { $zipContent['xl/comments' . ($i + 1) . '.xml'] = $this->getWriterPartComments()->writeComments($this->spreadSheet->getSheet($i)); // Media @@ -477,7 +480,9 @@ public function save($filename, int $flags = 0): void // Add unparsed relationship parts if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) { foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) { - $zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content']; + if (!isset($zipContent[$vmlDrawing['filePath']])) { + $zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content']; + } } } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php index e621dc3ea3..e1006ea934 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Rels.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Rels.php @@ -239,14 +239,16 @@ public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\ // Write comments relationship? $i = 1; - if (count($worksheet->getComments()) > 0) { + if (count($worksheet->getComments()) > 0 || isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['legacyDrawing'])) { $this->writeRelationship( $objWriter, '_comments_vml' . $i, Namespaces::VML, '../drawings/vmlDrawing' . $worksheetId . '.vml' ); + } + if (count($worksheet->getComments()) > 0) { $this->writeRelationship( $objWriter, '_comments' . $i, diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 58259a6394..b5bebc7a84 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1322,7 +1322,8 @@ private function writeDrawings(XMLWriter $objWriter, PhpspreadsheetWorksheet $wo private function writeLegacyDrawing(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void { // If sheet contains comments, add the relationships - if (count($worksheet->getComments()) > 0) { + $unparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData(); + if (count($worksheet->getComments()) > 0 || isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['legacyDrawing'])) { $objWriter->startElement('legacyDrawing'); $objWriter->writeAttribute('r:id', 'rId_comments_vml1'); $objWriter->endElement();