Skip to content

Commit b5ba4ff

Browse files
authored
Merge pull request #4655 from oleibman/issue850
Better Support For Style Alignment Read Order
2 parents d52dbaa + 5d0e2bd commit b5ba4ff

File tree

17 files changed

+417
-12
lines changed

17 files changed

+417
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a
1010
### Added
1111

1212
- Option to display numbers with less precision. [Issue #4626](https://github.com/PHPOffice/PhpSpreadsheet/issues/4626) [PR #4640](https://github.com/PHPOffice/PhpSpreadsheet/pull/4640)
13+
- Offer Tcpdf Interface which throws exception rather than die. [PR #4666](https://github.com/PHPOffice/PhpSpreadsheet/pull/4666)
1314

1415
### Removed
1516

@@ -39,6 +40,9 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a
3940
- Use prefix _xlfn for BASE function. [Issue #4638](https://github.com/PHPOffice/PhpSpreadsheet/issues/4638) [PR #4641](https://github.com/PHPOffice/PhpSpreadsheet/pull/4641)
4041
- Warning messages with corrupt Xls file. [Issue #4647](https://github.com/PHPOffice/PhpSpreadsheet/issues/4647) [PR #4648](https://github.com/PHPOffice/PhpSpreadsheet/pull/4648)
4142
- Additional support for union and intersection. [PR #4596](https://github.com/PHPOffice/PhpSpreadsheet/pull/4596)
43+
- Missing array keys x,o,v for Xml Reader. [Issue #4668](https://github.com/PHPOffice/PhpSpreadsheet/issues/4668) [PR #4669](https://github.com/PHPOffice/PhpSpreadsheet/pull/4669)
44+
- Missing array key x for Xlsx Reader VML. [Issue #4505](https://github.com/PHPOffice/PhpSpreadsheet/issues/4505) [PR #4676](https://github.com/PHPOffice/PhpSpreadsheet/pull/4676)
45+
- Better support for Style Alignment Read Order. [Issue #850](https://github.com/PHPOffice/PhpSpreadsheet/issues/850) [PR #4655](https://github.com/PHPOffice/PhpSpreadsheet/pull/4655)
4246

4347
## 2025-09-03 - 5.1.0
4448

src/PhpSpreadsheet/Reader/Html.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
1919
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
2020
use PhpOffice\PhpSpreadsheet\Spreadsheet;
21+
use PhpOffice\PhpSpreadsheet\Style\Alignment;
2122
use PhpOffice\PhpSpreadsheet\Style\Border;
2223
use PhpOffice\PhpSpreadsheet\Style\Color;
2324
use PhpOffice\PhpSpreadsheet\Style\Fill;
@@ -1014,6 +1015,17 @@ private function applyInlineStyle(Worksheet &$sheet, int $row, string $column, a
10141015

10151016
break;
10161017

1018+
case 'direction':
1019+
if ($styleValue === 'rtl') {
1020+
$cellStyle->getAlignment()
1021+
->setReadOrder(Alignment::READORDER_RTL);
1022+
} elseif ($styleValue === 'ltr') {
1023+
$cellStyle->getAlignment()
1024+
->setReadOrder(Alignment::READORDER_LTR);
1025+
}
1026+
1027+
break;
1028+
10171029
case 'font-weight':
10181030
if ($styleValue === 'bold' || $styleValue >= 500) {
10191031
$cellStyle->getFont()->setBold(true);
@@ -1083,8 +1095,11 @@ private function applyInlineStyle(Worksheet &$sheet, int $row, string $column, a
10831095
break;
10841096

10851097
case 'text-indent':
1098+
$indentDimension = new CssDimension($styleValueString);
1099+
$indent = $indentDimension
1100+
->toUnit(CssDimension::UOM_PIXELS);
10861101
$cellStyle->getAlignment()->setIndent(
1087-
(int) str_replace(['px'], '', $styleValueString)
1102+
(int) ($indent / Alignment::INDENT_UNITS_TO_PIXELS)
10881103
);
10891104

10901105
break;

src/PhpSpreadsheet/Reader/Xls.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,8 @@ protected function readXf(): void
12541254

12551255
break;
12561256
}
1257+
$readOrder = (0xC0 & ord($recordData[8])) >> 6;
1258+
$objStyle->getAlignment()->setReadOrder($readOrder);
12571259

12581260
// offset: 9; size: 1; Flags used for attribute groups
12591261

src/PhpSpreadsheet/Reader/Xml/Style/Alignment.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ public function parseStyle(SimpleXMLElement $styleAttributes): array
5454
case 'Indent':
5555
$style['alignment']['indent'] = $styleAttributeValue;
5656

57+
break;
58+
case 'ReadingOrder':
59+
if ($styleAttributeValue === 'RightToLeft') {
60+
$style['alignment']['readOrder'] = AlignmentStyles::READORDER_RTL;
61+
} elseif ($styleAttributeValue === 'LeftToRight') {
62+
$style['alignment']['readOrder'] = AlignmentStyles::READORDER_LTR;
63+
}
64+
5765
break;
5866
}
5967
}

src/PhpSpreadsheet/Style/Alignment.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class Alignment extends Supervisor
9292
const TEXTROTATION_STACK_EXCEL = 255;
9393
const TEXTROTATION_STACK_PHPSPREADSHEET = -165; // 90 - 255
9494

95+
public const INDENT_UNITS_TO_PIXELS = 9;
96+
9597
/**
9698
* Horizontal alignment.
9799
*/

src/PhpSpreadsheet/Writer/Html.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,12 @@ private function createCSSStyleAlignment(Alignment $alignment): array
11151115
if ($textAlign) {
11161116
$css['text-align'] = $textAlign;
11171117
if (in_array($textAlign, ['left', 'right'])) {
1118-
$css['padding-' . $textAlign] = (string) ((int) $alignment->getIndent() * 9) . 'px';
1118+
$css['padding-' . $textAlign] = (string) ($alignment->getIndent() * Alignment::INDENT_UNITS_TO_PIXELS) . 'px';
1119+
}
1120+
} else {
1121+
$indent = $alignment->getIndent();
1122+
if ($indent !== 0) {
1123+
$css['text-indent'] = (string) ($alignment->getIndent() * Alignment::INDENT_UNITS_TO_PIXELS) . 'px';
11191124
}
11201125
}
11211126
$rotation = $alignment->getTextRotation();
@@ -1126,6 +1131,12 @@ private function createCSSStyleAlignment(Alignment $alignment): array
11261131
$css['transform'] = "rotate({$rotation}deg)";
11271132
}
11281133
}
1134+
$direction = $alignment->getReadOrder();
1135+
if ($direction === Alignment::READORDER_LTR) {
1136+
$css['direction'] = 'ltr';
1137+
} elseif ($direction === Alignment::READORDER_RTL) {
1138+
$css['direction'] = 'rtl';
1139+
}
11291140

11301141
return $css;
11311142
}
@@ -1516,7 +1527,6 @@ private function generateRowCellDataValue(Worksheet $worksheet, Cell $cell, stri
15161527
/** @param string|string[] $cssClass */
15171528
private function generateRowCellData(Worksheet $worksheet, null|Cell|string $cell, array|string &$cssClass): string
15181529
{
1519-
$cellData = ' ';
15201530
if ($cell instanceof Cell) {
15211531
$cellData = '';
15221532
// Don't know what this does, and no test cases.
@@ -1565,13 +1575,21 @@ private function generateRowCellData(Worksheet $worksheet, null|Cell|string $cel
15651575
}
15661576
}
15671577
} else {
1578+
$cellData = "$cell";
15681579
// Use default borders for empty cell
15691580
if (is_string($cssClass)) {
15701581
$cssClass .= ' style0';
15711582
}
15721583
}
1584+
/*
1585+
* Browsers may remove an entirely empty row.
1586+
* An interesting option is to leave an empty cell empty using css.
1587+
* td:empty::after{content: "\00a0";}
1588+
* This works well in modern browsers.
1589+
* Alas, none of our Pdf writers can handle it.
1590+
*/
15731591

1574-
return $cellData;
1592+
return (trim($cellData) === '') ? ' ' : $cellData;
15751593
}
15761594

15771595
private function generateRowIncludeCharts(Worksheet $worksheet, string $coordinate): string

src/PhpSpreadsheet/Writer/Ods/Cell/Style.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ private function writeCellProperties(CellStyle $style): void
151151
$vAlign = $style->getAlignment()->getVertical();
152152
$wrap = $style->getAlignment()->getWrapText();
153153
$indent = $style->getAlignment()->getIndent();
154+
$readOrder = $style->getAlignment()->getReadOrder();
154155

155156
$this->writer->startElement('style:table-cell-properties');
156157
if (!empty($vAlign) || $wrap) {
@@ -172,7 +173,7 @@ private function writeCellProperties(CellStyle $style): void
172173

173174
$this->writer->endElement();
174175

175-
if ($hAlign !== '' || !empty($indent)) {
176+
if ($hAlign !== '' || !empty($indent) || $readOrder === Alignment::READORDER_RTL || $readOrder === Alignment::READORDER_LTR) {
176177
$this->writer
177178
->startElement('style:paragraph-properties');
178179
if ($hAlign !== '') {
@@ -182,6 +183,11 @@ private function writeCellProperties(CellStyle $style): void
182183
$indentString = sprintf('%.4f', $indent * self::INDENT_TO_INCHES) . 'in';
183184
$this->writer->writeAttribute('fo:margin-left', $indentString);
184185
}
186+
if ($readOrder === Alignment::READORDER_RTL) {
187+
$this->writer->writeAttribute('style:writing-mode', 'rl-tb');
188+
} elseif ($readOrder === Alignment::READORDER_LTR) {
189+
$this->writer->writeAttribute('style:writing-mode', 'lr-tb');
190+
}
185191
$this->writer->endElement();
186192
}
187193
}

src/PhpSpreadsheet/Writer/Xls/Xf.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,9 @@ public function writeXf(): string
220220
$header = pack('vv', $record, $length);
221221

222222
//BIFF8 options: identation, shrinkToFit and text direction
223-
$biff8_options = $this->style->getAlignment()->getIndent();
223+
$biff8_options = $this->style->getAlignment()->getIndent() & 15;
224224
$biff8_options |= (int) $this->style->getAlignment()->getShrinkToFit() << 4;
225+
$biff8_options |= $this->style->getAlignment()->getReadOrder() << 6;
225226

226227
$data = pack('vvvC', $ifnt, $ifmt, $style, $align);
227228
$data .= pack('CCC', self::mapTextRotation((int) $this->style->getAlignment()->getTextRotation()), $biff8_options, $used_attrib);

tests/PhpSpreadsheetTests/Reader/Html/HtmlTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public function testCanApplyAlignment(): void
199199
<td valign="center">Center valign</td>
200200
<td style="text-align: center;">Center align</td>
201201
<td style="vertical-align: center;">Center valign</td>
202-
<td style="text-indent: 10px;">Text indent</td>
202+
<td style="text-indent: 9px;">Text indent</td>
203203
<td style="word-wrap: break-word;">Wraptext</td>
204204
</tr>
205205
</table>';
@@ -220,7 +220,7 @@ public function testCanApplyAlignment(): void
220220
self::assertEquals(Alignment::VERTICAL_CENTER, $style->getAlignment()->getVertical());
221221

222222
$style = $firstSheet->getCell('E1')->getStyle();
223-
self::assertEquals(10, $style->getAlignment()->getIndent());
223+
self::assertEquals(1, $style->getAlignment()->getIndent());
224224

225225
$style = $firstSheet->getCell('F1')->getStyle();
226226
self::assertTrue($style->getAlignment()->getWrapText());
@@ -302,14 +302,14 @@ public function testTextIndentUseRowspan(): void
302302
</tr>
303303
<tr>
304304
<td>2</td>
305-
<td style="text-indent:10px">Text Indent</td>
305+
<td style="text-indent:9px">Text Indent</td>
306306
</tr>
307307
</table>';
308308
$filename = HtmlHelper::createHtml($html);
309309
$spreadsheet = HtmlHelper::loadHtmlIntoSpreadsheet($filename, true);
310310
$firstSheet = $spreadsheet->getSheet(0);
311311
$style = $firstSheet->getCell('C2')->getStyle();
312-
self::assertEquals(10, $style->getAlignment()->getIndent());
312+
self::assertEquals(1, $style->getAlignment()->getIndent());
313313
$spreadsheet->disconnectWorksheets();
314314
}
315315

tests/PhpSpreadsheetTests/Reader/Xlsx/Issue4248Test.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public function testHtml(): void
9393
$data = str_replace(["\r", "\n"], '', $writer->generateHtmlAll());
9494
$expected = ' <tr class="row17">' // Cell D18
9595
. ' <td class="column0 style0">&nbsp;</td>'
96-
. ' <td class="column1 style28 null"></td>'
96+
. ' <td class="column1 style28 null">&nbsp;</td>'
9797
. ' <td class="column2 style35 s">Eligible </td>'
9898
. ' <td class="column3 style70 s">Non</td>';
9999
self::assertStringContainsString($expected, $data, 'Cell D18 style');

0 commit comments

Comments
 (0)