Xlsx Reader Theme Support Broken After 17.1 (#2403)
Fix #2387. Fix #2075. There was substantial refactoring of Writer Xlsx styles in 18.0. An existing static property `$theme` was intended to be shared by both Writer Xlsx and the new Writer Xlsx Styles. However, the initialization of the property in the latter happened later than it should have. This PR makes that initialization happen as soon as the theme has been read. Also, declaring that property as static seems questionable; I have made it an instance member. This small re-factoring makes it possible to now support Themes in tab colors. Since this PR changes Reader/Xlsx/Styles, add type-hinting throughout that module to eliminate Phpstan/Scrutinizer problems. I also removed method readStyle from Reader/Xlsx, since it was essentially duplicated in Reader/Xlsx/Styles. And I added a small number of tests to ensure that Styles is 100% covered. All of this is necessary in preparation for Namespacing phase 2.
This commit is contained in:
parent
3257ae5c90
commit
290c18e4db
|
|
@ -4460,106 +4460,6 @@ parameters:
|
||||||
count: 1
|
count: 1
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
|
path: src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:dxfs\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:dxfs\\(\\) has parameter \\$readDataOnly with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:getArrayItem\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:getArrayItem\\(\\) has parameter \\$array with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:getArrayItem\\(\\) has parameter \\$key with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:readColor\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:readColor\\(\\) has parameter \\$background with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:readColor\\(\\) has parameter \\$color with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:readProtectionHidden\\(\\) has parameter \\$style with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:readProtectionLocked\\(\\) has parameter \\$style with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:readStyle\\(\\) has parameter \\$style with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:setStyleBaseData\\(\\) has parameter \\$cellStyles with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:setStyleBaseData\\(\\) has parameter \\$styles with no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:styles\\(\\) has no return type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$hexColourValue of static method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\:\\:changeBrightness\\(\\) expects string, string\\|null given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#2 \\$alignmentXml of static method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:readAlignmentStyle\\(\\) expects SimpleXMLElement, object given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:\\$cellStyles has no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:\\$styleXml has no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:\\$styles has no type specified\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Static property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Styles\\:\\:\\$theme \\(PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Theme\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Theme\\|null\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Xlsx/Styles.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\$haystack of function strpos expects string, string\\|false given\\.$#"
|
message: "#^Parameter \\#1 \\$haystack of function strpos expects string, string\\|false given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Properties as PropertyReader;
|
||||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViewOptions;
|
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViewOptions;
|
||||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViews;
|
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViews;
|
||||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Styles;
|
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Styles;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Theme;
|
||||||
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
|
use PhpOffice\PhpSpreadsheet\ReferenceHelper;
|
||||||
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
||||||
use PhpOffice\PhpSpreadsheet\Settings;
|
use PhpOffice\PhpSpreadsheet\Settings;
|
||||||
|
|
@ -34,7 +35,6 @@ use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||||
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
|
use PhpOffice\PhpSpreadsheet\Worksheet\HeaderFooterDrawing;
|
||||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
use SimpleXMLElement;
|
use SimpleXMLElement;
|
||||||
use stdClass;
|
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use XMLReader;
|
use XMLReader;
|
||||||
use ZipArchive;
|
use ZipArchive;
|
||||||
|
|
@ -50,18 +50,14 @@ class Xlsx extends BaseReader
|
||||||
*/
|
*/
|
||||||
private $referenceHelper;
|
private $referenceHelper;
|
||||||
|
|
||||||
/**
|
|
||||||
* Xlsx\Theme instance.
|
|
||||||
*
|
|
||||||
* @var Xlsx\Theme
|
|
||||||
*/
|
|
||||||
private static $theme;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ZipArchive
|
* @var ZipArchive
|
||||||
*/
|
*/
|
||||||
private $zip;
|
private $zip;
|
||||||
|
|
||||||
|
/** @var Styles */
|
||||||
|
private $styleReader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Xlsx Reader instance.
|
* Create a new Xlsx Reader instance.
|
||||||
*/
|
*/
|
||||||
|
|
@ -406,6 +402,8 @@ class Xlsx extends BaseReader
|
||||||
// Read the theme first, because we need the colour scheme when reading the styles
|
// Read the theme first, because we need the colour scheme when reading the styles
|
||||||
[$workbookBasename, $xmlNamespaceBase] = $this->getWorkbookBaseName();
|
[$workbookBasename, $xmlNamespaceBase] = $this->getWorkbookBaseName();
|
||||||
$wbRels = $this->loadZip("xl/_rels/${workbookBasename}.rels", Namespaces::RELATIONSHIPS);
|
$wbRels = $this->loadZip("xl/_rels/${workbookBasename}.rels", Namespaces::RELATIONSHIPS);
|
||||||
|
$theme = null;
|
||||||
|
$this->styleReader = new Styles();
|
||||||
foreach ($wbRels->Relationship as $relx) {
|
foreach ($wbRels->Relationship as $relx) {
|
||||||
$rel = self::getAttributes($relx);
|
$rel = self::getAttributes($relx);
|
||||||
$relTarget = (string) $rel['Target'];
|
$relTarget = (string) $rel['Target'];
|
||||||
|
|
@ -438,7 +436,8 @@ class Xlsx extends BaseReader
|
||||||
$themeColours[$themePos] = (string) $xmlColourData['val'];
|
$themeColours[$themePos] = (string) $xmlColourData['val'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self::$theme = new Xlsx\Theme($themeName, $colourSchemeName, $themeColours);
|
$theme = new Theme($themeName, $colourSchemeName, $themeColours);
|
||||||
|
$this->styleReader->setTheme($theme);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -599,7 +598,7 @@ class Xlsx extends BaseReader
|
||||||
|
|
||||||
// add style to cellXf collection
|
// add style to cellXf collection
|
||||||
$objStyle = new Style();
|
$objStyle = new Style();
|
||||||
self::readStyle($objStyle, $style);
|
$this->styleReader->readStyle($objStyle, $style);
|
||||||
if ($addingFirstCellXf) {
|
if ($addingFirstCellXf) {
|
||||||
$excel->removeCellXfByIndex(0); // remove the default style
|
$excel->removeCellXfByIndex(0); // remove the default style
|
||||||
$addingFirstCellXf = false;
|
$addingFirstCellXf = false;
|
||||||
|
|
@ -634,7 +633,7 @@ class Xlsx extends BaseReader
|
||||||
|
|
||||||
// add style to cellStyleXf collection
|
// add style to cellStyleXf collection
|
||||||
$objStyle = new Style();
|
$objStyle = new Style();
|
||||||
self::readStyle($objStyle, $cellStyle);
|
$this->styleReader->readStyle($objStyle, $cellStyle);
|
||||||
if ($addingFirstCellStyleXf) {
|
if ($addingFirstCellStyleXf) {
|
||||||
$excel->removeCellStyleXfByIndex(0); // remove the default style
|
$excel->removeCellStyleXfByIndex(0); // remove the default style
|
||||||
$addingFirstCellStyleXf = false;
|
$addingFirstCellStyleXf = false;
|
||||||
|
|
@ -642,10 +641,10 @@ class Xlsx extends BaseReader
|
||||||
$excel->addCellStyleXf($objStyle);
|
$excel->addCellStyleXf($objStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$styleReader = new Styles($xmlStyles);
|
$this->styleReader->setStyleXml($xmlStyles);
|
||||||
$styleReader->setStyleBaseData(self::$theme, $styles, $cellStyles);
|
$this->styleReader->setStyleBaseData($theme, $styles, $cellStyles);
|
||||||
$dxfs = $styleReader->dxfs($this->readDataOnly);
|
$dxfs = $this->styleReader->dxfs($this->readDataOnly);
|
||||||
$styles = $styleReader->styles();
|
$styles = $this->styleReader->styles();
|
||||||
|
|
||||||
$xmlWorkbook = $this->loadZipNoNamespace($relTarget, $mainNS);
|
$xmlWorkbook = $this->loadZipNoNamespace($relTarget, $mainNS);
|
||||||
$xmlWorkbookNS = $this->loadZip($relTarget, $mainNS);
|
$xmlWorkbookNS = $this->loadZip($relTarget, $mainNS);
|
||||||
|
|
@ -718,7 +717,7 @@ class Xlsx extends BaseReader
|
||||||
}
|
}
|
||||||
|
|
||||||
$sheetViewOptions = new SheetViewOptions($docSheet, $xmlSheet);
|
$sheetViewOptions = new SheetViewOptions($docSheet, $xmlSheet);
|
||||||
$sheetViewOptions->load($this->getReadDataOnly());
|
$sheetViewOptions->load($this->getReadDataOnly(), $this->styleReader);
|
||||||
|
|
||||||
(new ColumnAndRowAttributes($docSheet, $xmlSheet))
|
(new ColumnAndRowAttributes($docSheet, $xmlSheet))
|
||||||
->load($this->getReadFilter(), $this->getReadDataOnly());
|
->load($this->getReadFilter(), $this->getReadDataOnly());
|
||||||
|
|
@ -1618,45 +1617,6 @@ class Xlsx extends BaseReader
|
||||||
return $excel;
|
return $excel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SimpleXMLElement|stdClass $style
|
|
||||||
*/
|
|
||||||
private static function readStyle(Style $docStyle, $style): void
|
|
||||||
{
|
|
||||||
$docStyle->getNumberFormat()->setFormatCode($style->numFmt);
|
|
||||||
|
|
||||||
// font
|
|
||||||
if (isset($style->font)) {
|
|
||||||
Styles::readFontStyle($docStyle->getFont(), $style->font);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill
|
|
||||||
if (isset($style->fill)) {
|
|
||||||
Styles::readFillStyle($docStyle->getFill(), $style->fill);
|
|
||||||
}
|
|
||||||
|
|
||||||
// border
|
|
||||||
if (isset($style->border)) {
|
|
||||||
Styles::readBorderStyle($docStyle->getBorders(), $style->border);
|
|
||||||
}
|
|
||||||
|
|
||||||
// alignment
|
|
||||||
if (isset($style->alignment)) {
|
|
||||||
Styles::readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
// protection
|
|
||||||
if (isset($style->protection)) {
|
|
||||||
Styles::readProtectionLocked($docStyle, $style);
|
|
||||||
Styles::readProtectionHidden($docStyle, $style);
|
|
||||||
}
|
|
||||||
|
|
||||||
// top-level style settings
|
|
||||||
if (isset($style->quotePrefix)) {
|
|
||||||
$docStyle->setQuotePrefix((bool) $style->quotePrefix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return RichText
|
* @return RichText
|
||||||
*/
|
*/
|
||||||
|
|
@ -1685,7 +1645,7 @@ class Xlsx extends BaseReader
|
||||||
$objText->getFont()->setSize((float) $attr['val']);
|
$objText->getFont()->setSize((float) $attr['val']);
|
||||||
}
|
}
|
||||||
if (isset($run->rPr->color)) {
|
if (isset($run->rPr->color)) {
|
||||||
$objText->getFont()->setColor(new Color(Styles::readColor($run->rPr->color)));
|
$objText->getFont()->setColor(new Color($this->styleReader->readColor($run->rPr->color)));
|
||||||
}
|
}
|
||||||
if (isset($run->rPr->b)) {
|
if (isset($run->rPr->b)) {
|
||||||
$attr = $run->rPr->b->attributes();
|
$attr = $run->rPr->b->attributes();
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,14 @@ class SheetViewOptions extends BaseParserClass
|
||||||
$this->worksheetXml = $worksheetXml;
|
$this->worksheetXml = $worksheetXml;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function load(bool $readDataOnly, Styles $styleReader): void
|
||||||
* @param bool $readDataOnly
|
|
||||||
*/
|
|
||||||
public function load($readDataOnly = false): void
|
|
||||||
{
|
{
|
||||||
if ($this->worksheetXml === null) {
|
if ($this->worksheetXml === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->worksheetXml->sheetPr)) {
|
if (isset($this->worksheetXml->sheetPr)) {
|
||||||
$this->tabColor($this->worksheetXml->sheetPr);
|
$this->tabColor($this->worksheetXml->sheetPr, $styleReader);
|
||||||
$this->codeName($this->worksheetXml->sheetPr);
|
$this->codeName($this->worksheetXml->sheetPr);
|
||||||
$this->outlines($this->worksheetXml->sheetPr);
|
$this->outlines($this->worksheetXml->sheetPr);
|
||||||
$this->pageSetup($this->worksheetXml->sheetPr);
|
$this->pageSetup($this->worksheetXml->sheetPr);
|
||||||
|
|
@ -42,10 +39,10 @@ class SheetViewOptions extends BaseParserClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function tabColor(SimpleXMLElement $sheetPr): void
|
private function tabColor(SimpleXMLElement $sheetPr, Styles $styleReader): void
|
||||||
{
|
{
|
||||||
if (isset($sheetPr->tabColor, $sheetPr->tabColor['rgb'])) {
|
if (isset($sheetPr->tabColor)) {
|
||||||
$this->worksheet->getTabColor()->setARGB((string) $sheetPr->tabColor['rgb']);
|
$this->worksheet->getTabColor()->setARGB($styleReader->readColor($sheetPr->tabColor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,35 +13,44 @@ use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||||
use PhpOffice\PhpSpreadsheet\Style\Protection;
|
use PhpOffice\PhpSpreadsheet\Style\Protection;
|
||||||
use PhpOffice\PhpSpreadsheet\Style\Style;
|
use PhpOffice\PhpSpreadsheet\Style\Style;
|
||||||
use SimpleXMLElement;
|
use SimpleXMLElement;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
class Styles extends BaseParserClass
|
class Styles extends BaseParserClass
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Theme instance.
|
* Theme instance.
|
||||||
*
|
*
|
||||||
* @var Theme
|
* @var ?Theme
|
||||||
*/
|
*/
|
||||||
private static $theme;
|
private $theme;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
private $styles = [];
|
private $styles = [];
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
private $cellStyles = [];
|
private $cellStyles = [];
|
||||||
|
|
||||||
|
/** @var SimpleXMLElement */
|
||||||
private $styleXml;
|
private $styleXml;
|
||||||
|
|
||||||
public function __construct(SimpleXMLElement $styleXml)
|
public function setStyleXml(SimpleXmlElement $styleXml): void
|
||||||
{
|
{
|
||||||
$this->styleXml = $styleXml;
|
$this->styleXml = $styleXml;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setStyleBaseData(?Theme $theme = null, $styles = [], $cellStyles = []): void
|
public function setTheme(Theme $theme): void
|
||||||
{
|
{
|
||||||
self::$theme = $theme;
|
$this->theme = $theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setStyleBaseData(?Theme $theme = null, array $styles = [], array $cellStyles = []): void
|
||||||
|
{
|
||||||
|
$this->theme = $theme;
|
||||||
$this->styles = $styles;
|
$this->styles = $styles;
|
||||||
$this->cellStyles = $cellStyles;
|
$this->cellStyles = $cellStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
|
public function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
|
||||||
{
|
{
|
||||||
if (isset($fontStyleXml->name, $fontStyleXml->name['val'])) {
|
if (isset($fontStyleXml->name, $fontStyleXml->name['val'])) {
|
||||||
$fontStyle->setName((string) $fontStyleXml->name['val']);
|
$fontStyle->setName((string) $fontStyleXml->name['val']);
|
||||||
|
|
@ -60,7 +69,7 @@ class Styles extends BaseParserClass
|
||||||
!isset($fontStyleXml->strike['val']) || self::boolean((string) $fontStyleXml->strike['val'])
|
!isset($fontStyleXml->strike['val']) || self::boolean((string) $fontStyleXml->strike['val'])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$fontStyle->getColor()->setARGB(self::readColor($fontStyleXml->color));
|
$fontStyle->getColor()->setARGB($this->readColor($fontStyleXml->color));
|
||||||
|
|
||||||
if (isset($fontStyleXml->u) && !isset($fontStyleXml->u['val'])) {
|
if (isset($fontStyleXml->u) && !isset($fontStyleXml->u['val'])) {
|
||||||
$fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
|
$fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
|
||||||
|
|
@ -78,7 +87,7 @@ class Styles extends BaseParserClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
|
private function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
|
||||||
{
|
{
|
||||||
if ($numfmtStyleXml->count() === 0) {
|
if ($numfmtStyleXml->count() === 0) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -89,7 +98,7 @@ class Styles extends BaseParserClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
|
public function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
|
||||||
{
|
{
|
||||||
if ($fillStyleXml->gradientFill) {
|
if ($fillStyleXml->gradientFill) {
|
||||||
/** @var SimpleXMLElement $gradientFill */
|
/** @var SimpleXMLElement $gradientFill */
|
||||||
|
|
@ -99,16 +108,16 @@ class Styles extends BaseParserClass
|
||||||
}
|
}
|
||||||
$fillStyle->setRotation((float) ($gradientFill['degree']));
|
$fillStyle->setRotation((float) ($gradientFill['degree']));
|
||||||
$gradientFill->registerXPathNamespace('sml', Namespaces::MAIN);
|
$gradientFill->registerXPathNamespace('sml', Namespaces::MAIN);
|
||||||
$fillStyle->getStartColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
|
$fillStyle->getStartColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
|
||||||
$fillStyle->getEndColor()->setARGB(self::readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
|
$fillStyle->getEndColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
|
||||||
} elseif ($fillStyleXml->patternFill) {
|
} elseif ($fillStyleXml->patternFill) {
|
||||||
$defaultFillStyle = Fill::FILL_NONE;
|
$defaultFillStyle = Fill::FILL_NONE;
|
||||||
if ($fillStyleXml->patternFill->fgColor) {
|
if ($fillStyleXml->patternFill->fgColor) {
|
||||||
$fillStyle->getStartColor()->setARGB(self::readColor($fillStyleXml->patternFill->fgColor, true));
|
$fillStyle->getStartColor()->setARGB($this->readColor($fillStyleXml->patternFill->fgColor, true));
|
||||||
$defaultFillStyle = Fill::FILL_SOLID;
|
$defaultFillStyle = Fill::FILL_SOLID;
|
||||||
}
|
}
|
||||||
if ($fillStyleXml->patternFill->bgColor) {
|
if ($fillStyleXml->patternFill->bgColor) {
|
||||||
$fillStyle->getEndColor()->setARGB(self::readColor($fillStyleXml->patternFill->bgColor, true));
|
$fillStyle->getEndColor()->setARGB($this->readColor($fillStyleXml->patternFill->bgColor, true));
|
||||||
$defaultFillStyle = Fill::FILL_SOLID;
|
$defaultFillStyle = Fill::FILL_SOLID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,7 +129,7 @@ class Styles extends BaseParserClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
|
public function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
|
||||||
{
|
{
|
||||||
$diagonalUp = self::boolean((string) $borderStyleXml['diagonalUp']);
|
$diagonalUp = self::boolean((string) $borderStyleXml['diagonalUp']);
|
||||||
$diagonalDown = self::boolean((string) $borderStyleXml['diagonalDown']);
|
$diagonalDown = self::boolean((string) $borderStyleXml['diagonalDown']);
|
||||||
|
|
@ -134,24 +143,24 @@ class Styles extends BaseParserClass
|
||||||
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
|
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::readBorder($borderStyle->getLeft(), $borderStyleXml->left);
|
$this->readBorder($borderStyle->getLeft(), $borderStyleXml->left);
|
||||||
self::readBorder($borderStyle->getRight(), $borderStyleXml->right);
|
$this->readBorder($borderStyle->getRight(), $borderStyleXml->right);
|
||||||
self::readBorder($borderStyle->getTop(), $borderStyleXml->top);
|
$this->readBorder($borderStyle->getTop(), $borderStyleXml->top);
|
||||||
self::readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
|
$this->readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
|
||||||
self::readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
|
$this->readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function readBorder(Border $border, SimpleXMLElement $borderXml): void
|
private function readBorder(Border $border, SimpleXMLElement $borderXml): void
|
||||||
{
|
{
|
||||||
if (isset($borderXml['style'])) {
|
if (isset($borderXml['style'])) {
|
||||||
$border->setBorderStyle((string) $borderXml['style']);
|
$border->setBorderStyle((string) $borderXml['style']);
|
||||||
}
|
}
|
||||||
if (isset($borderXml->color)) {
|
if (isset($borderXml->color)) {
|
||||||
$border->getColor()->setARGB(self::readColor($borderXml->color));
|
$border->getColor()->setARGB($this->readColor($borderXml->color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
|
public function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
|
||||||
{
|
{
|
||||||
$alignment->setHorizontal((string) $alignmentXml['horizontal']);
|
$alignment->setHorizontal((string) $alignmentXml['horizontal']);
|
||||||
$alignment->setVertical((string) $alignmentXml['vertical']);
|
$alignment->setVertical((string) $alignmentXml['vertical']);
|
||||||
|
|
@ -174,43 +183,53 @@ class Styles extends BaseParserClass
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function readStyle(Style $docStyle, $style): void
|
/**
|
||||||
|
* Read style.
|
||||||
|
*
|
||||||
|
* @param SimpleXMLElement|stdClass $style
|
||||||
|
*/
|
||||||
|
public function readStyle(Style $docStyle, $style): void
|
||||||
{
|
{
|
||||||
if ($style->numFmt instanceof SimpleXMLElement) {
|
if ($style->numFmt instanceof SimpleXMLElement) {
|
||||||
self::readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
|
$this->readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
|
||||||
} else {
|
} else {
|
||||||
$docStyle->getNumberFormat()->setFormatCode($style->numFmt);
|
$docStyle->getNumberFormat()->setFormatCode($style->numFmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($style->font)) {
|
if (isset($style->font)) {
|
||||||
self::readFontStyle($docStyle->getFont(), $style->font);
|
$this->readFontStyle($docStyle->getFont(), $style->font);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($style->fill)) {
|
if (isset($style->fill)) {
|
||||||
self::readFillStyle($docStyle->getFill(), $style->fill);
|
$this->readFillStyle($docStyle->getFill(), $style->fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($style->border)) {
|
if (isset($style->border)) {
|
||||||
self::readBorderStyle($docStyle->getBorders(), $style->border);
|
$this->readBorderStyle($docStyle->getBorders(), $style->border);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($style->alignment->alignment)) {
|
if (isset($style->alignment)) {
|
||||||
self::readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
|
$this->readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
// protection
|
// protection
|
||||||
if (isset($style->protection)) {
|
if (isset($style->protection)) {
|
||||||
self::readProtectionLocked($docStyle, $style);
|
$this->readProtectionLocked($docStyle, $style);
|
||||||
self::readProtectionHidden($docStyle, $style);
|
$this->readProtectionHidden($docStyle, $style);
|
||||||
}
|
}
|
||||||
|
|
||||||
// top-level style settings
|
// top-level style settings
|
||||||
if (isset($style->quotePrefix)) {
|
if (isset($style->quotePrefix)) {
|
||||||
$docStyle->setQuotePrefix(true);
|
$docStyle->setQuotePrefix((bool) $style->quotePrefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readProtectionLocked(Style $docStyle, $style): void
|
/**
|
||||||
|
* Read protection locked attribute.
|
||||||
|
*
|
||||||
|
* @param SimpleXMLElement|stdClass $style
|
||||||
|
*/
|
||||||
|
public function readProtectionLocked(Style $docStyle, $style): void
|
||||||
{
|
{
|
||||||
if (isset($style->protection['locked'])) {
|
if (isset($style->protection['locked'])) {
|
||||||
if (self::boolean((string) $style->protection['locked'])) {
|
if (self::boolean((string) $style->protection['locked'])) {
|
||||||
|
|
@ -221,7 +240,12 @@ class Styles extends BaseParserClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readProtectionHidden(Style $docStyle, $style): void
|
/**
|
||||||
|
* Read protection hidden attribute.
|
||||||
|
*
|
||||||
|
* @param SimpleXMLElement|stdClass $style
|
||||||
|
*/
|
||||||
|
public function readProtectionHidden(Style $docStyle, $style): void
|
||||||
{
|
{
|
||||||
if (isset($style->protection['hidden'])) {
|
if (isset($style->protection['hidden'])) {
|
||||||
if (self::boolean((string) $style->protection['hidden'])) {
|
if (self::boolean((string) $style->protection['hidden'])) {
|
||||||
|
|
@ -232,18 +256,18 @@ class Styles extends BaseParserClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function readColor($color, $background = false)
|
public function readColor(SimpleXMLElement $color, bool $background = false): string
|
||||||
{
|
{
|
||||||
if (isset($color['rgb'])) {
|
if (isset($color['rgb'])) {
|
||||||
return (string) $color['rgb'];
|
return (string) $color['rgb'];
|
||||||
} elseif (isset($color['indexed'])) {
|
} elseif (isset($color['indexed'])) {
|
||||||
return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
|
return Color::indexedColor((int) ($color['indexed'] - 7), $background)->getARGB() ?? '';
|
||||||
} elseif (isset($color['theme'])) {
|
} elseif (isset($color['theme'])) {
|
||||||
if (self::$theme !== null) {
|
if ($this->theme !== null) {
|
||||||
$returnColour = self::$theme->getColourByIndex((int) $color['theme']);
|
$returnColour = $this->theme->getColourByIndex((int) $color['theme']);
|
||||||
if (isset($color['tint'])) {
|
if (isset($color['tint'])) {
|
||||||
$tintAdjust = (float) $color['tint'];
|
$tintAdjust = (float) $color['tint'];
|
||||||
$returnColour = Color::changeBrightness($returnColour, $tintAdjust);
|
$returnColour = Color::changeBrightness($returnColour ?? '', $tintAdjust);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'FF' . $returnColour;
|
return 'FF' . $returnColour;
|
||||||
|
|
@ -253,7 +277,7 @@ class Styles extends BaseParserClass
|
||||||
return ($background) ? 'FFFFFFFF' : 'FF000000';
|
return ($background) ? 'FFFFFFFF' : 'FF000000';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dxfs($readDataOnly = false)
|
public function dxfs(bool $readDataOnly = false): array
|
||||||
{
|
{
|
||||||
$dxfs = [];
|
$dxfs = [];
|
||||||
if (!$readDataOnly && $this->styleXml) {
|
if (!$readDataOnly && $this->styleXml) {
|
||||||
|
|
@ -285,13 +309,20 @@ class Styles extends BaseParserClass
|
||||||
return $dxfs;
|
return $dxfs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function styles()
|
public function styles(): array
|
||||||
{
|
{
|
||||||
return $this->styles;
|
return $this->styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getArrayItem($array, $key = 0)
|
/**
|
||||||
|
* Get array item.
|
||||||
|
*
|
||||||
|
* @param mixed $array (usually array, in theory can be false)
|
||||||
|
*
|
||||||
|
* @return stdClass
|
||||||
|
*/
|
||||||
|
private static function getArrayItem($array, int $key = 0)
|
||||||
{
|
{
|
||||||
return $array[$key] ?? null;
|
return is_array($array) ? ($array[$key] ?? null) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class ColorTabTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testIssue2316(): void
|
||||||
|
{
|
||||||
|
$filename = 'tests/data/Reader/XLSX/colortabs.xlsx';
|
||||||
|
$reader = new Xlsx();
|
||||||
|
$spreadsheet = $reader->load($filename);
|
||||||
|
|
||||||
|
// theme color
|
||||||
|
self::assertSame('FF548135', $spreadsheet->getSheet(0)->getTabColor()->getArgb());
|
||||||
|
// rgb color
|
||||||
|
self::assertSame('FFFFC000', $spreadsheet->getSheet(1)->getTabColor()->getArgb());
|
||||||
|
$spreadsheet->disconnectWorksheets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Border;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Borders;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Protection;
|
||||||
|
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
|
||||||
|
|
||||||
|
class CoverageGapsTest extends AbstractFunctional
|
||||||
|
{
|
||||||
|
public function testCoverageGaps(): void
|
||||||
|
{
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$sheet
|
||||||
|
->getStyle('A1')
|
||||||
|
->getBorders()
|
||||||
|
->setDiagonalDirection(Borders::DIAGONAL_BOTH)
|
||||||
|
->getDiagonal()
|
||||||
|
->setBorderStyle(Border::BORDER_DASHDOTDOT);
|
||||||
|
$sheet
|
||||||
|
->getStyle('A2')
|
||||||
|
->getProtection()
|
||||||
|
->setLocked(Protection::PROTECTION_PROTECTED);
|
||||||
|
$sheet
|
||||||
|
->getStyle('A3')
|
||||||
|
->getAlignment()
|
||||||
|
->setTextRotation(Alignment::TEXTROTATION_STACK_EXCEL);
|
||||||
|
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
|
||||||
|
$spreadsheet->disconnectWorksheets();
|
||||||
|
|
||||||
|
$rsheet = $reloadedSpreadsheet->getActiveSheet();
|
||||||
|
self::assertSame(Borders::DIAGONAL_BOTH, $rsheet->getStyle('A1')->getBorders()->getDiagonalDirection());
|
||||||
|
self::assertSame(Border::BORDER_DASHDOTDOT, $rsheet->getStyle('A1')->getBorders()->getDiagonal()->getBorderStyle());
|
||||||
|
self::assertSame(Protection::PROTECTION_PROTECTED, $rsheet->getStyle('A2')->getProtection()->getLocked());
|
||||||
|
self::assertSame(Alignment::TEXTROTATION_STACK_PHPSPREADSHEET, $rsheet->getStyle('A3')->getAlignment()->getTextRotation());
|
||||||
|
|
||||||
|
$reloadedSpreadsheet->disconnectWorksheets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Fill;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class Issue2387Test extends TestCase
|
||||||
|
{
|
||||||
|
public function testIssue2387(): void
|
||||||
|
{
|
||||||
|
// Theme was not being handled.
|
||||||
|
$filename = 'tests/data/Reader/XLSX/issue.2387.xlsx';
|
||||||
|
$reader = IOFactory::createReader('Xlsx');
|
||||||
|
$spreadsheet = $reader->load($filename);
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
self::assertSame('335593', $sheet->getCell('B2')->getStyle()->getFont()->getColor()->getRgb());
|
||||||
|
self::assertSame(Fill::FILL_NONE, $sheet->getCell('B2')->getStyle()->getFill()->getFillType());
|
||||||
|
self::assertSame('FFFFFF', $sheet->getCell('C2')->getStyle()->getFont()->getColor()->getRgb());
|
||||||
|
self::assertSame('000000', $sheet->getCell('C2')->getStyle()->getFill()->getStartColor()->getRgb());
|
||||||
|
self::assertSame(Fill::FILL_SOLID, $sheet->getCell('C2')->getStyle()->getFill()->getFillType());
|
||||||
|
|
||||||
|
$spreadsheet->disconnectWorksheets();
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue