Skip to content

Commit

Permalink
Support DataBar of conditional formatting rule
Browse files Browse the repository at this point in the history
Implemented the databar of Conditional Type.
- DataBar can be read, written, and added for basic use.
- Supports reading, writing and adding using "extLst".

About "extLst"
- https://docs.microsoft.com/en-us/openspecs/office_standards/ms-xlsx/07d607af-5618-4ca2-b683-6a78dc0d9627

The following setting items on the Excel setting screen can be read, written, and added.
- (minimum, maximum)type: Automatic, LowestValue, Number, Percent, Formula, Percentile
- Direction: context, leftToRight, rightToLeft (show data bar only)
- Fills Solid, Gradient
- FillColor: PositiveValues, NegativeValues
- Borders: Solid, None
- BorderColor: PositiveValues, NegativeValues
- Axis position: Automatic, Midpoint, None
- Axis color
  • Loading branch information
tera911 committed Jan 7, 2021
1 parent d2edab2 commit 52600ed
Show file tree
Hide file tree
Showing 12 changed files with 1,270 additions and 13 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).

### Added

- Nothing.
- Implemented DataBar for conditional formatting in Xlsx, providing read/write and creation of (type, value, direction, fills, border, axis position, color settings) as DataBar options in Excel. [#1754](https://github.com/PHPOffice/PhpSpreadsheet/pull/1754)

### Changed

Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions docs/topics/recipes.md
Expand Up @@ -884,6 +884,44 @@ $spreadsheet->getActiveSheet()
);
```

### DataBar of Conditional formatting
The basics are the same as conditional formatting.
Additional DataBar object to conditional formatting.

For example, the following code will result in the conditional formatting shown in the image.
```php
$conditional = new Conditional();
$conditional->setConditionType(Conditional::CONDITION_DATABAR);
$conditional->setDataBar(new ConditionalDataBar());
$conditional->getDataBar()
->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject('num', '2'))
->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject('max'))
->setColor('FFFF555A');
$ext = $conditional
->getDataBar()
->setConditionalFormattingRuleExt(new ConditionalFormattingRuleExtension())
->getConditionalFormattingRuleExt();

$ext->setCfRule('dataBar');
$ext->setSqref('A1:A5'); // target CellCoordinates
$ext->setDataBarExt(new ConditionalDataBarExtension());
$ext->getDataBarExt()
->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject('num', '2'))
->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject('autoMax'))
->setMinLength(0)
->setMaxLength(100)
->setBorder(true)
->setDirection('rightToLeft')
->setNegativeBarBorderColorSameAsPositive(false)
->setBorderColor('FFFF555A')
->setNegativeFillColor('FFFF0000')
->setNegativeBorderColor('FFFF0000')
->setAxisColor('FF000000');

```

![10-databar-of-conditional-formatting.png](./images/10-databar-of-conditional-formatting.png)

## Add a comment to a cell

To add a comment to a cell, use the following code. The example below
Expand Down
79 changes: 69 additions & 10 deletions src/PhpSpreadsheet/Reader/Xlsx/ConditionalStyles.php
Expand Up @@ -2,7 +2,11 @@

namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;

use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Conditional;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormattingRuleExtension;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalFormatValueObject;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;

Expand All @@ -25,7 +29,8 @@ public function load(): void
{
$this->setConditionalStyles(
$this->worksheet,
$this->readConditionalStyles($this->worksheetXml)
$this->readConditionalStyles($this->worksheetXml),
$this->worksheetXml->extLst
);
}

Expand All @@ -36,26 +41,28 @@ private function readConditionalStyles($xmlSheet)
foreach ($conditional->cfRule as $cfRule) {
if (
((string) $cfRule['type'] == Conditional::CONDITION_NONE
|| (string) $cfRule['type'] == Conditional::CONDITION_CELLIS
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS
|| (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS
|| (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION)
|| (string) $cfRule['type'] == Conditional::CONDITION_CELLIS
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSTEXT
|| (string) $cfRule['type'] == Conditional::CONDITION_CONTAINSBLANKS
|| (string) $cfRule['type'] == Conditional::CONDITION_NOTCONTAINSBLANKS
|| (string) $cfRule['type'] == Conditional::CONDITION_EXPRESSION)
&& isset($this->dxfs[(int) ($cfRule['dxfId'])])
) {
$conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
} elseif ((string) $cfRule['type'] == Conditional::CONDITION_DATABAR) {
$conditionals[(string) $conditional['sqref']][(int) ($cfRule['priority'])] = $cfRule;
}
}
}

return $conditionals;
}

private function setConditionalStyles(Worksheet $worksheet, array $conditionals): void
private function setConditionalStyles(Worksheet $worksheet, array $conditionals, $xmlExtLst): void
{
foreach ($conditionals as $ref => $cfRules) {
ksort($cfRules);
$conditionalStyles = $this->readStyleRules($cfRules);
$conditionalStyles = $this->readStyleRules($cfRules, $xmlExtLst);

// Extract all cell references in $ref
$cellBlocks = explode(' ', str_replace('$', '', strtoupper($ref)));
Expand All @@ -65,8 +72,9 @@ private function setConditionalStyles(Worksheet $worksheet, array $conditionals)
}
}

private function readStyleRules($cfRules)
private function readStyleRules($cfRules, $extLst)
{
$conditionalFormattingRuleExtensions = ConditionalFormattingRuleExtension::parseExtLstXml($extLst);
$conditionalStyles = [];
foreach ($cfRules as $cfRule) {
$objConditional = new Conditional();
Expand All @@ -88,10 +96,61 @@ private function readStyleRules($cfRules)
} else {
$objConditional->addCondition((string) $cfRule->formula);
}
$objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);

if (isset($cfRule->dataBar)) {
$objConditional->setDataBar($this->readDataBarOfConditionalRule($cfRule, $conditionalFormattingRuleExtensions));
} else {
$objConditional->setStyle(clone $this->dxfs[(int) ($cfRule['dxfId'])]);
}

$conditionalStyles[] = $objConditional;
}

return $conditionalStyles;
}

private function readDataBarOfConditionalRule($cfRule, $conditionalFormattingRuleExtensions): ConditionalDataBar
{
$dataBar = new ConditionalDataBar();
//dataBar attribute
if (isset($cfRule->dataBar['showValue'])) {
$dataBar->setShowValue((bool) $cfRule->dataBar['showValue']);
}

//dataBar children
//conditionalFormatValueObjects
$cfvoXml = $cfRule->dataBar->cfvo;
$cfvoIndex = 0;
foreach ((count($cfvoXml) > 1 ? $cfvoXml : [$cfvoXml]) as $cfvo) {
if ($cfvoIndex === 0) {
$dataBar->setMinimumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $cfvo['type'], (string) $cfvo['val']));
}
if ($cfvoIndex === 1) {
$dataBar->setMaximumConditionalFormatValueObject(new ConditionalFormatValueObject((string) $cfvo['type'], (string) $cfvo['val']));
}
++$cfvoIndex;
}

//color
if (isset($cfRule->dataBar->color)) {
$dataBar->setColor((string) $cfRule->dataBar->color['rgb']);
}
//extLst
$this->readDataBarExtLstOfConditionalRule($dataBar, $cfRule, $conditionalFormattingRuleExtensions);

return $dataBar;
}

private function readDataBarExtLstOfConditionalRule(ConditionalDataBar $dataBar, $cfRule, $conditionalFormattingRuleExtensions): void
{
if (isset($cfRule->extLst)) {
$ns = $cfRule->extLst->getNamespaces(true);
foreach ((count($cfRule->extLst) > 0 ? $cfRule->extLst->ext : [$cfRule->extLst->ext]) as $ext) {
$extId = (string) $ext->children($ns['x14'])->id;
if (isset($conditionalFormattingRuleExtensions[$extId]) && (string) $ext['uri'] === '{B025F937-C7B1-47D3-B67F-A62EFF666E3E}') {
$dataBar->setConditionalFormattingRuleExt($conditionalFormattingRuleExtensions[$extId]);
}
}
}
}
}
29 changes: 29 additions & 0 deletions src/PhpSpreadsheet/Style/Conditional.php
Expand Up @@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Style;

use PhpOffice\PhpSpreadsheet\IComparable;
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\ConditionalDataBar;

class Conditional implements IComparable
{
Expand All @@ -13,6 +14,7 @@ class Conditional implements IComparable
const CONDITION_EXPRESSION = 'expression';
const CONDITION_CONTAINSBLANKS = 'containsBlanks';
const CONDITION_NOTCONTAINSBLANKS = 'notContainsBlanks';
const CONDITION_DATABAR = 'dataBar';

// Operator types
const OPERATOR_NONE = '';
Expand Down Expand Up @@ -64,6 +66,11 @@ class Conditional implements IComparable
*/
private $condition = [];

/**
* @var ConditionalDataBar
*/
private $dataBar;

/**
* Style.
*
Expand Down Expand Up @@ -241,6 +248,28 @@ public function setStyle(?Style $pValue = null)
return $this;
}

/**
* get DataBar.
*
* @return ConditionalDataBar | null
*/
public function getDataBar()
{
return $this->dataBar;
}

/**
* set DataBar.
*
* @return $this
*/
public function setDataBar(ConditionalDataBar $dataBar)
{
$this->dataBar = $dataBar;

return $this;
}

/**
* Get hash code.
*
Expand Down
102 changes: 102 additions & 0 deletions src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalDataBar.php
@@ -0,0 +1,102 @@
<?php

namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;

class ConditionalDataBar
{
/** <dataBar> attribute */

/** @var null|bool */
private $showValue;

/** <dataBar> children */

/** @var ConditionalFormatValueObject */
private $minimumConditionalFormatValueObject;

/** @var ConditionalFormatValueObject */
private $maximumConditionalFormatValueObject;

/** @var string */
private $color;

/** <extLst> */

/** @var ConditionalFormattingRuleExtension */
private $conditionalFormattingRuleExt;

/**
* @return null|bool
*/
public function getShowValue()
{
return $this->showValue;
}

/**
* @param bool $showValue
*/
public function setShowValue($showValue)
{
$this->showValue = $showValue;

return $this;
}

/**
* @return ConditionalFormatValueObject
*/
public function getMinimumConditionalFormatValueObject()
{
return $this->minimumConditionalFormatValueObject;
}

public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject)
{
$this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;

return $this;
}

/**
* @return ConditionalFormatValueObject
*/
public function getMaximumConditionalFormatValueObject()
{
return $this->maximumConditionalFormatValueObject;
}

public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject)
{
$this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;

return $this;
}

public function getColor(): string
{
return $this->color;
}

public function setColor(string $color): self
{
$this->color = $color;

return $this;
}

/**
* @return ConditionalFormattingRuleExtension
*/
public function getConditionalFormattingRuleExt()
{
return $this->conditionalFormattingRuleExt;
}

public function setConditionalFormattingRuleExt(ConditionalFormattingRuleExtension $conditionalFormattingRuleExt)
{
$this->conditionalFormattingRuleExt = $conditionalFormattingRuleExt;

return $this;
}
}

0 comments on commit 52600ed

Please sign in to comment.