Skip to content

Commit b91a9ef

Browse files
committed
AutoColor for LibreOffice Dark Mode
See discussion #4502. I am not sure of what needs to be involved here. This is especially so because Excel also offers AutoColor, but its dark mode looks significantly different than LibreOffice's. So here's what I've come up with thus far. - Add boolean autoColor with setter and getter to Font. - If autoColor is true, ODS writer and XLSX Writer will record it in the xml, and will not write a font color. It is possible that both can co-exist; I'm just not sure how they are supposed to interact if that is the case. - It makes most sense to me if the spreadsheet's default font specifies autoColor, but PhpSpreadsheet will not insist on that. - Xlsx Reader will process autoColor. Like most other styles, Ods Reader is not yet set up to handle it. - LibreOffice Calc should follow the autoColor declarations when it reads either an Xlsx or Ods spreadsheet created by PhpSpreadsheet.
1 parent 44c3bd5 commit b91a9ef

File tree

6 files changed

+187
-7
lines changed

6 files changed

+187
-7
lines changed

src/PhpSpreadsheet/Reader/Xlsx/Styles.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ public function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml):
136136
$attr = $this->getStyleAttributes($fontStyleXml->scheme);
137137
$fontStyle->setScheme((string) $attr['val']);
138138
}
139+
if (isset($fontStyleXml->auto)) {
140+
$attr = $this->getStyleAttributes($fontStyleXml->auto);
141+
if (isset($attr['val'])) {
142+
$fontStyle->setAutoColor(self::boolean((string) $attr['val']));
143+
}
144+
}
139145
}
140146

141147
private function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void

src/PhpSpreadsheet/Style/Font.php

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class Font extends Supervisor
8585
*/
8686
protected Color $color;
8787

88+
protected bool $autoColor = false;
89+
8890
public ?int $colorIndex = null;
8991

9092
protected string $scheme = '';
@@ -166,7 +168,7 @@ public function getStyleArray(array $array): array
166168
* );
167169
* </code>
168170
*
169-
* @param array{name?: string, latin?: string, eastAsian?: string, complexScript?: string, bold?: bool, italic?: bool, superscript?: bool, subscript?: bool, underline?: bool|string, strikethrough?: bool, color?: string[], size?: ?int, chartColor?: ChartColor, scheme?: string, cap?: string} $styleArray Array containing style information
171+
* @param array{name?: string, latin?: string, eastAsian?: string, complexScript?: string, bold?: bool, italic?: bool, superscript?: bool, subscript?: bool, underline?: bool|string, strikethrough?: bool, color?: string[], size?: ?int, chartColor?: ChartColor, scheme?: string, cap?: string, autoColor?: bool} $styleArray Array containing style information
170172
*
171173
* @return $this
172174
*/
@@ -185,7 +187,9 @@ public function applyFromArray(array $styleArray): static
185187
$this->setEastAsian($styleArray['eastAsian']);
186188
}
187189
if (isset($styleArray['complexScript'])) {
188-
$this->setComplexScript($styleArray['complexScript']);
190+
$this->setComplexScript(
191+
$styleArray['complexScript']
192+
);
189193
}
190194
if (isset($styleArray['bold'])) {
191195
$this->setBold($styleArray['bold']);
@@ -203,7 +207,9 @@ public function applyFromArray(array $styleArray): static
203207
$this->setUnderline($styleArray['underline']);
204208
}
205209
if (isset($styleArray['strikethrough'])) {
206-
$this->setStrikethrough($styleArray['strikethrough']);
210+
$this->setStrikethrough(
211+
$styleArray['strikethrough']
212+
);
207213
}
208214
if (isset($styleArray['color'])) {
209215
/** @var array{rgb?: string, argb?: string, theme?: int} */
@@ -223,6 +229,9 @@ public function applyFromArray(array $styleArray): static
223229
if (isset($styleArray['cap'])) {
224230
$this->setCap($styleArray['cap']);
225231
}
232+
if (isset($styleArray['autoColor'])) {
233+
$this->setAutoColor($styleArray['autoColor']);
234+
}
226235
}
227236

228237
return $this;
@@ -736,6 +745,7 @@ public function getHashCode(): string
736745
. ($this->subscript ? 't' : 'f')
737746
. $this->underline
738747
. ($this->strikethrough ? 't' : 'f')
748+
. ($this->autoColor ? 't' : 'f')
739749
. $this->color->getHashCode()
740750
. $this->scheme
741751
. implode(
@@ -777,6 +787,7 @@ protected function exportArray1(): array
777787
$this->exportArray2($exportedArray, 'superscript', $this->getSuperscript());
778788
$this->exportArray2($exportedArray, 'underline', $this->getUnderline());
779789
$this->exportArray2($exportedArray, 'underlineColor', $this->getUnderlineColor());
790+
$this->exportArray2($exportedArray, 'autoColor', $this->getAutoColor());
780791

781792
return $exportedArray;
782793
}
@@ -831,6 +842,29 @@ public function setHyperlinkTheme(): self
831842
return $this;
832843
}
833844

845+
public function setAutoColor(bool $autoColor): self
846+
{
847+
if ($this->isSupervisor) {
848+
$styleArray = $this->getStyleArray(['autoColor' => $autoColor]);
849+
$this->getActiveSheet()
850+
->getStyle($this->getSelectedCells())
851+
->applyFromArray($styleArray);
852+
} else {
853+
$this->autoColor = $autoColor;
854+
}
855+
856+
return $this;
857+
}
858+
859+
public function getAutoColor(): bool
860+
{
861+
if ($this->isSupervisor) {
862+
return $this->getSharedComponent()->getAutoColor();
863+
}
864+
865+
return $this->autoColor;
866+
}
867+
834868
/**
835869
* Implement PHP __clone to create a deep clone, not just a shallow copy.
836870
*/

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,26 @@ protected function writeTextProperties(CellStyle $style): void
204204

205205
if ($font->getBold()) {
206206
$this->writer->writeAttribute('fo:font-weight', 'bold');
207-
$this->writer->writeAttribute('style:font-weight-complex', 'bold');
208-
$this->writer->writeAttribute('style:font-weight-asian', 'bold');
207+
$this->writer->writeAttribute(
208+
'style:font-weight-complex',
209+
'bold'
210+
);
211+
$this->writer->writeAttribute(
212+
'style:font-weight-asian',
213+
'bold'
214+
);
209215
}
210216

211217
if ($font->getItalic()) {
212218
$this->writer->writeAttribute('fo:font-style', 'italic');
213219
}
214220

215-
$this->writer->writeAttribute('fo:color', sprintf('#%s', $font->getColor()->getRGB()));
221+
if ($font->getAutoColor()) {
222+
$this->writer
223+
->writeAttribute('style:use-window-font-color', 'true');
224+
} else {
225+
$this->writer->writeAttribute('fo:color', sprintf('#%s', $font->getColor()->getRGB()));
226+
}
216227

217228
if ($family = $font->getName()) {
218229
$this->writer->writeAttribute('fo:font-family', $family);

src/PhpSpreadsheet/Writer/Xlsx/Style.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,12 @@ private function writeFont(XMLWriter $objWriter, Font $font): void
347347
}
348348

349349
// Foreground color
350-
if ($font->getColor()->getTheme() >= 0) {
350+
if ($font->getAutoColor()) {
351+
$this->startFont($objWriter, $fontStarted);
352+
$objWriter->startElement('auto');
353+
$objWriter->writeAttribute('val', '1');
354+
$objWriter->endElement();
355+
} elseif ($font->getColor()->getTheme() >= 0) {
351356
$this->startFont($objWriter, $fontStarted);
352357
$objWriter->startElement('color');
353358
$objWriter->writeAttribute('theme', (string) $font->getColor()->getTheme());
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\ODS;
6+
7+
use PhpOffice\PhpSpreadsheet\Shared\File;
8+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
9+
use PhpOffice\PhpSpreadsheet\Writer\Ods as OdsWriter;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class AutoColorTest extends TestCase
13+
{
14+
private string $outputFile = '';
15+
16+
protected function tearDown(): void
17+
{
18+
if ($this->outputFile !== '') {
19+
unlink($this->outputFile);
20+
$this->outputFile = '';
21+
}
22+
}
23+
24+
public function testAutoColor(): void
25+
{
26+
// It's not clear to me what AutoColor does in Excel.
27+
// However, LibreOffice Dark Mode
28+
// can make use of a spreadsheet which uses it.
29+
$spreadsheet = new Spreadsheet();
30+
$spreadsheet->getDefaultStyle()->getFont()
31+
->setAutoColor(true);
32+
$sheet = $spreadsheet->getActiveSheet();
33+
$sheet->setCellValue('A1', 'Hello World!');
34+
$sheet->setCellValue('A2', 'Hello World!');
35+
$sheet->getStyle('A2')->getFont()
36+
->setBold(true);
37+
$sheet->setCellValue('A3', 'Hello World!');
38+
$sheet->getStyle('A3')->getFont()
39+
->setItalic(true);
40+
$sheet->setCellValue('B1', 'Hello World!');
41+
42+
$writer = new OdsWriter($spreadsheet);
43+
$outputFile = $this->outputFile = File::temporaryFilename();
44+
$writer->save($outputFile);
45+
$spreadsheet->disconnectWorksheets();
46+
$zipfile = "zip://$outputFile#content.xml";
47+
$contents = file_get_contents($zipfile);
48+
if ($contents === false) {
49+
self::fail('Unable to open file');
50+
} else {
51+
self::assertStringContainsString('<style:text-properties style:use-window-font-color="true" fo:font-family="Calibri" fo:font-size="11.0pt"/>', $contents);
52+
self::assertStringContainsString('<style:text-properties fo:font-weight="bold" style:font-weight-complex="bold" style:font-weight-asian="bold" style:use-window-font-color="true" fo:font-family="Calibri" fo:font-size="11.0pt"/>', $contents);
53+
self::assertStringContainsString('<style:text-properties fo:font-style="italic" style:use-window-font-color="true" fo:font-family="Calibri" fo:font-size="11.0pt"/>', $contents);
54+
}
55+
}
56+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
8+
use PhpOffice\PhpSpreadsheet\Shared\File;
9+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
10+
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
11+
use PHPUnit\Framework\TestCase;
12+
13+
class AutoColorTest extends TestCase
14+
{
15+
private string $outputFile = '';
16+
17+
protected function tearDown(): void
18+
{
19+
if ($this->outputFile !== '') {
20+
unlink($this->outputFile);
21+
$this->outputFile = '';
22+
}
23+
}
24+
25+
public function testAutoColor(): void
26+
{
27+
// It's not clear to me what AutoColor does in Excel.
28+
// However, LibreOffice Dark Mode
29+
// can make use of a spreadsheet which uses it.
30+
$spreadsheet = new Spreadsheet();
31+
$spreadsheet->getDefaultStyle()->getFont()
32+
->setAutoColor(true);
33+
$sheet = $spreadsheet->getActiveSheet();
34+
$sheet->setCellValue('A1', 'Hello World!');
35+
$sheet->setCellValue('A2', 'Hello World!');
36+
$sheet->getStyle('A2')->getFont()
37+
->setBold(true);
38+
$sheet->setCellValue('A3', 'Hello World!');
39+
$sheet->getStyle('A3')->getFont()
40+
->setItalic(true);
41+
$sheet->setCellValue('B1', 'Hello World!');
42+
43+
$writer = new XlsxWriter($spreadsheet);
44+
$outputFile = $this->outputFile = File::temporaryFilename();
45+
$writer->save($outputFile);
46+
$spreadsheet->disconnectWorksheets();
47+
$zipfile = "zip://$outputFile#xl/styles.xml";
48+
$contents = file_get_contents($zipfile);
49+
if ($contents === false) {
50+
self::fail('Unable to open file');
51+
} else {
52+
self::assertStringContainsString('<fonts count="3">', $contents);
53+
self::assertStringContainsString('<font><b val="0"/><i val="0"/><strike val="0"/><u val="none"/><sz val="11"/><auto val="1"/><name val="Calibri"/></font>', $contents);
54+
self::assertStringContainsString('<font><b val="1"/><i val="0"/><strike val="0"/><u val="none"/><sz val="11"/><auto val="1"/><name val="Calibri"/></font>', $contents);
55+
self::assertStringContainsString('<font><b val="0"/><i val="1"/><strike val="0"/><u val="none"/><sz val="11"/><auto val="1"/><name val="Calibri"/></font>', $contents);
56+
}
57+
58+
$reader = new XlsxReader();
59+
$spreadsheet2 = $reader->load($outputFile);
60+
$sheet2 = $spreadsheet2->getActiveSheet();
61+
self::assertTrue(
62+
$sheet2->getStyle('A1')
63+
->getFont()
64+
->getAutoColor()
65+
);
66+
$spreadsheet2->disconnectWorksheets();
67+
}
68+
}

0 commit comments

Comments
 (0)