diff --git a/CHANGELOG.md b/CHANGELOG.md index 73e90dc6..67dbe238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org). ## Unreleased - TBD +- Fixed `ReferenceHelper@insertNewBefore` behavior when removing column before last column with null value + ### Added - Improved support for passing of array arguments to Excel function implementations to return array results (where appropriate). [Issue #2551](https://github.com/PHPOffice/PhpSpreadsheet/issues/2551) diff --git a/samples/Basic/02_Types.php b/samples/Basic/02_Types.php index 79f109f5..965071b8 100644 --- a/samples/Basic/02_Types.php +++ b/samples/Basic/02_Types.php @@ -1,5 +1,6 @@ getActiveSheet() $spreadsheet->getActiveSheet() ->setCellValue('C18', '=HYPERLINK("mailto:abc@def.com","abc@def.com")'); +$spreadsheet->getActiveSheet() + ->setCellValue('A20', 'String') + ->setCellValue('B20', 'inline') + ->setCellValueExplicit('C20', 'This will not be added to sharedStrings.xml', DataType::TYPE_INLINE); + $spreadsheet->getActiveSheet() ->getColumnDimension('B') ->setAutoSize(true); diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index bf696a60..f7625d5e 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -1691,13 +1691,17 @@ class Xlsx extends BaseReader } else { $objText = $value->createTextRun(StringHelper::controlCharacterOOXML2PHP((string) $run->t)); - $attr = $run->rPr->rFont->attributes(); - if (isset($attr['val'])) { - $objText->getFont()->setName((string) $attr['val']); + if (isset($run->rPr->rFont)) { + $attr = $run->rPr->rFont->attributes(); + if (isset($attr['val'])) { + $objText->getFont()->setName((string) $attr['val']); + } } - $attr = $run->rPr->sz->attributes(); - if (isset($attr['val'])) { - $objText->getFont()->setSize((float) $attr['val']); + if (isset($run->rPr->sz)) { + $attr = $run->rPr->sz->attributes(); + if (isset($attr['val'])) { + $objText->getFont()->setSize((float) $attr['val']); + } } if (isset($run->rPr->color)) { $objText->getFont()->setColor(new Color($this->styleReader->readColor($run->rPr->color))); diff --git a/src/PhpSpreadsheet/ReferenceHelper.php b/src/PhpSpreadsheet/ReferenceHelper.php index 0d72b305..98c4807a 100644 --- a/src/PhpSpreadsheet/ReferenceHelper.php +++ b/src/PhpSpreadsheet/ReferenceHelper.php @@ -398,6 +398,26 @@ class ReferenceHelper } } + // Find missing coordinates. This is important when inserting column before the last column + $missingCoordinates = array_filter( + array_map(function ($row) use ($highestColumn) { + return $highestColumn . $row; + }, range(1, $highestRow)), + function ($coordinate) use ($allCoordinates) { + return !in_array($coordinate, $allCoordinates); + } + ); + + // Create missing cells with null values + if (!empty($missingCoordinates)) { + foreach ($missingCoordinates as $coordinate) { + $worksheet->createNewCell($coordinate); + } + + // Refresh all coordinates + $allCoordinates = $worksheet->getCoordinates(); + } + // Loop through cells, bottom-up, and change cell coordinate if ($remove) { // It's faster to reverse and pop than to use unshift, especially with large cell collections diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index 51cf3c52..362f20f0 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -1291,7 +1291,7 @@ class Worksheet implements IComparable * * @return Cell Cell that was created */ - private function createNewCell($coordinate) + public function createNewCell($coordinate) { $cell = new Cell(null, DataType::TYPE_NULL, $this); $this->cellCollection->add($coordinate, $cell); diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php index 2387ceb0..979d3cb7 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -433,6 +433,7 @@ class Worksheet extends BIFFwriter } else { switch ($cell->getDatatype()) { case DataType::TYPE_STRING: + case DataType::TYPE_INLINE: case DataType::TYPE_NULL: if ($cVal === '' || $cVal === null) { $this->writeBlank($row, $column, $xfIndex); diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 86f966e7..ab390a90 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1189,10 +1189,12 @@ class Worksheet extends WriterPart { $objWriter->writeAttribute('t', $mappedType); if (!$cellValue instanceof RichText) { + $objWriter->startElement('is'); $objWriter->writeElement( 't', StringHelper::controlCharacterPHP2OOXML(htmlspecialchars($cellValue, Settings::htmlEntityFlags())) ); + $objWriter->endElement(); } elseif ($cellValue instanceof RichText) { $objWriter->startElement('is'); $this->getParentWriter()->getWriterPartstringtable()->writeRichText($objWriter, $cellValue); diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue2542Test.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue2542Test.php new file mode 100644 index 00000000..ad970001 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue2542Test.php @@ -0,0 +1,47 @@ +Factor group +(for Rental items only)', $data); + } + } + + public function testIssue2542(): void + { + $filename = self::$testbook; + $reader = new Xlsx(); + $spreadsheet = $reader->load($filename); + $sheet = $spreadsheet->getActiveSheet(); + $value = $sheet->getCell('P1')->getValue(); + if ($value instanceof RichText) { + self::assertSame("Factor group\n(for Rental items only)", $value->getPlainText()); + } else { + self::fail('Cell P1 is not RichText'); + } + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/ReferenceHelperTest.php b/tests/PhpSpreadsheetTests/ReferenceHelperTest.php index 874dbfb5..2c016b0b 100644 --- a/tests/PhpSpreadsheetTests/ReferenceHelperTest.php +++ b/tests/PhpSpreadsheetTests/ReferenceHelperTest.php @@ -149,4 +149,32 @@ class ReferenceHelperTest extends TestCase self::assertSame($oldValue, $newValue); self::assertSame($oldDataType, $newDataType); } + + public function testRemoveColumnShiftsCorrectColumnValueIntoRemovedColumnCoordinates(): void + { + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->fromArray([ + ['a1', 'b1', 'c1'], + ['a2', 'b2', null], + ]); + + $cells = $sheet->toArray(); + self::assertSame('a1', $cells[0][0]); + self::assertSame('b1', $cells[0][1]); + self::assertSame('c1', $cells[0][2]); + self::assertSame('a2', $cells[1][0]); + self::assertSame('b2', $cells[1][1]); + self::assertNull($cells[1][2]); + + $sheet->removeColumn('B'); + + $cells = $sheet->toArray(); + self::assertSame('a1', $cells[0][0]); + self::assertSame('c1', $cells[0][1]); + self::assertArrayNotHasKey(2, $cells[0]); + self::assertSame('a2', $cells[1][0]); + self::assertNull($cells[1][1]); + self::assertArrayNotHasKey(2, $cells[1]); + } } diff --git a/tests/data/Reader/XLSX/issue.2542.xlsx b/tests/data/Reader/XLSX/issue.2542.xlsx new file mode 100644 index 00000000..85faa63a Binary files /dev/null and b/tests/data/Reader/XLSX/issue.2542.xlsx differ