Skip to content
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

Add Gradient Fill to Chart/PlotArea #2929

Closed
2 of 8 tasks
bridgeplayr opened this issue Jul 10, 2022 · 5 comments · Fixed by #2950
Closed
2 of 8 tasks

Add Gradient Fill to Chart/PlotArea #2929

bridgeplayr opened this issue Jul 10, 2022 · 5 comments · Fixed by #2950
Labels

Comments

@bridgeplayr
Copy link

This is:

- [ ] a bug report
- [x] a feature request
- [ ] **not** a usage question (ask them on https://stackoverflow.com/questions/tagged/phpspreadsheet or https://gitter.im/PHPOffice/PhpSpreadsheet)

What is the expected behavior?

Control Gradient Fill properties

What is the current behavior?

not available

What are the steps to reproduce?

Please provide a Minimal, Complete, and Verifiable example of code that exhibits the issue without relying on an external Excel file or a web server:

<?php

require __DIR__ . '/vendor/autoload.php';

// Create new Spreadsheet object
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();

// add code that show the issue here...

If this is an issue with reading a specific spreadsheet file, then it may be appropriate to provide a sample file that demonstrates the problem; but please keep it as small as possible, and sanitize any confidential information before uploading.

What features do you think are causing the issue

  • Reader
  • Writer
  • Styles
  • Data Validations
  • Formula Calulations
  • Charts
  • AutoFilter
  • Form Elements

Does an issue affect all spreadsheet file formats? If not, which formats are affected?

Charts, Xlsx

Which versions of PhpSpreadsheet and PHP are affected?

1.23 7.3

@bridgeplayr
Copy link
Author

bridgeplayr commented Jul 11, 2022

Feature addition requires modifications to:

  • ChartColor.php (add new property $brightness; add brightness getter/setter; add to generic getter/setter functions)
  • PlotArea.php (add new property array $gradientFillProperties {} with getter/setter methods
  • Writer/Xlsx/Chart.php (add to method writePlotAread(); add to method writeColor() to include new attributes 'lumMod' & 'lumOff' derived from $brightness)

Chart/ChartColor.php changes

    /** @var ?int */
    private $brightness;

    public function getBrightness(): ?int
    {
        return $this->brightness;
    }
 . . .
    public function setBrightness(?int $brightness): self
    {
        $this->brightness = $brightness;

        return $this;
    }

    /**
     * @param null|float|int|string $color
     * @param null|int $alpha
     * @param null|string $type
     * @param null|int $brightness
     */
public function setColorProperties($color = null, $alpha = null, $type = '', $brightness = null ): self
    {
. . .

        if ($brightness === null) {
            $this->setBrightness(null);
        } elseif (is_numeric($brightness)) {
            $this->setBrightness((int) $brightness);
        }

        return $this;
    }

    public function setColorPropertiesArray(array $color): self
    {
. . .
        if (array_key_exists('brightness', $color)) {
            if ($color['brightness'] === null) {
                $this->setBrightness(null);
            } elseif (is_numeric($color['brightness'])) {
                $this->setBrightness((int) $color['brightness']);
            }
        }

        return $this;
    }
. . .
    /**
     * Get Color Property.
     *
     * @param string $propertyName
     *
     * @return null|int|string
     */
    public function getColorProperty($propertyName)
    {
. . .
        } elseif ($propertyName === 'brightness') {
            $retVal = $this->brightness;
        }

        return $retVal;
    }
`

Chart/PlotArea.php Changes

    /**
     * PlotArea Gradient Stop list.
     *
     * @var mixed array
     */
    private $gsLst = [
        'pos' => null, // percent
        'gsColor' => null // ChartColor
    ];

    /**
     * PlotArea Gradient Fill Properties.
     *
     * @var mixed array
     */
    protected $gradientFillProperties = [
        'gsLst' => null, // list of gradient stops
        'angle' => null, // angleToXml value
    ];
. . .
    /**
     * Set gradientFillProperties.
     *
     * @param array $gsLst
     * @param float $angle
     *
     */
    public function setGradientFillProperties(array $gsLst, float $angle): self
    {
        $this->gradientFillProperties['gsLst'] = $gsLst;
        $this->gradientFillProperties['angle'] = $angle;
        return $this ;
    }

    /**
     * Get gradientFillAngle.
     *
     * @return null|float
     *
     */
    public function getGradientFillAngle()
    {
        return $this->gradientFillProperties['angle'];
    }

    /**
     * Get gradientFillGsLst.
     *
     * @return array
     *
     */
    public function getGradientFillGsLst()
    {
        return $this->gradientFillProperties['gsLst'];
    }

`

Writer/Xlsx/Chart.php changes

private function WritePlotArea (...)
. . .

        // PlotArea Gradient Fill
        $gsLst = $plotArea->getGradientFillGsLst();
        if ($gsLst !== null && is_array($gsLst) ) {
            $objWriter->startElement('c:spPr');
            $objWriter->startElement('a:gradFill');
            $objWriter->writeAttribute('flip', 'none');
            $objWriter->writeAttribute('rotWithShape', "1");
            $objWriter->startElement('a:gsLst');
            foreach ($gsLst as $gs) {
                $pos = $gs['pos'];
                $gsColor = $gs['gsColor'];
                $objWriter->startElement('a:gs');
                $objWriter->writeAttribute('pos', Properties::tenthOfPercentToXml((float) $pos) );
                $this->writeColor($objWriter, $gsColor, false);
                $objWriter->endElement(); // gs
            }
            $objWriter->endElement(); // gsLst
            $objWriter->startElement('a:lin');
            $FillAngle = $plotArea->getGradientFillAngle();
            $objWriter->writeAttribute('ang', Properties::angleToXml((float) $FillAngle) );
            $objWriter->writeAttribute('scaled', "1");
            $objWriter->endElement(); // lin
            $objWriter->startElement('a:tileRect');
            $objWriter->endElement(); // tileRect
            $objWriter->endElement(); // gradFill
            $objWriter->endElement(); // spPr
        }

        $objWriter->endElement(); // plotArea
    }
. . .


    private function writeColor(...)
    {
. . . 
          $alpha = $chartColor->getAlpha();
            if (is_numeric($alpha)) {
                $objWriter->startElement('a:alpha');
                $objWriter->writeAttribute('val', ChartColor::alphaToXml((int) $alpha));
                $objWriter->endElement();
            }
            $brightness = $chartColor->getBrightness();
            if (is_numeric($brightness)) {
                $antiBrightness = 1 - $brightness; // dullness?
                $objWriter->startElement('a:lumMod');
                $objWriter->writeAttribute('val', Properties::tenthOfPercentToXml((float) $antiBrightness) );
                $objWriter->endElement();
                $objWriter->startElement('a:lumOff');
                $objWriter->writeAttribute('val', Properties::tenthOfPercentToXml((float) $brightness) );
                $objWriter->endElement();
            }
  . . .
    }


Template to show how to use this feature

add these lines to any scatter chart
. . .

// Set the series in the plot area
	$plotArea = new PlotArea(null, [$series]);

	$pos1 = 0; // pos = 0% (extreme low side or lower left corner)
	$brightness1 = 0; // 0%
	$gsColor1  = new ChartColor;
	$gsColor1->setColorProperties('FF0000', 75, 'srgbClr', $brightness1); // red
	$gradientStop1 = array('pos' => $pos1, 'gsColor' => $gsColor1);

	$pos2 = 0.5; // pos = 50% (middle)
	$brightness2 = 0.5; // 50%
	$gsColor2  = new ChartColor;
	$gsColor2->setColorProperties('FFFF00', 50, 'srgbClr', $brightness2); // yellow
	$gradientStop2 = array('pos' => $pos2, 'gsColor' => $gsColor2);

	$pos3 = 1.0; // pos = 100% (extreme high side or upper right corner)
	$brightness3 = 0.5; // 50%
	$gsColor3  = new ChartColor;
	$gsColor3->setColorProperties('00B050', 50, 'srgbClr', $brightness3); // green
	$gradientStop3 = array('pos' => $pos3, 'gsColor' => $gsColor3);

	$gsLst = 
	[    $gradientStop1, 
	     $gradientStop2,
	     $gradientStop3
	];
	$gradientAngle = "315"; // 45deg above horiz

	$plotArea->setGradientFillProperties($gsLst, $gradientAngle);

image

@oleibman
Copy link
Collaborator

For now, I have no problem with the Gradient part of this request. But the Brightness part is really puzzling me. It was difficult to find out even how to change it in Excel in the first place (custom colors, change scheme to HSL). But, even having solved that problem, when I do that and save the spreadsheet, the result shows srgbClr and no lumMod. And, when I manually add a lumMod tag, it seems to make no difference. Do you have any samples where lumMod exists and matters?

@bridgeplayr
Copy link
Author

Yes. Bedtime here. I'll outline the need tomorrow.

@bridgeplayr
Copy link
Author

I have no choice but to use Microsoft 365 subscription products, which includes Excel. It is version 2206. In Excel, when I format the PlotArea, I see this view. Do you also see something like this?
image
The generated xml looks like this:
<a:gsLst>
<a:gs pos="0">
<a:srgbClr val="FF0000">
<a:alpha val="50000"/>
<a:lumMod val="100000"/>
<a:lumOff val="0"/>
</a:srgbClr>
</a:gs>
<a:gs pos="50000">
<a:srgbClr val="FFFF00">
<a:alpha val="50000"/>
<a:lumMod val="100000"/>
<a:lumOff val="0"/>
</a:srgbClr>
</a:gs>
<a:gs pos="100000">
<a:srgbClr val="00B050">
<a:alpha val="50000"/>
<a:lumMod val="100000"/>
<a:lumOff val="0"/>
</a:srgbClr>
</a:gs>
</a:gsLst>

@oleibman
Copy link
Collaborator

Gee, you don't sleep long. I was actually investigating lumMod apart from its use in gradients. If it is used (or if it is useful) only for gradients, your example is fine. Thanks.

oleibman added a commit to oleibman/PhpSpreadsheet that referenced this issue Jul 19, 2022
Fix PHPOffice#2257. Fix PHPOffice#2929. Fix PHPOffice#2935 (probably in a way that will not satisfy the requester). 2257 and 2929 requested changes that ultimately affect the same section of code, so it's appropriate to deal with them together. 2257 requests the ability to make the chart background transparent (so that the Excel gridlines are visible beneath the chart), and the ability to hide an Axis. 2929 requests the ability to set a gradient background on the chart.
oleibman added a commit that referenced this issue Jul 23, 2022
Fix #2257. Fix #2929. Fix #2935 (probably in a way that will not satisfy the requester). 2257 and 2929 requested changes that ultimately affect the same section of code, so it's appropriate to deal with them together. 2257 requests the ability to make the chart background transparent (so that the Excel gridlines are visible beneath the chart), and the ability to hide an Axis. 2929 requests the ability to set a gradient background on the chart.
MarkBaker added a commit that referenced this issue Sep 25, 2022
### Added

- Implementation of the new `TEXTBEFORE()`, `TEXTAFTER()` and `TEXTSPLIT()` Excel Functions
- Implementation of the `ARRAYTOTEXT()` and `VALUETOTEXT()` Excel Functions
- Support for [mitoteam/jpgraph](https://packagist.org/packages/mitoteam/jpgraph) implementation of
  JpGraph library to render charts added.
- Charts: Add Gradients, Transparency, Hidden Axes, Rounded Corners, Trendlines, Date Axes.

### Changed

- Allow variant behaviour when merging cells [Issue #3065](#3065)
  - Merge methods now allow an additional `$behaviour` argument. Permitted values are:
    - Worksheet::MERGE_CELL_CONTENT_EMPTY - Empty the content of the hidden cells (the default behaviour)
    - Worksheet::MERGE_CELL_CONTENT_HIDE - Keep the content of the hidden cells
    - Worksheet::MERGE_CELL_CONTENT_MERGE - Move the content of the hidden cells into the first cell

### Deprecated

- Axis getLineProperty deprecated in favor of getLineColorProperty.
- Moved majorGridlines and minorGridlines from Chart to Axis. Setting either in Chart constructor or through Chart methods, or getting either using Chart methods is deprecated.
- Chart::EXCEL_COLOR_TYPE_* copied from Properties to ChartColor; use in Properties is deprecated.
- ChartColor::EXCEL_COLOR_TYPE_ARGB deprecated in favor of EXCEL_COLOR_TYPE_RGB ("A" component was never allowed).
- Misspelled Properties::LINE_STYLE_DASH_SQUERE_DOT deprecated in favor of LINE_STYLE_DASH_SQUARE_DOT.
- Clone not permitted for Spreadsheet. Spreadsheet->copy() can be used instead.

### Removed

- Nothing

### Fixed

- Fix update to defined names when inserting/deleting rows/columns [Issue #3076](#3076) [PR #3077](#3077)
- Fix DataValidation sqRef when inserting/deleting rows/columns [Issue #3056](#3056) [PR #3074](#3074)
- Named ranges not usable as anchors in OFFSET function [Issue #3013](#3013)
- Fully flatten an array [Issue #2955](#2955) [PR #2956](#2956)
- cellExists() and getCell() methods should support UTF-8 named cells [Issue #2987](#2987) [PR #2988](#2988)
- Spreadsheet copy fixed, clone disabled. [PR #2951](#2951)
- Fix PDF problems with text rotation and paper size. [Issue #1747](#1747) [Issue #1713](#1713) [PR #2960](#2960)
- Limited support for chart titles as formulas [Issue #2965](#2965) [Issue #749](#749) [PR #2971](#2971)
- Add Gradients, Transparency, and Hidden Axes to Chart [Issue #2257](#2257) [Issue #2229](#2929) [Issue #2935](#2935) [PR #2950](#2950)
- Chart Support for Rounded Corners and Trendlines [Issue #2968](#2968) [Issue #2815](#2815) [PR #2976](#2976)
- Add setName Method for Chart [Issue #2991](#2991) [PR #3001](#3001)
- Eliminate partial dependency on php-intl in StringHelper [Issue #2982](#2982) [PR #2994](#2994)
- Minor changes for Pdf [Issue #2999](#2999) [PR #3002](#3002) [PR #3006](#3006)
- Html/Pdf Do net set background color for cells using (default) nofill [PR #3016](#3016)
- Add support for Date Axis to Chart [Issue #2967](#2967) [PR #3018](#3018)
- Reconcile Differences Between Css and Excel for Cell Alignment [PR #3048](#3048)
- R1C1 Format Internationalization and Better Support for Relative Offsets [Issue #1704](#1704) [PR #3052](#3052)
- Minor Fix for Percentage Formatting [Issue #1929](#1929) [PR #3053](#3053)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging a pull request may close this issue.

2 participants