Skip to content

More RTL Support for Xlsx/Html Comments #4065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 21 additions & 6 deletions src/PhpSpreadsheet/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -232,24 +240,30 @@ public function getFillColor(): Color
return $this->fillColor;
}

/**
* Set Alignment.
*/
public function setAlignment(string $alignment): self
{
$this->alignment = $alignment;

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.
*/
Expand All @@ -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__
);
Expand Down
10 changes: 10 additions & 0 deletions src/PhpSpreadsheet/Reader/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
14 changes: 13 additions & 1 deletion src/PhpSpreadsheet/Reader/Xlsx.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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];

Expand All @@ -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]);
}
}
}
Expand All @@ -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)) {
Expand Down
9 changes: 8 additions & 1 deletion src/PhpSpreadsheet/Writer/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 .= '<a class="comment-indicator"></a>';
$result .= '<div class="comment">' . $sanitizedString . '</div>';
$result .= "<div class=\"comment\"$dir$alignment>" . $sanitizedString . '</div>';
$result .= PHP_EOL;
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/PhpSpreadsheet/Writer/Xlsx/Comments.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
110 changes: 110 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Html/CommentAlignmentTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;

use PhpOffice\PhpSpreadsheet\Comment;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;

class CommentAlignmentTest extends AbstractFunctional
{
public function testIssue4004(): void
{
$type = 'Html';
$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);

$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 = <<<EOF
Report : ProductsExcel
סטטוס הגדרות בזמן הרצת הדו"ח

2024-06-04 21:07:04
תאריך התחלה :
תאריך סיום :
[email protected]

הצגת ברקוד מקוצר = 0
הצגת חנויות אינטרנט בתוצאות = 1

הצגת מחיר ליחידת מידה = 0
הצגת כל רשומות המחיר לתאריך = 0
רשומות עם מחיר בכל הרשתות = 0
% נפיצות מינימלית = 0
נפיצות מינימלית= 0
% נפיצות מקסימלית = 0
נפיצות מקסימלית= 0
פער אחוזי = 0

התעלמות מכלל המבצעים = 0
התעלמות ממבצעי אשראי = 0
התעלמות ממבצעי מועדון = 0
התעלמות ממבצעים המותנים בסכום מעל 100 ₪. = 0
התעלמות ממבצעים המותנים בקניה של 3 מוצרים ומעלה. = 0

ניתוח מבצעים
============
הצגת כל המבצעים = 0
מבצעי מועדון = 0
מבצעי אשראי = 0
מבצעי ארנק = 0
מחיר מוזל = 0
X יחידות ב-Y ₪ = 0
השני ב = 0
X+Y מתנה = 0
אחוז הנחה הפעלה = 0
אחוז הנחה מספר = 0
מוצרים חסרים - הגבלת חודשים - כמות= 0
EOF;
$comment->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();
}
}
75 changes: 74 additions & 1 deletion tests/PhpSpreadsheetTests/Writer/Xlsx/CommentAlignmentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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 = <<<EOF
Report : ProductsExcel
סטטוס הגדרות בזמן הרצת הדו"ח

2024-06-04 21:07:04
תאריך התחלה :
תאריך סיום :
[email protected]

הצגת ברקוד מקוצר = 0
הצגת חנויות אינטרנט בתוצאות = 1

הצגת מחיר ליחידת מידה = 0
הצגת כל רשומות המחיר לתאריך = 0
רשומות עם מחיר בכל הרשתות = 0
% נפיצות מינימלית = 0
נפיצות מינימלית= 0
% נפיצות מקסימלית = 0
נפיצות מקסימלית= 0
פער אחוזי = 0

התעלמות מכלל המבצעים = 0
התעלמות ממבצעי אשראי = 0
התעלמות ממבצעי מועדון = 0
התעלמות ממבצעים המותנים בסכום מעל 100 ₪. = 0
התעלמות ממבצעים המותנים בקניה של 3 מוצרים ומעלה. = 0

ניתוח מבצעים
============
הצגת כל המבצעים = 0
מבצעי מועדון = 0
מבצעי אשראי = 0
מבצעי ארנק = 0
מחיר מוזל = 0
X יחידות ב-Y ₪ = 0
השני ב = 0
X+Y מתנה = 0
אחוז הנחה הפעלה = 0
אחוז הנחה מספר = 0
מוצרים חסרים - הגבלת חודשים - כמות= 0
EOF;
$comment->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();
}
Expand Down
Loading