Repository: PHPOffice/PhpSpreadsheet (php)

Issue:
Line around charts should be removed
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
charts shouldn't have a line around them. This looks ugly and also unprofessional. It should be possible to remove the line or to never have a line around a chart:

### What is the current behavior?
The line cannot be removed. I use a hard coded solution to get rid of it.


### What are the steps to reproduce?

This is my hard coded fix in "Chart.php" in the "Xlsx" folder starting line 101 right before
$this->writePrintSettings($objWriter);
```php
<?php
//Roland Finke: Remove shape outline of chart
        $objWriter->startElement('c:spPr');
            $objWriter->startElement('a:ln');
                $objWriter->startElement('a:noFill');
                $objWriter->endElement();
            $objWriter->endElement();
        $objWriter->endElement();
        //Roland Finke: end
```


### Which versions of PhpSpreadsheet and PHP are affected?


Pull request description:
Allow Omitting Chart Border
Fix #562.

This is:

- [ ] a bugfix
- [x] a new feature
- [ ] refactoring
- [ ] additional unit tests

Checklist:

- [x] Changes are covered by unit tests
  - [x] Changes are covered by existing unit tests
  - [x] New unit tests have been added
- [x] Code style is respected
- [x] Commit message explains **why** the change is made (see https://github.com/erlang/otp/wiki/Writing-good-commit-messages)
- [ ] CHANGELOG.md contains a short summary of the change and a link to the pull request if applicable
- [ ] Documentation is updated as necessary

### Why this change is needed?

Provide an explanation of why this change is needed, with links to any Issues (if appropriate).
If this is a bugfix or a new feature, and there are no existing Issues, then please also create an issue that will make it easier to track progress with this PR.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f8175b4b1..f03be9833 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
 ### Added
 
 - Add Dynamic valueBinder Property to Spreadsheet and Readers. [Issue #1395](https://github.com/PHPOffice/PhpSpreadsheet/issues/1395) [PR #4185](https://github.com/PHPOffice/PhpSpreadsheet/pull/4185)
+- Allow Omitting Chart Border. [Issue #562](https://github.com/PHPOffice/PhpSpreadsheet/issues/562) [PR #4188](https://github.com/PHPOffice/PhpSpreadsheet/pull/4188)
 
 ### Changed
 
diff --git a/samples/Chart33a/33_Chart_create_area.php b/samples/Chart33a/33_Chart_create_area.php
index c6e349aa1..4e6fd4ec4 100644
--- a/samples/Chart33a/33_Chart_create_area.php
+++ b/samples/Chart33a/33_Chart_create_area.php
@@ -91,6 +91,8 @@ $chart = new Chart(
 $chart->setTopLeftPosition('A7');
 $chart->setBottomRightPosition('H20');
 
+$chart->setNoBorder(true);
+
 // Add the chart to the worksheet
 $worksheet->addChart($chart);
 
diff --git a/src/PhpSpreadsheet/Chart/Chart.php b/src/PhpSpreadsheet/Chart/Chart.php
index dae43c95d..6bcae28c8 100644
--- a/src/PhpSpreadsheet/Chart/Chart.php
+++ b/src/PhpSpreadsheet/Chart/Chart.php
@@ -106,6 +106,8 @@ class Chart
 
     private bool $noFill = false;
 
+    private bool $noBorder = false;
+
     private bool $roundedCorners = false;
 
     private GridLines $borderLines;
@@ -696,6 +698,18 @@ class Chart
         return $this;
     }
 
+    public function getNoBorder(): bool
+    {
+        return $this->noBorder;
+    }
+
+    public function setNoBorder(bool $noBorder): self
+    {
+        $this->noBorder = $noBorder;
+
+        return $this;
+    }
+
     public function getRoundedCorners(): bool
     {
         return $this->roundedCorners;
diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
index 94d9c6af5..146944150 100644
--- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
+++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php
@@ -95,6 +95,7 @@ class Chart
         $gapWidth = null;
         $useUpBars = null;
         $useDownBars = null;
+        $noBorder = false;
         foreach ($chartElementsC as $chartElementKey => $chartElement) {
             switch ($chartElementKey) {
                 case 'spPr':
@@ -108,6 +109,9 @@ class Chart
                     if (isset($children->ln)) {
                         $chartBorderLines = new GridLines();
                         $this->readLineStyle($chartElementsC, $chartBorderLines);
+                        if (isset($children->ln->noFill)) {
+                            $noBorder = true;
+                        }
                     }
 
                     break;
@@ -470,6 +474,7 @@ class Chart
         if ($chartBorderLines !== null) {
             $chart->setBorderLines($chartBorderLines);
         }
+        $chart->setNoBorder($noBorder);
         $chart->setRoundedCorners($roundedCorners);
         if (is_bool($autoTitleDeleted)) {
             $chart->setAutoTitleDeleted($autoTitleDeleted);
diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
index ca16557a8..3bbcb3a88 100644
--- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
+++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php
@@ -118,7 +118,7 @@ class Chart extends WriterPart
             $this->writeColor($objWriter, $fillColor);
         }
         $borderLines = $chart->getBorderLines();
-        $this->writeLineStyles($objWriter, $borderLines);
+        $this->writeLineStyles($objWriter, $borderLines, $chart->getNoBorder());
         $this->writeEffects($objWriter, $borderLines);
         $objWriter->endElement(); // c:spPr
 


Test patch:
diff --git a/tests/PhpSpreadsheetTests/Chart/Issue562Test.php b/tests/PhpSpreadsheetTests/Chart/Issue562Test.php
new file mode 100644
index 000000000..21bb903fa
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Chart/Issue562Test.php
@@ -0,0 +1,128 @@
+<?php
+
+declare(strict_types=1);
+
+namespace PhpOffice\PhpSpreadsheetTests\Chart;
+
+use PhpOffice\PhpSpreadsheet\Chart\Chart;
+use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
+use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
+use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
+use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
+use PhpOffice\PhpSpreadsheet\Chart\Title;
+use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
+use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
+
+class Issue562Test extends AbstractFunctional
+{
+    // based on 33_Char_create_area
+    // test for chart borders
+    public function readCharts(XlsxReader $reader): void
+    {
+        $reader->setIncludeCharts(true);
+    }
+
+    public function writeCharts(XlsxWriter $writer): void
+    {
+        $writer->setIncludeCharts(true);
+    }
+
+    /**
+     * @dataProvider providerNoBorder
+     */
+    public function testNoBorder(?bool $noBorder, bool $expectedResult): void
+    {
+        $spreadsheet = new Spreadsheet();
+        $worksheet = $spreadsheet->getActiveSheet();
+        $worksheet->fromArray(
+            [
+                ['', 2010, 2011, 2012],
+                ['Q1', 12, 15, 21],
+                ['Q2', 56, 73, 86],
+                ['Q3', 52, 61, 69],
+                ['Q4', 30, 32, 0],
+            ]
+        );
+
+        $dataSeriesLabels = [
+            new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010
+            new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011
+            new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012
+        ];
+
+        $xAxisTickValues = [
+            new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
+        ];
+
+        $dataSeriesValues = [
+            new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4),
+            new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4),
+            new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4),
+        ];
+
+        // Build the dataseries
+        $series = new DataSeries(
+            DataSeries::TYPE_AREACHART, // plotType
+            DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping
+            range(0, count($dataSeriesValues) - 1), // plotOrder
+            $dataSeriesLabels, // plotLabel
+            $xAxisTickValues, // plotCategory
+            $dataSeriesValues          // plotValues
+        );
+
+        $plotArea = new PlotArea(null, [$series]);
+        $legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
+
+        $title = new Title('Test %age-Stacked Area Chart');
+        $yAxisLabel = new Title('Value ($k)');
+
+        $chart = new Chart(
+            'chart1', // name
+            $title, // title
+            $legend, // legend
+            $plotArea, // plotArea
+            true, // plotVisibleOnly
+            DataSeries::EMPTY_AS_GAP, // displayBlanksAs
+            null, // xAxisLabel
+            $yAxisLabel  // yAxisLabel
+        );
+
+        // Set the position where the chart should appear in the worksheet
+        $chart->setTopLeftPosition('A7');
+        $chart->setBottomRightPosition('H20');
+
+        if ($noBorder !== null) {
+            $chart->setNoBorder($noBorder);
+        }
+
+        // Add the chart to the worksheet
+        $worksheet->addChart($chart);
+
+        /** @var callable */
+        $callableReader = [$this, 'readCharts'];
+        /** @var callable */
+        $callableWriter = [$this, 'writeCharts'];
+        $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter);
+        $spreadsheet->disconnectWorksheets();
+
+        $sheet = $reloadedSpreadsheet->getActiveSheet();
+        $charts2 = $sheet->getChartCollection();
+        self::assertCount(1, $charts2);
+        $chart2 = $charts2[0];
+        self::assertNotNull($chart2);
+        self::assertSame($expectedResult, $chart2->getNoBorder());
+
+        $reloadedSpreadsheet->disconnectWorksheets();
+    }
+
+    public static function providerNoBorder(): array
+    {
+        return [
+            [true, true],
+            [false, false],
+            [null, false],
+        ];
+    }
+}