From b57a549fabad3dd07d18e4b385466fd4edd99987 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Thu, 6 Jun 2024 16:55:27 -0700 Subject: [PATCH 1/2] More RTL Support for Xlsx/Html Comments Following up from PR #4006. There is an additional RTL property available. It controls the placement of bidirectional neutral characters (mainly punctuation), as opposed to strong (alphabetic characters) or weak (numeric characters), especially at the beginning or end of a line. The new Comment property textboxDirection will be used for that purpose. In a discussion in issue #4004 following the implementation of the PR, the comment was mixed RTL and LTR, and this led to some formatting problems. The user was able to overcome these with the timely insertion of Unicode directional control characters, but it would be preferable to have it happen automatically, which this change will permit. However, the use of these control characters cannot be entirely done away with. In the new test case, if one of the all-English lines ended with, say, a colon, it would not display correctly; LRM (left-to-right mark) after the colon would be needed. Likewise, one or two of the comment lines with mixed RTL and LTR (discussed in the issue) is not formatted correctly, and might require LRO/PDF or equivalent. --- src/PhpSpreadsheet/Comment.php | 27 ++++- src/PhpSpreadsheet/Reader/Html.php | 10 ++ src/PhpSpreadsheet/Reader/Xlsx.php | 14 ++- src/PhpSpreadsheet/Writer/Html.php | 9 +- src/PhpSpreadsheet/Writer/Xlsx/Comments.php | 6 +- .../Writer/Html/CommentAlignmentTest.php | 110 ++++++++++++++++++ .../Writer/Xlsx/CommentAlignmentTest.php | 75 +++++++++++- 7 files changed, 240 insertions(+), 11 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/CommentAlignmentTest.php 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 9bfb354342..aa2a782547 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; @@ -1137,6 +1138,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]; @@ -1152,7 +1161,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet } $temp = $clientData->xpath('.//x:TextHAlign'); if (!empty($temp)) { - $textHAlign = $temp[0]; + $textHAlign = strtolower($temp[0]); } } } @@ -1161,6 +1170,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 .= '