diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c37310c60..c8612cad6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Xls Conditional Format Improvements. [PR #4030](https://github.com/PHPOffice/PhpSpreadsheet/pull/4030) [PR #4033](https://github.com/PHPOffice/PhpSpreadsheet/pull/4033) - Conditional Range Unions and Intersections [Issue #4039](https://github.com/PHPOffice/PhpSpreadsheet/issues/4039) [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042) - Csv Reader allow use of html mimetype. [Issue #4036](https://github.com/PHPOffice/PhpSpreadsheet/issues/4036) [PR #4049](https://github.com/PHPOffice/PhpSpreadsheet/pull/4040) +- More RTL in Xlsx/Html Comments [Issue #4004](https://github.com/PHPOffice/PhpSpreadsheet/issues/4004) [PR #4065](https://github.com/PHPOffice/PhpSpreadsheet/pull/4065) - Empty String in sharedStrings. [Issue #4063](https://github.com/PHPOffice/PhpSpreadsheet/issues/4063) [PR #4064](https://github.com/PHPOffice/PhpSpreadsheet/pull/4064) ## 2024-05-11 - 2.1.0 diff --git a/src/PhpSpreadsheet/Comment.php b/src/PhpSpreadsheet/Comment.php index 1c07b5154e..2e18270c1f 100644 --- a/src/PhpSpreadsheet/Comment.php +++ b/src/PhpSpreadsheet/Comment.php @@ -63,6 +63,14 @@ class Comment implements IComparable, Stringable */ private Drawing $backgroundImage; + public const TEXTBOX_DIRECTION_RTL = 'rtl'; + public const TEXTBOX_DIRECTION_LTR = 'ltr'; + // MS uses 'auto' in xml but 'context' in UI + public const TEXTBOX_DIRECTION_AUTO = 'auto'; + public const TEXTBOX_DIRECTION_CONTEXT = 'auto'; + + private string $textboxDirection = ''; + /** * Create a new Comment. */ @@ -232,9 +240,6 @@ public function getFillColor(): Color return $this->fillColor; } - /** - * Set Alignment. - */ public function setAlignment(string $alignment): self { $this->alignment = $alignment; @@ -242,14 +247,23 @@ public function setAlignment(string $alignment): self return $this; } - /** - * Get Alignment. - */ public function getAlignment(): string { return $this->alignment; } + public function setTextboxDirection(string $textboxDirection): self + { + $this->textboxDirection = $textboxDirection; + + return $this; + } + + public function getTextboxDirection(): string + { + return $this->textboxDirection; + } + /** * Get hash code. */ @@ -265,6 +279,7 @@ public function getHashCode(): string . ($this->visible ? 1 : 0) . $this->fillColor->getHashCode() . $this->alignment + . $this->textboxDirection . ($this->hasBackgroundImage() ? $this->backgroundImage->getHashCode() : '') . __CLASS__ ); diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index d365fbf23c..cc4a4859a9 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -9,6 +9,7 @@ use DOMText; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; +use PhpOffice\PhpSpreadsheet\Comment; use PhpOffice\PhpSpreadsheet\Document\Properties; use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException; use PhpOffice\PhpSpreadsheet\Helper\Dimension as CssDimension; @@ -332,6 +333,15 @@ private function processDomElementSpanEtc(Worksheet $sheet, int &$row, string &$ $sheet->getComment($column . $row) ->getText() ->createTextRun($child->textContent); + if (isset($attributeArray['dir']) && $attributeArray['dir'] === 'rtl') { + $sheet->getComment($column . $row)->setTextboxDirection(Comment::TEXTBOX_DIRECTION_RTL); + } + if (isset($attributeArray['style'])) { + $alignStyle = $attributeArray['style']; + if (preg_match('/\\btext-align:\\s*(left|right|center|justify)\\b/', $alignStyle, $matches) === 1) { + $sheet->getComment($column . $row)->setAlignment($matches[1]); + } + } } else { $this->processDomElement($child, $sheet, $row, $column, $cellContent); } diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index d049e79184..9fdf96aeca 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; use PhpOffice\PhpSpreadsheet\Cell\Hyperlink; +use PhpOffice\PhpSpreadsheet\Comment; use PhpOffice\PhpSpreadsheet\DefinedName; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; use PhpOffice\PhpSpreadsheet\Reader\Xlsx\AutoFilter; @@ -1139,6 +1140,14 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet $fillImageTitle = ''; $clientData = $shape->xpath('.//x:ClientData'); + $textboxDirection = ''; + $textboxPath = $shape->xpath('.//v:textbox'); + $textbox = (string) ($textboxPath[0]['style'] ?? ''); + if (preg_match('/rtl/i', $textbox) === 1) { + $textboxDirection = Comment::TEXTBOX_DIRECTION_RTL; + } elseif (preg_match('/ltr/i', $textbox) === 1) { + $textboxDirection = Comment::TEXTBOX_DIRECTION_LTR; + } if (is_array($clientData) && !empty($clientData)) { $clientData = $clientData[0]; @@ -1154,7 +1163,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet } $temp = $clientData->xpath('.//x:TextHAlign'); if (!empty($temp)) { - $textHAlign = $temp[0]; + $textHAlign = strtolower($temp[0]); } } } @@ -1163,6 +1172,9 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet if (is_numeric($rowx) && is_numeric($colx) && $textHAlign !== null) { $docSheet->getComment([1 + (int) $colx, 1 + (int) $rowx], false)->setAlignment((string) $textHAlign); } + if (is_numeric($rowx) && is_numeric($colx) && $textboxDirection !== '') { + $docSheet->getComment([1 + (int) $colx, 1 + (int) $rowx], false)->setTextboxDirection($textboxDirection); + } $fillImageRelNode = $shape->xpath('.//v:fill/@o:relid'); if (is_array($fillImageRelNode) && !empty($fillImageRelNode)) { diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index e344e33989..457af66120 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Chart\Chart; +use PhpOffice\PhpSpreadsheet\Comment; use PhpOffice\PhpSpreadsheet\Document\Properties; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\RichText\Run; @@ -1762,9 +1763,15 @@ private function writeComment(Worksheet $worksheet, string $coordinate): string $result = ''; if (!$this->isPdf && isset($worksheet->getComments()[$coordinate])) { $sanitizedString = $this->generateRowCellDataValueRich($worksheet->getComment($coordinate)->getText()); + $dir = ($worksheet->getComment($coordinate)->getTextboxDirection() === Comment::TEXTBOX_DIRECTION_RTL) ? ' dir="rtl"' : ''; + $align = strtolower($worksheet->getComment($coordinate)->getAlignment()); + $alignment = Alignment::HORIZONTAL_ALIGNMENT_FOR_HTML[$align] ?? ''; + if ($alignment !== '') { + $alignment = " style=\"text-align:$alignment\""; + } if ($sanitizedString !== '') { $result .= ''; - $result .= '
' . $sanitizedString . '
'; + $result .= "
" . $sanitizedString . '
'; $result .= PHP_EOL; } } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Comments.php b/src/PhpSpreadsheet/Writer/Xlsx/Comments.php index f645421977..f42c2427bd 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Comments.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Comments.php @@ -209,12 +209,14 @@ private function writeVMLComment(XMLWriter $objWriter, string $cellReference, Co $objWriter->endElement(); // v:textbox + $textBoxArray = [Comment::TEXTBOX_DIRECTION_RTL => 'rtl', Comment::TEXTBOX_DIRECTION_LTR => 'ltr']; + $textboxRtl = $textBoxArray[strtolower($comment->getTextBoxDirection())] ?? 'auto'; $objWriter->startElement('v:textbox'); - $objWriter->writeAttribute('style', 'mso-direction-alt:auto'); + $objWriter->writeAttribute('style', "mso-direction-alt:$textboxRtl"); // div $objWriter->startElement('div'); - $objWriter->writeAttribute('style', 'text-align:left'); + $objWriter->writeAttribute('style', ($textboxRtl === 'rtl' ? 'text-align:right;direction:rtl' : 'text-align:left')); $objWriter->endElement(); $objWriter->endElement(); diff --git a/tests/PhpSpreadsheetTests/Writer/Html/CommentAlignmentTest.php b/tests/PhpSpreadsheetTests/Writer/Html/CommentAlignmentTest.php new file mode 100644 index 0000000000..a5f65cbfc0 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/CommentAlignmentTest.php @@ -0,0 +1,110 @@ +getActiveSheet(); + $sheet->getCell('A3')->setValue('A3'); + $sheet->getCell('A4')->setValue('A4'); + $sheet->getComment('A3')->getText()->createText('Comment'); + $sheet->getComment('A4')->getText()->createText('שלום'); + $sheet->getComment('A4')->setAlignment(Alignment::HORIZONTAL_RIGHT); + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $type); + $spreadsheet->disconnectWorksheets(); + + self::assertCount(1, $reloadedSpreadsheet->getAllSheets()); + + $rsheet = $reloadedSpreadsheet->getActiveSheet(); + $comment1 = $rsheet->getComment('A3'); + self::assertSame('Comment', $comment1->getText()->getPlainText()); + self::assertSame('general', $comment1->getAlignment()); + $comment2 = $rsheet->getComment('A4'); + self::assertSame('שלום', $comment2->getText()->getPlainText()); + self::assertSame('right', $comment2->getAlignment()); + + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testIssue4004td(): void + { + $type = 'Html'; + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setRightToLeft(true); + $sheet->getCell('A1')->setValue('ברקוד'); + $comment = $sheet->getComment('A1'); + $comment->setTextboxDirection(Comment::TEXTBOX_DIRECTION_RTL); + $comment->setAlignment(Alignment::HORIZONTAL_RIGHT); + $text = <<getText()->createTextRun($text); + $comment->setWidth('300pt'); + $comment->setHeight('550pt'); + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $type); + $spreadsheet->disconnectWorksheets(); + + self::assertCount(1, $reloadedSpreadsheet->getAllSheets()); + + $rsheet = $reloadedSpreadsheet->getActiveSheet(); + $comment1 = $rsheet->getComment('A1'); + self::assertSame($text, $comment1->getText()->getPlainText()); + $comment->setTextboxDirection(Comment::TEXTBOX_DIRECTION_RTL); + self::assertSame('right', $comment1->getAlignment()); + self::assertSame('rtl', $comment1->getTextboxDirection()); + + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/CommentAlignmentTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/CommentAlignmentTest.php index 5f869216fd..bd337bc8db 100644 --- a/tests/PhpSpreadsheetTests/Writer/Xlsx/CommentAlignmentTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/CommentAlignmentTest.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx; +use PhpOffice\PhpSpreadsheet\Comment; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional; @@ -15,6 +16,8 @@ public function testIssue4004(): void $type = 'Xlsx'; $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); + $sheet->getCell('A3')->setValue('A3'); + $sheet->getCell('A4')->setValue('A4'); $sheet->getComment('A3')->getText()->createText('Comment'); $sheet->getComment('A4')->getText()->createText('שלום'); $sheet->getComment('A4')->setAlignment(Alignment::HORIZONTAL_RIGHT); @@ -30,7 +33,77 @@ public function testIssue4004(): void self::assertSame('general', $comment1->getAlignment()); $comment2 = $rsheet->getComment('A4'); self::assertSame('שלום', $comment2->getText()->getPlainText()); - self::assertSame('Right', $comment2->getAlignment()); + self::assertSame('right', $comment2->getAlignment()); + + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testIssue4004td(): void + { + $type = 'Xlsx'; + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->setRightToLeft(true); + $sheet->getCell('A1')->setValue('ברקוד'); + $comment = $sheet->getComment('A1'); + $comment->setTextboxDirection(Comment::TEXTBOX_DIRECTION_RTL); + $comment->setAlignment(Alignment::HORIZONTAL_RIGHT); + $text = <<getText()->createTextRun($text); + $comment->setWidth('300pt'); + $comment->setHeight('550pt'); + + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $type); + $spreadsheet->disconnectWorksheets(); + + self::assertCount(1, $reloadedSpreadsheet->getAllSheets()); + + $rsheet = $reloadedSpreadsheet->getActiveSheet(); + $comment1 = $rsheet->getComment('A1'); + self::assertSame($text, $comment1->getText()->getPlainText()); + $comment->setTextboxDirection(Comment::TEXTBOX_DIRECTION_RTL); + self::assertSame('right', $comment1->getAlignment()); + self::assertSame('rtl', $comment1->getTextboxDirection()); $reloadedSpreadsheet->disconnectWorksheets(); }