mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-09-16 13:06:31 +00:00
Writer Html/Pdf Support RTL Alignment of Table
Fix #1104, which went stale over 6 years ago, and is now reopened. This PR addresses the alignment of the table, not alignment of text. Support is added for the following: - Html. Full support. - Mpdf. Full support, except that, when mixed LTR and RTL worksheets are output, all tables will be on the left of the page (but will be properly aligned). - Tcpdf. Full support when all worksheets being output are RTL; no support when mixed LTR and RTL worksheets are output. - Dompdf. No support.
This commit is contained in:
@@ -2,12 +2,10 @@
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf;
|
||||
|
||||
require __DIR__ . '/../Header.php';
|
||||
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
|
||||
require_once __DIR__ . '/Mpdf2.php';
|
||||
|
||||
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
@@ -41,11 +39,6 @@ $helper->log('Merge drawing cells for Pdf');
|
||||
$spreadsheet->mergeDrawingCellsForPdf();
|
||||
|
||||
$helper->log('Write to Mpdf');
|
||||
$writer = new Mpdf($spreadsheet);
|
||||
$filename = $helper->getFileName(__FILE__, 'pdf');
|
||||
$writer->save($filename);
|
||||
$helper->log("Saved $filename");
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
echo '<a href="/download.php?type=pdf&name=' . basename($filename) . '">Download ' . basename($filename) . '</a><br />';
|
||||
}
|
||||
$helper->write($spreadsheet, __FILE__, ['Mpdf']);
|
||||
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
|
||||
require __DIR__ . '/../Header.php';
|
||||
|
||||
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$sheet1 = $spreadsheet->getActiveSheet();
|
||||
$sheet2 = $spreadsheet->createSheet();
|
||||
$sheet3 = $spreadsheet->createSheet();
|
||||
$cells = [
|
||||
['a1', 'b1', 'c1'],
|
||||
['a2', 'b2', 'c2'],
|
||||
];
|
||||
$sheet1->fromArray($cells);
|
||||
$sheet1->setRightToLeft(true);
|
||||
$sheet2->fromArray($cells);
|
||||
$sheet3->fromArray($cells);
|
||||
$sheet3->setRightToLeft(true);
|
||||
|
||||
$helper->log('Write to html, mpdf, tcpdf');
|
||||
// Save
|
||||
$helper->write($spreadsheet, __FILE__, ['Html', 'Mpdf', 'Tcpdf']);
|
||||
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter;
|
||||
|
||||
require __DIR__ . '/../Header.php';
|
||||
|
||||
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$sheet1 = $spreadsheet->getActiveSheet();
|
||||
$sheet2 = $spreadsheet->createSheet();
|
||||
$sheet3 = $spreadsheet->createSheet();
|
||||
$cells = [
|
||||
['a1', 'b1', 'c1'],
|
||||
['a2', 'b2', 'c2'],
|
||||
];
|
||||
$sheet1->fromArray($cells);
|
||||
$sheet1->setRightToLeft(true);
|
||||
$sheet2->fromArray($cells);
|
||||
$sheet3->fromArray($cells);
|
||||
$sheet3->setRightToLeft(true);
|
||||
|
||||
$helper->log('Write to html, mpdf');
|
||||
// Save
|
||||
$helper->write(
|
||||
$spreadsheet,
|
||||
__FILE__,
|
||||
['Html', 'Mpdf'],
|
||||
writerCallback: function (HtmlWriter $writer): void {
|
||||
$writer->writeAllSheets();
|
||||
}
|
||||
);
|
||||
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
@@ -532,6 +532,9 @@ class Html extends BaseReader
|
||||
$sheet->setShowGridlines(in_array('gridlines', $classes, true));
|
||||
$sheet->setPrintGridlines(in_array('gridlinesp', $classes, true));
|
||||
}
|
||||
if ('rtl' === ($attributeArray['dir'] ?? '')) {
|
||||
$sheet->setRightToLeft(true);
|
||||
}
|
||||
$this->currentColumn = 'A';
|
||||
$this->flushCell($sheet, $column, $row, $cellContent, $attributeArray);
|
||||
$column = $this->setTableStartColumn($column);
|
||||
|
||||
@@ -158,6 +158,10 @@ class Html extends BaseWriter
|
||||
|
||||
private string $getFalse = 'FALSE';
|
||||
|
||||
protected bool $rtlSheets = false;
|
||||
|
||||
protected bool $ltrSheets = false;
|
||||
|
||||
/**
|
||||
* Create a new HTML.
|
||||
*/
|
||||
@@ -186,11 +190,31 @@ class Html extends BaseWriter
|
||||
$this->maybeCloseFileHandle();
|
||||
}
|
||||
|
||||
protected function checkRtlAndLtr(): void
|
||||
{
|
||||
$this->rtlSheets = false;
|
||||
$this->ltrSheets = false;
|
||||
if ($this->sheetIndex === null) {
|
||||
foreach ($this->spreadsheet->getAllSheets() as $sheet) {
|
||||
if ($sheet->getRightToLeft()) {
|
||||
$this->rtlSheets = true;
|
||||
} else {
|
||||
$this->ltrSheets = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($this->spreadsheet->getSheet($this->sheetIndex)->getRightToLeft()) {
|
||||
$this->rtlSheets = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Spreadsheet as html to variable.
|
||||
*/
|
||||
public function generateHtmlAll(): string
|
||||
{
|
||||
$this->checkRtlAndLtr();
|
||||
$sheets = $this->generateSheetPrep();
|
||||
foreach ($sheets as $sheet) {
|
||||
$sheet->calculateArrays($this->preCalculateFormulas);
|
||||
@@ -369,7 +393,8 @@ class Html extends BaseWriter
|
||||
// Construct HTML
|
||||
$properties = $this->spreadsheet->getProperties();
|
||||
$html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' . PHP_EOL;
|
||||
$html .= '<html xmlns="http://www.w3.org/1999/xhtml">' . PHP_EOL;
|
||||
$rtl = ($this->rtlSheets && !$this->ltrSheets) ? " dir='rtl'" : '';
|
||||
$html .= '<html xmlns="http://www.w3.org/1999/xhtml"' . $rtl . '>' . PHP_EOL;
|
||||
$html .= ' <head>' . PHP_EOL;
|
||||
$html .= ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' . PHP_EOL;
|
||||
$html .= ' <meta name="generator" content="PhpSpreadsheet, https://github.com/PHPOffice/PhpSpreadsheet" />' . PHP_EOL;
|
||||
@@ -1013,6 +1038,9 @@ class Html extends BaseWriter
|
||||
// .s {}
|
||||
$css['.s']['text-align'] = 'left'; // STRING
|
||||
|
||||
$css['.floatright']['float'] = 'right';
|
||||
$css['.floatleft']['float'] = 'left';
|
||||
|
||||
// Calculate cell style hashes
|
||||
foreach ($this->spreadsheet->getCellXfCollection() as $index => $style) {
|
||||
$css['td.style' . $index . ', th.style' . $index] = $this->createCSSStyle($style);
|
||||
@@ -1221,21 +1249,52 @@ class Html extends BaseWriter
|
||||
return $html;
|
||||
}
|
||||
|
||||
private function getDir(Worksheet $worksheet): string
|
||||
{
|
||||
if ($worksheet->getRightToLeft()) {
|
||||
return " dir='rtl'";
|
||||
}
|
||||
if ($this->rtlSheets) {
|
||||
return " dir='ltr'";
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getFloat(Worksheet $worksheet): string
|
||||
{
|
||||
$float = '';
|
||||
if ($worksheet->getRightToLeft()) {
|
||||
if ($this->ltrSheets) {
|
||||
$float = ' floatright';
|
||||
}
|
||||
} else {
|
||||
if ($this->rtlSheets) {
|
||||
$float = ' floatleft';
|
||||
}
|
||||
}
|
||||
|
||||
return $float;
|
||||
}
|
||||
|
||||
private function generateTableTagInline(Worksheet $worksheet, string $id): string
|
||||
{
|
||||
$style = isset($this->cssStyles['table'])
|
||||
? $this->assembleCSS($this->cssStyles['table']) : '';
|
||||
|
||||
$rtl = $this->getDir($worksheet);
|
||||
$float = $this->getFloat($worksheet);
|
||||
$prntgrid = $worksheet->getPrintGridlines();
|
||||
$viewgrid = $this->isPdf ? $prntgrid : $worksheet->getShowGridlines();
|
||||
if ($viewgrid && $prntgrid) {
|
||||
$html = " <table border='1' cellpadding='1' $id cellspacing='1' style='$style' class='gridlines gridlinesp'>" . PHP_EOL;
|
||||
$html = " <table border='1' cellpadding='1'$rtl $id cellspacing='1' style='$style' class='gridlines gridlinesp$float'>" . PHP_EOL;
|
||||
} elseif ($viewgrid) {
|
||||
$html = " <table border='0' cellpadding='0' $id cellspacing='0' style='$style' class='gridlines'>" . PHP_EOL;
|
||||
$html = " <table border='0' cellpadding='0'$rtl $id cellspacing='0' style='$style' class='gridlines$float'>" . PHP_EOL;
|
||||
} elseif ($prntgrid) {
|
||||
$html = " <table border='0' cellpadding='0' $id cellspacing='0' style='$style' class='gridlinesp'>" . PHP_EOL;
|
||||
$html = " <table border='0' cellpadding='0'$rtl $id cellspacing='0' style='$style' class='gridlinesp$float'>" . PHP_EOL;
|
||||
} elseif ($float === '') {
|
||||
$html = " <table border='0' cellpadding='1'$rtl $id cellspacing='0' style='$style'>" . PHP_EOL;
|
||||
} else {
|
||||
$html = " <table border='0' cellpadding='1' $id cellspacing='0' style='$style'>" . PHP_EOL;
|
||||
$html = " <table border='0' cellpadding='1'$rtl $id cellspacing='0' style='$style' class='$float'>" . PHP_EOL;
|
||||
}
|
||||
|
||||
return $html;
|
||||
@@ -1244,9 +1303,11 @@ class Html extends BaseWriter
|
||||
private function generateTableTag(Worksheet $worksheet, string $id, string &$html, int $sheetIndex): void
|
||||
{
|
||||
if (!$this->useInlineCss) {
|
||||
$rtl = $this->getDir($worksheet);
|
||||
$float = $this->getFloat($worksheet);
|
||||
$gridlines = $worksheet->getShowGridlines() ? ' gridlines' : '';
|
||||
$gridlinesp = $worksheet->getPrintGridlines() ? ' gridlinesp' : '';
|
||||
$html .= " <table border='0' cellpadding='0' cellspacing='0' $id class='sheet$sheetIndex$gridlines$gridlinesp'>" . PHP_EOL;
|
||||
$html .= " <table border='0' cellpadding='0' cellspacing='0'$rtl $id class='sheet$sheetIndex$gridlines$gridlinesp$float'>" . PHP_EOL;
|
||||
} else {
|
||||
$html .= $this->generateTableTagInline($worksheet, $id);
|
||||
}
|
||||
@@ -1265,10 +1326,12 @@ class Html extends BaseWriter
|
||||
// Construct HTML
|
||||
$html = '';
|
||||
$id = $showid ? "id='sheet$sheetIndex'" : '';
|
||||
$clear = ($this->rtlSheets && $this->ltrSheets) ? '; clear:both' : '';
|
||||
|
||||
if ($showid) {
|
||||
$html .= "<div style='page: page$sheetIndex'>" . PHP_EOL;
|
||||
$html .= "<div style='page: page$sheetIndex$clear'>" . PHP_EOL;
|
||||
} else {
|
||||
$html .= "<div style='page: page$sheetIndex' class='scrpgbrk'>" . PHP_EOL;
|
||||
$html .= "<div style='page: page$sheetIndex$clear' class='scrpgbrk'>" . PHP_EOL;
|
||||
}
|
||||
|
||||
$this->generateTableTag($worksheet, $id, $html, $sheetIndex);
|
||||
|
||||
@@ -67,14 +67,28 @@ class Tcpdf extends Pdf
|
||||
|
||||
// Set the appropriate font
|
||||
$pdf->SetFont($this->getFont());
|
||||
$this->checkRtlAndLtr();
|
||||
if ($this->rtlSheets && !$this->ltrSheets) {
|
||||
$pdf->setRTL(true);
|
||||
}
|
||||
$pdf->writeHTML($this->generateHTMLAll());
|
||||
|
||||
// Document info
|
||||
$pdf->SetTitle($this->spreadsheet->getProperties()->getTitle());
|
||||
$pdf->SetAuthor($this->spreadsheet->getProperties()->getCreator());
|
||||
$pdf->SetSubject($this->spreadsheet->getProperties()->getSubject());
|
||||
$pdf->SetKeywords($this->spreadsheet->getProperties()->getKeywords());
|
||||
$pdf->SetCreator($this->spreadsheet->getProperties()->getCreator());
|
||||
$pdf->SetTitle(
|
||||
$this->spreadsheet->getProperties()->getTitle()
|
||||
);
|
||||
$pdf->SetAuthor(
|
||||
$this->spreadsheet->getProperties()->getCreator()
|
||||
);
|
||||
$pdf->SetSubject(
|
||||
$this->spreadsheet->getProperties()->getSubject()
|
||||
);
|
||||
$pdf->SetKeywords(
|
||||
$this->spreadsheet->getProperties()->getKeywords()
|
||||
);
|
||||
$pdf->SetCreator(
|
||||
$this->spreadsheet->getProperties()->getCreator()
|
||||
);
|
||||
|
||||
// Write to file
|
||||
fwrite($fileHandle, $pdf->output('', 'S'));
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Reader\Html;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Html as HtmlReader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DirectionTest extends TestCase
|
||||
{
|
||||
public function testRtl(): void
|
||||
{
|
||||
$inlines = [
|
||||
"<table border='0' cellpadding='0' cellspacing='0' dir='rtl' id='sheet0' class='sheet0 gridlines'>",
|
||||
'<tbody>',
|
||||
'<tr class="row0">',
|
||||
'<td class="column0 style0 s">a1</td>',
|
||||
'<td class="column1 style0 s">b1</td>',
|
||||
'<td class="column2 style0 s">c1</td>',
|
||||
'</tr>',
|
||||
'<tr class="row1">',
|
||||
'<td class="column0 style0 s">a2</td>',
|
||||
'<td class="column1 style0 s">b2</td>',
|
||||
'<td class="column2 style0 s">c2</td>',
|
||||
'</tr>',
|
||||
'</tbody></table>',
|
||||
];
|
||||
$html = implode("\n", $inlines);
|
||||
$reader = new HtmlReader();
|
||||
$spreadsheet = $reader->loadFromString($html);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
self::assertTrue($sheet->getRightToLeft());
|
||||
self::assertSame('a1', $sheet->getCell('A1')->getValue());
|
||||
self::assertSame('c2', $sheet->getCell('C2')->getValue());
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
|
||||
public function testLtr(): void
|
||||
{
|
||||
$inlines = [
|
||||
"<table border='0' cellpadding='0' cellspacing='0' dir='ltr' id='sheet0' class='sheet0 gridlines'>",
|
||||
'<tbody>',
|
||||
'<tr class="row0">',
|
||||
'<td class="column0 style0 s">a1</td>',
|
||||
'<td class="column1 style0 s">b1</td>',
|
||||
'<td class="column2 style0 s">c1</td>',
|
||||
'</tr>',
|
||||
'<tr class="row1">',
|
||||
'<td class="column0 style0 s">a2</td>',
|
||||
'<td class="column1 style0 s">b2</td>',
|
||||
'<td class="column2 style0 s">c2</td>',
|
||||
'</tr>',
|
||||
'</tbody></table>',
|
||||
];
|
||||
$html = implode("\n", $inlines);
|
||||
$reader = new HtmlReader();
|
||||
$spreadsheet = $reader->loadFromString($html);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
self::assertFalse($sheet->getRightToLeft());
|
||||
self::assertSame('a1', $sheet->getCell('A1')->getValue());
|
||||
self::assertSame('c2', $sheet->getCell('C2')->getValue());
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
|
||||
public function testDefault(): void
|
||||
{
|
||||
$inlines = [
|
||||
"<table border='0' cellpadding='0' cellspacing='0' id='sheet0' class='sheet0 gridlines'>",
|
||||
'<tbody>',
|
||||
'<tr class="row0">',
|
||||
'<td class="column0 style0 s">a1</td>',
|
||||
'<td class="column1 style0 s">b1</td>',
|
||||
'<td class="column2 style0 s">c1</td>',
|
||||
'</tr>',
|
||||
'<tr class="row1">',
|
||||
'<td class="column0 style0 s">a2</td>',
|
||||
'<td class="column1 style0 s">b2</td>',
|
||||
'<td class="column2 style0 s">c2</td>',
|
||||
'</tr>',
|
||||
'</tbody></table>',
|
||||
];
|
||||
$html = implode("\n", $inlines);
|
||||
$reader = new HtmlReader();
|
||||
$spreadsheet = $reader->loadFromString($html);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
self::assertFalse($sheet->getRightToLeft());
|
||||
self::assertSame('a1', $sheet->getCell('A1')->getValue());
|
||||
self::assertSame('c2', $sheet->getCell('C2')->getValue());
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Html;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DirectionTest extends TestCase
|
||||
{
|
||||
public function testMixedRtlAndLtr(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet1 = $spreadsheet->getActiveSheet();
|
||||
$sheet1->setRightToLeft(true);
|
||||
$sheet2 = $spreadsheet->createSheet();
|
||||
$sheet3 = $spreadsheet->createSheet();
|
||||
$sheet3->setRightToLeft(true);
|
||||
$cells = [
|
||||
['a1', 'b1', 'c1'],
|
||||
['a2', 'b2', 'c2'],
|
||||
];
|
||||
$sheet1->fromArray($cells);
|
||||
$sheet2->fromArray($cells);
|
||||
$sheet3->fromArray($cells);
|
||||
$writer = new Html($spreadsheet);
|
||||
$writer->writeAllSheets();
|
||||
$html = $writer->generateHTMLall();
|
||||
$rtlCount = substr_count($html, "dir='rtl'");
|
||||
self::assertSame(2, $rtlCount);
|
||||
$ltrCount = substr_count($html, "dir='ltr'");
|
||||
self::assertSame(1, $ltrCount);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
|
||||
public function testNoRtl(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet1 = $spreadsheet->getActiveSheet();
|
||||
$sheet2 = $spreadsheet->createSheet();
|
||||
$sheet3 = $spreadsheet->createSheet();
|
||||
$cells = [
|
||||
['a1', 'b1', 'c1'],
|
||||
['a2', 'b2', 'c2'],
|
||||
];
|
||||
$sheet1->fromArray($cells);
|
||||
$sheet2->fromArray($cells);
|
||||
$sheet3->fromArray($cells);
|
||||
$writer = new Html($spreadsheet);
|
||||
$writer->writeAllSheets();
|
||||
$html = $writer->generateHTMLall();
|
||||
$rtlCount = substr_count($html, "dir='rtl'");
|
||||
self::assertSame(0, $rtlCount);
|
||||
$ltrCount = substr_count($html, "dir='ltr'");
|
||||
self::assertSame(0, $ltrCount);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
|
||||
public function testOnlyRtl(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet1 = $spreadsheet->getActiveSheet();
|
||||
$sheet2 = $spreadsheet->createSheet();
|
||||
$sheet3 = $spreadsheet->createSheet();
|
||||
$cells = [
|
||||
['a1', 'b1', 'c1'],
|
||||
['a2', 'b2', 'c2'],
|
||||
];
|
||||
$sheet1->fromArray($cells);
|
||||
$sheet1->setRightToLeft(true);
|
||||
$sheet2->fromArray($cells);
|
||||
$sheet2->setRightToLeft(true);
|
||||
$sheet3->fromArray($cells);
|
||||
$sheet3->setRightToLeft(true);
|
||||
$writer = new Html($spreadsheet);
|
||||
$writer->writeAllSheets();
|
||||
$html = $writer->generateHTMLall();
|
||||
$rtlCount = substr_count($html, "dir='rtl'");
|
||||
self::assertSame(4, $rtlCount, '3 sheets plus html tag');
|
||||
$ltrCount = substr_count($html, "dir='ltr'");
|
||||
self::assertSame(0, $ltrCount);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user