Skip to content

Add Conditional Formatting with IconSet #4574

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions samples/ConditionalFormatting/cond09_iconset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Conditional;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormatValueObject;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalIconSet;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\IconSetValues;

require __DIR__ . '/../Header.php';
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */

// Create new Spreadsheet object
$helper->log('Create new Spreadsheet object');
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

// Set document properties
$helper->log('Set document properties');
$spreadsheet->getProperties()->setCreator('issakujitsuk')
->setLastModifiedBy('issakujitsuk')
->setTitle('PhpSpreadsheet Test Document')
->setSubject('PhpSpreadsheet Test Document')
->setDescription('Test document for PhpSpreadsheet, generated using PHP classes.')
->setKeywords('office PhpSpreadsheet php')
->setCategory('Test result file');

// Create the worksheet
$helper->log('Add data');
foreach (['A', 'B', 'C'] as $columnIndex) {
$sheet
->setCellValue("{$columnIndex}1", 1)
->setCellValue("{$columnIndex}2", 2)
->setCellValue("{$columnIndex}3", 8)
->setCellValue("{$columnIndex}4", 4)
->setCellValue("{$columnIndex}5", 5)
->setCellValue("{$columnIndex}6", 6)
->setCellValue("{$columnIndex}7", 7)
->setCellValue("{$columnIndex}8", 3)
->setCellValue("{$columnIndex}9", 9)
->setCellValue("{$columnIndex}10", 10);
}

// Set conditional formatting rules and styles
$helper->log('Define conditional formatting using Icon Set');

// 3 icons
$sheet->getStyle('A1:A10')
->setConditionalStyles([
makeConditionalIconSet(
IconSetValues::ThreeSymbols,
[
new ConditionalFormatValueObject('percent', 0),
new ConditionalFormatValueObject('percent', 33),
new ConditionalFormatValueObject('percent', 67),
]
),
]);

// 4 icons
$sheet->getStyle('B1:B10')
->setConditionalStyles([
makeConditionalIconSet(
IconSetValues::FourArrows,
[
new ConditionalFormatValueObject('percent', 0),
new ConditionalFormatValueObject('percent', 25),
new ConditionalFormatValueObject('percent', 50),
new ConditionalFormatValueObject('percent', 75),
]
),
]);

// 5 icons
$sheet->getStyle('C1:C10')
->setConditionalStyles([
makeConditionalIconSet(
IconSetValues::FiveQuarters,
[
new ConditionalFormatValueObject('percent', 0),
new ConditionalFormatValueObject('percent', 20),
new ConditionalFormatValueObject('percent', 40),
new ConditionalFormatValueObject('percent', 60),
new ConditionalFormatValueObject('percent', 80),
]
),
]);

// Save
$sheet->setSelectedCells('A1');
$helper->write($spreadsheet, __FILE__, ['Xlsx']);

/**
* Helper function to create a Conditional object with an IconSet.
*
* @param IconSetValues $type The type of icon set
* @param ConditionalFormatValueObject[] $cfvos The conditional format value objects
*/
function makeConditionalIconSet(
IconSetValues $type,
array $cfvos,
): Conditional {
$condition = new Conditional();
$condition->setConditionType(Conditional::CONDITION_ICONSET);
$iconSet = new ConditionalIconSet();
$condition->setIconSet($iconSet);
$iconSet->setIconSetType($type)
->setCfvos($cfvos);

return $condition;
}
38 changes: 38 additions & 0 deletions src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormattingRuleExtension;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormatValueObject;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalIconSet;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\IconSetValues;
use PhpOffice\PhpSpreadsheet\Style\Style as Style;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
Expand Down Expand Up @@ -265,6 +267,8 @@ private function readStyleRules(array $cfRules, SimpleXMLElement $extLst): array
$objConditional->setColorScale(
$this->readColorScale($cfRule)
);
} elseif (isset($cfRule->iconSet)) {
$objConditional->setIconSet($this->readIconSet($cfRule));
} elseif (isset($cfRule['dxfId'])) {
$objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);
}
Expand Down Expand Up @@ -349,6 +353,40 @@ private function readColorScale(SimpleXMLElement|stdClass $cfRule): ConditionalC
return $colorScale;
}

private function readIconSet(SimpleXMLElement $cfRule): ConditionalIconSet
{
$iconSet = new ConditionalIconSet();

if (isset($cfRule->iconSet['iconSet'])) {
$iconSet->setIconSetType(IconSetValues::from($cfRule->iconSet['iconSet']));
}
if (isset($cfRule->iconSet['reverse'])) {
$iconSet->setReverse('1' === (string) $cfRule->iconSet['reverse']);
}
if (isset($cfRule->iconSet['showValue'])) {
$iconSet->setShowValue('1' === (string) $cfRule->iconSet['showValue']);
}
if (isset($cfRule->iconSet['custom'])) {
$iconSet->setCustom('1' === (string) $cfRule->iconSet['custom']);
}

$cfvos = [];
foreach ($cfRule->iconSet->cfvo as $cfvoXml) {
$type = (string) $cfvoXml['type'];
$value = (string) ($cfvoXml['val'] ?? '');
$cfvo = new ConditionalFormatValueObject($type, $value);
if (isset($cfvoXml['gte'])) {
$cfvo->setGreaterThanOrEqual('1' === (string) $cfvoXml['gte']);
}
$cfvos[] = $cfvo;
}
$iconSet->setCfvos($cfvos);

// TODO: The cfIcon element is not implemented yet.

return $iconSet;
}

/** @param ConditionalFormattingRuleExtension[] $conditionalFormattingRuleExtensions */
private function readDataBarExtLstOfConditionalRule(ConditionalDataBar $dataBar, SimpleXMLElement $cfRule, array $conditionalFormattingRuleExtensions): void
{
Expand Down
25 changes: 21 additions & 4 deletions src/PhpSpreadsheet/Style/Conditional.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use PhpOffice\PhpSpreadsheet\IComparable;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalColorScale;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalIconSet;

class Conditional implements IComparable
{
Expand All @@ -25,6 +26,7 @@ class Conditional implements IComparable
const CONDITION_TIMEPERIOD = 'timePeriod';
const CONDITION_DUPLICATES = 'duplicateValues';
const CONDITION_UNIQUE = 'uniqueValues';
const CONDITION_ICONSET = 'iconSet';

private const CONDITION_TYPES = [
self::CONDITION_BEGINSWITH,
Expand All @@ -43,6 +45,7 @@ class Conditional implements IComparable
self::CONDITION_NOTCONTAINSTEXT,
self::CONDITION_TIMEPERIOD,
self::CONDITION_UNIQUE,
self::CONDITION_ICONSET,
];

// Operator types
Expand Down Expand Up @@ -102,6 +105,8 @@ class Conditional implements IComparable

private ?ConditionalColorScale $colorScale = null;

private ?ConditionalIconSet $iconSet = null;

private Style $style;

private bool $noFormatSet = false;
Expand Down Expand Up @@ -318,6 +323,18 @@ public function setColorScale(ConditionalColorScale $colorScale): static
return $this;
}

public function getIconSet(): ?ConditionalIconSet
{
return $this->iconSet;
}

public function setIconSet(ConditionalIconSet $iconSet): static
{
$this->iconSet = $iconSet;

return $this;
}

/**
* Get hash code.
*
Expand All @@ -327,10 +344,10 @@ public function getHashCode(): string
{
return md5(
$this->conditionType
. $this->operatorType
. implode(';', $this->condition)
. $this->style->getHashCode()
. __CLASS__
. $this->operatorType
. implode(';', $this->condition)
. $this->style->getHashCode()
. __CLASS__
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ class ConditionalFormatValueObject

private ?string $cellFormula;

/**
* For icon sets, determines whether this threshold value uses the greater
* than or equal to operator. False indicates 'greater than' is used instead
* of 'greater than or equal to'.
*/
private ?bool $greaterThanOrEqual = null;

public function __construct(string $type, null|float|int|string $value = null, ?string $cellFormula = null)
{
$this->type = $type;
Expand Down Expand Up @@ -52,4 +59,16 @@ public function setCellFormula(?string $cellFormula): self

return $this;
}

public function getGreaterThanOrEqual(): ?bool
{
return $this->greaterThanOrEqual;
}

public function setGreaterThanOrEqual(?bool $greaterThanOrEqual): self
{
$this->greaterThanOrEqual = $greaterThanOrEqual;

return $this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;

class ConditionalIconSet
{
/** The icon set to display. */
private ?IconSetValues $iconSetType = null;

/** If true, reverses the default order of the icons in this icon set. */
private ?bool $reverse = null;

/** Indicates whether to show the values of the cells on which this icon set is applied. */
private ?bool $showValue = null;

/**
* If true, indicates that the icon set is a custom icon set.
* If this value is "true", there MUST be the same number of cfIcon elements
* as cfvo elements.
* If this value is "false", there MUST be 0 cfIcon elements.
*/
private ?bool $custom = null;

/** @var ConditionalFormatValueObject[] */
private array $cfvos = [];

public function getIconSetType(): ?IconSetValues
{
return $this->iconSetType;
}

public function setIconSetType(IconSetValues $type): self
{
$this->iconSetType = $type;

return $this;
}

public function getReverse(): ?bool
{
return $this->reverse;
}

public function setReverse(bool $reverse): self
{
$this->reverse = $reverse;

return $this;
}

public function getShowValue(): ?bool
{
return $this->showValue;
}

public function setShowValue(bool $showValue): self
{
$this->showValue = $showValue;

return $this;
}

public function getCustom(): ?bool
{
return $this->custom;
}

public function setCustom(bool $custom): self
{
$this->custom = $custom;

return $this;
}

/**
* Get the conditional format value objects.
*
* @return ConditionalFormatValueObject[]
*/
public function getCfvos(): array
{
return $this->cfvos;
}

/**
* Set the conditional format value objects.
*
* @param ConditionalFormatValueObject[] $cfvos
*/
public function setCfvos(array $cfvos): self
{
$this->cfvos = $cfvos;

return $this;
}
}
28 changes: 28 additions & 0 deletions src/PhpSpreadsheet/Style/ConditionalFormatting/IconSetValues.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;

enum IconSetValues: string
{
case ThreeArrows = '3Arrows';
case ThreeArrowsGray = '3ArrowsGray';
case ThreeFlags = '3Flags';
case ThreeTrafficLights1 = '3TrafficLights1';
case ThreeTrafficLights2 = '3TrafficLights2';
case ThreeSigns = '3Signs';
case ThreeSymbols = '3Symbols';
case ThreeSymbols2 = '3Symbols2';
case FourArrows = '4Arrows';
case FourArrowsGray = '4ArrowsGray';
case FourRedToBlack = '4RedToBlack';
case FourRating = '4Rating';
case FourTrafficLights = '4TrafficLights';
case FiveArrows = '5Arrows';
case FiveArrowsGray = '5ArrowsGray';
case FiveRating = '5Rating';
case FiveQuarters = '5Quarters';
// The following icon sets are not implemented yet:
// case ThreeStars = "3Stars";
// case ThreeTriangles = "3Triangles";
// case FiveBoxes = "5Boxes";
}
Loading