Skip to content

Commit b442eef

Browse files
authored
Merge pull request #4535 from oleibman/issue1104
Writer Html/Pdf Support RTL Alignment of Table
2 parents e1d26a0 + 450c900 commit b442eef

File tree

9 files changed

+340
-24
lines changed

9 files changed

+340
-24
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
2929

3030
### Fixed
3131

32+
- Writer Html/Pdf support RTL alignment of tables. [Issue #1104](https://github.com/PHPOffice/PhpSpreadsheet/issues/1104) [PR #4535](https://github.com/PHPOffice/PhpSpreadsheet/pull/4535)
3233
- Xlsx Reader use dynamic arrays if spreadsheet did so. [PR #4533](https://github.com/PHPOffice/PhpSpreadsheet/pull/4533)
3334
- Ods Reader Nested table-row. [Issue #4528](https://github.com/PHPOffice/PhpSpreadsheet/issues/4528) [Issue #2507](https://github.com/PHPOffice/PhpSpreadsheet/issues/2507) [PR #4531](https://github.com/PHPOffice/PhpSpreadsheet/pull/4531)
3435
- Recognize application/x-empty mimetype. [Issue #4521](https://github.com/PHPOffice/PhpSpreadsheet/issues/4521) [PR #4524](https://github.com/PHPOffice/PhpSpreadsheet/pull/4524)

samples/Pdf/21f_Drawing_mpdf.php renamed to samples/Pdf/21f_Drawing.php

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
use PhpOffice\PhpSpreadsheet\Spreadsheet;
44
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
5-
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf;
65

76
require __DIR__ . '/../Header.php';
8-
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
9-
require_once __DIR__ . '/Mpdf2.php';
107

8+
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
119
$spreadsheet = new Spreadsheet();
1210
$sheet = $spreadsheet->getActiveSheet();
1311

@@ -41,11 +39,6 @@
4139
$spreadsheet->mergeDrawingCellsForPdf();
4240

4341
$helper->log('Write to Mpdf');
44-
$writer = new Mpdf($spreadsheet);
45-
$filename = $helper->getFileName(__FILE__, 'pdf');
46-
$writer->save($filename);
47-
$helper->log("Saved $filename");
48-
if (PHP_SAPI !== 'cli') {
49-
echo '<a href="/download.php?type=pdf&name=' . basename($filename) . '">Download ' . basename($filename) . '</a><br />';
50-
}
42+
$helper->write($spreadsheet, __FILE__, ['Mpdf']);
43+
5144
$spreadsheet->disconnectWorksheets();

samples/Pdf/21g_Direction.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
4+
5+
require __DIR__ . '/../Header.php';
6+
7+
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
8+
$spreadsheet = new Spreadsheet();
9+
$sheet = $spreadsheet->getActiveSheet();
10+
11+
$sheet1 = $spreadsheet->getActiveSheet();
12+
$sheet2 = $spreadsheet->createSheet();
13+
$sheet3 = $spreadsheet->createSheet();
14+
$cells = [
15+
['a1', 'b1', 'c1'],
16+
['a2', 'b2', 'c2'],
17+
];
18+
$sheet1->fromArray($cells);
19+
$sheet1->setRightToLeft(true);
20+
$sheet2->fromArray($cells);
21+
$sheet3->fromArray($cells);
22+
$sheet3->setRightToLeft(true);
23+
24+
$helper->log('Write to html, mpdf, tcpdf');
25+
// Save
26+
$helper->write($spreadsheet, __FILE__, ['Html', 'Mpdf', 'Tcpdf']);
27+
28+
$spreadsheet->disconnectWorksheets();

samples/Pdf/21h_DirectionMultiple.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
4+
use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter;
5+
6+
require __DIR__ . '/../Header.php';
7+
8+
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
9+
$spreadsheet = new Spreadsheet();
10+
$sheet = $spreadsheet->getActiveSheet();
11+
12+
$sheet1 = $spreadsheet->getActiveSheet();
13+
$sheet2 = $spreadsheet->createSheet();
14+
$sheet3 = $spreadsheet->createSheet();
15+
$cells = [
16+
['a1', 'b1', 'c1'],
17+
['a2', 'b2', 'c2'],
18+
];
19+
$sheet1->fromArray($cells);
20+
$sheet1->setRightToLeft(true);
21+
$sheet2->fromArray($cells);
22+
$sheet3->fromArray($cells);
23+
$sheet3->setRightToLeft(true);
24+
25+
$helper->log('Write to html, mpdf');
26+
// Save
27+
$helper->write(
28+
$spreadsheet,
29+
__FILE__,
30+
['Html', 'Mpdf'],
31+
writerCallback: function (HtmlWriter $writer): void {
32+
$writer->writeAllSheets();
33+
}
34+
);
35+
36+
$spreadsheet->disconnectWorksheets();

src/PhpSpreadsheet/Reader/Html.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,9 @@ private function processDomElementTable(Worksheet $sheet, int &$row, string &$co
532532
$sheet->setShowGridlines(in_array('gridlines', $classes, true));
533533
$sheet->setPrintGridlines(in_array('gridlinesp', $classes, true));
534534
}
535+
if ('rtl' === ($attributeArray['dir'] ?? '')) {
536+
$sheet->setRightToLeft(true);
537+
}
535538
$this->currentColumn = 'A';
536539
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
537540
$column = $this->setTableStartColumn($column);

src/PhpSpreadsheet/Writer/Html.php

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ class Html extends BaseWriter
158158

159159
private string $getFalse = 'FALSE';
160160

161+
protected bool $rtlSheets = false;
162+
163+
protected bool $ltrSheets = false;
164+
161165
/**
162166
* Create a new HTML.
163167
*/
@@ -186,11 +190,31 @@ public function save($filename, int $flags = 0): void
186190
$this->maybeCloseFileHandle();
187191
}
188192

193+
protected function checkRtlAndLtr(): void
194+
{
195+
$this->rtlSheets = false;
196+
$this->ltrSheets = false;
197+
if ($this->sheetIndex === null) {
198+
foreach ($this->spreadsheet->getAllSheets() as $sheet) {
199+
if ($sheet->getRightToLeft()) {
200+
$this->rtlSheets = true;
201+
} else {
202+
$this->ltrSheets = true;
203+
}
204+
}
205+
} else {
206+
if ($this->spreadsheet->getSheet($this->sheetIndex)->getRightToLeft()) {
207+
$this->rtlSheets = true;
208+
}
209+
}
210+
}
211+
189212
/**
190213
* Save Spreadsheet as html to variable.
191214
*/
192215
public function generateHtmlAll(): string
193216
{
217+
$this->checkRtlAndLtr();
194218
$sheets = $this->generateSheetPrep();
195219
foreach ($sheets as $sheet) {
196220
$sheet->calculateArrays($this->preCalculateFormulas);
@@ -369,7 +393,8 @@ public function generateHTMLHeader(bool $includeStyles = false): string
369393
// Construct HTML
370394
$properties = $this->spreadsheet->getProperties();
371395
$html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . PHP_EOL;
372-
$html .= '<html xmlns="http://www.w3.org/1999/xhtml">' . PHP_EOL;
396+
$rtl = ($this->rtlSheets && !$this->ltrSheets) ? " dir='rtl'" : '';
397+
$html .= '<html xmlns="http://www.w3.org/1999/xhtml"' . $rtl . '>' . PHP_EOL;
373398
$html .= ' <head>' . PHP_EOL;
374399
$html .= ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . PHP_EOL;
375400
$html .= ' <meta name="generator" content="PhpSpreadsheet, https://github.com/PHPOffice/PhpSpreadsheet" />' . PHP_EOL;
@@ -1013,6 +1038,9 @@ public function buildCSS(bool $generateSurroundingHTML = true): array
10131038
// .s {}
10141039
$css['.s']['text-align'] = 'left'; // STRING
10151040

1041+
$css['.floatright']['float'] = 'right';
1042+
$css['.floatleft']['float'] = 'left';
1043+
10161044
// Calculate cell style hashes
10171045
foreach ($this->spreadsheet->getCellXfCollection() as $index => $style) {
10181046
$css['td.style' . $index . ', th.style' . $index] = $this->createCSSStyle($style);
@@ -1221,21 +1249,52 @@ public function generateHTMLFooter(): string
12211249
return $html;
12221250
}
12231251

1252+
private function getDir(Worksheet $worksheet): string
1253+
{
1254+
if ($worksheet->getRightToLeft()) {
1255+
return " dir='rtl'";
1256+
}
1257+
if ($this->rtlSheets) {
1258+
return " dir='ltr'";
1259+
}
1260+
1261+
return '';
1262+
}
1263+
1264+
private function getFloat(Worksheet $worksheet): string
1265+
{
1266+
$float = '';
1267+
if ($worksheet->getRightToLeft()) {
1268+
if ($this->ltrSheets) {
1269+
$float = ' floatright';
1270+
}
1271+
} else {
1272+
if ($this->rtlSheets) {
1273+
$float = ' floatleft';
1274+
}
1275+
}
1276+
1277+
return $float;
1278+
}
1279+
12241280
private function generateTableTagInline(Worksheet $worksheet, string $id): string
12251281
{
12261282
$style = isset($this->cssStyles['table'])
12271283
? $this->assembleCSS($this->cssStyles['table']) : '';
1228-
1284+
$rtl = $this->getDir($worksheet);
1285+
$float = $this->getFloat($worksheet);
12291286
$prntgrid = $worksheet->getPrintGridlines();
12301287
$viewgrid = $this->isPdf ? $prntgrid : $worksheet->getShowGridlines();
12311288
if ($viewgrid && $prntgrid) {
1232-
$html = " <table border='1' cellpadding='1' $id cellspacing='1' style='$style' class='gridlines gridlinesp'>" . PHP_EOL;
1289+
$html = " <table border='1' cellpadding='1'$rtl $id cellspacing='1' style='$style' class='gridlines gridlinesp$float'>" . PHP_EOL;
12331290
} elseif ($viewgrid) {
1234-
$html = " <table border='0' cellpadding='0' $id cellspacing='0' style='$style' class='gridlines'>" . PHP_EOL;
1291+
$html = " <table border='0' cellpadding='0'$rtl $id cellspacing='0' style='$style' class='gridlines$float'>" . PHP_EOL;
12351292
} elseif ($prntgrid) {
1236-
$html = " <table border='0' cellpadding='0' $id cellspacing='0' style='$style' class='gridlinesp'>" . PHP_EOL;
1293+
$html = " <table border='0' cellpadding='0'$rtl $id cellspacing='0' style='$style' class='gridlinesp$float'>" . PHP_EOL;
1294+
} elseif ($float === '') {
1295+
$html = " <table border='0' cellpadding='1'$rtl $id cellspacing='0' style='$style'>" . PHP_EOL;
12371296
} else {
1238-
$html = " <table border='0' cellpadding='1' $id cellspacing='0' style='$style'>" . PHP_EOL;
1297+
$html = " <table border='0' cellpadding='1'$rtl $id cellspacing='0' style='$style' class='$float'>" . PHP_EOL;
12391298
}
12401299

12411300
return $html;
@@ -1244,9 +1303,11 @@ private function generateTableTagInline(Worksheet $worksheet, string $id): strin
12441303
private function generateTableTag(Worksheet $worksheet, string $id, string &$html, int $sheetIndex): void
12451304
{
12461305
if (!$this->useInlineCss) {
1306+
$rtl = $this->getDir($worksheet);
1307+
$float = $this->getFloat($worksheet);
12471308
$gridlines = $worksheet->getShowGridlines() ? ' gridlines' : '';
12481309
$gridlinesp = $worksheet->getPrintGridlines() ? ' gridlinesp' : '';
1249-
$html .= " <table border='0' cellpadding='0' cellspacing='0' $id class='sheet$sheetIndex$gridlines$gridlinesp'>" . PHP_EOL;
1310+
$html .= " <table border='0' cellpadding='0' cellspacing='0'$rtl $id class='sheet$sheetIndex$gridlines$gridlinesp$float'>" . PHP_EOL;
12501311
} else {
12511312
$html .= $this->generateTableTagInline($worksheet, $id);
12521313
}
@@ -1265,10 +1326,12 @@ private function generateTableHeader(Worksheet $worksheet, bool $showid = true):
12651326
// Construct HTML
12661327
$html = '';
12671328
$id = $showid ? "id='sheet$sheetIndex'" : '';
1329+
$clear = ($this->rtlSheets && $this->ltrSheets) ? '; clear:both' : '';
1330+
12681331
if ($showid) {
1269-
$html .= "<div style='page: page$sheetIndex'>" . PHP_EOL;
1332+
$html .= "<div style='page: page$sheetIndex$clear'>" . PHP_EOL;
12701333
} else {
1271-
$html .= "<div style='page: page$sheetIndex' class='scrpgbrk'>" . PHP_EOL;
1334+
$html .= "<div style='page: page$sheetIndex$clear' class='scrpgbrk'>" . PHP_EOL;
12721335
}
12731336

12741337
$this->generateTableTag($worksheet, $id, $html, $sheetIndex);

src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,28 @@ public function save($filename, int $flags = 0): void
6767

6868
// Set the appropriate font
6969
$pdf->SetFont($this->getFont());
70+
$this->checkRtlAndLtr();
71+
if ($this->rtlSheets && !$this->ltrSheets) {
72+
$pdf->setRTL(true);
73+
}
7074
$pdf->writeHTML($this->generateHTMLAll());
7175

7276
// Document info
73-
$pdf->SetTitle($this->spreadsheet->getProperties()->getTitle());
74-
$pdf->SetAuthor($this->spreadsheet->getProperties()->getCreator());
75-
$pdf->SetSubject($this->spreadsheet->getProperties()->getSubject());
76-
$pdf->SetKeywords($this->spreadsheet->getProperties()->getKeywords());
77-
$pdf->SetCreator($this->spreadsheet->getProperties()->getCreator());
77+
$pdf->SetTitle(
78+
$this->spreadsheet->getProperties()->getTitle()
79+
);
80+
$pdf->SetAuthor(
81+
$this->spreadsheet->getProperties()->getCreator()
82+
);
83+
$pdf->SetSubject(
84+
$this->spreadsheet->getProperties()->getSubject()
85+
);
86+
$pdf->SetKeywords(
87+
$this->spreadsheet->getProperties()->getKeywords()
88+
);
89+
$pdf->SetCreator(
90+
$this->spreadsheet->getProperties()->getCreator()
91+
);
7892

7993
// Write to file
8094
fwrite($fileHandle, $pdf->output('', 'S'));
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Reader\Html;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Html as HtmlReader;
8+
use PHPUnit\Framework\TestCase;
9+
10+
class DirectionTest extends TestCase
11+
{
12+
public function testRtl(): void
13+
{
14+
$inlines = [
15+
"<table border='0' cellpadding='0' cellspacing='0' dir='rtl' id='sheet0' class='sheet0 gridlines'>",
16+
'<tbody>',
17+
'<tr class="row0">',
18+
'<td class="column0 style0 s">a1</td>',
19+
'<td class="column1 style0 s">b1</td>',
20+
'<td class="column2 style0 s">c1</td>',
21+
'</tr>',
22+
'<tr class="row1">',
23+
'<td class="column0 style0 s">a2</td>',
24+
'<td class="column1 style0 s">b2</td>',
25+
'<td class="column2 style0 s">c2</td>',
26+
'</tr>',
27+
'</tbody></table>',
28+
];
29+
$html = implode("\n", $inlines);
30+
$reader = new HtmlReader();
31+
$spreadsheet = $reader->loadFromString($html);
32+
$sheet = $spreadsheet->getActiveSheet();
33+
self::assertTrue($sheet->getRightToLeft());
34+
self::assertSame('a1', $sheet->getCell('A1')->getValue());
35+
self::assertSame('c2', $sheet->getCell('C2')->getValue());
36+
$spreadsheet->disconnectWorksheets();
37+
}
38+
39+
public function testLtr(): void
40+
{
41+
$inlines = [
42+
"<table border='0' cellpadding='0' cellspacing='0' dir='ltr' id='sheet0' class='sheet0 gridlines'>",
43+
'<tbody>',
44+
'<tr class="row0">',
45+
'<td class="column0 style0 s">a1</td>',
46+
'<td class="column1 style0 s">b1</td>',
47+
'<td class="column2 style0 s">c1</td>',
48+
'</tr>',
49+
'<tr class="row1">',
50+
'<td class="column0 style0 s">a2</td>',
51+
'<td class="column1 style0 s">b2</td>',
52+
'<td class="column2 style0 s">c2</td>',
53+
'</tr>',
54+
'</tbody></table>',
55+
];
56+
$html = implode("\n", $inlines);
57+
$reader = new HtmlReader();
58+
$spreadsheet = $reader->loadFromString($html);
59+
$sheet = $spreadsheet->getActiveSheet();
60+
self::assertFalse($sheet->getRightToLeft());
61+
self::assertSame('a1', $sheet->getCell('A1')->getValue());
62+
self::assertSame('c2', $sheet->getCell('C2')->getValue());
63+
$spreadsheet->disconnectWorksheets();
64+
}
65+
66+
public function testDefault(): void
67+
{
68+
$inlines = [
69+
"<table border='0' cellpadding='0' cellspacing='0' id='sheet0' class='sheet0 gridlines'>",
70+
'<tbody>',
71+
'<tr class="row0">',
72+
'<td class="column0 style0 s">a1</td>',
73+
'<td class="column1 style0 s">b1</td>',
74+
'<td class="column2 style0 s">c1</td>',
75+
'</tr>',
76+
'<tr class="row1">',
77+
'<td class="column0 style0 s">a2</td>',
78+
'<td class="column1 style0 s">b2</td>',
79+
'<td class="column2 style0 s">c2</td>',
80+
'</tr>',
81+
'</tbody></table>',
82+
];
83+
$html = implode("\n", $inlines);
84+
$reader = new HtmlReader();
85+
$spreadsheet = $reader->loadFromString($html);
86+
$sheet = $spreadsheet->getActiveSheet();
87+
self::assertFalse($sheet->getRightToLeft());
88+
self::assertSame('a1', $sheet->getCell('A1')->getValue());
89+
self::assertSame('c2', $sheet->getCell('C2')->getValue());
90+
$spreadsheet->disconnectWorksheets();
91+
}
92+
}

0 commit comments

Comments
 (0)