Merge branch 'master' into Issue-2874_Calculation-Engine-Quoted-Worksheet-Regexp

This commit is contained in:
Mark Baker 2022-06-17 13:57:28 +02:00 committed by GitHub
commit 9f172733c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 3683 additions and 2011 deletions

View File

@ -13,6 +13,10 @@ jobs:
- '8.0'
- '8.1'
include:
- php-version: '8.2'
experimental: true
name: PHP ${{ matrix.php-version }}
steps:
@ -39,7 +43,7 @@ jobs:
- name: Delete composer lock file
id: composer-lock
if: ${{ matrix.php-version == '8.1' }}
if: ${{ matrix.php-version == '8.1' || matrix.php-version == '8.2' }}
run: |
rm composer.lock
echo "::set-output name=flags::--ignore-platform-reqs"

View File

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added
- Added `removeComment()` method for Worksheet [PR #2875](https://github.com/PHPOffice/PhpSpreadsheet/pull/2875/files)
- Add point size option for scatter charts [Issue #2298](https://github.com/PHPOffice/PhpSpreadsheet/issues/2298) [PR #2801](https://github.com/PHPOffice/PhpSpreadsheet/pull/2801)
- Basic support for Xlsx reading/writing Chart Sheets [PR #2830](https://github.com/PHPOffice/PhpSpreadsheet/pull/2830)
@ -18,12 +19,19 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Added Worksheet visibility in Ods Writer [PR #2850](https://github.com/PHPOffice/PhpSpreadsheet/pull/2850)
- Allow Csv Reader to treat string as contents of file [Issue #1285](https://github.com/PHPOffice/PhpSpreadsheet/issues/1285) [PR #2792](https://github.com/PHPOffice/PhpSpreadsheet/pull/2792)
- Allow Csv Reader to store null string rather than leave cell empty [Issue #2840](https://github.com/PHPOffice/PhpSpreadsheet/issues/2840) [PR #2842](https://github.com/PHPOffice/PhpSpreadsheet/pull/2842)
- Provide new Worksheet methods to identify if a row or column is "empty", making allowance for different definitions of "empty":
- Treat rows/columns containing no cell records as empty (default)
- Treat cells containing a null value as empty
- Treat cells containing an empty string as empty
### Changed
- Better enforcement of value modification to match specified datatype when using setValueExplicit()
- Relax validation of merge cells to allow merge for a single cell reference [Issue #2776](https://github.com/PHPOffice/PhpSpreadsheet/issues/2776)
- Memory and speed improvements, particularly for the Cell Collection, and the Writers.
See [the Discussion section on github](https://github.com/PHPOffice/PhpSpreadsheet/discussions/2821) for details of performance across versions
- Improved performance for removing rows/columns from a worksheet
### Deprecated
@ -41,7 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Time interval formatting [Issue #2768](https://github.com/PHPOffice/PhpSpreadsheet/issues/2768) [PR #2772](https://github.com/PHPOffice/PhpSpreadsheet/pull/2772)
- Copy from Xls(x) to Html/Pdf loses drawings [PR #2788](https://github.com/PHPOffice/PhpSpreadsheet/pull/2788)
- Html Reader converting cell containing 0 to null string [Issue #2810](https://github.com/PHPOffice/PhpSpreadsheet/issues/2810) [PR #2813](https://github.com/PHPOffice/PhpSpreadsheet/pull/2813)
- Many fixes for Charts, especially, but not limited to, Scatter, Bubble, and Surface charts. [Issue #2762](https://github.com/PHPOffice/PhpSpreadsheet/issues/2762) [Issue #2299](https://github.com/PHPOffice/PhpSpreadsheet/issues/2299) [Issue #2700](https://github.com/PHPOffice/PhpSpreadsheet/issues/2700) [Issue #2817](https://github.com/PHPOffice/PhpSpreadsheet/issues/2817) [Issue #2763](https://github.com/PHPOffice/PhpSpreadsheet/issues/2763) [PR #2828](https://github.com/PHPOffice/PhpSpreadsheet/pull/2828) [PR #2841](https://github.com/PHPOffice/PhpSpreadsheet/pull/2841) [PR #2846](https://github.com/PHPOffice/PhpSpreadsheet/pull/2846) [PR #2852](https://github.com/PHPOffice/PhpSpreadsheet/pull/2852)
- Many fixes for Charts, especially, but not limited to, Scatter, Bubble, and Surface charts. [Issue #2762](https://github.com/PHPOffice/PhpSpreadsheet/issues/2762) [Issue #2299](https://github.com/PHPOffice/PhpSpreadsheet/issues/2299) [Issue #2700](https://github.com/PHPOffice/PhpSpreadsheet/issues/2700) [Issue #2817](https://github.com/PHPOffice/PhpSpreadsheet/issues/2817) [Issue #2763](https://github.com/PHPOffice/PhpSpreadsheet/issues/2763) [Issue #2219](https://github.com/PHPOffice/PhpSpreadsheet/issues/2219) [PR #2828](https://github.com/PHPOffice/PhpSpreadsheet/pull/2828) [PR #2841](https://github.com/PHPOffice/PhpSpreadsheet/pull/2841) [PR #2846](https://github.com/PHPOffice/PhpSpreadsheet/pull/2846) [PR #2852](https://github.com/PHPOffice/PhpSpreadsheet/pull/2852) [PR #2856](https://github.com/PHPOffice/PhpSpreadsheet/pull/2856) [PR #2865](https://github.com/PHPOffice/PhpSpreadsheet/pull/2865) [PR #2872](https://github.com/PHPOffice/PhpSpreadsheet/pull/2872) [PR #2879](https://github.com/PHPOffice/PhpSpreadsheet/pull/2879)
## 1.23.0 - 2022-04-24

View File

@ -87,7 +87,7 @@
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^8.5 || ^9.0",
"squizlabs/php_codesniffer": "^3.6",
"squizlabs/php_codesniffer": "^3.7",
"tecnickcom/tcpdf": "^6.4"
},
"suggest": {

176
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "2e09b120ad90fc0fbbc055e1f341910e",
"content-hash": "fa9fa4814df8320d600551ca8ec11883",
"packages": [
{
"name": "ezyang/htmlpurifier",
@ -279,10 +279,6 @@
"keywords": [
"enum"
],
"support": {
"issues": "https://github.com/myclabs/php-enum/issues",
"source": "https://github.com/myclabs/php-enum/tree/1.8.3"
},
"funding": [
{
"url": "https://github.com/mnapoli",
@ -811,12 +807,12 @@
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
"url": "https://github.com/PHPCSStandards/composer-installer.git",
"reference": "7d5cb8826ed72d4ca4c07acf005bba2282e5a7c7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/7d5cb8826ed72d4ca4c07acf005bba2282e5a7c7",
"url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/7d5cb8826ed72d4ca4c07acf005bba2282e5a7c7",
"reference": "7d5cb8826ed72d4ca4c07acf005bba2282e5a7c7",
"shasum": ""
},
@ -830,7 +826,6 @@
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpcompatibility/php-compatibility": "^9.0"
},
"default-branch": true,
"type": "composer-plugin",
"extra": {
"class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
@ -999,10 +994,6 @@
"constructor",
"instantiate"
],
"support": {
"issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/1.4.1"
},
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
@ -1162,10 +1153,6 @@
],
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"support": {
"issues": "https://github.com/dompdf/dompdf/issues",
"source": "https://github.com/dompdf/dompdf/tree/v1.2.2"
},
"time": "2022-04-27T13:50:54+00:00"
},
{
@ -1366,11 +1353,6 @@
"php",
"utf-8"
],
"support": {
"docs": "http://mpdf.github.io",
"issues": "https://github.com/mpdf/mpdf/issues",
"source": "https://github.com/mpdf/mpdf"
},
"funding": [
{
"url": "https://www.paypal.me/mpdf",
@ -1426,10 +1408,6 @@
"object",
"object graph"
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
},
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
@ -1488,10 +1466,6 @@
"parser",
"php"
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
},
"time": "2021-11-30T19:35:32+00:00"
},
{
@ -1649,10 +1623,6 @@
}
],
"description": "Library for handling version information and constraints",
"support": {
"issues": "https://github.com/phar-io/version/issues",
"source": "https://github.com/phar-io/version/tree/3.2.1"
},
"time": "2022-02-21T01:04:05+00:00"
},
{
@ -1693,10 +1663,6 @@
],
"description": "A library to read, parse, export and make subsets of different types of font files.",
"homepage": "https://github.com/PhenX/php-font-lib",
"support": {
"issues": "https://github.com/dompdf/php-font-lib/issues",
"source": "https://github.com/dompdf/php-font-lib/tree/0.5.4"
},
"time": "2021-12-17T19:44:54+00:00"
},
{
@ -1739,10 +1705,6 @@
],
"description": "A library to read, parse and export to PDF SVG files.",
"homepage": "https://github.com/PhenX/php-svg-lib",
"support": {
"issues": "https://github.com/dompdf/php-svg-lib/issues",
"source": "https://github.com/dompdf/php-svg-lib/tree/0.4.1"
},
"time": "2022-03-07T12:52:04+00:00"
},
{
@ -1845,10 +1807,6 @@
"stream",
"uri"
],
"support": {
"issues": "https://github.com/php-http/message-factory/issues",
"source": "https://github.com/php-http/message-factory/tree/master"
},
"time": "2015-12-19T14:08:53+00:00"
},
{
@ -2067,10 +2025,6 @@
}
],
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1"
},
"time": "2022-03-15T21:29:03+00:00"
},
{
@ -2134,10 +2088,6 @@
"spy",
"stub"
],
"support": {
"issues": "https://github.com/phpspec/prophecy/issues",
"source": "https://github.com/phpspec/prophecy/tree/v1.15.0"
},
"time": "2021-12-08T12:19:24+00:00"
},
{
@ -2245,10 +2195,6 @@
"MIT"
],
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/1.1.1"
},
"time": "2022-04-20T15:24:25+00:00"
},
{
@ -2316,10 +2262,6 @@
"testing",
"xunit"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -2376,10 +2318,6 @@
"filesystem",
"iterator"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -2439,10 +2377,6 @@
"keywords": [
"process"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-invoker/issues",
"source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -2498,10 +2432,6 @@
"keywords": [
"template"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-text-template/issues",
"source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -2557,10 +2487,6 @@
"keywords": [
"timer"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-timer/issues",
"source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -2656,10 +2582,6 @@
"testing",
"xunit"
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20"
},
"funding": [
{
"url": "https://phpunit.de/sponsors.html",
@ -2916,10 +2838,6 @@
"parser",
"stylesheet"
],
"support": {
"issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues",
"source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0"
},
"time": "2021-12-11T13:40:54+00:00"
},
{
@ -2966,10 +2884,6 @@
],
"description": "Library for parsing CLI options",
"homepage": "https://github.com/sebastianbergmann/cli-parser",
"support": {
"issues": "https://github.com/sebastianbergmann/cli-parser/issues",
"source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3022,10 +2936,6 @@
],
"description": "Collection of value objects that represent the PHP code units",
"homepage": "https://github.com/sebastianbergmann/code-unit",
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit/issues",
"source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3077,10 +2987,6 @@
],
"description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
"source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3151,10 +3057,6 @@
"compare",
"equality"
],
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
"source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3208,10 +3110,6 @@
],
"description": "Library for calculating the complexity of PHP code units",
"homepage": "https://github.com/sebastianbergmann/complexity",
"support": {
"issues": "https://github.com/sebastianbergmann/complexity/issues",
"source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3274,10 +3172,6 @@
"unidiff",
"unified diff"
],
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3337,10 +3231,6 @@
"environment",
"hhvm"
],
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3414,10 +3304,6 @@
"export",
"exporter"
],
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3478,10 +3364,6 @@
"keywords": [
"global state"
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3535,10 +3417,6 @@
],
"description": "Library for counting the lines of code in PHP source code",
"homepage": "https://github.com/sebastianbergmann/lines-of-code",
"support": {
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3592,10 +3470,6 @@
],
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
"source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3647,10 +3521,6 @@
],
"description": "Allows reflection of object attributes, including inherited and non-public ones",
"homepage": "https://github.com/sebastianbergmann/object-reflector/",
"support": {
"issues": "https://github.com/sebastianbergmann/object-reflector/issues",
"source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3710,10 +3580,6 @@
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
"source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3765,10 +3631,6 @@
],
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"support": {
"issues": "https://github.com/sebastianbergmann/resource-operations/issues",
"source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3821,10 +3683,6 @@
],
"description": "Collection of value objects that represent the types of the PHP type system",
"homepage": "https://github.com/sebastianbergmann/type",
"support": {
"issues": "https://github.com/sebastianbergmann/type/issues",
"source": "https://github.com/sebastianbergmann/type/tree/3.0.0"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3874,10 +3732,6 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"support": {
"issues": "https://github.com/sebastianbergmann/version/issues",
"source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@ -3960,16 +3814,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.6.2",
"version": "3.7.0",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "5e4e71592f69da17871dba6e80dd51bce74a351a"
"reference": "a2cd51b45bcaef9c1f2a4bda48f2dd2fa2b95563"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a",
"reference": "5e4e71592f69da17871dba6e80dd51bce74a351a",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/a2cd51b45bcaef9c1f2a4bda48f2dd2fa2b95563",
"reference": "a2cd51b45bcaef9c1f2a4bda48f2dd2fa2b95563",
"shasum": ""
},
"require": {
@ -4007,12 +3861,7 @@
"phpcs",
"standards"
],
"support": {
"issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
"source": "https://github.com/squizlabs/PHP_CodeSniffer",
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
},
"time": "2021-12-12T21:44:58+00:00"
"time": "2022-06-13T06:31:38+00:00"
},
{
"name": "symfony/console",
@ -4603,9 +4452,6 @@
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@ -5381,10 +5227,6 @@
"pdf417",
"qrcode"
],
"support": {
"issues": "https://github.com/tecnickcom/TCPDF/issues",
"source": "https://github.com/tecnickcom/TCPDF/tree/6.4.4"
},
"funding": [
{
"url": "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&currency_code=GBP&business=paypal@tecnick.com&item_name=donation%20for%20tcpdf%20project",
@ -5526,5 +5368,5 @@
"ext-zlib": "*"
},
"platform-dev": [],
"plugin-api-version": "2.3.0"
"plugin-api-version": "1.1.0"
}

View File

@ -1348,7 +1348,7 @@ Removing a merge can be done using the unmergeCells method:
$spreadsheet->getActiveSheet()->unmergeCells('A18:E22');
```
## Inserting rows/columns
## Inserting or Removing rows/columns
You can insert/remove rows/columns at a specific position. The following
code inserts 2 new rows, right before row 7:
@ -1356,6 +1356,23 @@ code inserts 2 new rows, right before row 7:
```php
$spreadsheet->getActiveSheet()->insertNewRowBefore(7, 2);
```
while
```php
$spreadsheet->getActiveSheet()->removeRow(7, 2);
```
will remove 2 rows starting at row number 7 (ie. rows 7 and 8).
Equivalent methods exist for inserting/removing columns:
```php
$spreadsheet->getActiveSheet()->removeColumn('C', 2);
```
All subsequent rows (or columns) will be moved to allow the insertion (or removal) with all formulae referencing thise cells adjusted accordingly.
Note that this is a fairly intensive process, particularly with large worksheets, and especially if you are inserting/removing rows/columns from near beginning of the worksheet.
If you need to insert/remove several consecutive rows/columns, always use the second argument rather than making multiple calls to insert/remove a single row/column if possible.
## Add a drawing to a worksheet

View File

@ -1170,56 +1170,6 @@ parameters:
count: 2
path: src/PhpSpreadsheet/Chart/DataSeries.php
-
message: "#^Parameter \\#1 \\$angle of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:setShadowAngle\\(\\) expects int, int\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Parameter \\#1 \\$color of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:setGlowColor\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Parameter \\#1 \\$distance of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:setShadowDistance\\(\\) expects float, float\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Parameter \\#2 \\$alpha of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:setGlowColor\\(\\) expects int, int\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Parameter \\#3 \\$colorType of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:setGlowColor\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:\\$glowProperties has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:\\$lineProperties has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:\\$objectState has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:\\$shadowProperties has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:\\$softEdges has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/GridLines.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend\\:\\:\\$layout \\(PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Layout\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Layout\\|null\\.$#"
count: 1
@ -1275,36 +1225,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Chart/Properties.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getTrueAlpha\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Properties.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getTrueAlpha\\(\\) has parameter \\$alpha with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Properties.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:setColorProperties\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Properties.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:setColorProperties\\(\\) has parameter \\$alpha with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Properties.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:setColorProperties\\(\\) has parameter \\$color with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Properties.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:setColorProperties\\(\\) has parameter \\$colorType with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Properties.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Renderer\\\\JpGraph\\:\\:formatDataSetLabels\\(\\) has no return type specified\\.$#"
count: 1
@ -3155,26 +3075,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Shared/StringHelper.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:mbIsUpper\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/StringHelper.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:mbIsUpper\\(\\) has parameter \\$character with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/StringHelper.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:mbStrSplit\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/StringHelper.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:mbStrSplit\\(\\) has parameter \\$string with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Shared/StringHelper.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:sanitizeUTF8\\(\\) should return string but returns string\\|false\\.$#"
count: 1
@ -3200,11 +3100,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Shared/StringHelper.php
-
message: "#^Variable \\$textValue on left side of \\?\\? always exists and is not nullable\\.$#"
count: 3
path: src/PhpSpreadsheet/Shared/StringHelper.php
-
message: "#^Static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\TimeZone\\:\\:validateTimeZone\\(\\) is unused\\.$#"
count: 1
@ -3655,11 +3550,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Worksheet/CellIterator.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Column\\:\\:\\$parent \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/Column.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Drawing\\\\Shadow\\:\\:\\$color \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\|null\\.$#"
count: 1
@ -3685,11 +3575,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Worksheet/PageSetup.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Row\\:\\:\\$worksheet \\(PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/Row.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\SheetView\\:\\:\\$sheetViewTypes has no type specified\\.$#"
count: 1
@ -4460,11 +4345,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Else branch is unreachable because previous condition is always true\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#1 \\$plotSeriesValues of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeBubbles\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\|null, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\|false given\\.$#"
count: 1
@ -4475,16 +4355,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, array\\|int\\|string given\\.$#"
count: 8
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, array\\|int\\|string\\|null given\\.$#"
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, float given\\.$#"
count: 6
@ -4500,11 +4370,6 @@ parameters:
count: 42
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, string\\|null given\\.$#"
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#6 \\$yAxis of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeCategoryAxis\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis\\|null given\\.$#"
count: 1
@ -4525,16 +4390,6 @@ parameters:
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Part \\$xAxis\\-\\>getShadowProperty\\('effect'\\) \\(array\\|int\\|string\\|null\\) of encapsed string cannot be cast to string\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Part \\$xAxis\\-\\>getShadowProperty\\(\\['color', 'type'\\]\\) \\(array\\|int\\|string\\|null\\) of encapsed string cannot be cast to string\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:\\$calculateCellValues has no type specified\\.$#"
count: 1

Binary file not shown.

View File

@ -3088,7 +3088,7 @@ class Calculation
}
// Test whether we have any language data for this language (any locale)
if (in_array($language, self::$validLocaleLanguages)) {
if (in_array($language, self::$validLocaleLanguages, true)) {
// initialise language/locale settings
self::$localeFunctions = [];
self::$localeArgumentSeparator = ',';

View File

@ -152,7 +152,7 @@ class Functions
if ($condition === '') {
return '=""';
}
if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='])) {
if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='], true)) {
$condition = self::operandSpecialHandling($condition);
if (is_bool($condition)) {
return '=' . ($condition ? 'TRUE' : 'FALSE');

View File

@ -47,7 +47,7 @@ class ErrorValue
return false;
}
return in_array($value, ExcelError::$errorCodes) || $value === ExcelError::CALC();
return in_array($value, ExcelError::$errorCodes, true) || $value === ExcelError::CALC();
}
/**

View File

@ -11,7 +11,7 @@ class ExcelError
/**
* List of error codes.
*
* @var array
* @var array<string, string>
*/
public static $errorCodes = [
'null' => '#NULL!',
@ -60,7 +60,7 @@ class ExcelError
*
* @return string #NULL!
*/
public static function null()
public static function null(): string
{
return self::$errorCodes['null'];
}
@ -72,7 +72,7 @@ class ExcelError
*
* @return string #NUM!
*/
public static function NAN()
public static function NAN(): string
{
return self::$errorCodes['num'];
}
@ -84,7 +84,7 @@ class ExcelError
*
* @return string #REF!
*/
public static function REF()
public static function REF(): string
{
return self::$errorCodes['reference'];
}
@ -100,7 +100,7 @@ class ExcelError
*
* @return string #N/A!
*/
public static function NA()
public static function NA(): string
{
return self::$errorCodes['na'];
}
@ -112,7 +112,7 @@ class ExcelError
*
* @return string #VALUE!
*/
public static function VALUE()
public static function VALUE(): string
{
return self::$errorCodes['value'];
}
@ -124,7 +124,7 @@ class ExcelError
*
* @return string #NAME?
*/
public static function NAME()
public static function NAME(): string
{
return self::$errorCodes['name'];
}
@ -134,7 +134,7 @@ class ExcelError
*
* @return string #DIV/0!
*/
public static function DIV0()
public static function DIV0(): string
{
return self::$errorCodes['divisionbyzero'];
}
@ -142,9 +142,9 @@ class ExcelError
/**
* CALC.
*
* @return string #Not Yet Implemented
* @return string #CALC!
*/
public static function CALC()
public static function CALC(): string
{
return '#CALC!';
}

View File

@ -73,7 +73,7 @@ class HLookup extends LookupBase
// break if we have passed possible keys
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData);
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData);
$cellDataLower = StringHelper::strToLower($rowData);
$cellDataLower = StringHelper::strToLower((string) $rowData);
if (
$notExactMatch &&

View File

@ -90,7 +90,7 @@ class VLookup extends LookupBase
foreach ($lookupArray as $rowKey => $rowData) {
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
$cellDataLower = StringHelper::strToLower($rowData[$column]);
$cellDataLower = StringHelper::strToLower((string) $rowData[$column]);
// break if we have passed possible keys
if (

View File

@ -202,6 +202,11 @@ class Cell
*
* @param mixed $value Value
* @param string $dataType Explicit data type, see DataType::TYPE_*
* Note that PhpSpreadsheet does not validate that the value and datatype are consistent, in using this
* method, then it is your responsibility as an end-user developer to validate that the value and
* the datatype match.
* If you do mismatch value and datatpe, then the value you enter may be changed to match the datatype
* that you specify.
*
* @return Cell
*/
@ -210,7 +215,7 @@ class Cell
// set the value according to data type
switch ($dataType) {
case DataType::TYPE_NULL:
$this->value = $value;
$this->value = null;
break;
case DataType::TYPE_STRING2:

View File

@ -21,7 +21,7 @@ class DataType
/**
* List of error codes.
*
* @var array
* @var array<string, int>
*/
private static $errorCodes = [
'#NULL!' => 0,
@ -36,7 +36,7 @@ class DataType
/**
* Get list of error codes.
*
* @return array
* @return array<string, int>
*/
public static function getErrorCodes()
{
@ -48,7 +48,7 @@ class DataType
*
* @param null|RichText|string $textValue Value to sanitize to an Excel string
*
* @return null|RichText|string Sanitized value
* @return RichText|string Sanitized value
*/
public static function checkString($textValue)
{
@ -58,7 +58,7 @@ class DataType
}
// string must never be longer than 32,767 characters, truncate if necessary
$textValue = StringHelper::substring($textValue, 0, 32767);
$textValue = StringHelper::substring((string) $textValue, 0, 32767);
// we require that newline is represented as "\n" in core, not as "\r\n" or "\r"
$textValue = str_replace(["\r\n", "\r"], "\n", $textValue);

View File

@ -50,88 +50,6 @@ class Axis extends Properties
'alpha' => 0,
];
/**
* Line Properties.
*
* @var mixed[]
*/
private $lineProperties = [
'type' => self::EXCEL_COLOR_TYPE_ARGB,
'value' => null,
'alpha' => 0,
];
/**
* Line Style Properties.
*
* @var mixed[]
*/
private $lineStyleProperties = [
'width' => '9525',
'compound' => self::LINE_STYLE_COMPOUND_SIMPLE,
'dash' => self::LINE_STYLE_DASH_SOLID,
'cap' => self::LINE_STYLE_CAP_FLAT,
'join' => self::LINE_STYLE_JOIN_BEVEL,
'arrow' => [
'head' => [
'type' => self::LINE_STYLE_ARROW_TYPE_NOARROW,
'size' => self::LINE_STYLE_ARROW_SIZE_5,
],
'end' => [
'type' => self::LINE_STYLE_ARROW_TYPE_NOARROW,
'size' => self::LINE_STYLE_ARROW_SIZE_8,
],
],
];
/**
* Shadow Properties.
*
* @var mixed[]
*/
private $shadowProperties = [
'presets' => self::SHADOW_PRESETS_NOSHADOW,
'effect' => null,
'color' => [
'type' => self::EXCEL_COLOR_TYPE_STANDARD,
'value' => 'black',
'alpha' => 40,
],
'size' => [
'sx' => null,
'sy' => null,
'kx' => null,
],
'blur' => null,
'direction' => null,
'distance' => null,
'algn' => null,
'rotWithShape' => null,
];
/**
* Glow Properties.
*
* @var mixed[]
*/
private $glowProperties = [
'size' => null,
'color' => [
'type' => self::EXCEL_COLOR_TYPE_STANDARD,
'value' => 'black',
'alpha' => 40,
],
];
/**
* Soft Edge Properties.
*
* @var mixed[]
*/
private $softEdges = [
'size' => null,
];
private const NUMERIC_FORMAT = [
Properties::FORMAT_CODE_NUMBER,
Properties::FORMAT_CODE_DATE,
@ -179,33 +97,39 @@ class Axis extends Properties
return (bool) $this->axisNumber['numeric'];
}
public function setAxisOption(string $key, ?string $value): void
{
if (!empty($value)) {
$this->axisOptions[$key] = $value;
}
}
/**
* Set Axis Options Properties.
*
* @param string $axisLabels
* @param string $horizontalCrossesValue
* @param string $horizontalCrosses
* @param string $axisOrientation
* @param string $majorTmt
* @param string $minorTmt
* @param string $minimum
* @param string $maximum
* @param string $majorUnit
* @param string $minorUnit
*/
public function setAxisOptionsProperties($axisLabels, $horizontalCrossesValue = null, $horizontalCrosses = null, $axisOrientation = null, $majorTmt = null, $minorTmt = null, $minimum = null, $maximum = null, $majorUnit = null, $minorUnit = null): void
{
$this->axisOptions['axis_labels'] = (string) $axisLabels;
($horizontalCrossesValue !== null) ? $this->axisOptions['horizontal_crosses_value'] = (string) $horizontalCrossesValue : null;
($horizontalCrosses !== null) ? $this->axisOptions['horizontal_crosses'] = (string) $horizontalCrosses : null;
($axisOrientation !== null) ? $this->axisOptions['orientation'] = (string) $axisOrientation : null;
($majorTmt !== null) ? $this->axisOptions['major_tick_mark'] = (string) $majorTmt : null;
($minorTmt !== null) ? $this->axisOptions['minor_tick_mark'] = (string) $minorTmt : null;
($minorTmt !== null) ? $this->axisOptions['minor_tick_mark'] = (string) $minorTmt : null;
($minimum !== null) ? $this->axisOptions['minimum'] = (string) $minimum : null;
($maximum !== null) ? $this->axisOptions['maximum'] = (string) $maximum : null;
($majorUnit !== null) ? $this->axisOptions['major_unit'] = (string) $majorUnit : null;
($minorUnit !== null) ? $this->axisOptions['minor_unit'] = (string) $minorUnit : null;
public function setAxisOptionsProperties(
string $axisLabels,
?string $horizontalCrossesValue = null,
?string $horizontalCrosses = null,
?string $axisOrientation = null,
?string $majorTmt = null,
?string $minorTmt = null,
?string $minimum = null,
?string $maximum = null,
?string $majorUnit = null,
?string $minorUnit = null
): void {
$this->axisOptions['axis_labels'] = $axisLabels;
$this->setAxisOption('horizontal_crosses_value', $horizontalCrossesValue);
$this->setAxisOption('horizontal_crosses', $horizontalCrosses);
$this->setAxisOption('orientation', $axisOrientation);
$this->setAxisOption('major_tick_mark', $majorTmt);
$this->setAxisOption('minor_tick_mark', $minorTmt);
$this->setAxisOption('minor_tick_mark', $minorTmt);
$this->setAxisOption('minimum', $minimum);
$this->setAxisOption('maximum', $maximum);
$this->setAxisOption('major_unit', $majorUnit);
$this->setAxisOption('minor_unit', $minorUnit);
}
/**
@ -213,7 +137,7 @@ class Axis extends Properties
*
* @param string $property
*
* @return string
* @return ?string
*/
public function getAxisOptionsProperty($property)
{
@ -233,27 +157,15 @@ class Axis extends Properties
/**
* Set Fill Property.
*
* @param string $color
* @param int $alpha
* @param string $AlphaType
* @param ?string $color
* @param ?int $alpha
* @param ?string $AlphaType
*/
public function setFillParameters($color, $alpha = 0, $AlphaType = self::EXCEL_COLOR_TYPE_ARGB): void
public function setFillParameters($color, $alpha = null, $AlphaType = self::EXCEL_COLOR_TYPE_ARGB): void
{
$this->fillProperties = $this->setColorProperties($color, $alpha, $AlphaType);
}
/**
* Set Line Property.
*
* @param string $color
* @param int $alpha
* @param string $alphaType
*/
public function setLineParameters($color, $alpha = 0, $alphaType = self::EXCEL_COLOR_TYPE_ARGB): void
{
$this->lineProperties = $this->setColorProperties($color, $alpha, $alphaType);
}
/**
* Get Fill Property.
*
@ -263,309 +175,33 @@ class Axis extends Properties
*/
public function getFillProperty($property)
{
return $this->fillProperties[$property];
return (string) $this->fillProperties[$property];
}
/**
* Get Line Property.
* Get Line Color Property.
*
* @param string $property
* @param string $propertyName
*
* @return string
* @return null|int|string
*/
public function getLineProperty($property)
public function getLineProperty($propertyName)
{
return $this->lineProperties[$property];
return $this->lineProperties['color'][$propertyName];
}
/**
* Set Line Style Properties.
*
* @param float $lineWidth
* @param string $compoundType
* @param string $dashType
* @param string $capType
* @param string $joinType
* @param string $headArrowType
* @param string $headArrowSize
* @param string $endArrowType
* @param string $endArrowSize
*/
public function setLineStyleProperties($lineWidth = null, $compoundType = null, $dashType = null, $capType = null, $joinType = null, $headArrowType = null, $headArrowSize = null, $endArrowType = null, $endArrowSize = null): void
{
($lineWidth !== null) ? $this->lineStyleProperties['width'] = $this->getExcelPointsWidth((float) $lineWidth) : null;
($compoundType !== null) ? $this->lineStyleProperties['compound'] = (string) $compoundType : null;
($dashType !== null) ? $this->lineStyleProperties['dash'] = (string) $dashType : null;
($capType !== null) ? $this->lineStyleProperties['cap'] = (string) $capType : null;
($joinType !== null) ? $this->lineStyleProperties['join'] = (string) $joinType : null;
($headArrowType !== null) ? $this->lineStyleProperties['arrow']['head']['type'] = (string) $headArrowType : null;
($headArrowSize !== null) ? $this->lineStyleProperties['arrow']['head']['size'] = (string) $headArrowSize : null;
($endArrowType !== null) ? $this->lineStyleProperties['arrow']['end']['type'] = (string) $endArrowType : null;
($endArrowSize !== null) ? $this->lineStyleProperties['arrow']['end']['size'] = (string) $endArrowSize : null;
}
/** @var string */
private $crossBetween = ''; // 'between' or 'midCat' might be better
/**
* Get Line Style Property.
*
* @param array|string $elements
*
* @return string
*/
public function getLineStyleProperty($elements)
public function setCrossBetween(string $crossBetween): self
{
return $this->getArrayElementsValue($this->lineStyleProperties, $elements);
}
/**
* Get Line Style Arrow Excel Width.
*
* @param string $arrow
*
* @return string
*/
public function getLineStyleArrowWidth($arrow)
{
return $this->getLineStyleArrowSize($this->lineStyleProperties['arrow'][$arrow]['size'], 'w');
}
/**
* Get Line Style Arrow Excel Length.
*
* @param string $arrow
*
* @return string
*/
public function getLineStyleArrowLength($arrow)
{
return $this->getLineStyleArrowSize($this->lineStyleProperties['arrow'][$arrow]['size'], 'len');
}
/**
* Set Shadow Properties.
*
* @param int $shadowPresets
* @param string $colorValue
* @param string $colorType
* @param null|int|string $colorAlpha
* @param null|float $blur
* @param null|int $angle
* @param null|float $distance
*/
public function setShadowProperties($shadowPresets, $colorValue = null, $colorType = null, $colorAlpha = null, $blur = null, $angle = null, $distance = null): void
{
$this->setShadowPresetsProperties((int) $shadowPresets)
->setShadowColor(
$colorValue ?? $this->shadowProperties['color']['value'],
(int) ($colorAlpha ?? $this->shadowProperties['color']['alpha']),
$colorType ?? $this->shadowProperties['color']['type']
)
->setShadowBlur($blur)
->setShadowAngle($angle)
->setShadowDistance($distance);
}
/**
* Set Shadow Color.
*
* @param int $presets
*
* @return $this
*/
private function setShadowPresetsProperties($presets)
{
$this->shadowProperties['presets'] = $presets;
$this->setShadowPropertiesMapValues($this->getShadowPresetsMap($presets));
$this->crossBetween = $crossBetween;
return $this;
}
/**
* Set Shadow Properties from Mapped Values.
*
* @param mixed $reference
*
* @return $this
*/
private function setShadowPropertiesMapValues(array $propertiesMap, &$reference = null)
public function getCrossBetween(): string
{
$base_reference = $reference;
foreach ($propertiesMap as $property_key => $property_val) {
if (is_array($property_val)) {
if ($reference === null) {
$reference = &$this->shadowProperties[$property_key];
} else {
$reference = &$reference[$property_key];
}
$this->setShadowPropertiesMapValues($property_val, $reference);
} else {
if ($base_reference === null) {
$this->shadowProperties[$property_key] = $property_val;
} else {
$reference[$property_key] = $property_val;
}
}
}
return $this;
}
/**
* Set Shadow Color.
*
* @param null|string $color
* @param null|int $alpha
* @param null|string $alphaType
*
* @return $this
*/
private function setShadowColor($color, $alpha, $alphaType)
{
$this->shadowProperties['color'] = $this->setColorProperties($color, $alpha, $alphaType);
return $this;
}
/**
* Set Shadow Blur.
*
* @param null|float $blur
*
* @return $this
*/
private function setShadowBlur($blur)
{
if ($blur !== null) {
$this->shadowProperties['blur'] = (string) $this->getExcelPointsWidth($blur);
}
return $this;
}
/**
* Set Shadow Angle.
*
* @param null|int $angle
*
* @return $this
*/
private function setShadowAngle($angle)
{
if ($angle !== null) {
$this->shadowProperties['direction'] = (string) $this->getExcelPointsAngle($angle);
}
return $this;
}
/**
* Set Shadow Distance.
*
* @param null|float $distance
*
* @return $this
*/
private function setShadowDistance($distance)
{
if ($distance !== null) {
$this->shadowProperties['distance'] = (string) $this->getExcelPointsWidth($distance);
}
return $this;
}
/**
* Get Shadow Property.
*
* @param string|string[] $elements
*
* @return null|array|int|string
*/
public function getShadowProperty($elements)
{
return $this->getArrayElementsValue($this->shadowProperties, $elements);
}
/**
* Set Glow Properties.
*
* @param float $size
* @param null|string $colorValue
* @param null|int $colorAlpha
* @param null|string $colorType
*/
public function setGlowProperties($size, $colorValue = null, $colorAlpha = null, $colorType = null): void
{
$this->setGlowSize($size)
->setGlowColor(
$colorValue ?? $this->glowProperties['color']['value'],
$colorAlpha ?? (int) $this->glowProperties['color']['alpha'],
$colorType ?? $this->glowProperties['color']['type']
);
}
/**
* Get Glow Property.
*
* @param array|string $property
*
* @return null|string
*/
public function getGlowProperty($property)
{
return $this->getArrayElementsValue($this->glowProperties, $property);
}
/**
* Set Glow Color.
*
* @param float $size
*
* @return $this
*/
private function setGlowSize($size)
{
if ($size !== null) {
$this->glowProperties['size'] = $this->getExcelPointsWidth($size);
}
return $this;
}
/**
* Set Glow Color.
*
* @param string $color
* @param int $alpha
* @param string $colorType
*
* @return $this
*/
private function setGlowColor($color, $alpha, $colorType)
{
$this->glowProperties['color'] = $this->setColorProperties($color, $alpha, $colorType);
return $this;
}
/**
* Set Soft Edges Size.
*
* @param float $size
*/
public function setSoftEdges($size): void
{
if ($size !== null) {
$this->softEdges['size'] = (string) $this->getExcelPointsWidth($size);
}
}
/**
* Get Soft Edges Size.
*
* @return string
*/
public function getSoftEdgesSize()
{
return $this->softEdges['size'];
return $this->crossBetween;
}
}

View File

@ -76,6 +76,9 @@ class DataSeriesValues
/** @var string */
private $schemeClr = '';
/** @var string */
private $prstClr = '';
/**
* Line Width.
*
@ -262,7 +265,7 @@ class DataSeriesValues
/**
* Set fill color for series.
*
* @param null|string|string[] $color HEX color or array with HEX colors
* @param string|string[] $color HEX color or array with HEX colors
*
* @return DataSeriesValues
*/
@ -270,10 +273,14 @@ class DataSeriesValues
{
if (is_array($color)) {
foreach ($color as $colorValue) {
$this->validateColor($colorValue);
if (substr($colorValue, 0, 1) !== '*' && substr($colorValue, 0, 1) !== '/') {
$this->validateColor($colorValue);
}
}
} else {
$this->validateColor("$color");
if (substr($color, 0, 1) !== '*' && substr($color, 0, 1) !== '/') {
$this->validateColor("$color");
}
}
$this->fillColor = $color;
@ -470,4 +477,16 @@ class DataSeriesValues
return $this;
}
public function getPrstClr(): string
{
return $this->prstClr;
}
public function setPrstClr(string $prstClr): self
{
$this->prstClr = $prstClr;
return $this;
}
}

View File

@ -10,445 +10,4 @@ namespace PhpOffice\PhpSpreadsheet\Chart;
*/
class GridLines extends Properties
{
/**
* Properties of Class:
* Object State (State for Minor Tick Mark) @var bool
* Line Properties @var array of mixed
* Shadow Properties @var array of mixed
* Glow Properties @var array of mixed
* Soft Properties @var array of mixed.
*/
private $objectState = false;
private $lineProperties = [
'color' => [
'type' => self::EXCEL_COLOR_TYPE_STANDARD,
'value' => null,
'alpha' => 0,
],
'style' => [
'width' => '9525',
'compound' => self::LINE_STYLE_COMPOUND_SIMPLE,
'dash' => self::LINE_STYLE_DASH_SOLID,
'cap' => self::LINE_STYLE_CAP_FLAT,
'join' => self::LINE_STYLE_JOIN_BEVEL,
'arrow' => [
'head' => [
'type' => self::LINE_STYLE_ARROW_TYPE_NOARROW,
'size' => self::LINE_STYLE_ARROW_SIZE_5,
],
'end' => [
'type' => self::LINE_STYLE_ARROW_TYPE_NOARROW,
'size' => self::LINE_STYLE_ARROW_SIZE_8,
],
],
],
];
private $shadowProperties = [
'presets' => self::SHADOW_PRESETS_NOSHADOW,
'effect' => null,
'color' => [
'type' => self::EXCEL_COLOR_TYPE_STANDARD,
'value' => 'black',
'alpha' => 85,
],
'size' => [
'sx' => null,
'sy' => null,
'kx' => null,
],
'blur' => null,
'direction' => null,
'distance' => null,
'algn' => null,
'rotWithShape' => null,
];
private $glowProperties = [
'size' => null,
'color' => [
'type' => self::EXCEL_COLOR_TYPE_STANDARD,
'value' => 'black',
'alpha' => 40,
],
];
private $softEdges = [
'size' => null,
];
/**
* Get Object State.
*
* @return bool
*/
public function getObjectState()
{
return $this->objectState;
}
/**
* Change Object State to True.
*
* @return $this
*/
private function activateObject()
{
$this->objectState = true;
return $this;
}
/**
* Set Line Color Properties.
*
* @param string $value
* @param int $alpha
* @param string $colorType
*/
public function setLineColorProperties($value, $alpha = 0, $colorType = self::EXCEL_COLOR_TYPE_STANDARD): void
{
$this->activateObject()
->lineProperties['color'] = $this->setColorProperties(
$value,
$alpha,
$colorType
);
}
/**
* Set Line Color Properties.
*
* @param float $lineWidth
* @param string $compoundType
* @param string $dashType
* @param string $capType
* @param string $joinType
* @param string $headArrowType
* @param string $headArrowSize
* @param string $endArrowType
* @param string $endArrowSize
*/
public function setLineStyleProperties($lineWidth = null, $compoundType = null, $dashType = null, $capType = null, $joinType = null, $headArrowType = null, $headArrowSize = null, $endArrowType = null, $endArrowSize = null): void
{
$this->activateObject();
($lineWidth !== null)
? $this->lineProperties['style']['width'] = $this->getExcelPointsWidth((float) $lineWidth)
: null;
($compoundType !== null)
? $this->lineProperties['style']['compound'] = (string) $compoundType
: null;
($dashType !== null)
? $this->lineProperties['style']['dash'] = (string) $dashType
: null;
($capType !== null)
? $this->lineProperties['style']['cap'] = (string) $capType
: null;
($joinType !== null)
? $this->lineProperties['style']['join'] = (string) $joinType
: null;
($headArrowType !== null)
? $this->lineProperties['style']['arrow']['head']['type'] = (string) $headArrowType
: null;
($headArrowSize !== null)
? $this->lineProperties['style']['arrow']['head']['size'] = (string) $headArrowSize
: null;
($endArrowType !== null)
? $this->lineProperties['style']['arrow']['end']['type'] = (string) $endArrowType
: null;
($endArrowSize !== null)
? $this->lineProperties['style']['arrow']['end']['size'] = (string) $endArrowSize
: null;
}
/**
* Get Line Color Property.
*
* @param string $propertyName
*
* @return string
*/
public function getLineColorProperty($propertyName)
{
return $this->lineProperties['color'][$propertyName];
}
/**
* Get Line Style Property.
*
* @param array|string $elements
*
* @return string
*/
public function getLineStyleProperty($elements)
{
return $this->getArrayElementsValue($this->lineProperties['style'], $elements);
}
/**
* Set Glow Properties.
*
* @param float $size
* @param string $colorValue
* @param int $colorAlpha
* @param string $colorType
*/
public function setGlowProperties($size, $colorValue = null, $colorAlpha = null, $colorType = null): void
{
$this
->activateObject()
->setGlowSize($size)
->setGlowColor($colorValue, $colorAlpha, $colorType);
}
/**
* Get Glow Color Property.
*
* @param string $propertyName
*
* @return string
*/
public function getGlowColor($propertyName)
{
return $this->glowProperties['color'][$propertyName];
}
/**
* Get Glow Size.
*
* @return string
*/
public function getGlowSize()
{
return $this->glowProperties['size'];
}
/**
* Set Glow Size.
*
* @param float $size
*
* @return $this
*/
private function setGlowSize($size)
{
$this->glowProperties['size'] = $this->getExcelPointsWidth((float) $size);
return $this;
}
/**
* Set Glow Color.
*
* @param string $color
* @param int $alpha
* @param string $colorType
*
* @return $this
*/
private function setGlowColor($color, $alpha, $colorType)
{
if ($color !== null) {
$this->glowProperties['color']['value'] = (string) $color;
}
if ($alpha !== null) {
$this->glowProperties['color']['alpha'] = $this->getTrueAlpha((int) $alpha);
}
if ($colorType !== null) {
$this->glowProperties['color']['type'] = (string) $colorType;
}
return $this;
}
/**
* Get Line Style Arrow Parameters.
*
* @param string $arrowSelector
* @param string $propertySelector
*
* @return string
*/
public function getLineStyleArrowParameters($arrowSelector, $propertySelector)
{
return $this->getLineStyleArrowSize($this->lineProperties['style']['arrow'][$arrowSelector]['size'], $propertySelector);
}
/**
* Set Shadow Properties.
*
* @param int $presets
* @param string $colorValue
* @param string $colorType
* @param string $colorAlpha
* @param string $blur
* @param int $angle
* @param float $distance
*/
public function setShadowProperties($presets, $colorValue = null, $colorType = null, $colorAlpha = null, $blur = null, $angle = null, $distance = null): void
{
$this->activateObject()
->setShadowPresetsProperties((int) $presets)
->setShadowColor(
$colorValue ?? $this->shadowProperties['color']['value'],
$colorAlpha === null ? (int) $this->shadowProperties['color']['alpha'] : $this->getTrueAlpha($colorAlpha),
$colorType ?? $this->shadowProperties['color']['type']
)
->setShadowBlur((float) $blur)
->setShadowAngle($angle)
->setShadowDistance($distance);
}
/**
* Set Shadow Presets Properties.
*
* @param int $presets
*
* @return $this
*/
private function setShadowPresetsProperties($presets)
{
$this->shadowProperties['presets'] = $presets;
$this->setShadowPropertiesMapValues($this->getShadowPresetsMap($presets));
return $this;
}
/**
* Set Shadow Properties Values.
*
* @param mixed $reference
*
* @return $this
*/
private function setShadowPropertiesMapValues(array $propertiesMap, &$reference = null)
{
$base_reference = $reference;
foreach ($propertiesMap as $property_key => $property_val) {
if (is_array($property_val)) {
if ($reference === null) {
$reference = &$this->shadowProperties[$property_key];
} else {
$reference = &$reference[$property_key];
}
$this->setShadowPropertiesMapValues($property_val, $reference);
} else {
if ($base_reference === null) {
$this->shadowProperties[$property_key] = $property_val;
} else {
$reference[$property_key] = $property_val;
}
}
}
return $this;
}
/**
* Set Shadow Color.
*
* @param string $color
* @param int $alpha
* @param string $colorType
*
* @return $this
*/
private function setShadowColor($color, $alpha, $colorType)
{
if ($color !== null) {
$this->shadowProperties['color']['value'] = (string) $color;
}
if ($alpha !== null) {
$this->shadowProperties['color']['alpha'] = $this->getTrueAlpha((int) $alpha);
}
if ($colorType !== null) {
$this->shadowProperties['color']['type'] = (string) $colorType;
}
return $this;
}
/**
* Set Shadow Blur.
*
* @param float $blur
*
* @return $this
*/
private function setShadowBlur($blur)
{
if ($blur !== null) {
$this->shadowProperties['blur'] = (string) $this->getExcelPointsWidth($blur);
}
return $this;
}
/**
* Set Shadow Angle.
*
* @param int $angle
*
* @return $this
*/
private function setShadowAngle($angle)
{
if ($angle !== null) {
$this->shadowProperties['direction'] = (string) $this->getExcelPointsAngle($angle);
}
return $this;
}
/**
* Set Shadow Distance.
*
* @param float $distance
*
* @return $this
*/
private function setShadowDistance($distance)
{
if ($distance !== null) {
$this->shadowProperties['distance'] = (string) $this->getExcelPointsWidth($distance);
}
return $this;
}
/**
* Get Shadow Property.
*
* @param string|string[] $elements
*
* @return string
*/
public function getShadowProperty($elements)
{
return $this->getArrayElementsValue($this->shadowProperties, $elements);
}
/**
* Set Soft Edges Size.
*
* @param float $size
*/
public function setSoftEdgesSize($size): void
{
if ($size !== null) {
$this->activateObject();
$this->softEdges['size'] = (string) $this->getExcelPointsWidth($size);
}
}
/**
* Get Soft Edges Size.
*
* @return string
*/
public function getSoftEdgesSize()
{
return $this->softEdges['size'];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -632,16 +632,6 @@ class Html extends BaseReader
}
}
/**
* Make sure mb_convert_encoding returns string.
*
* @param mixed $result
*/
private static function ensureString($result): string
{
return is_string($result) ? $result : '';
}
/**
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
*
@ -660,8 +650,8 @@ class Html extends BaseReader
$dom = new DOMDocument();
// Reload the HTML file into the DOM object
try {
$convert = mb_convert_encoding($this->securityScanner->scanFile($filename), 'HTML-ENTITIES', 'UTF-8');
$loaded = $dom->loadHTML(self::ensureString($convert));
$convert = $this->securityScanner->scanFile($filename);
$loaded = $dom->loadHTML($convert);
} catch (Throwable $e) {
$loaded = false;
}
@ -683,8 +673,8 @@ class Html extends BaseReader
$dom = new DOMDocument();
// Reload the HTML file into the DOM object
try {
$convert = mb_convert_encoding($this->securityScanner->scan($content), 'HTML-ENTITIES', 'UTF-8');
$loaded = $dom->loadHTML(self::ensureString($convert));
$convert = $this->securityScanner->scan($content);
$loaded = $dom->loadHTML($convert);
} catch (Throwable $e) {
$loaded = false;
}

View File

@ -414,7 +414,7 @@ class Xlsx extends BaseReader
[$workbookBasename, $xmlNamespaceBase] = $this->getWorkbookBaseName();
$drawingNS = self::REL_TO_DRAWING[$xmlNamespaceBase] ?? Namespaces::DRAWINGML;
$chartNS = self::REL_TO_CHART[$xmlNamespaceBase] ?? Namespaces::CHART;
$wbRels = $this->loadZip("xl/_rels/${workbookBasename}.rels", Namespaces::RELATIONSHIPS);
$wbRels = $this->loadZip("xl/_rels/{$workbookBasename}.rels", Namespaces::RELATIONSHIPS);
$theme = null;
$this->styleReader = new Styles();
foreach ($wbRels->Relationship as $relx) {
@ -1849,11 +1849,6 @@ class Xlsx extends BaseReader
private static function dirAdd($base, $add): string
{
$add = "$add";
if (substr($add, 0, 4) === '/xl/') {
$add = substr($add, 4);
}
return (string) preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
}

View File

@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
use PhpOffice\PhpSpreadsheet\Chart\Axis;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\GridLines;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
@ -71,6 +72,7 @@ class Chart
$rotX = $rotY = $rAngAx = $perspective = null;
$xAxis = new Axis();
$yAxis = new Axis();
$majorGridlines = $minorGridlines = null;
foreach ($chartElementsC as $chartElementKey => $chartElement) {
switch ($chartElementKey) {
case 'chart':
@ -98,6 +100,14 @@ class Chart
$XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
}
$this->readEffects($chartDetail, $xAxis);
if (isset($chartDetail->spPr)) {
$sppr = $chartDetail->spPr->children($this->aNamespace);
if (isset($sppr->solidFill)) {
$axisColorArray = $this->readColor($sppr->solidFill);
$xAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
}
}
$this->setAxisProperties($chartDetail, $xAxis);
break;
case 'dateAx':
@ -108,26 +118,62 @@ class Chart
break;
case 'valAx':
$whichAxis = null;
if (isset($chartDetail->title, $chartDetail->axPos)) {
$axisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
$axPos = null;
if (isset($chartDetail->axPos)) {
$axPos = self::getAttribute($chartDetail->axPos, 'val', 'string');
switch ($axPos) {
case 't':
case 'b':
$XaxisLabel = $axisLabel;
$whichAxis = $xAxis;
break;
case 'r':
case 'l':
$YaxisLabel = $axisLabel;
$whichAxis = $yAxis;
break;
}
}
if (isset($chartDetail->title)) {
$axisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
switch ($axPos) {
case 't':
case 'b':
$XaxisLabel = $axisLabel;
break;
case 'r':
case 'l':
$YaxisLabel = $axisLabel;
break;
}
}
$this->readEffects($chartDetail, $whichAxis);
if ($whichAxis !== null && isset($chartDetail->spPr)) {
$sppr = $chartDetail->spPr->children($this->aNamespace);
if (isset($sppr->solidFill)) {
$axisColorArray = $this->readColor($sppr->solidFill);
$whichAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
}
}
if (isset($chartDetail->majorGridlines)) {
$majorGridlines = new GridLines();
if (isset($chartDetail->majorGridlines->spPr)) {
$this->readEffects($chartDetail->majorGridlines, $majorGridlines);
$this->readLineStyle($chartDetail->majorGridlines, $majorGridlines);
}
}
if (isset($chartDetail->minorGridlines)) {
$minorGridlines = new GridLines();
if (isset($chartDetail->minorGridlines->spPr)) {
$this->readEffects($chartDetail->minorGridlines, $minorGridlines);
$this->readLineStyle($chartDetail->minorGridlines, $minorGridlines);
}
}
$this->setAxisProperties($chartDetail, $whichAxis);
break;
case 'barChart':
@ -249,7 +295,7 @@ class Chart
}
}
}
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, (string) $dispBlanksAs, $XaxisLabel, $YaxisLabel, $xAxis, $yAxis);
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, (string) $dispBlanksAs, $XaxisLabel, $YaxisLabel, $xAxis, $yAxis, $majorGridlines, $minorGridlines);
if (is_int($rotX)) {
$chart->setRotX($rotX);
}
@ -331,7 +377,9 @@ class Chart
$pointSize = null;
$noFill = false;
$schemeClr = '';
$prstClr = '';
$bubble3D = false;
$dPtColors = [];
foreach ($seriesDetails as $seriesKey => $seriesDetail) {
switch ($seriesKey) {
case 'idx':
@ -355,7 +403,24 @@ class Chart
$noFill = true;
}
if (isset($children->solidFill)) {
$this->readColor($children->solidFill, $srgbClr, $schemeClr);
$this->readColor($children->solidFill, $srgbClr, $schemeClr, $prstClr);
}
break;
case 'dPt':
$dptIdx = (int) self::getAttribute($seriesDetail->idx, 'val', 'string');
if (isset($seriesDetail->spPr)) {
$children = $seriesDetail->spPr->children($this->aNamespace);
if (isset($children->solidFill)) {
$arrayColors = $this->readColor($children->solidFill);
if ($arrayColors['type'] === 'srgbClr') {
$dptColors[$dptIdx] = $arrayColors['value'];
} elseif ($arrayColors['type'] === 'prstClr') {
$dptColors[$dptIdx] = '/' . $arrayColors['value'];
} else {
$dptColors[$dptIdx] = '*' . $arrayColors['value'];
}
}
}
break;
@ -366,7 +431,7 @@ class Chart
if (count($seriesDetail->spPr) === 1) {
$ln = $seriesDetail->spPr->children($this->aNamespace);
if (isset($ln->solidFill)) {
$this->readColor($ln->solidFill, $srgbClr, $schemeClr);
$this->readColor($ln->solidFill, $srgbClr, $schemeClr, $prstClr);
}
}
@ -433,6 +498,16 @@ class Chart
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setSchemeClr($schemeClr);
}
} elseif ($prstClr) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setPrstClr($prstClr);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setPrstClr($prstClr);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setPrstClr($prstClr);
}
}
if ($bubble3D) {
if (isset($seriesLabel[$seriesIndex])) {
@ -445,6 +520,17 @@ class Chart
$seriesValues[$seriesIndex]->setBubble3D($bubble3D);
}
}
if (!empty($dptColors)) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setFillColor($dptColors);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setFillColor($dptColors);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setFillColor($dptColors);
}
}
}
}
/** @phpstan-ignore-next-line */
@ -893,7 +979,7 @@ class Chart
}
/**
* @param null|Axis $chartObject may be extended to include other types
* @param null|Axis|GridLines $chartObject may be extended to include other types
*/
private function readEffects(SimpleXMLElement $chartDetail, $chartObject): void
{
@ -905,42 +991,212 @@ class Chart
if (isset($sppr->effectLst->glow)) {
$axisGlowSize = (float) self::getAttribute($sppr->effectLst->glow, 'rad', 'integer') / Properties::POINTS_WIDTH_MULTIPLIER;
if ($axisGlowSize != 0.0) {
$srgbClr = $schemeClr = '';
$colorArray = $this->readColor($sppr->effectLst->glow, $srgbClr, $schemeClr);
$colorArray = $this->readColor($sppr->effectLst->glow);
$chartObject->setGlowProperties($axisGlowSize, $colorArray['value'], $colorArray['alpha'], $colorArray['type']);
}
}
if (isset($sppr->effectLst->softEdge)) {
$chartObject->setSoftEdges((float) self::getAttribute($sppr->effectLst->softEdge, 'rad', 'string') / Properties::POINTS_WIDTH_MULTIPLIER);
/** @var string */
$softEdgeSize = self::getAttribute($sppr->effectLst->softEdge, 'rad', 'string');
if (is_numeric($softEdgeSize)) {
$chartObject->setSoftEdges((float) Properties::xmlToPoints($softEdgeSize));
}
}
$type = '';
foreach (self::SHADOW_TYPES as $shadowType) {
if (isset($sppr->effectLst->$shadowType)) {
$type = $shadowType;
break;
}
}
if ($type !== '') {
/** @var string */
$blur = self::getAttribute($sppr->effectLst->$type, 'blurRad', 'string');
$blur = is_numeric($blur) ? Properties::xmlToPoints($blur) : null;
/** @var string */
$dist = self::getAttribute($sppr->effectLst->$type, 'dist', 'string');
$dist = is_numeric($dist) ? Properties::xmlToPoints($dist) : null;
/** @var string */
$direction = self::getAttribute($sppr->effectLst->$type, 'dir', 'string');
$direction = is_numeric($direction) ? Properties::xmlToAngle($direction) : null;
$algn = self::getAttribute($sppr->effectLst->$type, 'algn', 'string');
$rot = self::getAttribute($sppr->effectLst->$type, 'rotWithShape', 'string');
$size = [];
foreach (['sx', 'sy'] as $sizeType) {
$sizeValue = self::getAttribute($sppr->effectLst->$type, $sizeType, 'string');
if (is_numeric($sizeValue)) {
$size[$sizeType] = Properties::xmlToTenthOfPercent((string) $sizeValue);
} else {
$size[$sizeType] = null;
}
}
foreach (['kx', 'ky'] as $sizeType) {
$sizeValue = self::getAttribute($sppr->effectLst->$type, $sizeType, 'string');
if (is_numeric($sizeValue)) {
$size[$sizeType] = Properties::xmlToAngle((string) $sizeValue);
} else {
$size[$sizeType] = null;
}
}
$colorArray = $this->readColor($sppr->effectLst->$type);
$chartObject
->setShadowProperty('effect', $type)
->setShadowProperty('blur', $blur)
->setShadowProperty('direction', $direction)
->setShadowProperty('distance', $dist)
->setShadowProperty('algn', $algn)
->setShadowProperty('rotWithShape', $rot)
->setShadowProperty('size', $size)
->setShadowProperty('color', $colorArray);
}
}
private function readColor(SimpleXMLElement $colorXml, ?string &$srgbClr, ?string &$schemeClr): array
private const SHADOW_TYPES = [
'outerShdw',
'innerShdw',
];
private function readColor(SimpleXMLElement $colorXml, ?string &$srgbClr = null, ?string &$schemeClr = null, ?string &$prstClr = null): array
{
$result = [
'type' => null,
'value' => null,
'alpha' => null,
];
if (isset($colorXml->srgbClr)) {
$result['type'] = Properties::EXCEL_COLOR_TYPE_ARGB;
$result['value'] = $srgbClr = self::getAttribute($colorXml->srgbClr, 'val', 'string');
if (isset($colorXml->srgbClr->alpha)) {
$alpha = (int) self::getAttribute($colorXml->srgbClr->alpha, 'val', 'string');
$alpha = 100 - (int) ($alpha / 1000);
$result['alpha'] = $alpha;
}
} elseif (isset($colorXml->schemeClr)) {
$result['type'] = Properties::EXCEL_COLOR_TYPE_SCHEME;
$result['value'] = $schemeClr = self::getAttribute($colorXml->schemeClr, 'val', 'string');
if (isset($colorXml->schemeClr->alpha)) {
$alpha = (int) self::getAttribute($colorXml->schemeClr->alpha, 'val', 'string');
$alpha = 100 - (int) ($alpha / 1000);
$result['alpha'] = $alpha;
foreach (Properties::EXCEL_COLOR_TYPES as $type) {
if (isset($colorXml->$type)) {
$result['type'] = $type;
$result['value'] = self::getAttribute($colorXml->$type, 'val', 'string');
if ($type === Properties::EXCEL_COLOR_TYPE_ARGB) {
$srgbClr = $result['value'];
} elseif ($type === Properties::EXCEL_COLOR_TYPE_SCHEME) {
$schemeClr = $result['value'];
} elseif ($type === Properties::EXCEL_COLOR_TYPE_STANDARD) {
$prstClr = $result['value'];
}
if (isset($colorXml->$type->alpha)) {
$alpha = (int) self::getAttribute($colorXml->$type->alpha, 'val', 'string');
$alpha = 100 - (int) ($alpha / 1000);
$result['alpha'] = $alpha;
}
break;
}
}
return $result;
}
/**
* @param null|GridLines $chartObject may be extended to include other types
*/
private function readLineStyle(SimpleXMLElement $chartDetail, $chartObject): void
{
if (!isset($chartObject, $chartDetail->spPr)) {
return;
}
$sppr = $chartDetail->spPr->children($this->aNamespace);
if (!isset($sppr->ln)) {
return;
}
$lineWidth = null;
/** @var string */
$lineWidthTemp = self::getAttribute($sppr->ln, 'w', 'string');
if (is_numeric($lineWidthTemp)) {
$lineWidth = Properties::xmlToPoints($lineWidthTemp);
}
/** @var string */
$compoundType = self::getAttribute($sppr->ln, 'cmpd', 'string');
/** @var string */
$dashType = self::getAttribute($sppr->ln->prstDash, 'val', 'string');
/** @var string */
$capType = self::getAttribute($sppr->ln, 'cap', 'string');
if (isset($sppr->ln->miter)) {
$joinType = Properties::LINE_STYLE_JOIN_MITER;
} elseif (isset($sppr->ln->bevel)) {
$joinType = Properties::LINE_STYLE_JOIN_BEVEL;
} else {
$joinType = '';
}
$headArrowType = '';
$headArrowSize = '';
$endArrowType = '';
$endArrowSize = '';
/** @var string */
$headArrowType = self::getAttribute($sppr->ln->headEnd, 'type', 'string');
/** @var string */
$headArrowWidth = self::getAttribute($sppr->ln->headEnd, 'w', 'string');
/** @var string */
$headArrowLength = self::getAttribute($sppr->ln->headEnd, 'len', 'string');
/** @var string */
$endArrowType = self::getAttribute($sppr->ln->tailEnd, 'type', 'string');
/** @var string */
$endArrowWidth = self::getAttribute($sppr->ln->tailEnd, 'w', 'string');
/** @var string */
$endArrowLength = self::getAttribute($sppr->ln->tailEnd, 'len', 'string');
$chartObject->setLineStyleProperties(
$lineWidth,
$compoundType,
$dashType,
$capType,
$joinType,
$headArrowType,
$headArrowSize,
$endArrowType,
$endArrowSize,
$headArrowWidth,
$headArrowLength,
$endArrowWidth,
$endArrowLength
);
$colorArray = $this->readColor($sppr->ln->solidFill);
$chartObject->setColorPropertiesArray($colorArray);
}
private function setAxisProperties(SimpleXMLElement $chartDetail, ?Axis $whichAxis): void
{
if (!isset($whichAxis)) {
return;
}
if (isset($chartDetail->crossBetween)) {
$whichAxis->setCrossBetween((string) self::getAttribute($chartDetail->crossBetween, 'val', 'string'));
}
if (isset($chartDetail->majorTickMark)) {
$whichAxis->setAxisOption('major_tick_mark', (string) self::getAttribute($chartDetail->majorTickMark, 'val', 'string'));
}
if (isset($chartDetail->minorTickMark)) {
$whichAxis->setAxisOption('minor_tick_mark', (string) self::getAttribute($chartDetail->minorTickMark, 'val', 'string'));
}
if (isset($chartDetail->tickLblPos)) {
$whichAxis->setAxisOption('axis_labels', (string) self::getAttribute($chartDetail->tickLblPos, 'val', 'string'));
}
if (isset($chartDetail->crosses)) {
$whichAxis->setAxisOption('horizontal_crosses', (string) self::getAttribute($chartDetail->crosses, 'val', 'string'));
}
if (isset($chartDetail->crossesAt)) {
$whichAxis->setAxisOption('horizontal_crosses_value', (string) self::getAttribute($chartDetail->crossesAt, 'val', 'string'));
}
if (isset($chartDetail->scaling->orientation)) {
$whichAxis->setAxisOption('orientation', (string) self::getAttribute($chartDetail->scaling->orientation, 'val', 'string'));
}
if (isset($chartDetail->scaling->max)) {
$whichAxis->setAxisOption('maximum', (string) self::getAttribute($chartDetail->scaling->max, 'val', 'string'));
}
if (isset($chartDetail->scaling->min)) {
$whichAxis->setAxisOption('minimum', (string) self::getAttribute($chartDetail->scaling->min, 'val', 'string'));
}
if (isset($chartDetail->scaling->min)) {
$whichAxis->setAxisOption('minimum', (string) self::getAttribute($chartDetail->scaling->min, 'val', 'string'));
}
if (isset($chartDetail->majorUnit)) {
$whichAxis->setAxisOption('major_unit', (string) self::getAttribute($chartDetail->majorUnit, 'val', 'string'));
}
if (isset($chartDetail->minorUnit)) {
$whichAxis->setAxisOption('minor_unit', (string) self::getAttribute($chartDetail->minorUnit, 'val', 'string'));
}
}
}

View File

@ -367,7 +367,6 @@ class ReferenceHelper
Worksheet $worksheet
): void {
$remove = ($numberOfColumns < 0 || $numberOfRows < 0);
$allCoordinates = $worksheet->getCoordinates();
if (
$this->cellReferenceHelper === null ||
@ -394,12 +393,13 @@ class ReferenceHelper
}
// Find missing coordinates. This is important when inserting column before the last column
$cellCollection = $worksheet->getCellCollection();
$missingCoordinates = array_filter(
array_map(function ($row) use ($highestColumn) {
return $highestColumn . $row;
}, range(1, $highestRow)),
function ($coordinate) use ($allCoordinates) {
return !in_array($coordinate, $allCoordinates);
function ($coordinate) use ($cellCollection) {
return $cellCollection->has($coordinate) === false;
}
);
@ -408,16 +408,15 @@ class ReferenceHelper
foreach ($missingCoordinates as $coordinate) {
$worksheet->createNewCell($coordinate);
}
// Refresh all coordinates
$allCoordinates = $worksheet->getCoordinates();
}
// Loop through cells, bottom-up, and change cell coordinate
$allCoordinates = $worksheet->getCoordinates();
if ($remove) {
// It's faster to reverse and pop than to use unshift, especially with large cell collections
$allCoordinates = array_reverse($allCoordinates);
}
// Loop through cells, bottom-up, and change cell coordinate
while ($coordinate = array_pop($allCoordinates)) {
$cell = $worksheet->getCell($coordinate);
$cellIndex = Coordinate::columnIndexFromString($cell->getColumn());
@ -924,34 +923,43 @@ class ReferenceHelper
private function clearColumnStrips(int $highestRow, int $beforeColumn, int $numberOfColumns, Worksheet $worksheet): void
{
for ($i = 1; $i <= $highestRow - 1; ++$i) {
for ($j = $beforeColumn - 1 + $numberOfColumns; $j <= $beforeColumn - 2; ++$j) {
$coordinate = Coordinate::stringFromColumnIndex($j + 1) . $i;
$worksheet->removeConditionalStyles($coordinate);
if ($worksheet->cellExists($coordinate)) {
$worksheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
$worksheet->getCell($coordinate)->setXfIndex(0);
}
$startColumnId = Coordinate::stringFromColumnIndex($beforeColumn + $numberOfColumns);
$endColumnId = Coordinate::stringFromColumnIndex($beforeColumn);
for ($row = 1; $row <= $highestRow - 1; ++$row) {
for ($column = $startColumnId; $column !== $endColumnId; ++$column) {
$coordinate = $column . $row;
$this->clearStripCell($worksheet, $coordinate);
}
}
}
private function clearRowStrips(string $highestColumn, int $beforeColumn, int $beforeRow, int $numberOfRows, Worksheet $worksheet): void
{
$lastColumnIndex = Coordinate::columnIndexFromString($highestColumn) - 1;
$startColumnId = Coordinate::stringFromColumnIndex($beforeColumn);
++$highestColumn;
for ($i = $beforeColumn - 1; $i <= $lastColumnIndex; ++$i) {
for ($j = $beforeRow + $numberOfRows; $j <= $beforeRow - 1; ++$j) {
$coordinate = Coordinate::stringFromColumnIndex($i + 1) . $j;
$worksheet->removeConditionalStyles($coordinate);
if ($worksheet->cellExists($coordinate)) {
$worksheet->getCell($coordinate)->setValueExplicit('', DataType::TYPE_NULL);
$worksheet->getCell($coordinate)->setXfIndex(0);
}
for ($column = $startColumnId; $column !== $highestColumn; ++$column) {
for ($row = $beforeRow + $numberOfRows; $row <= $beforeRow - 1; ++$row) {
$coordinate = $column . $row;
$this->clearStripCell($worksheet, $coordinate);
}
}
}
private function clearStripCell(Worksheet $worksheet, string $coordinate): void
{
$worksheet->removeConditionalStyles($coordinate);
$worksheet->setHyperlink($coordinate);
$worksheet->setDataValidation($coordinate);
$worksheet->removeComment($coordinate);
if ($worksheet->cellExists($coordinate)) {
$worksheet->getCell($coordinate)->setValueExplicit(null, DataType::TYPE_NULL);
$worksheet->getCell($coordinate)->setXfIndex(0);
}
}
private function adjustAutoFilter(Worksheet $worksheet, string $beforeCellAddress, int $numberOfColumns): void
{
$autoFilter = $worksheet->getAutoFilter();

View File

@ -329,12 +329,8 @@ class StringHelper
/**
* Try to sanitize UTF8, stripping invalid byte sequences. Not perfect. Does not surrogate characters.
*
* @param string $textValue
*
* @return string
*/
public static function sanitizeUTF8($textValue)
public static function sanitizeUTF8(string $textValue): string
{
if (self::getIsIconvEnabled()) {
$textValue = @iconv('UTF-8', 'UTF-8', $textValue);
@ -349,12 +345,8 @@ class StringHelper
/**
* Check if a string contains UTF8 data.
*
* @param string $textValue
*
* @return bool
*/
public static function isUTF8($textValue)
public static function isUTF8(string $textValue): bool
{
return $textValue === '' || preg_match('/^./su', $textValue) === 1;
}
@ -364,10 +356,8 @@ class StringHelper
* point as decimal separator in case locale is other than English.
*
* @param mixed $numericValue
*
* @return string
*/
public static function formatNumber($numericValue)
public static function formatNumber($numericValue): string
{
if (is_float($numericValue)) {
return str_replace(',', '.', $numericValue);
@ -385,10 +375,8 @@ class StringHelper
*
* @param string $textValue UTF-8 encoded string
* @param mixed[] $arrcRuns Details of rich text runs in $value
*
* @return string
*/
public static function UTF8toBIFF8UnicodeShort($textValue, $arrcRuns = [])
public static function UTF8toBIFF8UnicodeShort(string $textValue, array $arrcRuns = []): string
{
// character count
$ln = self::countCharacters($textValue, 'UTF-8');
@ -419,10 +407,8 @@ class StringHelper
* see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function UTF8toBIFF8UnicodeLong($textValue)
public static function UTF8toBIFF8UnicodeLong(string $textValue): string
{
// character count
$ln = self::countCharacters($textValue, 'UTF-8');
@ -436,13 +422,10 @@ class StringHelper
/**
* Convert string from one encoding to another.
*
* @param string $textValue
* @param string $to Encoding to convert to, e.g. 'UTF-8'
* @param string $from Encoding to convert from, e.g. 'UTF-16LE'
*
* @return string
*/
public static function convertEncoding($textValue, $to, $from)
public static function convertEncoding(string $textValue, string $to, string $from): string
{
if (self::getIsIconvEnabled()) {
$result = iconv($from, $to . self::$iconvOptions, $textValue);
@ -457,52 +440,45 @@ class StringHelper
/**
* Get character count.
*
* @param string $textValue
* @param string $encoding Encoding
*
* @return int Character count
*/
public static function countCharacters($textValue, $encoding = 'UTF-8')
public static function countCharacters(string $textValue, string $encoding = 'UTF-8'): int
{
return mb_strlen($textValue ?? '', $encoding);
return mb_strlen($textValue, $encoding);
}
/**
* Get a substring of a UTF-8 encoded string.
*
* @param null|string $textValue UTF-8 encoded string
* @param string $textValue UTF-8 encoded string
* @param int $offset Start offset
* @param int $length Maximum number of characters in substring
*
* @return string
*/
public static function substring($textValue, $offset, $length = 0)
public static function substring(string $textValue, int $offset, int $length = 0): string
{
return mb_substr($textValue ?? '', $offset, $length, 'UTF-8');
return mb_substr($textValue, $offset, $length, 'UTF-8');
}
/**
* Convert a UTF-8 encoded string to upper case.
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function strToUpper($textValue)
public static function strToUpper(string $textValue): string
{
return mb_convert_case($textValue ?? '', MB_CASE_UPPER, 'UTF-8');
return mb_convert_case($textValue, MB_CASE_UPPER, 'UTF-8');
}
/**
* Convert a UTF-8 encoded string to lower case.
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function strToLower($textValue)
public static function strToLower(string $textValue): string
{
return mb_convert_case($textValue ?? '', MB_CASE_LOWER, 'UTF-8');
return mb_convert_case($textValue, MB_CASE_LOWER, 'UTF-8');
}
/**
@ -510,24 +486,27 @@ class StringHelper
* (uppercase every first character in each word, lower case all other characters).
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function strToTitle($textValue)
public static function strToTitle(string $textValue): string
{
return mb_convert_case($textValue, MB_CASE_TITLE, 'UTF-8');
}
public static function mbIsUpper($character)
public static function mbIsUpper(string $character): bool
{
return mb_strtolower($character, 'UTF-8') != $character;
return mb_strtolower($character, 'UTF-8') !== $character;
}
public static function mbStrSplit($string)
/**
* Splits a UTF-8 string into an array of individual characters.
*/
public static function mbStrSplit(string $string): array
{
// Split at all position not after the start: ^
// and not before the end: $
return preg_split('/(?<!^)(?!$)/u', $string);
$split = preg_split('/(?<!^)(?!$)/u', $string);
return ($split === false) ? [] : $split;
}
/**
@ -535,10 +514,8 @@ class StringHelper
* and all lowercase characters become uppercase.
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function strCaseReverse($textValue)
public static function strCaseReverse(string $textValue): string
{
$characters = self::mbStrSplit($textValue);
foreach ($characters as &$character) {
@ -557,10 +534,8 @@ class StringHelper
* and convert it to a numeric if it is.
*
* @param string $operand string value to test
*
* @return bool
*/
public static function convertToNumberIfFraction(&$operand)
public static function convertToNumberIfFraction(string &$operand): bool
{
if (preg_match('/^' . self::STRING_REGEXP_FRACTION . '$/i', $operand, $match)) {
$sign = ($match[1] == '-') ? '-' : '+';
@ -578,10 +553,8 @@ class StringHelper
/**
* Get the decimal separator. If it has not yet been set explicitly, try to obtain number
* formatting information from locale.
*
* @return string
*/
public static function getDecimalSeparator()
public static function getDecimalSeparator(): string
{
if (!isset(self::$decimalSeparator)) {
$localeconv = localeconv();
@ -603,7 +576,7 @@ class StringHelper
*
* @param string $separator Character for decimal separator
*/
public static function setDecimalSeparator($separator): void
public static function setDecimalSeparator(string $separator): void
{
self::$decimalSeparator = $separator;
}
@ -611,10 +584,8 @@ class StringHelper
/**
* Get the thousands separator. If it has not yet been set explicitly, try to obtain number
* formatting information from locale.
*
* @return string
*/
public static function getThousandsSeparator()
public static function getThousandsSeparator(): string
{
if (!isset(self::$thousandsSeparator)) {
$localeconv = localeconv();
@ -636,7 +607,7 @@ class StringHelper
*
* @param string $separator Character for thousands separator
*/
public static function setThousandsSeparator($separator): void
public static function setThousandsSeparator(string $separator): void
{
self::$thousandsSeparator = $separator;
}
@ -644,10 +615,8 @@ class StringHelper
/**
* Get the currency code. If it has not yet been set explicitly, try to obtain the
* symbol information from locale.
*
* @return string
*/
public static function getCurrencyCode()
public static function getCurrencyCode(): string
{
if (!empty(self::$currencyCode)) {
return self::$currencyCode;
@ -674,7 +643,7 @@ class StringHelper
*
* @param string $currencyCode Character for currency code
*/
public static function setCurrencyCode($currencyCode): void
public static function setCurrencyCode(string $currencyCode): void
{
self::$currencyCode = $currencyCode;
}
@ -682,11 +651,11 @@ class StringHelper
/**
* Convert SYLK encoded string to UTF-8.
*
* @param string $textValue
* @param string $textValue SYLK encoded string
*
* @return string UTF-8 encoded string
*/
public static function SYLKtoUTF8($textValue)
public static function SYLKtoUTF8(string $textValue): string
{
self::buildCharacterSets();

View File

@ -21,9 +21,9 @@ class TimeZone
*
* @return bool Success or failure
*/
private static function validateTimeZone($timezoneName)
private static function validateTimeZone(string $timezoneName): bool
{
return in_array($timezoneName, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC));
return in_array($timezoneName, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC), true);
}
/**
@ -33,7 +33,7 @@ class TimeZone
*
* @return bool Success or failure
*/
public static function setTimeZone($timezoneName)
public static function setTimeZone(string $timezoneName): bool
{
if (self::validateTimezone($timezoneName)) {
self::$timezone = $timezoneName;
@ -49,7 +49,7 @@ class TimeZone
*
* @return string Timezone (e.g. 'Europe/London')
*/
public static function getTimeZone()
public static function getTimeZone(): string
{
return self::$timezone;
}
@ -63,7 +63,7 @@ class TimeZone
*
* @return int Number of seconds for timezone adjustment
*/
public static function getTimeZoneAdjustment($timezoneName, $timestamp)
public static function getTimeZoneAdjustment(?string $timezoneName, $timestamp): int
{
$timezoneName = $timezoneName ?? self::$timezone;
$dtobj = Date::dateTimeFromTimestamp("$timestamp");

View File

@ -112,6 +112,9 @@ class Formatter
return $value;
}
// Ignore square-$-brackets prefix in format string, like "[$-411]ge.m.d", "[$-010419]0%", etc
$format = (string) preg_replace('/^\[\$-[^\]]*\]/', '', $format);
$format = (string) preg_replace_callback(
'/(["])(?:(?=(\\\\?))\\2.)*?\\1/u',
function ($matches) {

View File

@ -12,6 +12,10 @@ use PhpOffice\PhpSpreadsheet\Collection\Cells;
*/
abstract class CellIterator implements Iterator
{
public const TREAT_NULL_VALUE_AS_EMPTY_CELL = 1;
public const TREAT_EMPTY_STRING_AS_EMPTY_CELL = 2;
/**
* Worksheet to iterate.
*

View File

@ -9,7 +9,7 @@ class Column
*
* @var Worksheet
*/
private $parent;
private $worksheet;
/**
* Column index.
@ -23,10 +23,10 @@ class Column
*
* @param string $columnIndex
*/
public function __construct(?Worksheet $parent = null, $columnIndex = 'A')
public function __construct(Worksheet $worksheet, $columnIndex = 'A')
{
// Set parent and column index
$this->parent = $parent;
$this->worksheet = $worksheet;
$this->columnIndex = $columnIndex;
}
@ -36,7 +36,7 @@ class Column
public function __destruct()
{
// @phpstan-ignore-next-line
$this->parent = null;
$this->worksheet = null;
}
/**
@ -57,6 +57,53 @@ class Column
*/
public function getCellIterator($startRow = 1, $endRow = null)
{
return new ColumnCellIterator($this->parent, $this->columnIndex, $startRow, $endRow);
return new ColumnCellIterator($this->worksheet, $this->columnIndex, $startRow, $endRow);
}
/**
* Returns a boolean true if the column contains no cells. By default, this means that no cell records exist in the
* collection for this column. false will be returned otherwise.
* This rule can be modified by passing a $definitionOfEmptyFlags value:
* 1 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL If the only cells in the collection are null value
* cells, then the column will be considered empty.
* 2 - CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL If the only cells in the collection are empty
* string value cells, then the column will be considered empty.
* 3 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL | CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
* If the only cells in the collection are null value or empty string value cells, then the column
* will be considered empty.
*
* @param int $definitionOfEmptyFlags
* Possible Flag Values are:
* CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
* CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
*/
public function isEmpty(int $definitionOfEmptyFlags = 0): bool
{
$nullValueCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
$emptyStringCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL);
$cellIterator = $this->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $cell) {
$value = $cell->getValue();
if ($value === null && $nullValueCellIsEmpty === true) {
continue;
}
if ($value === '' && $emptyStringCellIsEmpty === true) {
continue;
}
return false;
}
return true;
}
/**
* Returns bound worksheet.
*/
public function getWorksheet(): Worksheet
{
return $this->worksheet;
}
}

View File

@ -23,7 +23,7 @@ class Row
*
* @param int $rowIndex
*/
public function __construct(?Worksheet $worksheet = null, $rowIndex = 1)
public function __construct(Worksheet $worksheet, $rowIndex = 1)
{
// Set parent and row index
$this->worksheet = $worksheet;
@ -59,6 +59,45 @@ class Row
return new RowCellIterator($this->worksheet, $this->rowIndex, $startColumn, $endColumn);
}
/**
* Returns a boolean true if the row contains no cells. By default, this means that no cell records exist in the
* collection for this row. false will be returned otherwise.
* This rule can be modified by passing a $definitionOfEmptyFlags value:
* 1 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL If the only cells in the collection are null value
* cells, then the row will be considered empty.
* 2 - CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL If the only cells in the collection are empty
* string value cells, then the row will be considered empty.
* 3 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL | CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
* If the only cells in the collection are null value or empty string value cells, then the row
* will be considered empty.
*
* @param int $definitionOfEmptyFlags
* Possible Flag Values are:
* CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
* CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
*/
public function isEmpty(int $definitionOfEmptyFlags = 0): bool
{
$nullValueCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
$emptyStringCellIsEmpty = (bool) ($definitionOfEmptyFlags & CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL);
$cellIterator = $this->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $cell) {
$value = $cell->getValue();
if ($value === null && $nullValueCellIsEmpty === true) {
continue;
}
if ($value === '' && $emptyStringCellIsEmpty === true) {
continue;
}
return false;
}
return true;
}
/**
* Returns bound worksheet.
*/

View File

@ -1177,6 +1177,11 @@ class Worksheet implements IComparable
* or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
* @param mixed $value Value of the cell
* @param string $dataType Explicit data type, see DataType::TYPE_*
* Note that PhpSpreadsheet does not validate that the value and datatype are consistent, in using this
* method, then it is your responsibility as an end-user developer to validate that the value and
* the datatype match.
* If you do mismatch value and datatpe, then the value you enter may be changed to match the datatype
* that you specify.
*
* @return $this
*/
@ -1199,6 +1204,11 @@ class Worksheet implements IComparable
* @param int $row Numeric row coordinate of the cell
* @param mixed $value Value of the cell
* @param string $dataType Explicit data type, see DataType::TYPE_*
* Note that PhpSpreadsheet does not validate that the value and datatype are consistent, in using this
* method, then it is your responsibility as an end-user developer to validate that the value and
* the datatype match.
* If you do mismatch value and datatpe, then the value you enter may be changed to match the datatype
* that you specify.
*
* @return $this
*/
@ -1752,31 +1762,39 @@ class Worksheet implements IComparable
{
$range = Functions::trimSheetFromCellReference(Validations::validateCellRange($range));
if (preg_match('/^([A-Z]+)(\\d+):([A-Z]+)(\\d+)$/', $range, $matches) === 1) {
$this->mergeCells[$range] = $range;
$firstRow = (int) $matches[2];
$lastRow = (int) $matches[4];
$firstColumn = $matches[1];
$lastColumn = $matches[3];
$firstColumnIndex = Coordinate::columnIndexFromString($firstColumn);
$lastColumnIndex = Coordinate::columnIndexFromString($lastColumn);
$numberRows = $lastRow - $firstRow;
$numberColumns = $lastColumnIndex - $firstColumnIndex;
if (strpos($range, ':') === false) {
$range .= ":{$range}";
}
// create upper left cell if it does not already exist
$upperLeft = "{$firstColumn}{$firstRow}";
if (!$this->cellExists($upperLeft)) {
$this->getCell($upperLeft)->setValueExplicit(null, DataType::TYPE_NULL);
}
if (preg_match('/^([A-Z]+)(\\d+):([A-Z]+)(\\d+)$/', $range, $matches) !== 1) {
throw new Exception('Merge must be on a valid range of cells.');
}
// Blank out the rest of the cells in the range (if they exist)
if ($numberRows > $numberColumns) {
$this->clearMergeCellsByColumn($firstColumn, $lastColumn, $firstRow, $lastRow, $upperLeft);
} else {
$this->clearMergeCellsByRow($firstColumn, $lastColumnIndex, $firstRow, $lastRow, $upperLeft);
}
$this->mergeCells[$range] = $range;
$firstRow = (int) $matches[2];
$lastRow = (int) $matches[4];
$firstColumn = $matches[1];
$lastColumn = $matches[3];
$firstColumnIndex = Coordinate::columnIndexFromString($firstColumn);
$lastColumnIndex = Coordinate::columnIndexFromString($lastColumn);
$numberRows = $lastRow - $firstRow;
$numberColumns = $lastColumnIndex - $firstColumnIndex;
if ($numberRows === 1 && $numberColumns === 1) {
return $this;
}
// create upper left cell if it does not already exist
$upperLeft = "{$firstColumn}{$firstRow}";
if (!$this->cellExists($upperLeft)) {
$this->getCell($upperLeft)->setValueExplicit(null, DataType::TYPE_NULL);
}
// Blank out the rest of the cells in the range (if they exist)
if ($numberRows > $numberColumns) {
$this->clearMergeCellsByColumn($firstColumn, $lastColumn, $firstRow, $lastRow, $upperLeft);
} else {
throw new Exception('Merge must be set on a range of cells.');
$this->clearMergeCellsByRow($firstColumn, $lastColumnIndex, $firstRow, $lastRow, $upperLeft);
}
return $this;
@ -2582,6 +2600,33 @@ class Worksheet implements IComparable
return $this;
}
/**
* Remove comment from cell.
*
* @param array<int>|CellAddress|string $cellCoordinate Coordinate of the cell as a string, eg: 'C5';
* or as an array of [$columnIndex, $row] (e.g. [3, 5]), or a CellAddress object.
*
* @return $this
*/
public function removeComment($cellCoordinate)
{
$cellAddress = Functions::trimSheetFromCellReference(Validations::validateCellAddress($cellCoordinate));
if (Coordinate::coordinateIsRange($cellAddress)) {
throw new Exception('Cell coordinate string can not be a range of cells.');
} elseif (strpos($cellAddress, '$') !== false) {
throw new Exception('Cell coordinate string must not be absolute.');
} elseif ($cellAddress == '') {
throw new Exception('Cell coordinate can not be zero-length string.');
}
// Check if we have a comment for this cell and delete it
if (isset($this->comments[$cellAddress])) {
unset($this->comments[$cellAddress]);
}
return $this;
}
/**
* Get comment for cell.
*
@ -3232,6 +3277,66 @@ class Worksheet implements IComparable
return clone $this;
}
/**
* Returns a boolean true if the specified row contains no cells. By default, this means that no cell records
* exist in the collection for this row. false will be returned otherwise.
* This rule can be modified by passing a $definitionOfEmptyFlags value:
* 1 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL If the only cells in the collection are null value
* cells, then the row will be considered empty.
* 2 - CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL If the only cells in the collection are empty
* string value cells, then the row will be considered empty.
* 3 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL | CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
* If the only cells in the collection are null value or empty string value cells, then the row
* will be considered empty.
*
* @param int $definitionOfEmptyFlags
* Possible Flag Values are:
* CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
* CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
*/
public function isEmptyRow(int $rowId, int $definitionOfEmptyFlags = 0): bool
{
try {
$iterator = new RowIterator($this, $rowId, $rowId);
$iterator->seek($rowId);
$row = $iterator->current();
} catch (Exception $e) {
return true;
}
return $row->isEmpty($definitionOfEmptyFlags);
}
/**
* Returns a boolean true if the specified column contains no cells. By default, this means that no cell records
* exist in the collection for this column. false will be returned otherwise.
* This rule can be modified by passing a $definitionOfEmptyFlags value:
* 1 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL If the only cells in the collection are null value
* cells, then the column will be considered empty.
* 2 - CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL If the only cells in the collection are empty
* string value cells, then the column will be considered empty.
* 3 - CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL | CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
* If the only cells in the collection are null value or empty string value cells, then the column
* will be considered empty.
*
* @param int $definitionOfEmptyFlags
* Possible Flag Values are:
* CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
* CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL
*/
public function isEmptyColumn(string $columnId, int $definitionOfEmptyFlags = 0): bool
{
try {
$iterator = new ColumnIterator($this, $columnId, $columnId);
$iterator->seek($columnId);
$column = $iterator->current();
} catch (Exception $e) {
return true;
}
return $column->isEmpty($definitionOfEmptyFlags);
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/

View File

@ -1744,7 +1744,7 @@ class Html extends BaseWriter
while ($c++ < $e) {
$baseCell = $this->isSpannedCell[$sheetIndex][$rowIndex][$c]['baseCell'];
if (!in_array($baseCell, $adjustedBaseCells)) {
if (!in_array($baseCell, $adjustedBaseCells, true)) {
// subtract rowspan by 1
--$this->isBaseCell[$sheetIndex][$baseCell[0]][$baseCell[1]]['rowspan'];
$adjustedBaseCells[] = $baseCell;

View File

@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\Chart\GridLines;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Properties;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
@ -248,11 +249,11 @@ class Chart extends WriterPart
$groupType = $plotGroup->getPlotType();
if ($groupType == $chartType) {
$plotStyle = $plotGroup->getPlotStyle();
if ($groupType === DataSeries::TYPE_RADARCHART) {
if (!empty($plotStyle) && $groupType === DataSeries::TYPE_RADARCHART) {
$objWriter->startElement('c:radarStyle');
$objWriter->writeAttribute('val', $plotStyle);
$objWriter->endElement();
} elseif ($groupType === DataSeries::TYPE_SCATTERCHART) {
} elseif (!empty($plotStyle) && $groupType === DataSeries::TYPE_SCATTERCHART) {
$objWriter->startElement('c:scatterStyle');
$objWriter->writeAttribute('val', $plotStyle);
$objWriter->endElement();
@ -430,10 +431,22 @@ class Chart extends WriterPart
}
$objWriter->startElement('c:scaling');
$objWriter->startElement('c:orientation');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('orientation'));
$objWriter->endElement();
$objWriter->endElement();
if ($yAxis->getAxisOptionsProperty('maximum') !== null) {
$objWriter->startElement('c:max');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('maximum'));
$objWriter->endElement();
}
if ($yAxis->getAxisOptionsProperty('minimum') !== null) {
$objWriter->startElement('c:min');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('minimum'));
$objWriter->endElement();
}
if (!empty($yAxis->getAxisOptionsProperty('orientation'))) {
$objWriter->startElement('c:orientation');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('orientation'));
$objWriter->endElement();
}
$objWriter->endElement(); // c:scaling
$objWriter->startElement('c:delete');
$objWriter->writeAttribute('val', 0);
@ -485,47 +498,67 @@ class Chart extends WriterPart
$objWriter->writeAttribute('sourceLinked', $yAxis->getAxisNumberSourceLinked());
$objWriter->endElement();
$objWriter->startElement('c:majorTickMark');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('major_tick_mark'));
$objWriter->endElement();
if (!empty($yAxis->getAxisOptionsProperty('major_tick_mark'))) {
$objWriter->startElement('c:majorTickMark');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('major_tick_mark'));
$objWriter->endElement();
}
$objWriter->startElement('c:minorTickMark');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('minor_tick_mark'));
$objWriter->endElement();
if (!empty($yAxis->getAxisOptionsProperty('minor_tick_mark'))) {
$objWriter->startElement('c:minorTickMark');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('minor_tick_mark'));
$objWriter->endElement();
}
$objWriter->startElement('c:tickLblPos');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('axis_labels'));
$objWriter->endElement();
if (!empty($yAxis->getAxisOptionsProperty('axis_labels'))) {
$objWriter->startElement('c:tickLblPos');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('axis_labels'));
$objWriter->endElement();
}
$objWriter->startElement('c:spPr');
if (!empty($yAxis->getFillProperty('value'))) {
$objWriter->startElement('a:solidFill');
$objWriter->startElement('a:' . $yAxis->getFillProperty('type'));
$objWriter->writeAttribute('val', $yAxis->getFillProperty('value'));
$alpha = $yAxis->getFillProperty('alpha');
if (is_numeric($alpha)) {
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', Properties::alphaToXml((int) $alpha));
$objWriter->endElement();
}
$objWriter->endElement();
$objWriter->endElement();
}
$objWriter->startElement('a:effectLst');
if ($yAxis->getGlowProperty('size') !== null) {
$objWriter->startElement('a:glow');
$objWriter->writeAttribute('rad', $yAxis->getGlowProperty('size'));
$objWriter->startElement("a:{$yAxis->getGlowProperty(['color', 'type'])}");
$objWriter->writeAttribute('val', (string) $yAxis->getGlowProperty(['color', 'value']));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', (string) $yAxis->getGlowProperty(['color', 'alpha']));
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
}
if ($yAxis->getSoftEdgesSize() !== null) {
$objWriter->startElement('a:softEdge');
$objWriter->writeAttribute('rad', $yAxis->getSoftEdgesSize());
$objWriter->endElement(); //end softEdge
}
$this->writeGlow($objWriter, $yAxis);
$this->writeShadow($objWriter, $yAxis);
$this->writeSoftEdge($objWriter, $yAxis);
$objWriter->endElement(); // effectLst
$objWriter->endElement(); // spPr
if ($yAxis->getAxisOptionsProperty('major_unit') !== null) {
$objWriter->startElement('c:majorUnit');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('major_unit'));
$objWriter->endElement();
}
if ($yAxis->getAxisOptionsProperty('minor_unit') !== null) {
$objWriter->startElement('c:minorUnit');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('minor_unit'));
$objWriter->endElement();
}
if ($id2 !== '0') {
$objWriter->startElement('c:crossAx');
$objWriter->writeAttribute('val', $id2);
$objWriter->endElement();
$objWriter->startElement('c:crosses');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('horizontal_crosses'));
$objWriter->endElement();
if (!empty($yAxis->getAxisOptionsProperty('horizontal_crosses'))) {
$objWriter->startElement('c:crosses');
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('horizontal_crosses'));
$objWriter->endElement();
}
}
$objWriter->startElement('c:auto');
@ -580,11 +613,13 @@ class Chart extends WriterPart
$objWriter->endElement();
}
$objWriter->startElement('c:orientation');
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('orientation'));
if (!empty($xAxis->getAxisOptionsProperty('orientation'))) {
$objWriter->startElement('c:orientation');
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('orientation'));
$objWriter->endElement();
}
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement(); // c:scaling
$objWriter->startElement('c:delete');
$objWriter->writeAttribute('val', 0);
@ -597,104 +632,12 @@ class Chart extends WriterPart
$objWriter->startElement('c:majorGridlines');
$objWriter->startElement('c:spPr');
if ($majorGridlines->getLineColorProperty('value') !== null) {
$objWriter->startElement('a:ln');
$objWriter->writeAttribute('w', $majorGridlines->getLineStyleProperty('width'));
$objWriter->startElement('a:solidFill');
$objWriter->startElement("a:{$majorGridlines->getLineColorProperty('type')}");
$objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('value'));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('alpha'));
$objWriter->endElement(); //end alpha
$objWriter->endElement(); //end srgbClr
$objWriter->endElement(); //end solidFill
$this->writeGridlinesLn($objWriter, $majorGridlines);
$objWriter->startElement('a:prstDash');
$objWriter->writeAttribute('val', $majorGridlines->getLineStyleProperty('dash'));
$objWriter->endElement();
if ($majorGridlines->getLineStyleProperty('join') == 'miter') {
$objWriter->startElement('a:miter');
$objWriter->writeAttribute('lim', '800000');
$objWriter->endElement();
} else {
$objWriter->startElement('a:bevel');
$objWriter->endElement();
}
if ($majorGridlines->getLineStyleProperty(['arrow', 'head', 'type']) !== null) {
$objWriter->startElement('a:headEnd');
$objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(['arrow', 'head', 'type']));
$objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('head', 'w'));
$objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('head', 'len'));
$objWriter->endElement();
}
if ($majorGridlines->getLineStyleProperty(['arrow', 'end', 'type']) !== null) {
$objWriter->startElement('a:tailEnd');
$objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(['arrow', 'end', 'type']));
$objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('end', 'w'));
$objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('end', 'len'));
$objWriter->endElement();
}
$objWriter->endElement(); //end ln
}
$objWriter->startElement('a:effectLst');
if ($majorGridlines->getGlowSize() !== null) {
$objWriter->startElement('a:glow');
$objWriter->writeAttribute('rad', $majorGridlines->getGlowSize());
$objWriter->startElement("a:{$majorGridlines->getGlowColor('type')}");
$objWriter->writeAttribute('val', $majorGridlines->getGlowColor('value'));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $majorGridlines->getGlowColor('alpha'));
$objWriter->endElement(); //end alpha
$objWriter->endElement(); //end schemeClr
$objWriter->endElement(); //end glow
}
if ($majorGridlines->getShadowProperty('presets') !== null) {
$objWriter->startElement("a:{$majorGridlines->getShadowProperty('effect')}");
if ($majorGridlines->getShadowProperty('blur') !== null) {
$objWriter->writeAttribute('blurRad', $majorGridlines->getShadowProperty('blur'));
}
if ($majorGridlines->getShadowProperty('distance') !== null) {
$objWriter->writeAttribute('dist', $majorGridlines->getShadowProperty('distance'));
}
if ($majorGridlines->getShadowProperty('direction') !== null) {
$objWriter->writeAttribute('dir', $majorGridlines->getShadowProperty('direction'));
}
if ($majorGridlines->getShadowProperty('algn') !== null) {
$objWriter->writeAttribute('algn', $majorGridlines->getShadowProperty('algn'));
}
if ($majorGridlines->getShadowProperty(['size', 'sx']) !== null) {
$objWriter->writeAttribute('sx', $majorGridlines->getShadowProperty(['size', 'sx']));
}
if ($majorGridlines->getShadowProperty(['size', 'sy']) !== null) {
$objWriter->writeAttribute('sy', $majorGridlines->getShadowProperty(['size', 'sy']));
}
if ($majorGridlines->getShadowProperty(['size', 'kx']) !== null) {
$objWriter->writeAttribute('kx', $majorGridlines->getShadowProperty(['size', 'kx']));
}
if ($majorGridlines->getShadowProperty('rotWithShape') !== null) {
$objWriter->writeAttribute('rotWithShape', $majorGridlines->getShadowProperty('rotWithShape'));
}
$objWriter->startElement("a:{$majorGridlines->getShadowProperty(['color', 'type'])}");
$objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(['color', 'value']));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(['color', 'alpha']));
$objWriter->endElement(); //end alpha
$objWriter->endElement(); //end color:type
$objWriter->endElement(); //end shadow
}
if ($majorGridlines->getSoftEdgesSize() !== null) {
$objWriter->startElement('a:softEdge');
$objWriter->writeAttribute('rad', $majorGridlines->getSoftEdgesSize());
$objWriter->endElement(); //end softEdge
}
$this->writeGlow($objWriter, $majorGridlines);
$this->writeShadow($objWriter, $majorGridlines);
$this->writeSoftEdge($objWriter, $majorGridlines);
$objWriter->endElement(); //end effectLst
$objWriter->endElement(); //end spPr
@ -704,105 +647,14 @@ class Chart extends WriterPart
$objWriter->startElement('c:minorGridlines');
$objWriter->startElement('c:spPr');
if ($minorGridlines->getLineColorProperty('value') !== null) {
$objWriter->startElement('a:ln');
$objWriter->writeAttribute('w', $minorGridlines->getLineStyleProperty('width'));
$objWriter->startElement('a:solidFill');
$objWriter->startElement("a:{$minorGridlines->getLineColorProperty('type')}");
$objWriter->writeAttribute('val', $minorGridlines->getLineColorProperty('value'));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $minorGridlines->getLineColorProperty('alpha'));
$objWriter->endElement(); //end alpha
$objWriter->endElement(); //end srgbClr
$objWriter->endElement(); //end solidFill
$objWriter->startElement('a:prstDash');
$objWriter->writeAttribute('val', $minorGridlines->getLineStyleProperty('dash'));
$objWriter->endElement();
if ($minorGridlines->getLineStyleProperty('join') == 'miter') {
$objWriter->startElement('a:miter');
$objWriter->writeAttribute('lim', '800000');
$objWriter->endElement();
} else {
$objWriter->startElement('a:bevel');
$objWriter->endElement();
}
if ($minorGridlines->getLineStyleProperty(['arrow', 'head', 'type']) !== null) {
$objWriter->startElement('a:headEnd');
$objWriter->writeAttribute('type', $minorGridlines->getLineStyleProperty(['arrow', 'head', 'type']));
$objWriter->writeAttribute('w', $minorGridlines->getLineStyleArrowParameters('head', 'w'));
$objWriter->writeAttribute('len', $minorGridlines->getLineStyleArrowParameters('head', 'len'));
$objWriter->endElement();
}
if ($minorGridlines->getLineStyleProperty(['arrow', 'end', 'type']) !== null) {
$objWriter->startElement('a:tailEnd');
$objWriter->writeAttribute('type', $minorGridlines->getLineStyleProperty(['arrow', 'end', 'type']));
$objWriter->writeAttribute('w', $minorGridlines->getLineStyleArrowParameters('end', 'w'));
$objWriter->writeAttribute('len', $minorGridlines->getLineStyleArrowParameters('end', 'len'));
$objWriter->endElement();
}
$objWriter->endElement(); //end ln
}
$this->writeGridlinesLn($objWriter, $minorGridlines);
$objWriter->startElement('a:effectLst');
if ($minorGridlines->getGlowSize() !== null) {
$objWriter->startElement('a:glow');
$objWriter->writeAttribute('rad', $minorGridlines->getGlowSize());
$objWriter->startElement("a:{$minorGridlines->getGlowColor('type')}");
$objWriter->writeAttribute('val', $minorGridlines->getGlowColor('value'));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $minorGridlines->getGlowColor('alpha'));
$objWriter->endElement(); //end alpha
$objWriter->endElement(); //end schemeClr
$objWriter->endElement(); //end glow
}
if ($minorGridlines->getShadowProperty('presets') !== null) {
$objWriter->startElement("a:{$minorGridlines->getShadowProperty('effect')}");
if ($minorGridlines->getShadowProperty('blur') !== null) {
$objWriter->writeAttribute('blurRad', $minorGridlines->getShadowProperty('blur'));
}
if ($minorGridlines->getShadowProperty('distance') !== null) {
$objWriter->writeAttribute('dist', $minorGridlines->getShadowProperty('distance'));
}
if ($minorGridlines->getShadowProperty('direction') !== null) {
$objWriter->writeAttribute('dir', $minorGridlines->getShadowProperty('direction'));
}
if ($minorGridlines->getShadowProperty('algn') !== null) {
$objWriter->writeAttribute('algn', $minorGridlines->getShadowProperty('algn'));
}
if ($minorGridlines->getShadowProperty(['size', 'sx']) !== null) {
$objWriter->writeAttribute('sx', $minorGridlines->getShadowProperty(['size', 'sx']));
}
if ($minorGridlines->getShadowProperty(['size', 'sy']) !== null) {
$objWriter->writeAttribute('sy', $minorGridlines->getShadowProperty(['size', 'sy']));
}
if ($minorGridlines->getShadowProperty(['size', 'kx']) !== null) {
$objWriter->writeAttribute('kx', $minorGridlines->getShadowProperty(['size', 'kx']));
}
if ($minorGridlines->getShadowProperty('rotWithShape') !== null) {
$objWriter->writeAttribute('rotWithShape', $minorGridlines->getShadowProperty('rotWithShape'));
}
$objWriter->startElement("a:{$minorGridlines->getShadowProperty(['color', 'type'])}");
$objWriter->writeAttribute('val', $minorGridlines->getShadowProperty(['color', 'value']));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $minorGridlines->getShadowProperty(['color', 'alpha']));
$objWriter->endElement(); //end alpha
$objWriter->endElement(); //end color:type
$objWriter->endElement(); //end shadow
}
if ($minorGridlines->getSoftEdgesSize() !== null) {
$objWriter->startElement('a:softEdge');
$objWriter->writeAttribute('rad', $minorGridlines->getSoftEdgesSize());
$objWriter->endElement(); //end softEdge
}
$this->writeGlow($objWriter, $minorGridlines);
$this->writeShadow($objWriter, $minorGridlines);
$this->writeSoftEdge($objWriter, $minorGridlines);
$objWriter->endElement(); //end effectLst
$objWriter->endElement(); //end spPr
$objWriter->endElement(); //end minorGridLines
}
@ -851,138 +703,48 @@ class Chart extends WriterPart
$objWriter->writeAttribute('sourceLinked', $xAxis->getAxisNumberSourceLinked());
$objWriter->endElement();
$objWriter->startElement('c:majorTickMark');
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('major_tick_mark'));
$objWriter->endElement();
if (!empty($xAxis->getAxisOptionsProperty('major_tick_mark'))) {
$objWriter->startElement('c:majorTickMark');
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('major_tick_mark'));
$objWriter->endElement();
}
$objWriter->startElement('c:minorTickMark');
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('minor_tick_mark'));
$objWriter->endElement();
if (!empty($xAxis->getAxisOptionsProperty('minor_tick_mark'))) {
$objWriter->startElement('c:minorTickMark');
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('minor_tick_mark'));
$objWriter->endElement();
}
$objWriter->startElement('c:tickLblPos');
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('axis_labels'));
$objWriter->endElement();
if (!empty($xAxis->getAxisOptionsProperty('axis_labels'))) {
$objWriter->startElement('c:tickLblPos');
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('axis_labels'));
$objWriter->endElement();
}
$objWriter->startElement('c:spPr');
if ($xAxis->getFillProperty('value') !== null) {
if (!empty($xAxis->getFillProperty('value'))) {
$objWriter->startElement('a:solidFill');
$objWriter->startElement('a:' . $xAxis->getFillProperty('type'));
$objWriter->writeAttribute('val', $xAxis->getFillProperty('value'));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $xAxis->getFillProperty('alpha'));
$objWriter->endElement();
$alpha = $xAxis->getFillProperty('alpha');
if (is_numeric($alpha)) {
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', Properties::alphaToXml((int) $alpha));
$objWriter->endElement();
}
$objWriter->endElement();
$objWriter->endElement();
}
$objWriter->startElement('a:ln');
$objWriter->writeAttribute('w', $xAxis->getLineStyleProperty('width'));
$objWriter->writeAttribute('cap', $xAxis->getLineStyleProperty('cap'));
$objWriter->writeAttribute('cmpd', $xAxis->getLineStyleProperty('compound'));
if ($xAxis->getLineProperty('value') !== null) {
$objWriter->startElement('a:solidFill');
$objWriter->startElement('a:' . $xAxis->getLineProperty('type'));
$objWriter->writeAttribute('val', $xAxis->getLineProperty('value'));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $xAxis->getLineProperty('alpha'));
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
}
$objWriter->startElement('a:prstDash');
$objWriter->writeAttribute('val', $xAxis->getLineStyleProperty('dash'));
$objWriter->endElement();
if ($xAxis->getLineStyleProperty('join') == 'miter') {
$objWriter->startElement('a:miter');
$objWriter->writeAttribute('lim', '800000');
$objWriter->endElement();
} else {
$objWriter->startElement('a:bevel');
$objWriter->endElement();
}
if ($xAxis->getLineStyleProperty(['arrow', 'head', 'type']) !== null) {
$objWriter->startElement('a:headEnd');
$objWriter->writeAttribute('type', $xAxis->getLineStyleProperty(['arrow', 'head', 'type']));
$objWriter->writeAttribute('w', $xAxis->getLineStyleArrowWidth('head'));
$objWriter->writeAttribute('len', $xAxis->getLineStyleArrowLength('head'));
$objWriter->endElement();
}
if ($xAxis->getLineStyleProperty(['arrow', 'end', 'type']) !== null) {
$objWriter->startElement('a:tailEnd');
$objWriter->writeAttribute('type', $xAxis->getLineStyleProperty(['arrow', 'end', 'type']));
$objWriter->writeAttribute('w', $xAxis->getLineStyleArrowWidth('end'));
$objWriter->writeAttribute('len', $xAxis->getLineStyleArrowLength('end'));
$objWriter->endElement();
}
$objWriter->endElement();
$this->writeGridlinesLn($objWriter, $xAxis);
$objWriter->startElement('a:effectLst');
if ($xAxis->getGlowProperty('size') !== null) {
$objWriter->startElement('a:glow');
$objWriter->writeAttribute('rad', $xAxis->getGlowProperty('size'));
$objWriter->startElement("a:{$xAxis->getGlowProperty(['color', 'type'])}");
$objWriter->writeAttribute('val', (string) $xAxis->getGlowProperty(['color', 'value']));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', (string) $xAxis->getGlowProperty(['color', 'alpha']));
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
}
if ($xAxis->getShadowProperty('presets') !== null) {
$objWriter->startElement("a:{$xAxis->getShadowProperty('effect')}");
if ($xAxis->getShadowProperty('blur') !== null) {
$objWriter->writeAttribute('blurRad', $xAxis->getShadowProperty('blur'));
}
if ($xAxis->getShadowProperty('distance') !== null) {
$objWriter->writeAttribute('dist', $xAxis->getShadowProperty('distance'));
}
if ($xAxis->getShadowProperty('direction') !== null) {
$objWriter->writeAttribute('dir', $xAxis->getShadowProperty('direction'));
}
if ($xAxis->getShadowProperty('algn') !== null) {
$objWriter->writeAttribute('algn', $xAxis->getShadowProperty('algn'));
}
if ($xAxis->getShadowProperty(['size', 'sx']) !== null) {
$objWriter->writeAttribute('sx', $xAxis->getShadowProperty(['size', 'sx']));
}
if ($xAxis->getShadowProperty(['size', 'sy']) !== null) {
$objWriter->writeAttribute('sy', $xAxis->getShadowProperty(['size', 'sy']));
}
if ($xAxis->getShadowProperty(['size', 'kx']) !== null) {
$objWriter->writeAttribute('kx', $xAxis->getShadowProperty(['size', 'kx']));
}
if ($xAxis->getShadowProperty('rotWithShape') !== null) {
$objWriter->writeAttribute('rotWithShape', $xAxis->getShadowProperty('rotWithShape'));
}
$objWriter->startElement("a:{$xAxis->getShadowProperty(['color', 'type'])}");
$objWriter->writeAttribute('val', $xAxis->getShadowProperty(['color', 'value']));
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', $xAxis->getShadowProperty(['color', 'alpha']));
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
}
if ($xAxis->getSoftEdgesSize() !== null) {
$objWriter->startElement('a:softEdge');
$objWriter->writeAttribute('rad', $xAxis->getSoftEdgesSize());
$objWriter->endElement();
}
$this->writeGlow($objWriter, $xAxis);
$this->writeShadow($objWriter, $xAxis);
$this->writeSoftEdge($objWriter, $xAxis);
$objWriter->endElement(); //effectList
$objWriter->endElement(); //end spPr
if ($id1 !== '0') {
@ -995,14 +757,20 @@ class Chart extends WriterPart
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('horizontal_crosses_value'));
$objWriter->endElement();
} else {
$objWriter->startElement('c:crosses');
$objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('horizontal_crosses'));
$objWriter->endElement();
$crosses = $xAxis->getAxisOptionsProperty('horizontal_crosses');
if ($crosses) {
$objWriter->startElement('c:crosses');
$objWriter->writeAttribute('val', $crosses);
$objWriter->endElement();
}
}
$objWriter->startElement('c:crossBetween');
$objWriter->writeAttribute('val', 'midCat');
$objWriter->endElement();
$crossBetween = $xAxis->getCrossBetween();
if ($crossBetween !== '') {
$objWriter->startElement('c:crossBetween');
$objWriter->writeAttribute('val', $crossBetween);
$objWriter->endElement();
}
if ($xAxis->getAxisOptionsProperty('major_unit') !== null) {
$objWriter->startElement('c:majorUnit');
@ -1109,6 +877,9 @@ class Chart extends WriterPart
*/
private function writePlotSeriesValuesElement(XMLWriter $objWriter, $val = 3, $fillColor = 'FF9900'): void
{
if ($fillColor === '') {
return;
}
$objWriter->startElement('c:dPt');
$objWriter->startElement('c:idx');
$objWriter->writeAttribute('val', $val);
@ -1120,8 +891,16 @@ class Chart extends WriterPart
$objWriter->startElement('c:spPr');
$objWriter->startElement('a:solidFill');
$objWriter->startElement('a:srgbClr');
$objWriter->writeAttribute('val', $fillColor);
if (substr($fillColor, 0, 1) === '*') {
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', substr($fillColor, 1));
} elseif (substr($fillColor, 0, 1) === '/') {
$objWriter->startElement('a:prstClr');
$objWriter->writeAttribute('val', substr($fillColor, 1));
} else {
$objWriter->startElement('a:srgbClr');
$objWriter->writeAttribute('val', $fillColor);
}
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement();
@ -1206,7 +985,7 @@ class Chart extends WriterPart
$fillColorValues = $plotSeriesValues->getFillColor();
if ($fillColorValues !== null && is_array($fillColorValues)) {
foreach ($plotSeriesValues->getDataValues() as $dataKey => $dataValue) {
$this->writePlotSeriesValuesElement($objWriter, $dataKey, ($fillColorValues[$dataKey] ?? 'FF9900'));
$this->writePlotSeriesValuesElement($objWriter, $dataKey, $fillColorValues[$dataKey] ?? '');
}
} else {
$this->writePlotSeriesValuesElement($objWriter);
@ -1228,7 +1007,7 @@ class Chart extends WriterPart
$groupType == DataSeries::TYPE_LINECHART
|| $groupType == DataSeries::TYPE_STOCKCHART
|| ($groupType === DataSeries::TYPE_SCATTERCHART && $plotSeriesValues !== false && !$plotSeriesValues->getScatterLines())
|| ($plotSeriesValues !== false && $plotSeriesValues->getSchemeClr())
|| ($plotSeriesValues !== false && ($plotSeriesValues->getSchemeClr() || $plotSeriesValues->getPrstClr()))
) {
$plotLineWidth = 12700;
if ($plotSeriesValues) {
@ -1236,10 +1015,21 @@ class Chart extends WriterPart
}
$objWriter->startElement('c:spPr');
$schemeClr = $plotLabel ? $plotLabel->getSchemeClr() : null;
$schemeClr = $typeClr = '';
if ($plotLabel) {
$schemeClr = $plotLabel->getSchemeClr();
if ($schemeClr) {
$typeClr = 'schemeClr';
} else {
$schemeClr = $plotLabel->getPrstClr();
if ($schemeClr) {
$typeClr = 'prstClr';
}
}
}
if ($schemeClr) {
$objWriter->startElement('a:solidFill');
$objWriter->startElement('a:schemeClr');
$objWriter->startElement("a:$typeClr");
$objWriter->writeAttribute('val', $schemeClr);
$objWriter->endElement();
$objWriter->endElement();
@ -1658,4 +1448,169 @@ class Chart extends WriterPart
$objWriter->endElement();
}
/**
* Write shadow properties.
*
* @param Axis|GridLines $xAxis
*/
private function writeShadow(XMLWriter $objWriter, $xAxis): void
{
if (empty($xAxis->getShadowProperty('effect'))) {
return;
}
/** @var string */
$effect = $xAxis->getShadowProperty('effect');
$objWriter->startElement("a:$effect");
if (is_numeric($xAxis->getShadowProperty('blur'))) {
$objWriter->writeAttribute('blurRad', Properties::pointsToXml((float) $xAxis->getShadowProperty('blur')));
}
if (is_numeric($xAxis->getShadowProperty('distance'))) {
$objWriter->writeAttribute('dist', Properties::pointsToXml((float) $xAxis->getShadowProperty('distance')));
}
if (is_numeric($xAxis->getShadowProperty('direction'))) {
$objWriter->writeAttribute('dir', Properties::angleToXml((float) $xAxis->getShadowProperty('direction')));
}
if ($xAxis->getShadowProperty('algn') !== null) {
$objWriter->writeAttribute('algn', $xAxis->getShadowProperty('algn'));
}
foreach (['sx', 'sy'] as $sizeType) {
$sizeValue = $xAxis->getShadowProperty(['size', $sizeType]);
if (is_numeric($sizeValue)) {
$objWriter->writeAttribute($sizeType, Properties::tenthOfPercentToXml((float) $sizeValue));
}
}
foreach (['kx', 'ky'] as $sizeType) {
$sizeValue = $xAxis->getShadowProperty(['size', $sizeType]);
if (is_numeric($sizeValue)) {
$objWriter->writeAttribute($sizeType, Properties::angleToXml((float) $sizeValue));
}
}
if ($xAxis->getShadowProperty('rotWithShape') !== null) {
$objWriter->writeAttribute('rotWithShape', $xAxis->getShadowProperty('rotWithShape'));
}
$objWriter->startElement("a:{$xAxis->getShadowProperty(['color', 'type'])}");
$objWriter->writeAttribute('val', $xAxis->getShadowProperty(['color', 'value']));
$alpha = $xAxis->getShadowProperty(['color', 'alpha']);
if (is_numeric($alpha)) {
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', Properties::alphaToXml((int) $alpha));
$objWriter->endElement();
}
$objWriter->endElement();
$objWriter->endElement();
}
/**
* Write glow properties.
*
* @param Axis|GridLines $yAxis
*/
private function writeGlow(XMLWriter $objWriter, $yAxis): void
{
$size = $yAxis->getGlowProperty('size');
if (empty($size)) {
return;
}
$objWriter->startElement('a:glow');
$objWriter->writeAttribute('rad', Properties::pointsToXml((float) $size));
$objWriter->startElement("a:{$yAxis->getGlowProperty(['color', 'type'])}");
$objWriter->writeAttribute('val', (string) $yAxis->getGlowProperty(['color', 'value']));
$alpha = $yAxis->getGlowProperty(['color', 'alpha']);
if (is_numeric($alpha)) {
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', Properties::alphaToXml((int) $alpha));
$objWriter->endElement(); // alpha
}
$objWriter->endElement(); // color
$objWriter->endElement(); // glow
}
/**
* Write soft edge properties.
*
* @param Axis|GridLines $yAxis
*/
private function writeSoftEdge(XMLWriter $objWriter, $yAxis): void
{
$softEdgeSize = $yAxis->getSoftEdgesSize();
if (empty($softEdgeSize)) {
return;
}
$objWriter->startElement('a:softEdge');
$objWriter->writeAttribute('rad', Properties::pointsToXml((float) $softEdgeSize));
$objWriter->endElement(); //end softEdge
}
/**
* Write Line Style for Gridlines.
*
* @param Axis|GridLines $gridlines
*/
private function writeGridlinesLn(XMLWriter $objWriter, $gridlines): void
{
$objWriter->startElement('a:ln');
$widthTemp = $gridlines->getLineStyleProperty('width');
if (is_numeric($widthTemp)) {
$objWriter->writeAttribute('w', Properties::pointsToXml((float) $widthTemp));
}
$this->writeNotEmpty($objWriter, 'cap', $gridlines->getLineStyleProperty('cap'));
$this->writeNotEmpty($objWriter, 'cmpd', $gridlines->getLineStyleProperty('compound'));
if (!empty($gridlines->getLineColorProperty('value'))) {
$objWriter->startElement('a:solidFill');
$objWriter->startElement("a:{$gridlines->getLineColorProperty('type')}");
$objWriter->writeAttribute('val', (string) $gridlines->getLineColorProperty('value'));
$alpha = $gridlines->getLineColorProperty('alpha');
if (is_numeric($alpha)) {
$objWriter->startElement('a:alpha');
$objWriter->writeAttribute('val', Properties::alphaToXml((int) $alpha));
$objWriter->endElement(); // alpha
}
$objWriter->endElement(); //end srgbClr
$objWriter->endElement(); //end solidFill
}
$dash = $gridlines->getLineStyleProperty('dash');
if (!empty($dash)) {
$objWriter->startElement('a:prstDash');
$this->writeNotEmpty($objWriter, 'val', $dash);
$objWriter->endElement();
}
if ($gridlines->getLineStyleProperty('join') === 'miter') {
$objWriter->startElement('a:miter');
$objWriter->writeAttribute('lim', '800000');
$objWriter->endElement();
} elseif ($gridlines->getLineStyleProperty('join') === 'bevel') {
$objWriter->startElement('a:bevel');
$objWriter->endElement();
}
if ($gridlines->getLineStyleProperty(['arrow', 'head', 'type'])) {
$objWriter->startElement('a:headEnd');
$objWriter->writeAttribute('type', $gridlines->getLineStyleProperty(['arrow', 'head', 'type']));
$this->writeNotEmpty($objWriter, 'w', $gridlines->getLineStyleArrowParameters('head', 'w'));
$this->writeNotEmpty($objWriter, 'len', $gridlines->getLineStyleArrowParameters('head', 'len'));
$objWriter->endElement();
}
if ($gridlines->getLineStyleProperty(['arrow', 'end', 'type'])) {
$objWriter->startElement('a:tailEnd');
$objWriter->writeAttribute('type', $gridlines->getLineStyleProperty(['arrow', 'end', 'type']));
$this->writeNotEmpty($objWriter, 'w', $gridlines->getLineStyleArrowParameters('end', 'w'));
$this->writeNotEmpty($objWriter, 'len', $gridlines->getLineStyleArrowParameters('end', 'len'));
$objWriter->endElement();
}
$objWriter->endElement(); //end ln
}
private function writeNotEmpty(XMLWriter $objWriter, string $name, ?string $value): void
{
if ($value !== null && $value !== '') {
$objWriter->writeAttribute($name, $value);
}
}
}

View File

@ -100,7 +100,7 @@ class MergedCellTest extends TestCase
$sheet->mergeCells($range);
self::fail("Expected invalid merge range $range");
} catch (SpreadException $e) {
self::assertSame('Merge must be set on a range of cells.', $e->getMessage());
self::assertSame('Merge must be on a valid range of cells.', $e->getMessage());
}
}
@ -109,7 +109,8 @@ class MergedCellTest extends TestCase
$spreadSheet = new Spreadsheet();
$dataSheet = $spreadSheet->getActiveSheet();
$this->setBadRange($dataSheet, 'B1');
// TODO - Reinstate full validation and disallow single cell merging for version 2.0
// $this->setBadRange($dataSheet, 'B1');
$this->setBadRange($dataSheet, 'Invalid');
$this->setBadRange($dataSheet, '1');
$this->setBadRange($dataSheet, 'C');

View File

@ -1,6 +1,6 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
@ -106,21 +106,21 @@ class AxisGlowTest extends AbstractFunctional
);
$yAxis = $chart->getChartAxisY();
$xAxis = $chart->getChartAxisX();
$yAxis->setGlowProperties(10, 'FFFF00', 30, Properties::EXCEL_COLOR_TYPE_ARGB);
$expectedSize = 127000.0;
$yGlowSize = 10.0;
$yAxis->setGlowProperties($yGlowSize, 'FFFF00', 30, Properties::EXCEL_COLOR_TYPE_ARGB);
$expectedGlowColor = [
'type' => 'srgbClr',
'value' => 'FFFF00',
'alpha' => '70000',
'alpha' => 30,
];
$yAxis->setSoftEdges(2.5);
$xAxis->setSoftEdges(5);
$expectedSoftEdgesY = '31750';
$expectedSoftEdgesX = '63500';
self::assertEquals($expectedSize, $yAxis->getGlowProperty('size'));
$softEdgesY = 2.5;
$yAxis->setSoftEdges($softEdgesY);
$softEdgesX = 5;
$xAxis->setSoftEdges($softEdgesX);
self::assertEquals($yGlowSize, $yAxis->getGlowProperty('size'));
self::assertEquals($expectedGlowColor, $yAxis->getGlowProperty('color'));
self::assertEquals($expectedSoftEdgesY, $yAxis->getSoftEdgesSize());
self::assertEquals($expectedSoftEdgesX, $xAxis->getSoftEdgesSize());
self::assertEquals($softEdgesY, $yAxis->getSoftEdgesSize());
self::assertEquals($softEdgesX, $xAxis->getSoftEdgesSize());
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
@ -142,9 +142,9 @@ class AxisGlowTest extends AbstractFunctional
$chart2 = $charts2[0];
self::assertNotNull($chart2);
$yAxis2 = $chart2->getChartAxisY();
self::assertEquals($expectedSize, $yAxis2->getGlowProperty('size'));
self::assertEquals($yGlowSize, $yAxis2->getGlowProperty('size'));
self::assertEquals($expectedGlowColor, $yAxis2->getGlowProperty('color'));
self::assertEquals($expectedSoftEdgesY, $yAxis2->getSoftEdgesSize());
self::assertEquals($softEdgesY, $yAxis2->getSoftEdgesSize());
$xAxis2 = $chart2->getChartAxisX();
self::assertNull($xAxis2->getGlowProperty('size'));
$reloadedSpreadsheet->disconnectWorksheets();
@ -229,14 +229,14 @@ class AxisGlowTest extends AbstractFunctional
$yAxisLabel // yAxisLabel
);
$yAxis = $chart->getChartAxisX(); // deliberate
$yAxis->setGlowProperties(20, 'accent1', 20, Properties::EXCEL_COLOR_TYPE_SCHEME);
$expectedSize = 254000.0;
$yGlowSize = 20.0;
$yAxis->setGlowProperties($yGlowSize, 'accent1', 20, Properties::EXCEL_COLOR_TYPE_SCHEME);
$expectedGlowColor = [
'type' => 'schemeClr',
'value' => 'accent1',
'alpha' => '80000',
'alpha' => 20,
];
self::assertEquals($expectedSize, $yAxis->getGlowProperty('size'));
self::assertEquals($yGlowSize, $yAxis->getGlowProperty('size'));
self::assertEquals($expectedGlowColor, $yAxis->getGlowProperty('color'));
// Set the position where the chart should appear in the worksheet
@ -259,7 +259,7 @@ class AxisGlowTest extends AbstractFunctional
$chart2 = $charts2[0];
self::assertNotNull($chart2);
$yAxis2 = $chart2->getChartAxisX(); // deliberate
self::assertEquals($expectedSize, $yAxis2->getGlowProperty('size'));
self::assertEquals($yGlowSize, $yAxis2->getGlowProperty('size'));
self::assertEquals($expectedGlowColor, $yAxis2->getGlowProperty('color'));
$xAxis2 = $chart2->getChartAxisY(); // deliberate
self::assertNull($xAxis2->getGlowProperty('size'));

View File

@ -0,0 +1,226 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Axis;
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\Properties;
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 AxisPropertiesTest extends AbstractFunctional
{
public function readCharts(XlsxReader $reader): void
{
$reader->setIncludeCharts(true);
}
public function writeCharts(XlsxWriter $writer): void
{
$writer->setIncludeCharts(true);
}
public function testAxisProperties(): 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],
]
);
// Set the Labels for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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
];
// Set the X-Axis Labels
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$xAxisTickValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
];
// Set the Data values for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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_LINECHART, // plotType
DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues // plotValues
);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');
$xAxis = new Axis();
$xAxis->setFillParameters('FF0000', null, 'srgbClr');
self::assertSame('FF0000', $xAxis->getFillProperty('value'));
self::assertSame('', $xAxis->getFillProperty('alpha'));
self::assertSame('srgbClr', $xAxis->getFillProperty('type'));
$xAxis->setAxisOptionsProperties(
Properties::AXIS_LABELS_HIGH, // axisLabels,
null, // $horizontalCrossesValue,
Properties::HORIZONTAL_CROSSES_MAXIMUM, //horizontalCrosses
Properties::ORIENTATION_REVERSED, //axisOrientation
Properties::TICK_MARK_INSIDE, //majorTmt
Properties::TICK_MARK_OUTSIDE, //minorTmt
'8', //minimum
'68', //maximum
'20', //majorUnit
'5' //minorUnit
);
self::assertSame(Properties::AXIS_LABELS_HIGH, $xAxis->getAxisOptionsProperty('axis_labels'));
self::assertNull($xAxis->getAxisOptionsProperty('horizontal_crosses_value'));
self::assertSame(Properties::HORIZONTAL_CROSSES_MAXIMUM, $xAxis->getAxisOptionsProperty('horizontal_crosses'));
self::assertSame(Properties::ORIENTATION_REVERSED, $xAxis->getAxisOptionsProperty('orientation'));
self::assertSame(Properties::TICK_MARK_INSIDE, $xAxis->getAxisOptionsProperty('major_tick_mark'));
self::assertSame(Properties::TICK_MARK_OUTSIDE, $xAxis->getAxisOptionsProperty('minor_tick_mark'));
self::assertSame('8', $xAxis->getAxisOptionsProperty('minimum'));
self::assertSame('68', $xAxis->getAxisOptionsProperty('maximum'));
self::assertSame('20', $xAxis->getAxisOptionsProperty('major_unit'));
self::assertSame('5', $xAxis->getAxisOptionsProperty('minor_unit'));
$yAxis = new Axis();
$yAxis->setFillParameters('accent1', 30, 'schemeClr');
self::assertSame('accent1', $yAxis->getFillProperty('value'));
self::assertSame('30', $yAxis->getFillProperty('alpha'));
self::assertSame('schemeClr', $yAxis->getFillProperty('type'));
// Create the chart
$chart = new Chart(
'chart1', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel, // yAxisLabel
$xAxis, // xAxis
$yAxis, // yAxis
null, //majorGridlines,
null // minorGridlines
);
$xAxis2 = $chart->getChartAxisX();
self::assertSame('FF0000', $xAxis2->getFillProperty('value'));
self::assertSame('', $xAxis2->getFillProperty('alpha'));
self::assertSame('srgbClr', $xAxis2->getFillProperty('type'));
self::assertSame(Properties::AXIS_LABELS_HIGH, $xAxis2->getAxisOptionsProperty('axis_labels'));
self::assertNull($xAxis2->getAxisOptionsProperty('horizontal_crosses_value'));
self::assertSame(Properties::HORIZONTAL_CROSSES_MAXIMUM, $xAxis2->getAxisOptionsProperty('horizontal_crosses'));
self::assertSame(Properties::ORIENTATION_REVERSED, $xAxis2->getAxisOptionsProperty('orientation'));
self::assertSame(Properties::TICK_MARK_INSIDE, $xAxis2->getAxisOptionsProperty('major_tick_mark'));
self::assertSame(Properties::TICK_MARK_OUTSIDE, $xAxis2->getAxisOptionsProperty('minor_tick_mark'));
self::assertSame('8', $xAxis2->getAxisOptionsProperty('minimum'));
self::assertSame('68', $xAxis2->getAxisOptionsProperty('maximum'));
self::assertSame('20', $xAxis2->getAxisOptionsProperty('major_unit'));
self::assertSame('5', $xAxis2->getAxisOptionsProperty('minor_unit'));
$yAxis2 = $chart->getChartAxisY();
self::assertSame('accent1', $yAxis2->getFillProperty('value'));
self::assertSame('30', $yAxis2->getFillProperty('alpha'));
self::assertSame('schemeClr', $yAxis2->getFillProperty('type'));
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
$chart->setBottomRightPosition('H20');
// 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);
$xAxis3 = $chart2->getChartAxisX();
self::assertSame('FF0000', $xAxis3->getFillProperty('value'));
self::assertSame('', $xAxis3->getFillProperty('alpha'));
self::assertSame('srgbClr', $xAxis3->getFillProperty('type'));
self::assertSame(Properties::AXIS_LABELS_HIGH, $xAxis3->getAxisOptionsProperty('axis_labels'));
self::assertSame(Properties::TICK_MARK_INSIDE, $xAxis3->getAxisOptionsProperty('major_tick_mark'));
self::assertSame(Properties::TICK_MARK_OUTSIDE, $xAxis3->getAxisOptionsProperty('minor_tick_mark'));
self::assertNull($xAxis3->getAxisOptionsProperty('horizontal_crosses_value'));
self::assertSame(Properties::HORIZONTAL_CROSSES_MAXIMUM, $xAxis3->getAxisOptionsProperty('horizontal_crosses'));
self::assertSame(Properties::ORIENTATION_REVERSED, $xAxis3->getAxisOptionsProperty('orientation'));
self::assertSame('8', $xAxis3->getAxisOptionsProperty('minimum'));
self::assertSame('68', $xAxis3->getAxisOptionsProperty('maximum'));
self::assertSame('20', $xAxis3->getAxisOptionsProperty('major_unit'));
self::assertSame('5', $xAxis3->getAxisOptionsProperty('minor_unit'));
$yAxis3 = $chart2->getChartAxisY();
self::assertSame('accent1', $yAxis3->getFillProperty('value'));
self::assertSame('30', $yAxis3->getFillProperty('alpha'));
self::assertSame('schemeClr', $yAxis3->getFillProperty('type'));
$xAxis3->setAxisOrientation(Properties::ORIENTATION_NORMAL);
self::assertSame(Properties::ORIENTATION_NORMAL, $xAxis3->getAxisOptionsProperty('orientation'));
$xAxis3->setAxisOptionsProperties(
Properties::AXIS_LABELS_HIGH, // axisLabels,
'5' // $horizontalCrossesValue,
);
self::assertSame('5', $xAxis3->getAxisOptionsProperty('horizontal_crosses_value'));
$yAxis3->setLineColorProperties('0000FF', null, 'srgbClr');
self::assertSame('0000FF', $yAxis3->getLineProperty('value'));
self::assertNull($yAxis3->getLineProperty('alpha'));
self::assertSame('srgbClr', $yAxis3->getLineProperty('type'));
$yAxis3->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL);
self::assertFalse($yAxis3->getAxisIsNumericFormat());
$yAxis3->setAxisNumberProperties(Properties::FORMAT_CODE_NUMBER);
self::assertTrue($yAxis3->getAxisIsNumericFormat());
$reloadedSpreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,184 @@
<?php
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\Properties;
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 AxisShadowTest extends AbstractFunctional
{
public function readCharts(XlsxReader $reader): void
{
$reader->setIncludeCharts(true);
}
public function writeCharts(XlsxWriter $writer): void
{
$writer->setIncludeCharts(true);
}
public function testGlowY(): 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],
]
);
// Set the Labels for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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
];
// Set the X-Axis Labels
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$xAxisTickValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
];
// Set the Data values for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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
);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');
// Create the chart
$chart = new Chart(
'chart1', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel // yAxisLabel
);
$yAxis = $chart->getChartAxisY();
$expectedY = [
'effect' => 'outerShdw',
'algn' => 'tl',
'blur' => 5,
'direction' => 45,
'distance' => 3,
'rotWithShape' => 0,
'color' => [
'type' => Properties::EXCEL_COLOR_TYPE_STANDARD,
'value' => 'black',
'alpha' => 40,
],
];
foreach ($expectedY as $key => $value) {
$yAxis->setShadowProperty($key, $value);
}
foreach ($expectedY as $key => $value) {
self::assertEquals($value, $yAxis->getShadowProperty($key), $key);
}
$xAxis = $chart->getChartAxisX();
$expectedX = [
'effect' => 'outerShdw',
'algn' => 'bl',
'blur' => 6,
'direction' => 315,
'distance' => 3,
'rotWithShape' => 0,
'size' => [
'sx' => null,
'sy' => 254,
'kx' => -94,
'ky' => null,
],
'color' => [
'type' => Properties::EXCEL_COLOR_TYPE_ARGB,
'value' => 'FF0000',
'alpha' => 20,
],
];
foreach ($expectedX as $key => $value) {
$xAxis->setShadowProperty($key, $value);
}
foreach ($expectedX as $key => $value) {
self::assertEquals($value, $xAxis->getShadowProperty($key), $key);
}
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
$chart->setBottomRightPosition('H20');
// 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);
$yAxis2 = $chart2->getChartAxisY();
foreach ($expectedY as $key => $value) {
self::assertEquals($value, $yAxis2->getShadowProperty($key), $key);
}
$xAxis2 = $chart2->getChartAxisX();
foreach ($expectedX as $key => $value) {
self::assertEquals($value, $xAxis2->getShadowProperty($key), $key);
}
$reloadedSpreadsheet->disconnectWorksheets();
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Axis;
use PhpOffice\PhpSpreadsheet\Chart\Chart;

View File

@ -1,6 +1,6 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\RichText\RichText;

View File

@ -1,6 +1,6 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\RichText\RichText;

View File

@ -1,10 +1,9 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Properties;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PHPUnit\Framework\TestCase;
@ -13,17 +12,6 @@ class Charts32XmlTest extends TestCase
// These tests can only be performed by examining xml.
private const DIRECTORY = 'samples' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR;
/** @var string */
private $outputFileName = '';
protected function tearDown(): void
{
if ($this->outputFileName !== '') {
unlink($this->outputFileName);
$this->outputFileName = '';
}
}
/**
* @dataProvider providerScatterCharts
*/
@ -33,25 +21,21 @@ class Charts32XmlTest extends TestCase
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$this->outputFileName = File::temporaryFilename();
$writer->save($this->outputFileName);
$writerChart = new XlsxWriter\Chart($writer);
$data = $writerChart->writeChart($chart);
$spreadsheet->disconnectWorksheets();
$file = 'zip://';
$file .= $this->outputFileName;
$file .= '#xl/charts/chart2.xml';
$data = file_get_contents($file);
// confirm that file contains expected tags
if ($data === false) {
self::fail('Unable to read file');
} else {
self::assertSame(1, substr_count($data, '<c:scatterStyle val='));
self::assertSame($expectedCount ? 1 : 0, substr_count($data, '<c:scatterStyle val="smoothMarker"/>'));
self::assertSame($expectedCount, substr_count($data, '<c:smooth val="1"/>'));
}
self::assertSame(1, substr_count($data, '<c:scatterStyle val='));
self::assertSame($expectedCount ? 1 : 0, substr_count($data, '<c:scatterStyle val="smoothMarker"/>'));
self::assertSame($expectedCount, substr_count($data, '<c:smooth val="1"/>'));
}
public function providerScatterCharts(): array
@ -69,23 +53,20 @@ class Charts32XmlTest extends TestCase
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$this->outputFileName = File::temporaryFilename();
$writer->save($this->outputFileName);
$writerChart = new XlsxWriter\Chart($writer);
$data = $writerChart->writeChart($chart);
$spreadsheet->disconnectWorksheets();
$file = 'zip://';
$file .= $this->outputFileName;
$file .= '#xl/charts/chart1.xml';
$data = file_get_contents($file);
// confirm that file contains expected tags
if ($data === false) {
self::fail('Unable to read file');
} else {
self::assertSame(0, substr_count($data, '<c:cat>'));
}
self::assertSame(0, substr_count($data, '<c:cat>'));
}
/**
@ -116,18 +97,11 @@ class Charts32XmlTest extends TestCase
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$this->outputFileName = File::temporaryFilename();
$writer->save($this->outputFileName);
$writerChart = new XlsxWriter\Chart($writer);
$data = $writerChart->writeChart($chart);
$spreadsheet->disconnectWorksheets();
$file = 'zip://';
$file .= $this->outputFileName;
$file .= '#xl/charts/chart2.xml';
$data = file_get_contents($file);
// confirm that file contains expected tags
if ($data === false) {
self::fail('Unable to read file');
} elseif ($numeric === true) {
if ($numeric === true) {
self::assertSame(0, substr_count($data, '<c:catAx>'));
self::assertSame(2, substr_count($data, '<c:valAx>'));
} else {
@ -144,4 +118,31 @@ class Charts32XmlTest extends TestCase
[null],
];
}
public function testAreaPrstClr(): void
{
$file = self::DIRECTORY . '32readwriteAreaChart4.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$writerChart = new XlsxWriter\Chart($writer);
$data = $writerChart->writeChart($chart);
$spreadsheet->disconnectWorksheets();
self::assertSame(
1,
substr_count(
$data,
'</c:tx><c:spPr><a:solidFill><a:prstClr val="red"/>'
)
);
}
}

View File

@ -1,6 +1,6 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PHPUnit\Framework\TestCase;

View File

@ -1,6 +1,6 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;

View File

@ -0,0 +1,215 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\GridLines;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Properties;
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 GridlinesLineStyleTest extends AbstractFunctional
{
public function readCharts(XlsxReader $reader): void
{
$reader->setIncludeCharts(true);
}
public function writeCharts(XlsxWriter $writer): void
{
$writer->setIncludeCharts(true);
}
public function testLineStyles(): 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],
]
);
// Set the Labels for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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
];
// Set the X-Axis Labels
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$xAxisTickValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
];
// Set the Data values for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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_LINECHART, // plotType
DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues // plotValues
);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');
$majorGridlines = new GridLines();
$width = 2;
$compound = Properties::LINE_STYLE_COMPOUND_THICKTHIN;
$dash = Properties::LINE_STYLE_DASH_ROUND_DOT;
$cap = Properties::LINE_STYLE_CAP_ROUND;
$join = Properties::LINE_STYLE_JOIN_MITER;
$headArrowType = Properties::LINE_STYLE_ARROW_TYPE_DIAMOND;
$headArrowSize = (string) Properties::LINE_STYLE_ARROW_SIZE_2;
$endArrowType = Properties::LINE_STYLE_ARROW_TYPE_OVAL;
$endArrowSize = (string) Properties::LINE_STYLE_ARROW_SIZE_3;
$majorGridlines->setLineStyleProperties(
$width,
$compound,
$dash,
$cap,
$join,
$headArrowType,
$headArrowSize,
$endArrowType,
$endArrowSize
);
$minorGridlines = new GridLines();
$minorGridlines->setLineColorProperties('00FF00', 30, 'srgbClr');
self::assertEquals($width, $majorGridlines->getLineStyleProperty('width'));
self::assertEquals($compound, $majorGridlines->getLineStyleProperty('compound'));
self::assertEquals($dash, $majorGridlines->getLineStyleProperty('dash'));
self::assertEquals($cap, $majorGridlines->getLineStyleProperty('cap'));
self::assertEquals($join, $majorGridlines->getLineStyleProperty('join'));
self::assertEquals($headArrowType, $majorGridlines->getLineStyleProperty(['arrow', 'head', 'type']));
self::assertEquals($headArrowSize, $majorGridlines->getLineStyleProperty(['arrow', 'head', 'size']));
self::assertEquals($endArrowType, $majorGridlines->getLineStyleProperty(['arrow', 'end', 'type']));
self::assertEquals($endArrowSize, $majorGridlines->getLineStyleProperty(['arrow', 'end', 'size']));
self::assertEquals('sm', $majorGridlines->getLineStyleProperty(['arrow', 'head', 'w']));
self::assertEquals('med', $majorGridlines->getLineStyleProperty(['arrow', 'head', 'len']));
self::assertEquals('sm', $majorGridlines->getLineStyleProperty(['arrow', 'end', 'w']));
self::assertEquals('lg', $majorGridlines->getLineStyleProperty(['arrow', 'end', 'len']));
self::assertEquals('sm', $majorGridlines->getLineStyleArrowWidth('end'));
self::assertEquals('lg', $majorGridlines->getLineStyleArrowLength('end'));
self::assertEquals('lg', $majorGridlines->getLineStyleArrowParameters('end', 'len'));
self::assertSame('00FF00', $minorGridlines->getLineColorProperty('value'));
self::assertSame(30, $minorGridlines->getLineColorProperty('alpha'));
self::assertSame('srgbClr', $minorGridlines->getLineColorProperty('type'));
// Create the chart
$chart = new Chart(
'chart1', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel, // yAxisLabel
null, // xAxis
null, // yAxis
$majorGridlines,
$minorGridlines // minorGridlines
);
$majorGridlines2 = $chart->getMajorGridlines();
self::assertEquals($width, $majorGridlines2->getLineStyleProperty('width'));
self::assertEquals($compound, $majorGridlines2->getLineStyleProperty('compound'));
self::assertEquals($dash, $majorGridlines2->getLineStyleProperty('dash'));
self::assertEquals($cap, $majorGridlines2->getLineStyleProperty('cap'));
self::assertEquals($join, $majorGridlines2->getLineStyleProperty('join'));
self::assertEquals($headArrowType, $majorGridlines2->getLineStyleProperty(['arrow', 'head', 'type']));
self::assertEquals($headArrowSize, $majorGridlines2->getLineStyleProperty(['arrow', 'head', 'size']));
self::assertEquals($endArrowType, $majorGridlines2->getLineStyleProperty(['arrow', 'end', 'type']));
self::assertEquals($endArrowSize, $majorGridlines2->getLineStyleProperty(['arrow', 'end', 'size']));
self::assertEquals('sm', $majorGridlines2->getLineStyleProperty(['arrow', 'head', 'w']));
self::assertEquals('med', $majorGridlines2->getLineStyleProperty(['arrow', 'head', 'len']));
self::assertEquals('sm', $majorGridlines2->getLineStyleProperty(['arrow', 'end', 'w']));
self::assertEquals('lg', $majorGridlines2->getLineStyleProperty(['arrow', 'end', 'len']));
$minorGridlines2 = $chart->getMinorGridlines();
self::assertSame('00FF00', $minorGridlines2->getLineColorProperty('value'));
self::assertSame(30, $minorGridlines2->getLineColorProperty('alpha'));
self::assertSame('srgbClr', $minorGridlines2->getLineColorProperty('type'));
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
$chart->setBottomRightPosition('H20');
// 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);
$majorGridlines3 = $chart2->getMajorGridlines();
self::assertEquals($width, $majorGridlines3->getLineStyleProperty('width'));
self::assertEquals($compound, $majorGridlines3->getLineStyleProperty('compound'));
self::assertEquals($dash, $majorGridlines3->getLineStyleProperty('dash'));
self::assertEquals($cap, $majorGridlines3->getLineStyleProperty('cap'));
self::assertEquals($join, $majorGridlines3->getLineStyleProperty('join'));
self::assertEquals($headArrowType, $majorGridlines3->getLineStyleProperty(['arrow', 'head', 'type']));
self::assertEquals($endArrowType, $majorGridlines3->getLineStyleProperty(['arrow', 'end', 'type']));
self::assertEquals('sm', $majorGridlines3->getLineStyleProperty(['arrow', 'head', 'w']));
self::assertEquals('med', $majorGridlines3->getLineStyleProperty(['arrow', 'head', 'len']));
self::assertEquals('sm', $majorGridlines3->getLineStyleProperty(['arrow', 'end', 'w']));
self::assertEquals('lg', $majorGridlines3->getLineStyleProperty(['arrow', 'end', 'len']));
$minorGridlines3 = $chart2->getMinorGridlines();
self::assertSame('00FF00', $minorGridlines3->getLineColorProperty('value'));
self::assertSame(30, $minorGridlines3->getLineColorProperty('alpha'));
self::assertSame('srgbClr', $minorGridlines3->getLineColorProperty('type'));
$reloadedSpreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,187 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\GridLines;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Properties;
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 GridlinesShadowGlowTest extends AbstractFunctional
{
public function readCharts(XlsxReader $reader): void
{
$reader->setIncludeCharts(true);
}
public function writeCharts(XlsxWriter $writer): void
{
$writer->setIncludeCharts(true);
}
public function testGlowY(): 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],
]
);
// Set the Labels for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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
];
// Set the X-Axis Labels
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$xAxisTickValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
];
// Set the Data values for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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_LINECHART, // plotType
DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues // plotValues
);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');
$majorGridlines = new GridLines();
$majorGlowSize = 10.0;
$majorGridlines->setGlowProperties($majorGlowSize, 'FFFF00', 30, Properties::EXCEL_COLOR_TYPE_ARGB);
$softEdgeSize = 2.5;
$majorGridlines->setSoftEdges($softEdgeSize);
$expectedGlowColor = [
'type' => 'srgbClr',
'value' => 'FFFF00',
'alpha' => 30,
];
self::assertEquals($majorGlowSize, $majorGridlines->getGlowProperty('size'));
self::assertEquals($majorGlowSize, $majorGridlines->getGlowSize());
self::assertEquals($expectedGlowColor['value'], $majorGridlines->getGlowColor('value'));
self::assertEquals($expectedGlowColor, $majorGridlines->getGlowProperty('color'));
self::assertEquals($softEdgeSize, $majorGridlines->getSoftEdgesSize());
$minorGridlines = new GridLines();
$expectedShadow = [
'effect' => 'outerShdw',
'algn' => 'tl',
'blur' => 4,
'direction' => 45,
'distance' => 3,
'rotWithShape' => 0,
'color' => [
'type' => Properties::EXCEL_COLOR_TYPE_STANDARD,
'value' => 'black',
'alpha' => 40,
],
];
foreach ($expectedShadow as $key => $value) {
$minorGridlines->setShadowProperty($key, $value);
}
foreach ($expectedShadow as $key => $value) {
self::assertEquals($value, $minorGridlines->getShadowProperty($key), $key);
}
// Create the chart
$chart = new Chart(
'chart1', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel, // yAxisLabel
null, // xAxis
null, // yAxis
$majorGridlines,
$minorGridlines
);
$majorGridlines2 = $chart->getMajorGridlines();
self::assertEquals($majorGlowSize, $majorGridlines2->getGlowProperty('size'));
self::assertEquals($expectedGlowColor, $majorGridlines2->getGlowProperty('color'));
self::assertEquals($softEdgeSize, $majorGridlines2->getSoftEdgesSize());
$minorGridlines2 = $chart->getMinorGridlines();
foreach ($expectedShadow as $key => $value) {
self::assertEquals($value, $minorGridlines2->getShadowProperty($key), $key);
}
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
$chart->setBottomRightPosition('H20');
// 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);
$majorGridlines3 = $chart2->getMajorGridlines();
self::assertEquals($majorGlowSize, $majorGridlines3->getGlowProperty('size'));
self::assertEquals($expectedGlowColor, $majorGridlines3->getGlowProperty('color'));
self::assertEquals($softEdgeSize, $majorGridlines3->getSoftEdgesSize());
$minorGridlines3 = $chart->getMinorGridlines();
foreach ($expectedShadow as $key => $value) {
self::assertEquals($value, $minorGridlines3->getShadowProperty($key), $key);
}
$reloadedSpreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,157 @@
<?php
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\Properties;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PHPUnit\Framework\TestCase;
class MultiplierTest extends TestCase
{
public function testMultiplier(): 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],
]
);
// Set the Labels for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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
];
// Set the X-Axis Labels
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$xAxisTickValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
];
// Set the Data values for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$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
);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');
// Create the chart
$chart = new Chart(
'chart1', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel // yAxisLabel
);
$xAxis = $chart->getChartAxisX();
$expectedX = [
'effect' => 'outerShdw',
'algn' => 'bl',
'blur' => 6,
'direction' => 315,
'distance' => 3,
'rotWithShape' => 0,
'size' => [
'sx' => null,
'sy' => 254,
'kx' => -94,
'ky' => null,
],
'color' => [
'type' => Properties::EXCEL_COLOR_TYPE_ARGB,
'value' => 'FF0000',
'alpha' => 20,
],
];
$expectedXmlX = [
'<a:outerShdw ',
' algn="bl"',
' blurRad="76200"',
' dir="18900000"',
' dist="38100"',
' rotWithShape="0"',
' sy="25400000"',
' kx="-5640000"',
'<a:srgbClr val="FF0000">',
'<a:alpha val="80000"/>',
];
$expectedXmlNoX = [
' sx=',
' ky=',
];
foreach ($expectedX as $key => $value) {
$xAxis->setShadowProperty($key, $value);
}
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
$chart->setBottomRightPosition('H20');
// Add the chart to the worksheet
$worksheet->addChart($chart);
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$writerChart = new XlsxWriter\Chart($writer);
$data = $writerChart->writeChart($chart);
// confirm that file contains expected tags
foreach ($expectedXmlX as $expected) {
self::assertSame(1, substr_count($data, $expected), $expected);
}
foreach ($expectedXmlNoX as $expected) {
self::assertSame(0, substr_count($data, $expected), $expected);
}
$spreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,160 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
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 PieFillTest extends AbstractFunctional
{
public function readCharts(XlsxReader $reader): void
{
$reader->setIncludeCharts(true);
}
public function writeCharts(XlsxWriter $writer): void
{
$writer->setIncludeCharts(true);
}
public function testPieFill(): 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],
]
);
// Custom colors for dataSeries (gray, blue, red, orange)
$colors = [
'cccccc',
'*accent1', // use schemeClr, was '00abb8',
'/green', // use prstClr, was 'b8292f',
'eb8500',
];
// Set the Labels for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$dataSeriesLabels1 = [
new DataSeriesValues(
DataSeriesValues::DATASERIES_TYPE_STRING,
'Worksheet!$C$1',
null,
1
), // 2011
];
// Set the X-Axis Labels
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$xAxisTickValues1 = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
];
// Set the Data values for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
// Custom Colors
$dataSeriesValues1Element = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4);
$dataSeriesValues1Element->setFillColor($colors);
$dataSeriesValues1 = [$dataSeriesValues1Element];
// Build the dataseries
$series1 = new DataSeries(
DataSeries::TYPE_PIECHART, // plotType
null, // plotGrouping (Pie charts don't have any grouping)
range(0, count($dataSeriesValues1) - 1), // plotOrder
$dataSeriesLabels1, // plotLabel
$xAxisTickValues1, // plotCategory
$dataSeriesValues1 // plotValues
);
// Set up a layout object for the Pie chart
$layout1 = new Layout();
$layout1->setShowVal(true);
$layout1->setShowPercent(true);
// Set the series in the plot area
$plotArea1 = new PlotArea($layout1, [$series1]);
// Set the chart legend
$legend1 = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$title1 = new Title('Test Pie Chart');
// Create the chart
$chart1 = new Chart(
'chart1', // name
$title1, // title
$legend1, // legend
$plotArea1, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
null // no Y-Axis for Pie Chart
);
// Set the position where the chart should appear in the worksheet
$chart1->setTopLeftPosition('A7');
$chart1->setBottomRightPosition('H20');
// Add the chart to the worksheet
$worksheet->addChart($chart1);
/** @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);
$plotArea2 = $chart2->getPlotArea();
$dataSeries2 = $plotArea2->getPlotGroup();
self::assertCount(1, $dataSeries2);
$plotValues = $dataSeries2[0]->getPlotValues();
self::assertCount(1, $plotValues);
$fillColors = $plotValues[0]->getFillColor();
self::assertSame($colors, $fillColors);
$writer = new XlsxWriter($reloadedSpreadsheet);
$writer->setIncludeCharts(true);
$writerChart = new XlsxWriter\Chart($writer);
$data = $writerChart->writeChart($chart2);
self::assertSame(1, substr_count($data, '<a:srgbClr val="cccccc"/>'));
self::assertSame(1, substr_count($data, '<a:schemeClr val="accent1"/>'));
self::assertSame(1, substr_count($data, '<a:prstClr val="green"/>'));
self::assertSame(1, substr_count($data, '<a:srgbClr val="eb8500"/>'));
self::assertSame(4, substr_count($data, '<c:dPt>'));
$reloadedSpreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,183 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Axis;
use PhpOffice\PhpSpreadsheet\Chart\GridLines;
use PhpOffice\PhpSpreadsheet\Chart\Properties;
use PHPUnit\Framework\TestCase;
class ShadowPresetsTest extends TestCase
{
public function testGridlineShadowPresets(): void
{
$gridlines = new GridLines();
$gridlines->setShadowProperties(17);
$expectedShadow = [
'effect' => 'innerShdw',
'distance' => 4,
'direction' => 270,
'blur' => 5,
];
foreach ($expectedShadow as $key => $value) {
self::assertEquals($gridlines->getShadowProperty($key), $value, $key);
}
}
public function testGridlineShadowPresetsWithArray(): void
{
$gridlines = new GridLines();
$gridlines->setShadowProperties(20);
$expectedShadow = [
'effect' => 'outerShdw',
'blur' => 6,
'direction' => 315,
'size' => [
'sx' => null,
'sy' => 0.23,
'kx' => -20,
'ky' => null,
],
'algn' => 'bl',
'rotWithShape' => '0',
];
foreach ($expectedShadow as $key => $value) {
self::assertEquals($gridlines->getShadowProperty($key), $value, $key);
}
}
public function testAxisShadowPresets(): void
{
$axis = new Axis();
$axis->setShadowProperties(9);
$expectedShadow = [
'effect' => 'outerShdw',
'blur' => 4,
'distance' => 3,
'direction' => 225,
'algn' => 'br',
'rotWithShape' => '0',
];
foreach ($expectedShadow as $key => $value) {
self::assertEquals($axis->getShadowProperty($key), $value, $key);
}
}
public function testAxisShadowPresetsWithChanges(): void
{
$axis = new Axis();
$axis->setShadowProperties(
9, // preset
'FF0000', // colorValue
'srgbClr', // colorType
20, // alpha
6, // blur
30, // direction
4, // distance
);
$expectedShadow = [
'effect' => 'outerShdw',
'blur' => 6,
'distance' => 4,
'direction' => 30,
'algn' => 'br',
'rotWithShape' => '0',
'color' => [
'value' => 'FF0000',
'type' => 'srgbClr',
'alpha' => 20,
],
];
foreach ($expectedShadow as $key => $value) {
self::assertEquals($axis->getShadowProperty($key), $value, $key);
}
}
public function testGridlinesShadowPresetsWithChanges(): void
{
$gridline = new GridLines();
$gridline->setShadowProperties(
9, // preset
'FF0000', // colorValue
'srgbClr', // colorType
20, // alpha
6, // blur
30, // direction
4, // distance
);
$expectedShadow = [
'effect' => 'outerShdw',
'blur' => 6,
'distance' => 4,
'direction' => 30,
'algn' => 'br',
'rotWithShape' => '0',
'color' => [
'value' => 'FF0000',
'type' => 'srgbClr',
'alpha' => 20,
],
];
foreach ($expectedShadow as $key => $value) {
self::assertEquals($gridline->getShadowProperty($key), $value, $key);
}
}
public function testOutOfRangePresets(): void
{
$axis = new Axis();
$axis->setShadowProperties(99);
$expectedShadow = [
'presets' => Properties::SHADOW_PRESETS_NOSHADOW,
'effect' => null,
'color' => [
'type' => Properties::EXCEL_COLOR_TYPE_STANDARD,
'value' => 'black',
'alpha' => 40,
],
'size' => [
'sx' => null,
'sy' => null,
'kx' => null,
'ky' => null,
],
'blur' => null,
'direction' => null,
'distance' => null,
'algn' => null,
'rotWithShape' => null,
];
foreach ($expectedShadow as $key => $value) {
self::assertEquals($value, $axis->getShadowProperty($key), $key);
}
}
public function testOutOfRangeGridlines(): void
{
$gridline = new GridLines();
$gridline->setShadowProperties(99);
$expectedShadow = [
'presets' => Properties::SHADOW_PRESETS_NOSHADOW,
'effect' => null,
'color' => [
'type' => Properties::EXCEL_COLOR_TYPE_STANDARD,
'value' => 'black',
'alpha' => 40,
],
'size' => [
'sx' => null,
'sy' => null,
'kx' => null,
'ky' => null,
],
'blur' => null,
'direction' => null,
'distance' => null,
'algn' => null,
'rotWithShape' => null,
];
foreach ($expectedShadow as $key => $value) {
self::assertEquals($value, $gridline->getShadowProperty($key), $key);
}
}
}

View File

@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheetTests;
use PhpOffice\PhpSpreadsheet\Comment;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\RichText\TextElement;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PHPUnit\Framework\TestCase;
@ -83,4 +84,14 @@ class CommentTest extends TestCase
$comment->setText($test);
self::assertEquals('This is a test comment', (string) $comment);
}
public function testRemoveComment(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getComment('A2')->getText()->createText('Comment to delete');
self::assertArrayHasKey('A2', $sheet->getComments());
$sheet->removeComment('A2');
self::assertEmpty($sheet->getComments());
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PHPUnit\Framework\TestCase;
class Issue2885Test extends TestCase
{
/**
* @var string
*/
private static $testbook = 'tests/data/Reader/XLSX/issue.2885.xlsx';
public function testIssue2885(): void
{
$filename = self::$testbook;
$reader = new Xlsx();
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame('[$-809]0%', $sheet->getStyle('A1')->getNumberFormat()->getFormatCode());
$finishColumns = $sheet->getHighestColumn();
$rowsCount = $sheet->getHighestRow();
$rows = $sheet->rangeToArray("A1:{$finishColumns}{$rowsCount}");
self::assertSame('8%', $rows[0][0]);
$spreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class RibbonTest extends AbstractFunctional
{
/**
* Test read/rewrite spreadsheet with ribbon data.
*/
public function testRibbon(): void
{
// The following file is downloaded, with the author's
// permission, from:
// https://www.rondebruin.nl/win/s2/win003.htm
// It is renamed, including changing its extension to zip.
$filename = 'tests/data/Reader/XLSX/ribbon.donotopen.zip';
$reader = IOFactory::createReader('Xlsx');
$spreadsheet = $reader->load($filename);
self::assertTrue($spreadsheet->hasRibbon());
$target = $spreadsheet->getRibbonXMLData('target');
self::assertSame('customUI/customUI.xml', $target);
$data = $spreadsheet->getRibbonXMLData('data');
self::assertIsString($data);
self::assertSame(1522, strlen($data));
$vbaCode = (string) $spreadsheet->getMacrosCode();
self::assertSame(13312, strlen($vbaCode));
self::assertNull($spreadsheet->getRibbonBinObjects());
self::assertNull($spreadsheet->getRibbonBinObjects('names'));
self::assertNull($spreadsheet->getRibbonBinObjects('data'));
self::assertEmpty($spreadsheet->getRibbonBinObjects('types'));
self::assertNull($spreadsheet->getRibbonBinObjects('xxxxx'));
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
$spreadsheet->disconnectWorksheets();
self::assertTrue($reloadedSpreadsheet->hasRibbon());
$ribbonData = $reloadedSpreadsheet->getRibbonXmlData();
self::assertIsArray($ribbonData);
self::assertSame($target, $ribbonData['target'] ?? '');
self::assertSame($data, $ribbonData['data'] ?? '');
self::assertSame($vbaCode, $reloadedSpreadsheet->getMacrosCode());
self::assertNull($reloadedSpreadsheet->getRibbonBinObjects());
$reloadedSpreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,154 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\CellIterator;
use PhpOffice\PhpSpreadsheet\Worksheet\ColumnIterator;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class ColumnIteratorEmptyTest extends TestCase
{
private static function getPopulatedSheet(Spreadsheet $spreadsheet): Worksheet
{
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValueExplicit('A1', 'Hello World', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('C2', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('D2', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('E2', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('E3', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('F2', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('F3', 'PHP', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('G2', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('G3', 'PHP', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('H2', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('H3', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('H4', 'PHP', DataType::TYPE_STRING);
return $sheet;
}
/**
* @dataProvider emptyColumnBasic
*/
public function testIteratorEmptyColumn(string $columnId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheet($spreadsheet);
$iterator = new ColumnIterator($sheet, 'A', 'I');
$iterator->seek($columnId);
$row = $iterator->current();
$isEmpty = $row->isEmpty();
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyColumnBasic(): array
{
return [
['A', false],
['B', true],
['C', false],
['D', false],
['E', false],
['F', false],
['G', false],
['H', false],
['I', true],
];
}
/**
* @dataProvider emptyColumnNullAsEmpty
*/
public function testIteratorEmptyColumnWithNull(string $columnId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheet($spreadsheet);
$iterator = new ColumnIterator($sheet, 'A', 'I');
$iterator->seek($columnId);
$row = $iterator->current();
$isEmpty = $row->isEmpty(CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyColumnNullAsEmpty(): array
{
return [
['A', false],
['B', true],
['C', true],
['D', false],
['E', false],
['F', false],
['G', false],
['H', false],
['I', true],
];
}
/**
* @dataProvider emptyColumnEmptyStringAsEmpty
*/
public function testIteratorEmptyColumnWithEmptyString(string $columnId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheet($spreadsheet);
$iterator = new ColumnIterator($sheet, 'A', 'I');
$iterator->seek($columnId);
$row = $iterator->current();
$isEmpty = $row->isEmpty(CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL);
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyColumnEmptyStringAsEmpty(): array
{
return [
['A', false],
['B', true],
['C', false],
['D', true],
['E', false],
['F', false],
['G', false],
['H', false],
['I', true],
];
}
/**
* @dataProvider emptyColumnNullAndEmptyStringAsEmpty
*/
public function testIteratorEmptyColumnWithNullAndEmptyString(string $columnId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheet($spreadsheet);
$iterator = new ColumnIterator($sheet, 'A', 'I');
$iterator->seek($columnId);
$row = $iterator->current();
$isEmpty = $row->isEmpty(
CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL | CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
);
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyColumnNullAndEmptyStringAsEmpty(): array
{
return [
['A', false],
['B', true],
['C', true],
['D', true],
['E', true],
['F', false],
['G', false],
['H', false],
['I', true],
];
}
}

View File

@ -0,0 +1,154 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\CellIterator;
use PhpOffice\PhpSpreadsheet\Worksheet\RowIterator;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class RowIteratorEmptyTest extends TestCase
{
private static function getPopulatedSheet(Spreadsheet $spreadsheet): Worksheet
{
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValueExplicit('A1', 'Hello World', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B3', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('B4', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B5', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('C5', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B6', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('C6', 'PHP', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B7', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('C7', 'PHP', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B8', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('C8', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('D8', 'PHP', DataType::TYPE_STRING);
return $sheet;
}
/**
* @dataProvider emptyRowBasic
*/
public function testIteratorEmptyRow(int $rowId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheet($spreadsheet);
$iterator = new RowIterator($sheet, 1, 9);
$iterator->seek($rowId);
$row = $iterator->current();
$isEmpty = $row->isEmpty();
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyRowBasic(): array
{
return [
[1, false],
[2, true],
[3, false],
[4, false],
[5, false],
[6, false],
[7, false],
[8, false],
[9, true],
];
}
/**
* @dataProvider emptyRowNullAsEmpty
*/
public function testIteratorEmptyRowWithNull(int $rowId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheet($spreadsheet);
$iterator = new RowIterator($sheet, 1, 9);
$iterator->seek($rowId);
$row = $iterator->current();
$isEmpty = $row->isEmpty(CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyRowNullAsEmpty(): array
{
return [
[1, false],
[2, true],
[3, true],
[4, false],
[5, false],
[6, false],
[7, false],
[8, false],
[9, true],
];
}
/**
* @dataProvider emptyRowEmptyStringAsEmpty
*/
public function testIteratorEmptyRowWithEmptyString(int $rowId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheet($spreadsheet);
$iterator = new RowIterator($sheet, 1, 9);
$iterator->seek($rowId);
$row = $iterator->current();
$isEmpty = $row->isEmpty(CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL);
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyRowEmptyStringAsEmpty(): array
{
return [
[1, false],
[2, true],
[3, false],
[4, true],
[5, false],
[6, false],
[7, false],
[8, false],
[9, true],
];
}
/**
* @dataProvider emptyRowNullAndEmptyStringAsEmpty
*/
public function testIteratorEmptyRowWithNullAndEmptyString(int $rowId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheet($spreadsheet);
$iterator = new RowIterator($sheet, 1, 9);
$iterator->seek($rowId);
$row = $iterator->current();
$isEmpty = $row->isEmpty(
CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL | CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL
);
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyRowNullAndEmptyStringAsEmpty(): array
{
return [
[1, false],
[2, true],
[3, true],
[4, true],
[5, true],
[6, false],
[7, false],
[8, false],
[9, true],
];
}
}

View File

@ -3,7 +3,9 @@
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use Exception;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\CellIterator;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
@ -405,4 +407,100 @@ class WorksheetTest extends TestCase
self::assertSame($expectedData, $worksheet->toArray());
self::assertSame($expectedHighestRow, $worksheet->getHighestRow());
}
private static function getPopulatedSheetForEmptyRowTest(Spreadsheet $spreadsheet): Worksheet
{
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValueExplicit('A1', 'Hello World', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B3', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('B4', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B5', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('C5', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B6', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('C6', 'PHP', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B7', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('C7', 'PHP', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('B8', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('C8', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('D8', 'PHP', DataType::TYPE_STRING);
return $sheet;
}
private static function getPopulatedSheetForEmptyColumnTest(Spreadsheet $spreadsheet): Worksheet
{
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValueExplicit('A1', 'Hello World', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('C2', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('D2', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('E2', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('E3', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('F2', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('F3', 'PHP', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('G2', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('G3', 'PHP', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('H2', null, DataType::TYPE_NULL);
$sheet->setCellValueExplicit('H3', '', DataType::TYPE_STRING);
$sheet->setCellValueExplicit('H4', 'PHP', DataType::TYPE_STRING);
return $sheet;
}
/**
* @dataProvider emptyRowProvider
*/
public function testIsEmptyRow(int $rowId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheetForEmptyRowTest($spreadsheet);
$isEmpty = $sheet->isEmptyRow($rowId, CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL | CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyRowProvider(): array
{
return [
[1, false],
[2, true],
[3, true],
[4, true],
[5, true],
[6, false],
[7, false],
[8, false],
[9, true],
];
}
/**
* @dataProvider emptyColumnProvider
*/
public function testIsEmptyColumn(string $columnId, bool $expectedEmpty): void
{
$spreadsheet = new Spreadsheet();
$sheet = self::getPopulatedSheetForEmptyColumnTest($spreadsheet);
$isEmpty = $sheet->isEmptyColumn($columnId, CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL | CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL);
self::assertSame($expectedEmpty, $isEmpty);
$spreadsheet->disconnectWorksheets();
}
public function emptyColumnProvider(): array
{
return [
['A', false],
['B', true],
['C', true],
['D', true],
['E', true],
['F', false],
['G', false],
['H', false],
['I', true],
];
}
}

Binary file not shown.

Binary file not shown.