<?php
|
|
namespace PhpOffice\PhpSpreadsheet\Style;
|
|
class Color extends Supervisor
|
{
|
const NAMED_COLORS = [
|
'Black',
|
'White',
|
'Red',
|
'Green',
|
'Blue',
|
'Yellow',
|
'Magenta',
|
'Cyan',
|
];
|
|
// Colors
|
const COLOR_BLACK = 'FF000000';
|
const COLOR_WHITE = 'FFFFFFFF';
|
const COLOR_RED = 'FFFF0000';
|
const COLOR_DARKRED = 'FF800000';
|
const COLOR_BLUE = 'FF0000FF';
|
const COLOR_DARKBLUE = 'FF000080';
|
const COLOR_GREEN = 'FF00FF00';
|
const COLOR_DARKGREEN = 'FF008000';
|
const COLOR_YELLOW = 'FFFFFF00';
|
const COLOR_DARKYELLOW = 'FF808000';
|
const COLOR_MAGENTA = 'FFFF00FF';
|
const COLOR_CYAN = 'FF00FFFF';
|
|
const NAMED_COLOR_TRANSLATIONS = [
|
'Black' => self::COLOR_BLACK,
|
'White' => self::COLOR_WHITE,
|
'Red' => self::COLOR_RED,
|
'Green' => self::COLOR_GREEN,
|
'Blue' => self::COLOR_BLUE,
|
'Yellow' => self::COLOR_YELLOW,
|
'Magenta' => self::COLOR_MAGENTA,
|
'Cyan' => self::COLOR_CYAN,
|
];
|
|
const VALIDATE_ARGB_SIZE = 8;
|
const VALIDATE_RGB_SIZE = 6;
|
const VALIDATE_COLOR_6 = '/^[A-F0-9]{6}$/i';
|
const VALIDATE_COLOR_8 = '/^[A-F0-9]{8}$/i';
|
|
private const INDEXED_COLORS = [
|
1 => 'FF000000', // System Colour #1 - Black
|
2 => 'FFFFFFFF', // System Colour #2 - White
|
3 => 'FFFF0000', // System Colour #3 - Red
|
4 => 'FF00FF00', // System Colour #4 - Green
|
5 => 'FF0000FF', // System Colour #5 - Blue
|
6 => 'FFFFFF00', // System Colour #6 - Yellow
|
7 => 'FFFF00FF', // System Colour #7- Magenta
|
8 => 'FF00FFFF', // System Colour #8- Cyan
|
9 => 'FF800000', // Standard Colour #9
|
10 => 'FF008000', // Standard Colour #10
|
11 => 'FF000080', // Standard Colour #11
|
12 => 'FF808000', // Standard Colour #12
|
13 => 'FF800080', // Standard Colour #13
|
14 => 'FF008080', // Standard Colour #14
|
15 => 'FFC0C0C0', // Standard Colour #15
|
16 => 'FF808080', // Standard Colour #16
|
17 => 'FF9999FF', // Chart Fill Colour #17
|
18 => 'FF993366', // Chart Fill Colour #18
|
19 => 'FFFFFFCC', // Chart Fill Colour #19
|
20 => 'FFCCFFFF', // Chart Fill Colour #20
|
21 => 'FF660066', // Chart Fill Colour #21
|
22 => 'FFFF8080', // Chart Fill Colour #22
|
23 => 'FF0066CC', // Chart Fill Colour #23
|
24 => 'FFCCCCFF', // Chart Fill Colour #24
|
25 => 'FF000080', // Chart Line Colour #25
|
26 => 'FFFF00FF', // Chart Line Colour #26
|
27 => 'FFFFFF00', // Chart Line Colour #27
|
28 => 'FF00FFFF', // Chart Line Colour #28
|
29 => 'FF800080', // Chart Line Colour #29
|
30 => 'FF800000', // Chart Line Colour #30
|
31 => 'FF008080', // Chart Line Colour #31
|
32 => 'FF0000FF', // Chart Line Colour #32
|
33 => 'FF00CCFF', // Standard Colour #33
|
34 => 'FFCCFFFF', // Standard Colour #34
|
35 => 'FFCCFFCC', // Standard Colour #35
|
36 => 'FFFFFF99', // Standard Colour #36
|
37 => 'FF99CCFF', // Standard Colour #37
|
38 => 'FFFF99CC', // Standard Colour #38
|
39 => 'FFCC99FF', // Standard Colour #39
|
40 => 'FFFFCC99', // Standard Colour #40
|
41 => 'FF3366FF', // Standard Colour #41
|
42 => 'FF33CCCC', // Standard Colour #42
|
43 => 'FF99CC00', // Standard Colour #43
|
44 => 'FFFFCC00', // Standard Colour #44
|
45 => 'FFFF9900', // Standard Colour #45
|
46 => 'FFFF6600', // Standard Colour #46
|
47 => 'FF666699', // Standard Colour #47
|
48 => 'FF969696', // Standard Colour #48
|
49 => 'FF003366', // Standard Colour #49
|
50 => 'FF339966', // Standard Colour #50
|
51 => 'FF003300', // Standard Colour #51
|
52 => 'FF333300', // Standard Colour #52
|
53 => 'FF993300', // Standard Colour #53
|
54 => 'FF993366', // Standard Colour #54
|
55 => 'FF333399', // Standard Colour #55
|
56 => 'FF333333', // Standard Colour #56
|
];
|
|
/**
|
* ARGB - Alpha RGB.
|
*/
|
protected ?string $argb = null;
|
|
private bool $hasChanged = false;
|
|
/**
|
* Create a new Color.
|
*
|
* @param string $colorValue ARGB value for the colour, or named colour
|
* @param bool $isSupervisor Flag indicating if this is a supervisor or not
|
* Leave this value at default unless you understand exactly what
|
* its ramifications are
|
* @param bool $isConditional Flag indicating if this is a conditional style or not
|
* Leave this value at default unless you understand exactly what
|
* its ramifications are
|
*/
|
public function __construct(string $colorValue = self::COLOR_BLACK, bool $isSupervisor = false, bool $isConditional = false)
|
{
|
// Supervisor?
|
parent::__construct($isSupervisor);
|
|
// Initialise values
|
if (!$isConditional) {
|
$this->argb = $this->validateColor($colorValue) ?: self::COLOR_BLACK;
|
}
|
}
|
|
/**
|
* Get the shared style component for the currently active cell in currently active sheet.
|
* Only used for style supervisor.
|
*/
|
public function getSharedComponent(): self
|
{
|
/** @var Style $parent */
|
$parent = $this->parent;
|
/** @var Border|Fill $sharedComponent */
|
$sharedComponent = $parent->getSharedComponent();
|
if ($sharedComponent instanceof Fill) {
|
if ($this->parentPropertyName === 'endColor') {
|
return $sharedComponent->getEndColor();
|
}
|
|
return $sharedComponent->getStartColor();
|
}
|
|
return $sharedComponent->getColor();
|
}
|
|
/**
|
* Build style array from subcomponents.
|
*/
|
public function getStyleArray(array $array): array
|
{
|
/** @var Style $parent */
|
$parent = $this->parent;
|
|
return $parent->getStyleArray([$this->parentPropertyName => $array]);
|
}
|
|
/**
|
* Apply styles from array.
|
*
|
* <code>
|
* $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray(['rgb' => '808080']);
|
* </code>
|
*
|
* @param array $styleArray Array containing style information
|
*
|
* @return $this
|
*/
|
public function applyFromArray(array $styleArray): static
|
{
|
if ($this->isSupervisor) {
|
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
|
} else {
|
if (isset($styleArray['rgb'])) {
|
$this->setRGB($styleArray['rgb']);
|
}
|
if (isset($styleArray['argb'])) {
|
$this->setARGB($styleArray['argb']);
|
}
|
}
|
|
return $this;
|
}
|
|
private function validateColor(?string $colorValue): string
|
{
|
if ($colorValue === null || $colorValue === '') {
|
return self::COLOR_BLACK;
|
}
|
$named = ucfirst(strtolower($colorValue));
|
if (array_key_exists($named, self::NAMED_COLOR_TRANSLATIONS)) {
|
return self::NAMED_COLOR_TRANSLATIONS[$named];
|
}
|
if (preg_match(self::VALIDATE_COLOR_8, $colorValue) === 1) {
|
return $colorValue;
|
}
|
if (preg_match(self::VALIDATE_COLOR_6, $colorValue) === 1) {
|
return 'FF' . $colorValue;
|
}
|
|
return '';
|
}
|
|
/**
|
* Get ARGB.
|
*/
|
public function getARGB(): ?string
|
{
|
if ($this->isSupervisor) {
|
return $this->getSharedComponent()->getARGB();
|
}
|
|
return $this->argb;
|
}
|
|
/**
|
* Set ARGB.
|
*
|
* @param ?string $colorValue ARGB value, or a named color
|
*
|
* @return $this
|
*/
|
public function setARGB(?string $colorValue = self::COLOR_BLACK): static
|
{
|
$this->hasChanged = true;
|
$colorValue = $this->validateColor($colorValue);
|
if ($colorValue === '') {
|
return $this;
|
}
|
|
if ($this->isSupervisor) {
|
$styleArray = $this->getStyleArray(['argb' => $colorValue]);
|
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
|
} else {
|
$this->argb = $colorValue;
|
}
|
|
return $this;
|
}
|
|
/**
|
* Get RGB.
|
*/
|
public function getRGB(): string
|
{
|
if ($this->isSupervisor) {
|
return $this->getSharedComponent()->getRGB();
|
}
|
|
return substr($this->argb ?? '', 2);
|
}
|
|
/**
|
* Set RGB.
|
*
|
* @param ?string $colorValue RGB value, or a named color
|
*
|
* @return $this
|
*/
|
public function setRGB(?string $colorValue = self::COLOR_BLACK): static
|
{
|
return $this->setARGB($colorValue);
|
}
|
|
/**
|
* Get a specified colour component of an RGB value.
|
*
|
* @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
|
* @param int $offset Position within the RGB value to extract
|
* @param bool $hex Flag indicating whether the component should be returned as a hex or a
|
* decimal value
|
*
|
* @return int|string The extracted colour component
|
*/
|
private static function getColourComponent(string $rgbValue, int $offset, bool $hex = true): string|int
|
{
|
$colour = substr($rgbValue, $offset, 2) ?: '';
|
if (preg_match('/^[0-9a-f]{2}$/i', $colour) !== 1) {
|
$colour = '00';
|
}
|
|
return ($hex) ? $colour : (int) hexdec($colour);
|
}
|
|
/**
|
* Get the red colour component of an RGB value.
|
*
|
* @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
|
* @param bool $hex Flag indicating whether the component should be returned as a hex or a
|
* decimal value
|
*
|
* @return int|string The red colour component
|
*/
|
public static function getRed(string $rgbValue, bool $hex = true)
|
{
|
return self::getColourComponent($rgbValue, strlen($rgbValue) - 6, $hex);
|
}
|
|
/**
|
* Get the green colour component of an RGB value.
|
*
|
* @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
|
* @param bool $hex Flag indicating whether the component should be returned as a hex or a
|
* decimal value
|
*
|
* @return int|string The green colour component
|
*/
|
public static function getGreen(string $rgbValue, bool $hex = true)
|
{
|
return self::getColourComponent($rgbValue, strlen($rgbValue) - 4, $hex);
|
}
|
|
/**
|
* Get the blue colour component of an RGB value.
|
*
|
* @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
|
* @param bool $hex Flag indicating whether the component should be returned as a hex or a
|
* decimal value
|
*
|
* @return int|string The blue colour component
|
*/
|
public static function getBlue(string $rgbValue, bool $hex = true)
|
{
|
return self::getColourComponent($rgbValue, strlen($rgbValue) - 2, $hex);
|
}
|
|
/**
|
* Adjust the brightness of a color.
|
*
|
* @param string $hexColourValue The colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
|
* @param float $adjustPercentage The percentage by which to adjust the colour as a float from -1 to 1
|
*
|
* @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
|
*/
|
public static function changeBrightness(string $hexColourValue, float $adjustPercentage): string
|
{
|
$rgba = (strlen($hexColourValue) === 8);
|
$adjustPercentage = max(-1.0, min(1.0, $adjustPercentage));
|
|
/** @var int $red */
|
$red = self::getRed($hexColourValue, false);
|
/** @var int $green */
|
$green = self::getGreen($hexColourValue, false);
|
/** @var int $blue */
|
$blue = self::getBlue($hexColourValue, false);
|
|
return (($rgba) ? 'FF' : '') . RgbTint::rgbAndTintToRgb($red, $green, $blue, $adjustPercentage);
|
}
|
|
/**
|
* Get indexed color.
|
*
|
* @param int $colorIndex Index entry point into the colour array
|
* @param bool $background Flag to indicate whether default background or foreground colour
|
* should be returned if the indexed colour doesn't exist
|
*/
|
public static function indexedColor(int $colorIndex, bool $background = false, ?array $palette = null): self
|
{
|
// Clean parameter
|
$colorIndex = (int) $colorIndex;
|
|
if (empty($palette)) {
|
if (isset(self::INDEXED_COLORS[$colorIndex])) {
|
return new self(self::INDEXED_COLORS[$colorIndex]);
|
}
|
} else {
|
if (isset($palette[$colorIndex])) {
|
return new self($palette[$colorIndex]);
|
}
|
}
|
|
return ($background) ? new self(self::COLOR_WHITE) : new self(self::COLOR_BLACK);
|
}
|
|
/**
|
* Get hash code.
|
*
|
* @return string Hash code
|
*/
|
public function getHashCode(): string
|
{
|
if ($this->isSupervisor) {
|
return $this->getSharedComponent()->getHashCode();
|
}
|
|
return md5(
|
$this->argb
|
. __CLASS__
|
);
|
}
|
|
protected function exportArray1(): array
|
{
|
$exportedArray = [];
|
$this->exportArray2($exportedArray, 'argb', $this->getARGB());
|
|
return $exportedArray;
|
}
|
|
public function getHasChanged(): bool
|
{
|
if ($this->isSupervisor) {
|
return $this->getSharedComponent()->hasChanged;
|
}
|
|
return $this->hasChanged;
|
}
|
}
|