Merge branch 'master' into Table-Reader-for-Xlsx

This commit is contained in:
aswinkumar863 2022-05-29 18:59:53 +05:30 committed by GitHub
commit e1ab240419
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 3076 additions and 816 deletions

View File

@ -9,13 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added ### Added
- Add point size option for scatter charts - 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)
Note that a ChartSheet is still only written as a normal Worksheet containing a single chart, not as an actual ChartSheet.
- Added Worksheet visibility in Ods Reader [PR #2851](https://github.com/PHPOffice/PhpSpreadsheet/pull/2851) and Gnumeric Reader [PR #2853](https://github.com/PHPOffice/PhpSpreadsheet/pull/2853)
- 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)
### Changed ### Changed
- Memory and speed improvements, particularly for the Cell Collection, and the Writers. - Memory and speed improvements, particularly for the Cell Collection, and the Writers.
See [the Discussion](https://github.com/PHPOffice/PhpSpreadsheet/discussions/2821) for details of performance See [the Discussion section on github](https://github.com/PHPOffice/PhpSpreadsheet/discussions/2821) for details of performance across versions
### Deprecated ### Deprecated
@ -28,7 +36,12 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed ### Fixed
- Xls Reader resolving absolute named ranges to relative ranges [Issue #2826](https://github.com/PHPOffice/PhpSpreadsheet/issues/2826) [PR #2827](https://github.com/PHPOffice/PhpSpreadsheet/pull/2827) - Xls Reader resolving absolute named ranges to relative ranges [Issue #2826](https://github.com/PHPOffice/PhpSpreadsheet/issues/2826) [PR #2827](https://github.com/PHPOffice/PhpSpreadsheet/pull/2827)
- Null value handling in the Excel Math/Trig PRODUCT() function [Issue #2833](https://github.com/PHPOffice/PhpSpreadsheet/issues/2833) [PR #2834](https://github.com/PHPOffice/PhpSpreadsheet/pull/2834)
- Invalid Print Area defined in Xlsx corrupts internal storage of print area [Issue #2848](https://github.com/PHPOffice/PhpSpreadsheet/issues/2848) [PR #2849](https://github.com/PHPOffice/PhpSpreadsheet/pull/2849)
- 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)
## 1.23.0 - 2022-04-24 ## 1.23.0 - 2022-04-24

View File

@ -11,6 +11,44 @@
PhpSpreadsheet is a library written in pure PHP and offers a set of classes that PhpSpreadsheet is a library written in pure PHP and offers a set of classes that
allow you to read and write various spreadsheet file formats such as Excel and LibreOffice Calc. allow you to read and write various spreadsheet file formats such as Excel and LibreOffice Calc.
## PHP version support
LTS: Support for PHP versions will only be maintained for a period of six months beyond the
[end of life of that PHP version](https://www.php.net/eol.php).
Currently the required PHP minimum version is PHP __7.3__.
See the `composer.json` for other requirements.
## Installation
Use [composer](https://getcomposer.org) to install PhpSpreadsheet into your project:
```sh
composer require phpoffice/phpspreadsheet
```
If you are building your installation on a development machine that is on a different PHP version to the server where it will be deployed, or if your PHP CLI version is not the same as your run-time such as `php-fpm` or Apache's `mod_php`, then you might want to add the following to your `composer.json` before installing:
```json lines
{
"require": {
"phpoffice/phpspreadsheet": "^1.23"
},
"config": {
"platform": {
"php": "7.3"
}
}
}
```
and then run
```sh
composer install
```
to ensure that the correct dependencies are retrieved to match your deployment environment.
See [CLI vs Application run-time](https://php.watch/articles/composer-platform-check) for more details.
## Documentation ## Documentation
Read more about it, including install instructions, in the [official documentation](https://phpspreadsheet.readthedocs.io). Or check out the [API documentation](https://phpoffice.github.io/PhpSpreadsheet). Read more about it, including install instructions, in the [official documentation](https://phpspreadsheet.readthedocs.io). Or check out the [API documentation](https://phpoffice.github.io/PhpSpreadsheet).

View File

@ -30,9 +30,14 @@ for details.
### PHP version support ### PHP version support
Support for PHP versions will only be maintained for a period of six months beyond the LTS: Support for PHP versions will only be maintained for a period of six months beyond the
[end of life of that PHP version](https://www.php.net/eol.php). [end of life of that PHP version](https://www.php.net/eol.php).
Currently the required PHP minimum version is PHP 7.3. The last PHP release was 7.3.33 on 6th December 2021, so PhpSpreadsheet will support PHP 7.3 until 6th June 2022.
PHP 7.4 is officially [End of Life](https://www.php.net/supported-versions.php) on 28th November 2022, and PhpSpreadsheet will continue to support PHP 7.4 for six months after that date.
See the `composer.json` for other requirements.
## Installation ## Installation
Use [composer](https://getcomposer.org) to install PhpSpreadsheet into your project: Use [composer](https://getcomposer.org) to install PhpSpreadsheet into your project:
@ -47,6 +52,26 @@ Or also download the documentation and samples if you plan to use them:
composer require phpoffice/phpspreadsheet --prefer-source composer require phpoffice/phpspreadsheet --prefer-source
``` ```
If you are building your installation on a development machine that is on a different PHP version to the server where it will be deployed, or if your PHP CLI version is not the same as your run-time such as `php-fpm` or Apache's `mod_php`, then you might want to add the following to your `composer.json` before installing:
```json lines
{
"require": {
"phpoffice/phpspreadsheet": "^1.23"
},
"config": {
"platform": {
"php": "7.3"
}
}
}
```
and then run
```sh
composer install
```
to ensure that the correct dependencies are retrieved to match your deployment environment.
See [CLI vs Application run-time](https://php.watch/articles/composer-platform-check) for more details.
## Hello World ## Hello World

View File

@ -16,7 +16,7 @@
<th></th> <th></th>
<th>XLS</th> <th>XLS</th>
<th>XLSX</th> <th>XLSX</th>
<th>Excel2003XML</th> <th>XML (Excel2003XML)</th>
<th>Ods</th> <th>Ods</th>
<th>Gnumeric</th> <th>Gnumeric</th>
<th>CSV</th> <th>CSV</th>
@ -732,12 +732,32 @@
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
<tr>
<td style="padding-left: 1em;">Hidden Worksheets</td>
<td style="text-align: center; color: green;"></td>
<td style="text-align: center; color: green;"></td>
<td></td>
<td style="text-align: center; color: green;"></td>
<td style="text-align: center; color: green;"></td>
<td style="text-align: center;">N/A</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr> <tr>
<td style="padding-left: 1em;">Coloured Tabs</td> <td style="padding-left: 1em;">Coloured Tabs</td>
<td></td> <td></td>
<td></td> <td></td>
<td></td> <td></td>
<td></td> <td></td>
<td></td>
<td style="text-align: center;">N/A</td> <td style="text-align: center;">N/A</td>
<td></td> <td></td>
<td></td> <td></td>

View File

@ -449,8 +449,7 @@ $spreadsheet = $reader->loadSpreadsheetFromString($data);
#### Setting CSV options #### Setting CSV options
Often, CSV files are not really "comma separated", or use semicolon (`;`) Often, CSV files are not really "comma separated", or use semicolon (`;`)
as a separator. You can instruct as a separator. You can set some options before reading a CSV
`\PhpOffice\PhpSpreadsheet\Reader\Csv` some options before reading a CSV
file. file.
The separator will be auto-detected, so in most cases it should not be necessary The separator will be auto-detected, so in most cases it should not be necessary
@ -506,6 +505,12 @@ $reader->setSheetIndex(0);
$spreadsheet = $reader->load('sample.csv'); $spreadsheet = $reader->load('sample.csv');
``` ```
The CSV reader will normally not load null strings into the spreadsheet.
To load them:
```php
$reader->setPreserveNullString(true);
```
Finally, you can set a callback to be invoked when the constructor is executed, Finally, you can set a callback to be invoked when the constructor is executed,
either through `new Csv()` or `IOFactory::load`, either through `new Csv()` or `IOFactory::load`,
and have that callback set the customizable attributes to whatever and have that callback set the customizable attributes to whatever
@ -584,8 +589,7 @@ $writer->save("05featuredemo.csv");
#### Setting CSV options #### Setting CSV options
Often, CSV files are not really "comma separated", or use semicolon (`;`) Often, CSV files are not really "comma separated", or use semicolon (`;`)
as a separator. You can instruct as a separator. You can set some options before writing a CSV
`\PhpOffice\PhpSpreadsheet\Writer\Csv` some options before writing a CSV
file: file:
```php ```php

View File

@ -125,11 +125,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Calculation/Calculation.php path: src/PhpSpreadsheet/Calculation/Calculation.php
-
message: "#^Parameter \\#3 \\$formula of static method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Calculation\\:\\:translateSeparator\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/Calculation.php
- -
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Calculation\\:\\:\\$cellStack has no type specified\\.$#" message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Calculation\\:\\:\\$cellStack has no type specified\\.$#"
count: 1 count: 1
@ -750,11 +745,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php path: src/PhpSpreadsheet/Calculation/LookupRef/Offset.php
-
message: "#^Parameter \\#1 \\$columnAddress of static method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\Coordinate\\:\\:columnIndexFromString\\(\\) expects string, string\\|null given\\.$#"
count: 3
path: src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
- -
message: "#^Binary operation \"/\" between array\\|float\\|int\\|string and array\\|float\\|int\\|string results in an error\\.$#" message: "#^Binary operation \"/\" between array\\|float\\|int\\|string and array\\|float\\|int\\|string results in an error\\.$#"
count: 2 count: 2
@ -1160,76 +1150,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Chart/Chart.php path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:getBottomRightXOffset\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:getBottomRightYOffset\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:getTopLeftXOffset\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:getTopLeftYOffset\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setBottomRightCell\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setBottomRightCell\\(\\) has parameter \\$cell with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setBottomRightXOffset\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setBottomRightXOffset\\(\\) has parameter \\$xOffset with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setBottomRightYOffset\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setBottomRightYOffset\\(\\) has parameter \\$yOffset with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setTopLeftXOffset\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setTopLeftXOffset\\(\\) has parameter \\$xOffset with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setTopLeftYOffset\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:setTopLeftYOffset\\(\\) has parameter \\$yOffset with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/Chart.php
- -
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:\\$legend \\(PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend\\|null\\.$#" message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart\\:\\:\\$legend \\(PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend\\|null\\.$#"
count: 1 count: 1
@ -1285,26 +1205,6 @@ parameters:
count: 2 count: 2
path: src/PhpSpreadsheet/Chart/DataSeries.php path: src/PhpSpreadsheet/Chart/DataSeries.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\:\\:refresh\\(\\) has parameter \\$flatten with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/DataSeriesValues.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\:\\:\\$dataSource \\(string\\) does not accept string\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/DataSeriesValues.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\:\\:\\$dataTypeValues has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/DataSeriesValues.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\:\\:\\$fillColor \\(array\\<string\\>\\|string\\) does not accept array\\<string\\>\\|string\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Chart/DataSeriesValues.php
- -
message: "#^Parameter \\#1 \\$angle of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:setShadowAngle\\(\\) expects int, int\\|null given\\.$#" message: "#^Parameter \\#1 \\$angle of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\:\\:setShadowAngle\\(\\) expects int, int\\|null given\\.$#"
count: 1 count: 1
@ -1800,11 +1700,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Helper/Html.php path: src/PhpSpreadsheet/Helper/Html.php
-
message: "#^Parameter \\#1 \\$text of method PhpOffice\\\\PhpSpreadsheet\\\\RichText\\\\ITextElement\\:\\:setText\\(\\) expects string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Helper/Html.php
- -
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Helper\\\\Html\\:\\:\\$bold has no type specified\\.$#" message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Helper\\\\Html\\:\\:\\$bold has no type specified\\.$#"
count: 1 count: 1
@ -1905,11 +1800,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Helper/Sample.php path: src/PhpSpreadsheet/Helper/Sample.php
-
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Helper/Sample.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\IOFactory\\:\\:createReader\\(\\) should return PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\IReader but returns object\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\IOFactory\\:\\:createReader\\(\\) should return PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\IReader but returns object\\.$#"
count: 1 count: 1
@ -2035,36 +1925,11 @@ parameters:
count: 6 count: 6
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$masterPrintStylesCrossReference has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$masterStylesCrossReference has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$officeNs has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
- -
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$pageLayoutStyles has no type specified\\.$#" message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$pageLayoutStyles has no type specified\\.$#"
count: 1 count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$stylesFo has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\PageSettings\\:\\:\\$stylesNs has no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods/PageSettings.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\Properties\\:\\:load\\(\\) has parameter \\$namespacesMeta with no type specified\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\\\Properties\\:\\:load\\(\\) has parameter \\$namespacesMeta with no type specified\\.$#"
count: 1 count: 1
@ -2396,7 +2261,7 @@ parameters:
path: src/PhpSpreadsheet/Reader/Xlsx.php path: src/PhpSpreadsheet/Reader/Xlsx.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:boolean\\(\\) has no return type specified\\.$#" message: "#^Comparison operation \"\\>\" between SimpleXMLElement\\|null and 0 results in an error\\.$#"
count: 1 count: 1
path: src/PhpSpreadsheet/Reader/Xlsx.php path: src/PhpSpreadsheet/Reader/Xlsx.php
@ -2470,11 +2335,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Reader/Xlsx.php path: src/PhpSpreadsheet/Reader/Xlsx.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:dirAdd\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:dirAdd\\(\\) has parameter \\$add with no type specified\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:dirAdd\\(\\) has parameter \\$add with no type specified\\.$#"
count: 1 count: 1
@ -2535,21 +2395,11 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Reader/Xlsx.php path: src/PhpSpreadsheet/Reader/Xlsx.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:stripWhiteSpaceFromStyleString\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:stripWhiteSpaceFromStyleString\\(\\) has parameter \\$string with no type specified\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:stripWhiteSpaceFromStyleString\\(\\) has parameter \\$string with no type specified\\.$#"
count: 1 count: 1
path: src/PhpSpreadsheet/Reader/Xlsx.php path: src/PhpSpreadsheet/Reader/Xlsx.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:toCSSArray\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:toCSSArray\\(\\) has parameter \\$style with no type specified\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\:\\:toCSSArray\\(\\) has parameter \\$style with no type specified\\.$#"
count: 1 count: 1
@ -2625,206 +2475,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php path: src/PhpSpreadsheet/Reader/Xlsx/BaseParserClass.php
-
message: "#^Cannot call method getFont\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\RichText\\\\Run\\|null\\.$#"
count: 12
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setBold\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setColor\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setItalic\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setName\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setSize\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setStrikethrough\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setSubscript\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setSuperscript\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Cannot call method setUnderline\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 3
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeries\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeries\\(\\) has parameter \\$chartDetail with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeries\\(\\) has parameter \\$namespacesChartMeta with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeries\\(\\) has parameter \\$plotType with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValueSet\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValueSet\\(\\) has parameter \\$marker with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValueSet\\(\\) has parameter \\$namespacesChartMeta with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValueSet\\(\\) has parameter \\$seriesDetail with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValues\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValues\\(\\) has parameter \\$dataType with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValues\\(\\) has parameter \\$seriesValueSet with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValuesMultiLevel\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValuesMultiLevel\\(\\) has parameter \\$dataType with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartDataSeriesValuesMultiLevel\\(\\) has parameter \\$seriesValueSet with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartLayoutDetails\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartLayoutDetails\\(\\) has parameter \\$chartDetail with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartLayoutDetails\\(\\) has parameter \\$namespacesChartMeta with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:chartTitle\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:parseRichText\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readChartAttributes\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readChartAttributes\\(\\) has parameter \\$chartDetail with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readColor\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readColor\\(\\) has parameter \\$background with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\Chart\\:\\:readColor\\(\\) has parameter \\$color with no type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#1 \\$position of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend constructor expects string, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#3 \\$overlay of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Legend constructor expects bool, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#3 \\$plotOrder of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeries constructor expects array\\<int\\>, array\\<int\\|string, bool\\|float\\|int\\|string\\|null\\> given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#4 \\$pointCount of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues constructor expects int, null given\\.$#"
count: 4
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#6 \\$displayBlanksAs of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Chart constructor expects string, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
-
message: "#^Parameter \\#7 \\$plotDirection of class PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeries constructor expects string\\|null, bool\\|float\\|int\\|string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Xlsx/Chart.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\ColumnAndRowAttributes\\:\\:isFilteredColumn\\(\\) has no return type specified\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Xlsx\\\\ColumnAndRowAttributes\\:\\:isFilteredColumn\\(\\) has no return type specified\\.$#"
count: 1 count: 1
@ -3035,11 +2685,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/ReferenceHelper.php path: src/PhpSpreadsheet/ReferenceHelper.php
-
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/ReferenceHelper.php
- -
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\RichText\\\\Run\\:\\:\\$font \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#" message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\RichText\\\\Run\\:\\:\\$font \\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1 count: 1
@ -4035,26 +3680,11 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
message: "#^Parameter \\#2 \\$subject of function preg_split expects string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
-
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Style/NumberFormat/Formatter.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\PercentageFormatter\\:\\:format\\(\\) has parameter \\$value with no type specified\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\PercentageFormatter\\:\\:format\\(\\) has parameter \\$value with no type specified\\.$#"
count: 1 count: 1
path: src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php path: src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
-
message: "#^Parameter \\#1 \\$format of function sprintf expects string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
- -
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\CellIterator\\:\\:adjustForExistingOnlyRange\\(\\) has no return type specified\\.$#" message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\CellIterator\\:\\:adjustForExistingOnlyRange\\(\\) has no return type specified\\.$#"
count: 1 count: 1
@ -4490,11 +4120,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Writer/Html.php path: src/PhpSpreadsheet/Writer/Html.php
-
message: "#^Parameter \\#1 \\$string of function htmlspecialchars expects string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Html.php
- -
message: "#^Parameter \\#1 \\$vAlign of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Html\\:\\:mapVAlign\\(\\) expects string, string\\|null given\\.$#" message: "#^Parameter \\#1 \\$vAlign of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Html\\:\\:mapVAlign\\(\\) expects string, string\\|null given\\.$#"
count: 1 count: 1
@ -4690,16 +4315,6 @@ parameters:
count: 7 count: 7
path: src/PhpSpreadsheet/Writer/Xls/Parser.php path: src/PhpSpreadsheet/Writer/Xls/Parser.php
-
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xls/Parser.php
-
message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xls/Parser.php
- -
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Parser\\:\\:\\$spreadsheet has no type specified\\.$#" message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Parser\\:\\:\\$spreadsheet has no type specified\\.$#"
count: 1 count: 1
@ -4800,21 +4415,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|null given\\.$#"
count: 2
path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
message: "#^Parameter \\#2 \\$subject of function preg_match_all expects string, string\\|null given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
-
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#"
count: 2
path: src/PhpSpreadsheet/Writer/Xls/Worksheet.php
- -
message: "#^Parameter \\#4 \\$isError of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:writeBoolErr\\(\\) expects bool, int given\\.$#" message: "#^Parameter \\#4 \\$isError of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xls\\\\Worksheet\\:\\:writeBoolErr\\(\\) expects bool, int given\\.$#"
count: 3 count: 3
@ -4947,7 +4547,7 @@ parameters:
- -
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int given\\.$#" message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int given\\.$#"
count: 44 count: 42
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
- -
@ -4955,26 +4555,6 @@ parameters:
count: 2 count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#3 \\$id1 of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeCategoryAxis\\(\\) expects string, int\\|string given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#4 \\$id1 of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeValueAxis\\(\\) expects string, int\\|string given\\.$#"
count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#4 \\$id2 of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeCategoryAxis\\(\\) expects string, int\\|string given\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php
-
message: "#^Parameter \\#5 \\$id2 of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeValueAxis\\(\\) expects string, int\\|string 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\\.$#" 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 count: 1
@ -5062,7 +4642,7 @@ parameters:
- -
message: "#^Cannot call method getBold\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#" message: "#^Cannot call method getBold\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2 count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
- -
@ -5072,12 +4652,12 @@ parameters:
- -
message: "#^Cannot call method getItalic\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#" message: "#^Cannot call method getItalic\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2 count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
- -
message: "#^Cannot call method getName\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#" message: "#^Cannot call method getName\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2 count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
- -
@ -5087,7 +4667,7 @@ parameters:
- -
message: "#^Cannot call method getStrikethrough\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#" message: "#^Cannot call method getStrikethrough\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2 count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
- -
@ -5102,7 +4682,7 @@ parameters:
- -
message: "#^Cannot call method getUnderline\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#" message: "#^Cannot call method getUnderline\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 2 count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
- -
@ -5137,7 +4717,7 @@ parameters:
- -
message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, string\\|null given\\.$#" message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, string\\|null given\\.$#"
count: 5 count: 4
path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php path: src/PhpSpreadsheet/Writer/Xlsx/StringTable.php
- -
@ -5285,8 +4865,3 @@ parameters:
count: 2 count: 2
path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php path: src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Xlfn\\:\\:addXlfn\\(\\) should return string but returns string\\|null\\.$#"
count: 1
path: src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php

View File

@ -0,0 +1,124 @@
<?php
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../Header.php';
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray(
[
['Number of Products', 'Sales in USD', 'Market share'],
[14, 12200, 15],
[20, 60000, 33],
[18, 24400, 10],
[22, 32000, 42],
[],
[12, 8200, 18],
[15, 50000, 30],
[19, 22400, 15],
[25, 25000, 50],
]
);
// 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, null, null, 1, ['2013']), // 2013
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, null, null, 1, ['2014']), // 2014
];
// Set the X-Axis values
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$dataSeriesCategories = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', null, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$7:$A$10', null, 4),
];
// Set the Y-Axis values
// 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!$B$7:$B$10', null, 4),
];
// Set the Z-Axis values (bubble size)
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$dataSeriesBubbles = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$7:$C$10', null, 4),
];
// Build the dataseries
$series = new DataSeries(
DataSeries::TYPE_BUBBLECHART, // plotType
null, // plotGrouping
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$dataSeriesCategories, // plotCategory
$dataSeriesValues // plotValues
);
$series->setPlotBubbleSizes($dataSeriesBubbles);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
// Create the chart
$chart = new Chart(
'chart1', // name
null, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
null // yAxisLabel
);
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('E1');
$chart->setBottomRightPosition('M15');
// Add the chart to the worksheet
$worksheet->addChart($chart);
$worksheet->getColumnDimension('A')->setAutoSize(true);
$worksheet->getColumnDimension('B')->setAutoSize(true);
$worksheet->getColumnDimension('C')->setAutoSize(true);
// Save Excel 2007 file
$filename = $helper->getFilename(__FILE__);
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->setIncludeCharts(true);
$callStartTime = microtime(true);
$writer->save($filename);
$helper->logWrite($writer, $filename, $callStartTime);

View File

@ -0,0 +1,121 @@
<?php
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\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
require __DIR__ . '/../Header.php';
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// changed data to simulate a trend chart - Xaxis are dates; Yaxis are 3 meausurements from each date
$worksheet->fromArray(
[
['', 'metric1', 'metric2', 'metric3'],
['=DATEVALUE("2021-01-01")', 12.1, 15.1, 21.1],
['=DATEVALUE("2021-01-04")', 56.2, 73.2, 86.2],
['=DATEVALUE("2021-01-07")', 52.2, 61.2, 69.2],
['=DATEVALUE("2021-01-10")', 30.2, 32.2, 0.2],
]
);
$worksheet->getStyle('A2:A5')->getNumberFormat()->setFormatCode(Properties::FORMAT_CODE_DATE_ISO8601);
$worksheet->getColumnDimension('A')->setAutoSize(true);
$worksheet->setSelectedCells('A1');
// 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), // was 2010
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // was 2011
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // was 2012
];
// Set the X-Axis Labels
// changed from STRING to NUMBER
// added 2 additional x-axis values associated with each of the 3 metrics
// added FORMATE_CODE_NUMBER
$xAxisTickValues = [
//new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', Properties::FORMAT_CODE_DATE, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', Properties::FORMAT_CODE_DATE, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', Properties::FORMAT_CODE_DATE, 4),
];
// 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
// added FORMAT_CODE_NUMBER
$dataSeriesValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', Properties::FORMAT_CODE_NUMBER, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', Properties::FORMAT_CODE_NUMBER, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', Properties::FORMAT_CODE_NUMBER, 4),
];
// Added so that Xaxis shows dates instead of Excel-equivalent-year1900-numbers
$xAxis = new Axis();
//$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE );
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601, true);
// Build the dataseries
$series = new DataSeries(
DataSeries::TYPE_SCATTERCHART, // plotType
null, // plotGrouping (Scatter charts don't have any grouping)
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues, // plotValues
null, // plotDirection
false, // smooth line
//DataSeries::STYLE_LINEMARKER // plotStyle
DataSeries::STYLE_MARKER // plotStyle
);
// 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 Scatter Trend 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
// added xAxis for correct date display
$xAxis, // xAxis
);
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
$chart->setBottomRightPosition('P20');
// Add the chart to the worksheet
$worksheet->addChart($chart);
// Save Excel 2007 file
$filename = $helper->getFilename(__FILE__);
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->setIncludeCharts(true);
$callStartTime = microtime(true);
$writer->save($filename);
$spreadsheet->disconnectWorksheets();
$helper->logWrite($writer, $filename, $callStartTime);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3197,7 +3197,7 @@ class Calculation
string $toSeparator string $toSeparator
): string { ): string {
// Function Names // Function Names
$formula = preg_replace($from, $to, $formula); $formula = (string) preg_replace($from, $to, $formula);
// Temporarily adjust matrix separators so that they won't be confused with function arguments // Temporarily adjust matrix separators so that they won't be confused with function arguments
$formula = self::translateSeparator(';', '|', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE); $formula = self::translateSeparator(';', '|', $formula, $inMatrixBracesLevel, self::FORMULA_OPEN_MATRIX_BRACE, self::FORMULA_CLOSE_MATRIX_BRACE);
@ -4180,7 +4180,7 @@ class Calculation
$length = strlen($val); $length = strlen($val);
if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) { if (preg_match('/^' . self::CALCULATION_REGEXP_FUNCTION . '$/miu', $val, $matches)) {
$val = preg_replace('/\s/u', '', $val); $val = (string) preg_replace('/\s/u', '', $val);
if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function if (isset(self::$phpSpreadsheetFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) { // it's a function
$valToUpper = strtoupper($val); $valToUpper = strtoupper($val);
} else { } else {

View File

@ -49,7 +49,7 @@ class DateValue
$baseYear = SharedDateHelper::getExcelCalendar(); $baseYear = SharedDateHelper::getExcelCalendar();
$dateValue = trim($dateValue ?? '', '"'); $dateValue = trim($dateValue ?? '', '"');
// Strip any ordinals because they're allowed in Excel (English only) // Strip any ordinals because they're allowed in Excel (English only)
$dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue) ?? ''; $dateValue = (string) preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
// Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany) // Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
$dateValue = str_replace(['/', '.', '-', ' '], ' ', $dateValue); $dateValue = str_replace(['/', '.', '-', ' '], ' ', $dateValue);

View File

@ -159,7 +159,7 @@ class Functions
} elseif (!is_numeric($condition)) { } elseif (!is_numeric($condition)) {
if ($condition !== '""') { // Not an empty string if ($condition !== '""') { // Not an empty string
// Escape any quotes in the string value // Escape any quotes in the string value
$condition = preg_replace('/"/ui', '""', $condition); $condition = (string) preg_replace('/"/ui', '""', $condition);
} }
$condition = Calculation::wrapResult(strtoupper($condition)); $condition = Calculation::wrapResult(strtoupper($condition));
} }

View File

@ -43,7 +43,7 @@ class Helpers
if ($namedRange !== null) { if ($namedRange !== null) {
$workSheet = $namedRange->getWorkSheet(); $workSheet = $namedRange->getWorkSheet();
$sheetTitle = ($workSheet === null) ? '' : $workSheet->getTitle(); $sheetTitle = ($workSheet === null) ? '' : $workSheet->getTitle();
$value = preg_replace('/^=/', '', $namedRange->getValue()); $value = (string) preg_replace('/^=/', '', $namedRange->getValue());
self::adjustSheetTitle($sheetTitle, $value); self::adjustSheetTitle($sheetTitle, $value);
$cellAddress1 = $sheetTitle . $value; $cellAddress1 = $sheetTitle . $value;
$cellAddress = $cellAddress1; $cellAddress = $cellAddress1;

View File

@ -50,7 +50,7 @@ class RowColumnInformation
if (is_array($cellAddress)) { if (is_array($cellAddress)) {
foreach ($cellAddress as $columnKey => $value) { foreach ($cellAddress as $columnKey => $value) {
$columnKey = preg_replace('/[^a-z]/i', '', $columnKey); $columnKey = (string) preg_replace('/[^a-z]/i', '', $columnKey);
return (int) Coordinate::columnIndexFromString($columnKey); return (int) Coordinate::columnIndexFromString($columnKey);
} }
@ -66,8 +66,8 @@ class RowColumnInformation
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true); [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) { if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress); [$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/[^a-z]/i', '', $startAddress); $startAddress = (string) preg_replace('/[^a-z]/i', '', $startAddress);
$endAddress = preg_replace('/[^a-z]/i', '', $endAddress); $endAddress = (string) preg_replace('/[^a-z]/i', '', $endAddress);
return range( return range(
(int) Coordinate::columnIndexFromString($startAddress), (int) Coordinate::columnIndexFromString($startAddress),
@ -75,7 +75,7 @@ class RowColumnInformation
); );
} }
$cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress); $cellAddress = (string) preg_replace('/[^a-z]/i', '', $cellAddress);
return (int) Coordinate::columnIndexFromString($cellAddress); return (int) Coordinate::columnIndexFromString($cellAddress);
} }
@ -159,14 +159,13 @@ class RowColumnInformation
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true); [, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) { if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress); [$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/\D/', '', $startAddress); $startAddress = (string) preg_replace('/\D/', '', $startAddress);
$endAddress = preg_replace('/\D/', '', $endAddress); $endAddress = (string) preg_replace('/\D/', '', $endAddress);
return array_map( return array_map(
function ($value) { function ($value) {
return [$value]; return [$value];
}, },
// @phpstan-ignore-next-line
range($startAddress, $endAddress) range($startAddress, $endAddress)
); );
} }

View File

@ -102,29 +102,27 @@ class Operations
*/ */
public static function product(...$args) public static function product(...$args)
{ {
$args = array_filter(
Functions::flattenArray($args),
function ($value) {
return $value !== null;
}
);
// Return value // Return value
$returnValue = null; $returnValue = (count($args) === 0) ? 0.0 : 1.0;
// Loop through arguments // Loop through arguments
foreach (Functions::flattenArray($args) as $arg) { foreach ($args as $arg) {
// Is it a numeric value? // Is it a numeric value?
if (is_numeric($arg)) { if (is_numeric($arg)) {
if ($returnValue === null) {
$returnValue = $arg;
} else {
$returnValue *= $arg; $returnValue *= $arg;
}
} else { } else {
return ExcelError::VALUE(); return ExcelError::VALUE();
} }
} }
// Return return (float) $returnValue;
if ($returnValue === null) {
return 0;
}
return $returnValue;
} }
/** /**

View File

@ -14,7 +14,7 @@ class Trim
* @param mixed $stringValue String Value to check * @param mixed $stringValue String Value to check
* Or can be an array of values * Or can be an array of values
* *
* @return null|array|string * @return array|string
* If an array of values is passed as the argument, then the returned result will also be an array * If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions * with the same dimensions
*/ */
@ -26,7 +26,7 @@ class Trim
$stringValue = Helpers::extractString($stringValue); $stringValue = Helpers::extractString($stringValue);
return preg_replace('/[\\x00-\\x1f]/', '', "$stringValue"); return (string) preg_replace('/[\\x00-\\x1f]/', '', "$stringValue");
} }
/** /**

View File

@ -18,6 +18,7 @@ class Axis extends Properties
private $axisNumber = [ private $axisNumber = [
'format' => self::FORMAT_CODE_GENERAL, 'format' => self::FORMAT_CODE_GENERAL,
'source_linked' => 1, 'source_linked' => 1,
'numeric' => null,
]; ];
/** /**
@ -131,15 +132,26 @@ class Axis extends Properties
'size' => null, 'size' => null,
]; ];
private const NUMERIC_FORMAT = [
Properties::FORMAT_CODE_NUMBER,
Properties::FORMAT_CODE_DATE,
];
/** /**
* Get Series Data Type. * Get Series Data Type.
* *
* @param mixed $format_code * @param mixed $format_code
*/ */
public function setAxisNumberProperties($format_code): void public function setAxisNumberProperties($format_code, ?bool $numeric = null): void
{ {
$this->axisNumber['format'] = (string) $format_code; $format = (string) $format_code;
$this->axisNumber['format'] = $format;
$this->axisNumber['source_linked'] = 0; $this->axisNumber['source_linked'] = 0;
if (is_bool($numeric)) {
$this->axisNumber['numeric'] = $numeric;
} elseif (in_array($format, self::NUMERIC_FORMAT, true)) {
$this->axisNumber['numeric'] = true;
}
} }
/** /**
@ -162,6 +174,11 @@ class Axis extends Properties
return (string) $this->axisNumber['source_linked']; return (string) $this->axisNumber['source_linked'];
} }
public function getAxisIsNumericFormat(): bool
{
return (bool) $this->axisNumber['numeric'];
}
/** /**
* Set Axis Options Properties. * Set Axis Options Properties.
* *

View File

@ -124,7 +124,7 @@ class Chart
* *
* @var string * @var string
*/ */
private $bottomRightCellRef = 'A1'; private $bottomRightCellRef = '';
/** /**
* Bottom-Right X-Offset. * Bottom-Right X-Offset.
@ -140,6 +140,21 @@ class Chart
*/ */
private $bottomRightYOffset = 10; private $bottomRightYOffset = 10;
/** @var ?int */
private $rotX;
/** @var ?int */
private $rotY;
/** @var ?int */
private $rAngAx;
/** @var ?int */
private $perspective;
/** @var bool */
private $oneCellAnchor = false;
/** /**
* Create a new Chart. * Create a new Chart.
* *
@ -351,8 +366,9 @@ class Chart
if ($this->yAxis !== null) { if ($this->yAxis !== null) {
return $this->yAxis; return $this->yAxis;
} }
$this->yAxis = new Axis();
return new Axis(); return $this->yAxis;
} }
/** /**
@ -365,8 +381,9 @@ class Chart
if ($this->xAxis !== null) { if ($this->xAxis !== null) {
return $this->xAxis; return $this->xAxis;
} }
$this->xAxis = new Axis();
return new Axis(); return $this->xAxis;
} }
/** /**
@ -400,15 +417,15 @@ class Chart
/** /**
* Set the Top Left position for the chart. * Set the Top Left position for the chart.
* *
* @param string $cell * @param string $cellAddress
* @param int $xOffset * @param int $xOffset
* @param int $yOffset * @param int $yOffset
* *
* @return $this * @return $this
*/ */
public function setTopLeftPosition($cell, $xOffset = null, $yOffset = null) public function setTopLeftPosition($cellAddress, $xOffset = null, $yOffset = null)
{ {
$this->topLeftCellRef = $cell; $this->topLeftCellRef = $cellAddress;
if ($xOffset !== null) { if ($xOffset !== null) {
$this->setTopLeftXOffset($xOffset); $this->setTopLeftXOffset($xOffset);
} }
@ -446,13 +463,13 @@ class Chart
/** /**
* Set the Top Left cell position for the chart. * Set the Top Left cell position for the chart.
* *
* @param string $cell * @param string $cellAddress
* *
* @return $this * @return $this
*/ */
public function setTopLeftCell($cell) public function setTopLeftCell($cellAddress)
{ {
$this->topLeftCellRef = $cell; $this->topLeftCellRef = $cellAddress;
return $this; return $this;
} }
@ -491,6 +508,11 @@ class Chart
]; ];
} }
/**
* @param int $xOffset
*
* @return $this
*/
public function setTopLeftXOffset($xOffset) public function setTopLeftXOffset($xOffset)
{ {
$this->topLeftXOffset = $xOffset; $this->topLeftXOffset = $xOffset;
@ -498,11 +520,16 @@ class Chart
return $this; return $this;
} }
public function getTopLeftXOffset() public function getTopLeftXOffset(): int
{ {
return $this->topLeftXOffset; return $this->topLeftXOffset;
} }
/**
* @param int $yOffset
*
* @return $this
*/
public function setTopLeftYOffset($yOffset) public function setTopLeftYOffset($yOffset)
{ {
$this->topLeftYOffset = $yOffset; $this->topLeftYOffset = $yOffset;
@ -510,7 +537,7 @@ class Chart
return $this; return $this;
} }
public function getTopLeftYOffset() public function getTopLeftYOffset(): int
{ {
return $this->topLeftYOffset; return $this->topLeftYOffset;
} }
@ -518,15 +545,15 @@ class Chart
/** /**
* Set the Bottom Right position of the chart. * Set the Bottom Right position of the chart.
* *
* @param string $cell * @param string $cellAddress
* @param int $xOffset * @param int $xOffset
* @param int $yOffset * @param int $yOffset
* *
* @return $this * @return $this
*/ */
public function setBottomRightPosition($cell, $xOffset = null, $yOffset = null) public function setBottomRightPosition($cellAddress = '', $xOffset = null, $yOffset = null)
{ {
$this->bottomRightCellRef = $cell; $this->bottomRightCellRef = $cellAddress;
if ($xOffset !== null) { if ($xOffset !== null) {
$this->setBottomRightXOffset($xOffset); $this->setBottomRightXOffset($xOffset);
} }
@ -551,19 +578,22 @@ class Chart
]; ];
} }
public function setBottomRightCell($cell) /**
* Set the Bottom Right cell for the chart.
*
* @return $this
*/
public function setBottomRightCell(string $cellAddress = '')
{ {
$this->bottomRightCellRef = $cell; $this->bottomRightCellRef = $cellAddress;
return $this; return $this;
} }
/** /**
* Get the cell address where the bottom right of the chart is fixed. * Get the cell address where the bottom right of the chart is fixed.
*
* @return string
*/ */
public function getBottomRightCell() public function getBottomRightCell(): string
{ {
return $this->bottomRightCellRef; return $this->bottomRightCellRef;
} }
@ -602,6 +632,11 @@ class Chart
]; ];
} }
/**
* @param int $xOffset
*
* @return $this
*/
public function setBottomRightXOffset($xOffset) public function setBottomRightXOffset($xOffset)
{ {
$this->bottomRightXOffset = $xOffset; $this->bottomRightXOffset = $xOffset;
@ -609,11 +644,16 @@ class Chart
return $this; return $this;
} }
public function getBottomRightXOffset() public function getBottomRightXOffset(): int
{ {
return $this->bottomRightXOffset; return $this->bottomRightXOffset;
} }
/**
* @param int $yOffset
*
* @return $this
*/
public function setBottomRightYOffset($yOffset) public function setBottomRightYOffset($yOffset)
{ {
$this->bottomRightYOffset = $yOffset; $this->bottomRightYOffset = $yOffset;
@ -621,7 +661,7 @@ class Chart
return $this; return $this;
} }
public function getBottomRightYOffset() public function getBottomRightYOffset(): int
{ {
return $this->bottomRightYOffset; return $this->bottomRightYOffset;
} }
@ -658,4 +698,64 @@ class Chart
return $renderer->render($outputDestination); return $renderer->render($outputDestination);
} }
public function getRotX(): ?int
{
return $this->rotX;
}
public function setRotX(?int $rotX): self
{
$this->rotX = $rotX;
return $this;
}
public function getRotY(): ?int
{
return $this->rotY;
}
public function setRotY(?int $rotY): self
{
$this->rotY = $rotY;
return $this;
}
public function getRAngAx(): ?int
{
return $this->rAngAx;
}
public function setRAngAx(?int $rAngAx): self
{
$this->rAngAx = $rAngAx;
return $this;
}
public function getPerspective(): ?int
{
return $this->perspective;
}
public function setPerspective(?int $perspective): self
{
$this->perspective = $perspective;
return $this;
}
public function getOneCellAnchor(): bool
{
return $this->oneCellAnchor;
}
public function setOneCellAnchor(bool $oneCellAnchor): self
{
$this->oneCellAnchor = $oneCellAnchor;
return $this;
}
} }

View File

@ -107,6 +107,13 @@ class DataSeries
*/ */
private $plotValues = []; private $plotValues = [];
/**
* Plot Bubble Sizes.
*
* @var DataSeriesValues[]
*/
private $plotBubbleSizes = [];
/** /**
* Create a new DataSeries. * Create a new DataSeries.
* *
@ -339,6 +346,28 @@ class DataSeries
return false; return false;
} }
/**
* Get Plot Bubble Sizes.
*
* @return DataSeriesValues[]
*/
public function getPlotBubbleSizes(): array
{
return $this->plotBubbleSizes;
}
/**
* Set Plot Bubble Sizes.
*
* @param DataSeriesValues[] $plotBubbleSizes
*/
public function setPlotBubbleSizes(array $plotBubbleSizes): self
{
$this->plotBubbleSizes = $plotBubbleSizes;
return $this;
}
/** /**
* Get Number of Plot Series. * Get Number of Plot Series.
* *

View File

@ -12,7 +12,7 @@ class DataSeriesValues
const DATASERIES_TYPE_STRING = 'String'; const DATASERIES_TYPE_STRING = 'String';
const DATASERIES_TYPE_NUMBER = 'Number'; const DATASERIES_TYPE_NUMBER = 'Number';
private static $dataTypeValues = [ private const DATA_TYPE_VALUES = [
self::DATASERIES_TYPE_STRING, self::DATASERIES_TYPE_STRING,
self::DATASERIES_TYPE_NUMBER, self::DATASERIES_TYPE_NUMBER,
]; ];
@ -27,7 +27,7 @@ class DataSeriesValues
/** /**
* Series Data Source. * Series Data Source.
* *
* @var string * @var ?string
*/ */
private $dataSource; private $dataSource;
@ -69,10 +69,13 @@ class DataSeriesValues
/** /**
* Fill color (can be array with colors if dataseries have custom colors). * Fill color (can be array with colors if dataseries have custom colors).
* *
* @var string|string[] * @var null|string|string[]
*/ */
private $fillColor; private $fillColor;
/** @var string */
private $schemeClr = '';
/** /**
* Line Width. * Line Width.
* *
@ -80,6 +83,12 @@ class DataSeriesValues
*/ */
private $lineWidth = 12700; private $lineWidth = 12700;
/** @var bool */
private $scatterLines = true;
/** @var bool */
private $bubble3D = false;
/** /**
* Create a new DataSeriesValues object. * Create a new DataSeriesValues object.
* *
@ -90,8 +99,9 @@ class DataSeriesValues
* @param mixed $dataValues * @param mixed $dataValues
* @param null|mixed $marker * @param null|mixed $marker
* @param null|string|string[] $fillColor * @param null|string|string[] $fillColor
* @param string $pointSize
*/ */
public function __construct($dataType = self::DATASERIES_TYPE_NUMBER, $dataSource = null, $formatCode = null, $pointCount = 0, $dataValues = [], $marker = null, $fillColor = null) public function __construct($dataType = self::DATASERIES_TYPE_NUMBER, $dataSource = null, $formatCode = null, $pointCount = 0, $dataValues = [], $marker = null, $fillColor = null, $pointSize = '3')
{ {
$this->setDataType($dataType); $this->setDataType($dataType);
$this->dataSource = $dataSource; $this->dataSource = $dataSource;
@ -100,6 +110,9 @@ class DataSeriesValues
$this->dataValues = $dataValues; $this->dataValues = $dataValues;
$this->pointMarker = $marker; $this->pointMarker = $marker;
$this->fillColor = $fillColor; $this->fillColor = $fillColor;
if (is_numeric($pointSize)) {
$this->pointSize = (int) $pointSize;
}
} }
/** /**
@ -126,7 +139,7 @@ class DataSeriesValues
*/ */
public function setDataType($dataType) public function setDataType($dataType)
{ {
if (!in_array($dataType, self::$dataTypeValues)) { if (!in_array($dataType, self::DATA_TYPE_VALUES)) {
throw new Exception('Invalid datatype for chart data series values'); throw new Exception('Invalid datatype for chart data series values');
} }
$this->dataType = $dataType; $this->dataType = $dataType;
@ -137,7 +150,7 @@ class DataSeriesValues
/** /**
* Get Series Data Source (formula). * Get Series Data Source (formula).
* *
* @return string * @return ?string
*/ */
public function getDataSource() public function getDataSource()
{ {
@ -147,7 +160,7 @@ class DataSeriesValues
/** /**
* Set Series Data Source (formula). * Set Series Data Source (formula).
* *
* @param string $dataSource * @param ?string $dataSource
* *
* @return $this * @return $this
*/ */
@ -239,7 +252,7 @@ class DataSeriesValues
/** /**
* Get fill color. * Get fill color.
* *
* @return string|string[] HEX color or array with HEX colors * @return null|string|string[] HEX color or array with HEX colors
*/ */
public function getFillColor() public function getFillColor()
{ {
@ -249,7 +262,7 @@ class DataSeriesValues
/** /**
* Set fill color for series. * Set fill color for series.
* *
* @param string|string[] $color HEX color or array with HEX colors * @param null|string|string[] $color HEX color or array with HEX colors
* *
* @return DataSeriesValues * @return DataSeriesValues
*/ */
@ -260,7 +273,7 @@ class DataSeriesValues
$this->validateColor($colorValue); $this->validateColor($colorValue);
} }
} else { } else {
$this->validateColor($color); $this->validateColor("$color");
} }
$this->fillColor = $color; $this->fillColor = $color;
@ -315,7 +328,7 @@ class DataSeriesValues
*/ */
public function isMultiLevelSeries() public function isMultiLevelSeries()
{ {
if (count($this->dataValues) > 0) { if (!empty($this->dataValues)) {
return is_array(array_values($this->dataValues)[0]); return is_array(array_values($this->dataValues)[0]);
} }
@ -379,7 +392,7 @@ class DataSeriesValues
return $this; return $this;
} }
public function refresh(Worksheet $worksheet, $flatten = true): void public function refresh(Worksheet $worksheet, bool $flatten = true): void
{ {
if ($this->dataSource !== null) { if ($this->dataSource !== null) {
$calcEngine = Calculation::getInstance($worksheet->getParent()); $calcEngine = Calculation::getInstance($worksheet->getParent());
@ -421,4 +434,40 @@ class DataSeriesValues
$this->pointCount = count($this->dataValues); $this->pointCount = count($this->dataValues);
} }
} }
public function getScatterLines(): bool
{
return $this->scatterLines;
}
public function setScatterLines(bool $scatterLines): self
{
$this->scatterLines = $scatterLines;
return $this;
}
public function getBubble3D(): bool
{
return $this->bubble3D;
}
public function setBubble3D(bool $bubble3D): self
{
$this->bubble3D = $bubble3D;
return $this;
}
public function getSchemeClr(): string
{
return $this->schemeClr;
}
public function setSchemeClr(string $schemeClr): self
{
$this->schemeClr = $schemeClr;
return $this;
}
} }

View File

@ -37,6 +37,7 @@ abstract class Properties
const FORMAT_CODE_CURRENCY = '$#,##0.00'; const FORMAT_CODE_CURRENCY = '$#,##0.00';
const FORMAT_CODE_ACCOUNTING = '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)'; const FORMAT_CODE_ACCOUNTING = '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)';
const FORMAT_CODE_DATE = 'm/d/yyyy'; const FORMAT_CODE_DATE = 'm/d/yyyy';
const FORMAT_CODE_DATE_ISO8601 = 'yyyy-mm-dd';
const FORMAT_CODE_TIME = '[$-F400]h:mm:ss AM/PM'; const FORMAT_CODE_TIME = '[$-F400]h:mm:ss AM/PM';
const FORMAT_CODE_PERCENTAGE = '0.00%'; const FORMAT_CODE_PERCENTAGE = '0.00%';
const FORMAT_CODE_FRACTION = '# ?/?'; const FORMAT_CODE_FRACTION = '# ?/?';

View File

@ -171,9 +171,9 @@ class Properties
if (is_numeric($timestamp)) { if (is_numeric($timestamp)) {
$timestamp = (float) $timestamp; $timestamp = (float) $timestamp;
} else { } else {
$timestamp = preg_replace('/[.][0-9]*$/', '', $timestamp) ?? ''; $timestamp = (string) preg_replace('/[.][0-9]*$/', '', $timestamp);
$timestamp = preg_replace('/^(\\d{4})- (\\d)/', '$1-0$2', $timestamp) ?? ''; $timestamp = (string) preg_replace('/^(\\d{4})- (\\d)/', '$1-0$2', $timestamp);
$timestamp = preg_replace('/^(\\d{4}-\\d{2})- (\\d)/', '$1-0$2', $timestamp) ?? ''; $timestamp = (string) preg_replace('/^(\\d{4}-\\d{2})- (\\d)/', '$1-0$2', $timestamp);
$timestamp = (float) (new DateTime($timestamp))->format('U'); $timestamp = (float) (new DateTime($timestamp))->format('U');
} }
} }

View File

@ -642,7 +642,7 @@ class Html
$text = ltrim($text); $text = ltrim($text);
} }
// Trim any spaces immediately after a line break // Trim any spaces immediately after a line break
$text = preg_replace('/\n */mu', "\n", $text); $text = (string) preg_replace('/\n */mu', "\n", $text);
$element->setText($text); $element->setText($text);
} }
} }
@ -792,7 +792,7 @@ class Html
protected function parseTextNode(DOMText $textNode): void protected function parseTextNode(DOMText $textNode): void
{ {
$domText = preg_replace( $domText = (string) preg_replace(
'/\s+/u', '/\s+/u',
' ', ' ',
str_replace(["\r", "\n"], ' ', $textNode->nodeValue ?? '') str_replace(["\r", "\n"], ' ', $textNode->nodeValue ?? '')

View File

@ -85,7 +85,7 @@ class Sample
$file = str_replace(str_replace('\\', '/', $baseDir) . '/', '', str_replace('\\', '/', $file[0])); $file = str_replace(str_replace('\\', '/', $baseDir) . '/', '', str_replace('\\', '/', $file[0]));
$info = pathinfo($file); $info = pathinfo($file);
$category = str_replace('_', ' ', $info['dirname']); $category = str_replace('_', ' ', $info['dirname']);
$name = str_replace('_', ' ', preg_replace('/(|\.php)/', '', $info['filename'])); $name = str_replace('_', ' ', (string) preg_replace('/(|\.php)/', '', $info['filename']));
if (!in_array($category, ['.', 'boostrap', 'templates'])) { if (!in_array($category, ['.', 'boostrap', 'templates'])) {
if (!isset($files[$category])) { if (!isset($files[$category])) {
$files[$category] = []; $files[$category] = [];

View File

@ -103,6 +103,9 @@ class Csv extends BaseReader
*/ */
protected $preserveNumericFormatting = false; protected $preserveNumericFormatting = false;
/** @var bool */
private $preserveNullString = false;
/** /**
* Create a new CSV Reader instance. * Create a new CSV Reader instance.
*/ */
@ -300,9 +303,11 @@ class Csv extends BaseReader
} }
} }
public function setTestAutoDetect(bool $value): void public function setTestAutoDetect(bool $value): self
{ {
$this->testAutodetect = $value; $this->testAutodetect = $value;
return $this;
} }
private function setAutoDetect(?string $value): ?string private function setAutoDetect(?string $value): ?string
@ -390,7 +395,7 @@ class Csv extends BaseReader
foreach ($rowData as $rowDatum) { foreach ($rowData as $rowDatum) {
$this->convertBoolean($rowDatum, $preserveBooleanString); $this->convertBoolean($rowDatum, $preserveBooleanString);
$numberFormatMask = $this->convertFormattedNumber($rowDatum); $numberFormatMask = $this->convertFormattedNumber($rowDatum);
if ($rowDatum !== '' && $this->readFilter->readCell($columnLetter, $currentRow)) { if (($rowDatum !== '' || $this->preserveNullString) && $this->readFilter->readCell($columnLetter, $currentRow)) {
if ($this->contiguous) { if ($this->contiguous) {
if ($noOutputYet) { if ($noOutputYet) {
$noOutputYet = false; $noOutputYet = false;
@ -625,4 +630,16 @@ class Csv extends BaseReader
return ($encoding === '') ? $dflt : $encoding; return ($encoding === '') ? $dflt : $encoding;
} }
public function setPreserveNullString(bool $value): self
{
$this->preserveNullString = $value;
return $this;
}
public function getPreserveNullString(): bool
{
return $this->preserveNullString;
}
} }

View File

@ -140,12 +140,12 @@ class Delimiter
$line = $line . $newLine; $line = $line . $newLine;
// Drop everything that is enclosed to avoid counting false positives in enclosures // Drop everything that is enclosed to avoid counting false positives in enclosures
$line = preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line); $line = (string) preg_replace('/(' . $enclosure . '.*' . $enclosure . ')/Us', '', $line);
// See if we have any enclosures left in the line // See if we have any enclosures left in the line
// if we still have an enclosure then we need to read the next line as well // if we still have an enclosure then we need to read the next line as well
} while (preg_match('/(' . $enclosure . ')/', $line ?? '') > 0); } while (preg_match('/(' . $enclosure . ')/', $line) > 0);
return $line ?? false; return ($line !== '') ? $line : false;
} }
} }

View File

@ -272,6 +272,11 @@ class Gnumeric extends BaseReader
// name in line with the formula, not the reverse // name in line with the formula, not the reverse
$this->spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); $this->spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
$visibility = $sheetOrNull->attributes()['Visibility'] ?? 'GNM_SHEET_VISIBILITY_VISIBLE';
if ((string) $visibility !== 'GNM_SHEET_VISIBILITY_VISIBLE') {
$this->spreadsheet->getActiveSheet()->setSheetState(Worksheet::SHEETSTATE_HIDDEN);
}
if (!$this->readDataOnly) { if (!$this->readDataOnly) {
(new PageSetup($this->spreadsheet)) (new PageSetup($this->spreadsheet))
->printInformation($sheet) ->printInformation($sheet)

View File

@ -619,7 +619,7 @@ class Html extends BaseReader
{ {
foreach ($element->childNodes as $child) { foreach ($element->childNodes as $child) {
if ($child instanceof DOMText) { if ($child instanceof DOMText) {
$domText = preg_replace('/\s+/u', ' ', trim($child->nodeValue ?? '')); $domText = (string) preg_replace('/\s+/u', ' ', trim($child->nodeValue ?? ''));
if (is_string($cellContent)) { if (is_string($cellContent)) {
// simply append the text if the cell content is a plain text string // simply append the text if the cell content is a plain text string
$cellContent .= $domText; $cellContent .= $domText;

View File

@ -588,6 +588,7 @@ class Ods extends BaseReader
break; break;
} }
} }
$pageSettings->setVisibilityForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName);
$pageSettings->setPrintSettingsForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName); $pageSettings->setPrintSettingsForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName);
++$worksheetID; ++$worksheetID;
} }

View File

@ -13,17 +13,25 @@ class FormulaTranslator
// Cell range 3-d reference // Cell range 3-d reference
// As we don't support 3-d ranges, we're just going to take a quick and dirty approach // As we don't support 3-d ranges, we're just going to take a quick and dirty approach
// and assume that the second worksheet reference is the same as the first // and assume that the second worksheet reference is the same as the first
$excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\$?([^\.]+)\.([^\.]+)/miu', '$1!$2:$4', $excelAddress); $excelAddress = (string) preg_replace(
// Cell range reference in another sheet [
$excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\.([^\.]+)/miu', '$1!$2:$3', $excelAddress ?? ''); '/\$?([^\.]+)\.([^\.]+):\$?([^\.]+)\.([^\.]+)/miu',
// Cell reference in another sheet '/\$?([^\.]+)\.([^\.]+):\.([^\.]+)/miu', // Cell range reference in another sheet
$excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+)/miu', '$1!$2', $excelAddress ?? ''); '/\$?([^\.]+)\.([^\.]+)/miu', // Cell reference in another sheet
// Cell range reference '/\.([^\.]+):\.([^\.]+)/miu', // Cell range reference
$excelAddress = preg_replace('/\.([^\.]+):\.([^\.]+)/miu', '$1:$2', $excelAddress ?? ''); '/\.([^\.]+)/miu', // Simple cell reference
// Simple cell reference ],
$excelAddress = preg_replace('/\.([^\.]+)/miu', '$1', $excelAddress ?? ''); [
'$1!$2:$4',
'$1!$2:$3',
'$1!$2',
'$1:$2',
'$1',
],
$excelAddress
);
return $excelAddress ?? ''; return $excelAddress;
} }
public static function convertToExcelFormulaValue(string $openOfficeFormula): string public static function convertToExcelFormulaValue(string $openOfficeFormula): string
@ -37,16 +45,23 @@ class FormulaTranslator
// Only replace in alternate array entries (i.e. non-quoted blocks) // Only replace in alternate array entries (i.e. non-quoted blocks)
// so that conversion isn't done in string values // so that conversion isn't done in string values
if ($tKey = !$tKey) { if ($tKey = !$tKey) {
// Cell range reference in another sheet $value = (string) preg_replace(
$value = preg_replace('/\[\$?([^\.]+)\.([^\.]+):\.([^\.]+)\]/miu', '$1!$2:$3', $value); [
// Cell reference in another sheet '/\[\$?([^\.]+)\.([^\.]+):\.([^\.]+)\]/miu', // Cell range reference in another sheet
$value = preg_replace('/\[\$?([^\.]+)\.([^\.]+)\]/miu', '$1!$2', $value ?? ''); '/\[\$?([^\.]+)\.([^\.]+)\]/miu', // Cell reference in another sheet
// Cell range reference '/\[\.([^\.]+):\.([^\.]+)\]/miu', // Cell range reference
$value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/miu', '$1:$2', $value ?? ''); '/\[\.([^\.]+)\]/miu', // Simple cell reference
// Simple cell reference ],
$value = preg_replace('/\[\.([^\.]+)\]/miu', '$1', $value ?? ''); [
'$1!$2:$3',
'$1!$2',
'$1:$2',
'$1',
],
$value
);
// Convert references to defined names/formulae // Convert references to defined names/formulae
$value = str_replace('$$', '', $value ?? ''); $value = str_replace('$$', '', $value);
// Convert ODS function argument separators to Excel function argument separators // Convert ODS function argument separators to Excel function argument separators
$value = Calculation::translateSeparator(';', ',', $value, $inFunctionBracesLevel); $value = Calculation::translateSeparator(';', ',', $value, $inFunctionBracesLevel);
@ -69,7 +84,7 @@ class FormulaTranslator
Calculation::FORMULA_CLOSE_MATRIX_BRACE Calculation::FORMULA_CLOSE_MATRIX_BRACE
); );
$value = preg_replace('/COM\.MICROSOFT\./ui', '', $value); $value = (string) preg_replace('/COM\.MICROSOFT\./ui', '', $value);
} }
} }

View File

@ -8,16 +8,41 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class PageSettings class PageSettings
{ {
/**
* @var string
*/
private $officeNs; private $officeNs;
/**
* @var string
*/
private $stylesNs; private $stylesNs;
/**
* @var string
*/
private $stylesFo; private $stylesFo;
/**
* @var string
*/
private $tableNs;
/**
* @var string[]
*/
private $tableStylesCrossReference = [];
private $pageLayoutStyles = []; private $pageLayoutStyles = [];
/**
* @var string[]
*/
private $masterStylesCrossReference = []; private $masterStylesCrossReference = [];
/**
* @var string[]
*/
private $masterPrintStylesCrossReference = []; private $masterPrintStylesCrossReference = [];
public function __construct(DOMDocument $styleDom) public function __construct(DOMDocument $styleDom)
@ -32,6 +57,7 @@ class PageSettings
$this->officeNs = $styleDom->lookupNamespaceUri('office'); $this->officeNs = $styleDom->lookupNamespaceUri('office');
$this->stylesNs = $styleDom->lookupNamespaceUri('style'); $this->stylesNs = $styleDom->lookupNamespaceUri('style');
$this->stylesFo = $styleDom->lookupNamespaceUri('fo'); $this->stylesFo = $styleDom->lookupNamespaceUri('fo');
$this->tableNs = $styleDom->lookupNamespaceUri('table');
} }
private function readPageSettingStyles(DOMDocument $styleDom): void private function readPageSettingStyles(DOMDocument $styleDom): void
@ -98,12 +124,33 @@ class PageSettings
foreach ($styleXReferences as $styleXreferenceSet) { foreach ($styleXReferences as $styleXreferenceSet) {
$styleXRefName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'name'); $styleXRefName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'name');
$stylePageLayoutName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'master-page-name'); $stylePageLayoutName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'master-page-name');
$styleFamilyName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'family');
if (!empty($styleFamilyName) && $styleFamilyName === 'table') {
$styleVisibility = 'true';
foreach ($styleXreferenceSet->getElementsByTagNameNS($this->stylesNs, 'table-properties') as $tableProperties) {
$styleVisibility = $tableProperties->getAttributeNS($this->tableNs, 'display');
}
$this->tableStylesCrossReference[$styleXRefName] = $styleVisibility;
}
if (!empty($stylePageLayoutName)) { if (!empty($stylePageLayoutName)) {
$this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName; $this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName;
} }
} }
} }
public function setVisibilityForWorksheet(Worksheet $worksheet, string $styleName): void
{
if (!array_key_exists($styleName, $this->tableStylesCrossReference)) {
return;
}
$worksheet->setSheetState(
$this->tableStylesCrossReference[$styleName] === 'false'
? Worksheet::SHEETSTATE_HIDDEN
: Worksheet::SHEETSTATE_VISIBLE
);
}
public function setPrintSettingsForWorksheet(Worksheet $worksheet, string $styleName): void public function setPrintSettingsForWorksheet(Worksheet $worksheet, string $styleName): void
{ {
if (!array_key_exists($styleName, $this->masterStylesCrossReference)) { if (!array_key_exists($styleName, $this->masterStylesCrossReference)) {

View File

@ -4521,17 +4521,17 @@ class Xls extends BaseReader
// first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!) // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!)
if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) { if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
$selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells); $selectedCells = (string) preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells);
} }
// first row '1' + last row '65536' indicates that full column is selected // first row '1' + last row '65536' indicates that full column is selected
if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) { if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
$selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells); $selectedCells = (string) preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells);
} }
// first column 'A' + last column 'IV' indicates that full row is selected // first column 'A' + last column 'IV' indicates that full row is selected
if (preg_match('/^(A\d+\:)IV(\d+)$/', $selectedCells)) { if (preg_match('/^(A\d+\:)IV(\d+)$/', $selectedCells)) {
$selectedCells = preg_replace('/^(A\d+\:)IV(\d+)$/', '${1}XFD${2}', $selectedCells); $selectedCells = (string) preg_replace('/^(A\d+\:)IV(\d+)$/', '${1}XFD${2}', $selectedCells);
} }
$this->phpSheet->setSelectedCells($selectedCells); $this->phpSheet->setSelectedCells($selectedCells);

View File

@ -158,6 +158,10 @@ class Xlsx extends BaseReader
Namespaces::PURL_RELATIONSHIPS => Namespaces::PURL_DRAWING, Namespaces::PURL_RELATIONSHIPS => Namespaces::PURL_DRAWING,
]; ];
private const REL_TO_CHART = [
Namespaces::PURL_RELATIONSHIPS => Namespaces::PURL_CHART,
];
/** /**
* Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object. * Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
* *
@ -227,7 +231,10 @@ class Xlsx extends BaseReader
$worksheets = []; $worksheets = [];
foreach ($relsWorkbook->Relationship as $elex) { foreach ($relsWorkbook->Relationship as $elex) {
$ele = self::getAttributes($elex); $ele = self::getAttributes($elex);
if ((string) $ele['Type'] === "$namespace/worksheet") { if (
((string) $ele['Type'] === "$namespace/worksheet") ||
((string) $ele['Type'] === "$namespace/chartsheet")
) {
$worksheets[(string) $ele['Id']] = $ele['Target']; $worksheets[(string) $ele['Id']] = $ele['Target'];
} }
} }
@ -406,17 +413,21 @@ class Xlsx extends BaseReader
// Read the theme first, because we need the colour scheme when reading the styles // Read the theme first, because we need the colour scheme when reading the styles
[$workbookBasename, $xmlNamespaceBase] = $this->getWorkbookBaseName(); [$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; $theme = null;
$this->styleReader = new Styles(); $this->styleReader = new Styles();
foreach ($wbRels->Relationship as $relx) { foreach ($wbRels->Relationship as $relx) {
$rel = self::getAttributes($relx); $rel = self::getAttributes($relx);
$relTarget = (string) $rel['Target']; $relTarget = (string) $rel['Target'];
if (substr($relTarget, 0, 4) === '/xl/') {
$relTarget = substr($relTarget, 4);
}
switch ($rel['Type']) { switch ($rel['Type']) {
case "$xmlNamespaceBase/theme": case "$xmlNamespaceBase/theme":
$themeOrderArray = ['lt1', 'dk1', 'lt2', 'dk2']; $themeOrderArray = ['lt1', 'dk1', 'lt2', 'dk2'];
$themeOrderAdditional = count($themeOrderArray); $themeOrderAdditional = count($themeOrderArray);
$drawingNS = self::REL_TO_DRAWING[$xmlNamespaceBase] ?? Namespaces::DRAWINGML;
$xmlTheme = $this->loadZip("xl/{$relTarget}", $drawingNS); $xmlTheme = $this->loadZip("xl/{$relTarget}", $drawingNS);
$xmlThemeName = self::getAttributes($xmlTheme); $xmlThemeName = self::getAttributes($xmlTheme);
@ -513,6 +524,12 @@ class Xlsx extends BaseReader
case Namespaces::PURL_WORKSHEET: case Namespaces::PURL_WORKSHEET:
$worksheets[(string) $ele['Id']] = $ele['Target']; $worksheets[(string) $ele['Id']] = $ele['Target'];
break;
case Namespaces::CHARTSHEET:
if ($this->includeCharts === true) {
$worksheets[(string) $ele['Id']] = $ele['Target'];
}
break; break;
// a vbaProject ? (: some macros) // a vbaProject ? (: some macros)
case Namespaces::VBA: case Namespaces::VBA:
@ -691,6 +708,13 @@ class Xlsx extends BaseReader
continue; continue;
} }
$sheetReferenceId = (string) self::getArrayItem(self::getAttributes($eleSheet, $xmlNamespaceBase), 'id');
if (isset($worksheets[$sheetReferenceId]) === false) {
++$countSkippedSheets;
$mapSheetId[$oldSheetId] = null;
continue;
}
// Map old sheet id in original workbook to new sheet id. // Map old sheet id in original workbook to new sheet id.
// They will differ if loadSheetsOnly() is being used // They will differ if loadSheetsOnly() is being used
$mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets; $mapSheetId[$oldSheetId] = $oldSheetId - $countSkippedSheets;
@ -702,7 +726,8 @@ class Xlsx extends BaseReader
// and we're simply bringing the worksheet name in line with the formula, not the // and we're simply bringing the worksheet name in line with the formula, not the
// reverse // reverse
$docSheet->setTitle((string) $eleSheetAttr['name'], false, false); $docSheet->setTitle((string) $eleSheetAttr['name'], false, false);
$fileWorksheet = (string) $worksheets[(string) self::getArrayItem(self::getAttributes($eleSheet, $xmlNamespaceBase), 'id')];
$fileWorksheet = (string) $worksheets[$sheetReferenceId];
$xmlSheet = $this->loadZipNoNamespace("$dir/$fileWorksheet", $mainNS); $xmlSheet = $this->loadZipNoNamespace("$dir/$fileWorksheet", $mainNS);
$xmlSheetNS = $this->loadZip("$dir/$fileWorksheet", $mainNS); $xmlSheetNS = $this->loadZip("$dir/$fileWorksheet", $mainNS);
@ -1189,14 +1214,23 @@ class Xlsx extends BaseReader
. '/_rels/' . '/_rels/'
. basename($fileWorksheet) . basename($fileWorksheet)
. '.rels'; . '.rels';
if (substr($drawingFilename, 0, 7) === 'xl//xl/') {
$drawingFilename = substr($drawingFilename, 4);
}
if ($zip->locateName($drawingFilename)) { if ($zip->locateName($drawingFilename)) {
$relsWorksheet = $this->loadZipNoNamespace($drawingFilename, Namespaces::RELATIONSHIPS); $relsWorksheet = $this->loadZipNoNamespace($drawingFilename, Namespaces::RELATIONSHIPS);
$drawings = []; $drawings = [];
foreach ($relsWorksheet->Relationship as $ele) { foreach ($relsWorksheet->Relationship as $ele) {
if ((string) $ele['Type'] === "$xmlNamespaceBase/drawing") { if ((string) $ele['Type'] === "$xmlNamespaceBase/drawing") {
$eleTarget = (string) $ele['Target'];
if (substr($eleTarget, 0, 4) === '/xl/') {
$drawings[(string) $ele['Id']] = substr($eleTarget, 1);
} else {
$drawings[(string) $ele['Id']] = self::dirAdd("$dir/$fileWorksheet", $ele['Target']); $drawings[(string) $ele['Id']] = self::dirAdd("$dir/$fileWorksheet", $ele['Target']);
} }
} }
}
if ($xmlSheet->drawing && !$this->readDataOnly) { if ($xmlSheet->drawing && !$this->readDataOnly) {
$unparsedDrawings = []; $unparsedDrawings = [];
$fileDrawing = null; $fileDrawing = null;
@ -1205,6 +1239,7 @@ class Xlsx extends BaseReader
$fileDrawing = $drawings[$drawingRelId]; $fileDrawing = $drawings[$drawingRelId];
$drawingFilename = dirname($fileDrawing) . '/_rels/' . basename($fileDrawing) . '.rels'; $drawingFilename = dirname($fileDrawing) . '/_rels/' . basename($fileDrawing) . '.rels';
$relsDrawing = $this->loadZipNoNamespace($drawingFilename, $xmlNamespaceBase); $relsDrawing = $this->loadZipNoNamespace($drawingFilename, $xmlNamespaceBase);
$images = []; $images = [];
$hyperlinks = []; $hyperlinks = [];
if ($relsDrawing && $relsDrawing->Relationship) { if ($relsDrawing && $relsDrawing->Relationship) {
@ -1217,7 +1252,13 @@ class Xlsx extends BaseReader
$images[(string) $ele['Id']] = self::dirAdd($fileDrawing, $ele['Target']); $images[(string) $ele['Id']] = self::dirAdd($fileDrawing, $ele['Target']);
} elseif ($eleType === "$xmlNamespaceBase/chart") { } elseif ($eleType === "$xmlNamespaceBase/chart") {
if ($this->includeCharts) { if ($this->includeCharts) {
$charts[self::dirAdd($fileDrawing, $ele['Target'])] = [ $eleTarget = (string) $ele['Target'];
if (substr($eleTarget, 0, 4) === '/xl/') {
$index = substr($eleTarget, 1);
} else {
$index = self::dirAdd($fileDrawing, $eleTarget);
}
$charts[$index] = [
'id' => (string) $ele['Id'], 'id' => (string) $ele['Id'],
'sheet' => $docSheet->getTitle(), 'sheet' => $docSheet->getTitle(),
]; ];
@ -1225,6 +1266,7 @@ class Xlsx extends BaseReader
} }
} }
} }
$xmlDrawing = $this->loadZipNoNamespace($fileDrawing, ''); $xmlDrawing = $this->loadZipNoNamespace($fileDrawing, '');
$xmlDrawingChildren = $xmlDrawing->children(Namespaces::SPREADSHEET_DRAWING); $xmlDrawingChildren = $xmlDrawing->children(Namespaces::SPREADSHEET_DRAWING);
@ -1308,6 +1350,7 @@ class Xlsx extends BaseReader
'width' => $width, 'width' => $width,
'height' => $height, 'height' => $height,
'worksheetTitle' => $docSheet->getTitle(), 'worksheetTitle' => $docSheet->getTitle(),
'oneCellAnchor' => true,
]; ];
} }
} }
@ -1403,6 +1446,27 @@ class Xlsx extends BaseReader
} }
} }
} }
if ($xmlDrawingChildren->absoluteAnchor) {
foreach ($xmlDrawingChildren->absoluteAnchor as $absoluteAnchor) {
if (($this->includeCharts) && ($absoluteAnchor->graphicFrame)) {
$graphic = $absoluteAnchor->graphicFrame->children(Namespaces::DRAWINGML)->graphic;
/** @var SimpleXMLElement $chartRef */
$chartRef = $graphic->graphicData->children(Namespaces::CHART)->chart;
$thisChart = (string) self::getAttributes($chartRef, $xmlNamespaceBase);
$width = Drawing::EMUToPixels((int) self::getArrayItem(self::getAttributes($absoluteAnchor->ext), 'cx')[0]);
$height = Drawing::EMUToPixels((int) self::getArrayItem(self::getAttributes($absoluteAnchor->ext), 'cy')[0]);
$chartDetails[$docSheet->getTitle() . '!' . $thisChart] = [
'fromCoordinate' => 'A1',
'fromOffsetX' => 0,
'fromOffsetY' => 0,
'width' => $width,
'height' => $height,
'worksheetTitle' => $docSheet->getTitle(),
];
}
}
}
if (empty($relsDrawing) && $xmlDrawing->count() == 0) { if (empty($relsDrawing) && $xmlDrawing->count() == 0) {
// Save Drawing without rels and children as unparsed // Save Drawing without rels and children as unparsed
$unparsedDrawings[$drawingRelId] = $xmlDrawing->asXML(); $unparsedDrawings[$drawingRelId] = $xmlDrawing->asXML();
@ -1422,7 +1486,7 @@ class Xlsx extends BaseReader
} }
// unparsed drawing AlternateContent // unparsed drawing AlternateContent
$xmlAltDrawing = $this->loadZip($fileDrawing, Namespaces::COMPATIBILITY); $xmlAltDrawing = $this->loadZip((string) $fileDrawing, Namespaces::COMPATIBILITY);
if ($xmlAltDrawing->AlternateContent) { if ($xmlAltDrawing->AlternateContent) {
foreach ($xmlAltDrawing->AlternateContent as $alternateContent) { foreach ($xmlAltDrawing->AlternateContent as $alternateContent) {
@ -1491,13 +1555,18 @@ class Xlsx extends BaseReader
$rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); $rangeSets = preg_split("/('?(?:.*?)'?(?:![A-Z0-9]+:[A-Z0-9]+)),?/", $extractedRange, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$newRangeSets = []; $newRangeSets = [];
foreach ($rangeSets as $rangeSet) { foreach ($rangeSets as $rangeSet) {
[$sheetName, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true); [, $rangeSet] = Worksheet::extractSheetTitle($rangeSet, true);
if (empty($rangeSet)) {
continue;
}
if (strpos($rangeSet, ':') === false) { if (strpos($rangeSet, ':') === false) {
$rangeSet = $rangeSet . ':' . $rangeSet; $rangeSet = $rangeSet . ':' . $rangeSet;
} }
$newRangeSets[] = str_replace('$', '', $rangeSet); $newRangeSets[] = str_replace('$', '', $rangeSet);
} }
if (count($newRangeSets) > 0) {
$docSheet->getPageSetup()->setPrintArea(implode(',', $newRangeSets)); $docSheet->getPageSetup()->setPrintArea(implode(',', $newRangeSets));
}
break; break;
default: default:
@ -1601,17 +1670,26 @@ class Xlsx extends BaseReader
if ($this->includeCharts) { if ($this->includeCharts) {
$chartEntryRef = ltrim((string) $contentType['PartName'], '/'); $chartEntryRef = ltrim((string) $contentType['PartName'], '/');
$chartElements = $this->loadZip($chartEntryRef); $chartElements = $this->loadZip($chartEntryRef);
$objChart = Chart::readChart($chartElements, basename($chartEntryRef, '.xml')); $chartReader = new Chart($chartNS, $drawingNS);
$objChart = $chartReader->readChart($chartElements, basename($chartEntryRef, '.xml'));
if (isset($charts[$chartEntryRef])) { if (isset($charts[$chartEntryRef])) {
$chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id']; $chartPositionRef = $charts[$chartEntryRef]['sheet'] . '!' . $charts[$chartEntryRef]['id'];
if (isset($chartDetails[$chartPositionRef])) { if (isset($chartDetails[$chartPositionRef])) {
$excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart); $excel->getSheetByName($charts[$chartEntryRef]['sheet'])->addChart($objChart);
$objChart->setWorksheet($excel->getSheetByName($charts[$chartEntryRef]['sheet'])); $objChart->setWorksheet($excel->getSheetByName($charts[$chartEntryRef]['sheet']));
$objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']); // For oneCellAnchor or absoluteAnchor positioned charts,
// toCoordinate is not in the data. Does it need to be calculated?
if (array_key_exists('toCoordinate', $chartDetails[$chartPositionRef])) { if (array_key_exists('toCoordinate', $chartDetails[$chartPositionRef])) {
// For oneCellAnchor positioned charts, toCoordinate is not in the data. Does it need to be calculated? // twoCellAnchor
$objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']);
$objChart->setBottomRightPosition($chartDetails[$chartPositionRef]['toCoordinate'], $chartDetails[$chartPositionRef]['toOffsetX'], $chartDetails[$chartPositionRef]['toOffsetY']); $objChart->setBottomRightPosition($chartDetails[$chartPositionRef]['toCoordinate'], $chartDetails[$chartPositionRef]['toOffsetX'], $chartDetails[$chartPositionRef]['toOffsetY']);
} else {
// oneCellAnchor or absoluteAnchor (e.g. Chart sheet)
$objChart->setTopLeftPosition($chartDetails[$chartPositionRef]['fromCoordinate'], $chartDetails[$chartPositionRef]['fromOffsetX'], $chartDetails[$chartPositionRef]['fromOffsetY']);
$objChart->setBottomRightPosition('', $chartDetails[$chartPositionRef]['width'], $chartDetails[$chartPositionRef]['height']);
if (array_key_exists('oneCellAnchor', $chartDetails[$chartPositionRef])) {
$objChart->setOneCellAnchor($chartDetails[$chartPositionRef]['oneCellAnchor']);
}
} }
} }
} }
@ -1771,12 +1849,17 @@ class Xlsx extends BaseReader
return $array[$key] ?? null; return $array[$key] ?? null;
} }
private static function dirAdd($base, $add) private static function dirAdd($base, $add): string
{ {
return preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add"); $add = "$add";
if (substr($add, 0, 4) === '/xl/') {
$add = substr($add, 4);
} }
private static function toCSSArray($style) return (string) preg_replace('~[^/]+/\.\./~', '', dirname($base) . "/$add");
}
private static function toCSSArray($style): array
{ {
$style = self::stripWhiteSpaceFromStyleString($style); $style = self::stripWhiteSpaceFromStyleString($style);
@ -1807,12 +1890,12 @@ class Xlsx extends BaseReader
return $style; return $style;
} }
public static function stripWhiteSpaceFromStyleString($string) public static function stripWhiteSpaceFromStyleString($string): string
{ {
return trim(str_replace(["\r", "\n", ' '], '', $string), ';'); return trim(str_replace(["\r", "\n", ' '], '', $string), ';');
} }
private static function boolean($value) private static function boolean($value): bool
{ {
if (is_object($value)) { if (is_object($value)) {
$value = (string) $value; $value = (string) $value;

View File

@ -22,7 +22,7 @@ class AutoFilter
public function load(): void public function load(): void
{ {
// Remove all "$" in the auto filter range // Remove all "$" in the auto filter range
$autoFilterRange = preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref'] ?? ''); $autoFilterRange = (string) preg_replace('/\$/', '', $this->worksheetXml->autoFilter['ref'] ?? '');
if (strpos($autoFilterRange, ':') !== false) { if (strpos($autoFilterRange, ':') !== false) {
$this->readAutoFilter($autoFilterRange, $this->worksheetXml); $this->readAutoFilter($autoFilterRange, $this->worksheetXml);
} }

View File

@ -16,6 +16,18 @@ use SimpleXMLElement;
class Chart class Chart
{ {
/** @var string */
private $cNamespace;
/** @var string */
private $aNamespace;
public function __construct(string $cNamespace = Namespaces::CHART, string $aNamespace = Namespaces::DRAWINGML)
{
$this->cNamespace = $cNamespace;
$this->aNamespace = $aNamespace;
}
/** /**
* @param string $name * @param string $name
* @param string $format * @param string $format
@ -25,7 +37,7 @@ class Chart
private static function getAttribute(SimpleXMLElement $component, $name, $format) private static function getAttribute(SimpleXMLElement $component, $name, $format)
{ {
$attributes = $component->attributes(); $attributes = $component->attributes();
if (isset($attributes[$name])) { if (@isset($attributes[$name])) {
if ($format == 'string') { if ($format == 'string') {
return (string) $attributes[$name]; return (string) $attributes[$name];
} elseif ($format == 'integer') { } elseif ($format == 'integer') {
@ -42,58 +54,56 @@ class Chart
return null; return null;
} }
private static function readColor($color, $background = false)
{
if (isset($color['rgb'])) {
return (string) $color['rgb'];
} elseif (isset($color['indexed'])) {
return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
}
}
/** /**
* @param string $chartName * @param string $chartName
* *
* @return \PhpOffice\PhpSpreadsheet\Chart\Chart * @return \PhpOffice\PhpSpreadsheet\Chart\Chart
*/ */
public static function readChart(SimpleXMLElement $chartElements, $chartName) public function readChart(SimpleXMLElement $chartElements, $chartName)
{ {
$namespacesChartMeta = $chartElements->getNamespaces(true); $chartElementsC = $chartElements->children($this->cNamespace);
$chartElementsC = $chartElements->children($namespacesChartMeta['c']);
$XaxisLabel = $YaxisLabel = $legend = $title = null; $XaxisLabel = $YaxisLabel = $legend = $title = null;
$dispBlanksAs = $plotVisOnly = null; $dispBlanksAs = $plotVisOnly = null;
$plotArea = null; $plotArea = null;
$rotX = $rotY = $rAngAx = $perspective = null;
foreach ($chartElementsC as $chartElementKey => $chartElement) { foreach ($chartElementsC as $chartElementKey => $chartElement) {
switch ($chartElementKey) { switch ($chartElementKey) {
case 'chart': case 'chart':
foreach ($chartElement as $chartDetailsKey => $chartDetails) { foreach ($chartElement as $chartDetailsKey => $chartDetails) {
$chartDetailsC = $chartDetails->children($namespacesChartMeta['c']); $chartDetailsC = $chartDetails->children($this->cNamespace);
switch ($chartDetailsKey) { switch ($chartDetailsKey) {
case 'view3D':
$rotX = self::getAttribute($chartDetails->rotX, 'val', 'integer');
$rotY = self::getAttribute($chartDetails->rotY, 'val', 'integer');
$rAngAx = self::getAttribute($chartDetails->rAngAx, 'val', 'integer');
$perspective = self::getAttribute($chartDetails->perspective, 'val', 'integer');
break;
case 'plotArea': case 'plotArea':
$plotAreaLayout = $XaxisLabel = $YaxisLabel = null; $plotAreaLayout = $XaxisLabel = $YaxisLabel = null;
$plotSeries = $plotAttributes = []; $plotSeries = $plotAttributes = [];
foreach ($chartDetails as $chartDetailKey => $chartDetail) { foreach ($chartDetails as $chartDetailKey => $chartDetail) {
switch ($chartDetailKey) { switch ($chartDetailKey) {
case 'layout': case 'layout':
$plotAreaLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta); $plotAreaLayout = $this->chartLayoutDetails($chartDetail);
break; break;
case 'catAx': case 'catAx':
if (isset($chartDetail->title)) { if (isset($chartDetail->title)) {
$XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta); $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
} }
break; break;
case 'dateAx': case 'dateAx':
if (isset($chartDetail->title)) { if (isset($chartDetail->title)) {
$XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta); $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
} }
break; break;
case 'valAx': case 'valAx':
if (isset($chartDetail->title, $chartDetail->axPos)) { if (isset($chartDetail->title, $chartDetail->axPos)) {
$axisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta); $axisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
$axPos = self::getAttribute($chartDetail->axPos, 'val', 'string'); $axPos = self::getAttribute($chartDetail->axPos, 'val', 'string');
switch ($axPos) { switch ($axPos) {
@ -114,70 +124,72 @@ class Chart
case 'barChart': case 'barChart':
case 'bar3DChart': case 'bar3DChart':
$barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string'); $barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotDirection($barDirection); $plotSer->setPlotDirection("$barDirection");
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'lineChart': case 'lineChart':
case 'line3DChart': case 'line3DChart':
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'areaChart': case 'areaChart':
case 'area3DChart': case 'area3DChart':
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'doughnutChart': case 'doughnutChart':
case 'pieChart': case 'pieChart':
case 'pie3DChart': case 'pie3DChart':
$explosion = isset($chartDetail->ser->explosion); $explosion = isset($chartDetail->ser->explosion);
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($explosion); $plotSer->setPlotStyle("$explosion");
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'scatterChart': case 'scatterChart':
/** @var string */
$scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string'); $scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($scatterStyle); $plotSer->setPlotStyle($scatterStyle);
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'bubbleChart': case 'bubbleChart':
$bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer'); $bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($bubbleScale); $plotSer->setPlotStyle("$bubbleScale");
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'radarChart': case 'radarChart':
/** @var string */
$radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string'); $radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($radarStyle); $plotSer->setPlotStyle($radarStyle);
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'surfaceChart': case 'surfaceChart':
case 'surface3DChart': case 'surface3DChart':
$wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean'); $wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean');
$plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotSer->setPlotStyle($wireFrame); $plotSer->setPlotStyle("$wireFrame");
$plotSeries[] = $plotSer; $plotSeries[] = $plotSer;
$plotAttributes = self::readChartAttributes($chartDetail); $plotAttributes = $this->readChartAttributes($chartDetail);
break; break;
case 'stockChart': case 'stockChart':
$plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey); $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
$plotAttributes = self::readChartAttributes($plotAreaLayout); $plotAttributes = $this->readChartAttributes($plotAreaLayout);
break; break;
} }
@ -186,7 +198,7 @@ class Chart
$plotAreaLayout = new Layout(); $plotAreaLayout = new Layout();
} }
$plotArea = new PlotArea($plotAreaLayout, $plotSeries); $plotArea = new PlotArea($plotAreaLayout, $plotSeries);
self::setChartAttributes($plotAreaLayout, $plotAttributes); $this->setChartAttributes($plotAreaLayout, $plotAttributes);
break; break;
case 'plotVisOnly': case 'plotVisOnly':
@ -198,7 +210,7 @@ class Chart
break; break;
case 'title': case 'title':
$title = self::chartTitle($chartDetails, $namespacesChartMeta); $title = $this->chartTitle($chartDetails);
break; break;
case 'legend': case 'legend':
@ -216,42 +228,54 @@ class Chart
break; break;
case 'layout': case 'layout':
$legendLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta); $legendLayout = $this->chartLayoutDetails($chartDetail);
break; break;
} }
} }
$legend = new Legend($legendPos, $legendLayout, $legendOverlay); $legend = new Legend("$legendPos", $legendLayout, (bool) $legendOverlay);
break; break;
} }
} }
} }
} }
$chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, $dispBlanksAs, $XaxisLabel, $YaxisLabel); $chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, (string) $dispBlanksAs, $XaxisLabel, $YaxisLabel);
if (is_int($rotX)) {
$chart->setRotX($rotX);
}
if (is_int($rotY)) {
$chart->setRotY($rotY);
}
if (is_int($rAngAx)) {
$chart->setRAngAx($rAngAx);
}
if (is_int($perspective)) {
$chart->setPerspective($perspective);
}
return $chart; return $chart;
} }
private static function chartTitle(SimpleXMLElement $titleDetails, array $namespacesChartMeta) private function chartTitle(SimpleXMLElement $titleDetails): Title
{ {
$caption = []; $caption = [];
$titleLayout = null; $titleLayout = null;
foreach ($titleDetails as $titleDetailKey => $chartDetail) { foreach ($titleDetails as $titleDetailKey => $chartDetail) {
switch ($titleDetailKey) { switch ($titleDetailKey) {
case 'tx': case 'tx':
$titleDetails = $chartDetail->rich->children($namespacesChartMeta['a']); $titleDetails = $chartDetail->rich->children($this->aNamespace);
foreach ($titleDetails as $titleKey => $titleDetail) { foreach ($titleDetails as $titleKey => $titleDetail) {
switch ($titleKey) { switch ($titleKey) {
case 'p': case 'p':
$titleDetailPart = $titleDetail->children($namespacesChartMeta['a']); $titleDetailPart = $titleDetail->children($this->aNamespace);
$caption[] = self::parseRichText($titleDetailPart); $caption[] = $this->parseRichText($titleDetailPart);
} }
} }
break; break;
case 'layout': case 'layout':
$titleLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta); $titleLayout = $this->chartLayoutDetails($chartDetail);
break; break;
} }
@ -260,12 +284,12 @@ class Chart
return new Title($caption, $titleLayout); return new Title($caption, $titleLayout);
} }
private static function chartLayoutDetails($chartDetail, $namespacesChartMeta) private function chartLayoutDetails(SimpleXMLElement $chartDetail): ?Layout
{ {
if (!isset($chartDetail->manualLayout)) { if (!isset($chartDetail->manualLayout)) {
return null; return null;
} }
$details = $chartDetail->manualLayout->children($namespacesChartMeta['c']); $details = $chartDetail->manualLayout->children($this->cNamespace);
if ($details === null) { if ($details === null) {
return null; return null;
} }
@ -277,13 +301,13 @@ class Chart
return new Layout($layout); return new Layout($layout);
} }
private static function chartDataSeries($chartDetail, $namespacesChartMeta, $plotType) private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType): DataSeries
{ {
$multiSeriesType = null; $multiSeriesType = null;
$smoothLine = false; $smoothLine = false;
$seriesLabel = $seriesCategory = $seriesValues = $plotOrder = []; $seriesLabel = $seriesCategory = $seriesValues = $plotOrder = $seriesBubbles = [];
$seriesDetailSet = $chartDetail->children($namespacesChartMeta['c']); $seriesDetailSet = $chartDetail->children($this->cNamespace);
foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) { foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
switch ($seriesDetailKey) { switch ($seriesDetailKey) {
case 'grouping': case 'grouping':
@ -293,6 +317,12 @@ class Chart
case 'ser': case 'ser':
$marker = null; $marker = null;
$seriesIndex = ''; $seriesIndex = '';
$srgbClr = null;
$lineWidth = null;
$pointSize = null;
$noFill = false;
$schemeClr = '';
$bubble3D = false;
foreach ($seriesDetails as $seriesKey => $seriesDetail) { foreach ($seriesDetails as $seriesKey => $seriesDetail) {
switch ($seriesKey) { switch ($seriesKey) {
case 'idx': case 'idx':
@ -305,11 +335,32 @@ class Chart
break; break;
case 'tx': case 'tx':
$seriesLabel[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta); $seriesLabel[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
break;
case 'spPr':
$children = $seriesDetail->children($this->aNamespace);
$ln = $children->ln;
$lineWidth = self::getAttribute($ln, 'w', 'string');
if (is_countable($ln->noFill) && count($ln->noFill) === 1) {
$noFill = true;
}
$sf = $children->solidFill->schemeClr;
if ($sf) {
$schemeClr = self::getAttribute($sf, 'val', 'string');
}
break; break;
case 'marker': case 'marker':
$marker = self::getAttribute($seriesDetail->symbol, 'val', 'string'); $marker = self::getAttribute($seriesDetail->symbol, 'val', 'string');
$pointSize = self::getAttribute($seriesDetail->size, 'val', 'string');
$pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null;
if (count($seriesDetail->spPr) === 1) {
$ln = $seriesDetail->spPr->children($this->aNamespace);
if (count($ln->solidFill) === 1) {
$srgbClr = self::getAttribute($ln->solidFill->srgbClr, 'val', 'string');
}
}
break; break;
case 'smooth': case 'smooth':
@ -317,37 +368,95 @@ class Chart
break; break;
case 'cat': case 'cat':
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta); $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
break; break;
case 'val': case 'val':
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker); $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize");
break; break;
case 'xVal': case 'xVal':
$seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker); $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize");
break; break;
case 'yVal': case 'yVal':
$seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker); $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize");
break;
case 'bubbleSize':
$seriesBubbles[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize");
break;
case 'bubble3D':
$bubble3D = self::getAttribute($seriesDetail, 'val', 'boolean');
break; break;
} }
} }
if ($noFill) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setScatterLines(false);
} }
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setScatterLines(false);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setScatterLines(false);
}
}
if (is_numeric($lineWidth)) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setLineWidth((int) $lineWidth);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setLineWidth((int) $lineWidth);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setLineWidth((int) $lineWidth);
}
}
if ($schemeClr) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setSchemeClr($schemeClr);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setSchemeClr($schemeClr);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setSchemeClr($schemeClr);
}
}
if ($bubble3D) {
if (isset($seriesLabel[$seriesIndex])) {
$seriesLabel[$seriesIndex]->setBubble3D($bubble3D);
}
if (isset($seriesCategory[$seriesIndex])) {
$seriesCategory[$seriesIndex]->setBubble3D($bubble3D);
}
if (isset($seriesValues[$seriesIndex])) {
$seriesValues[$seriesIndex]->setBubble3D($bubble3D);
}
}
}
}
/** @phpstan-ignore-next-line */
$series = new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
$series->setPlotBubbleSizes($seriesBubbles);
return $series;
} }
return new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine); /**
} * @return mixed
*/
private static function chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker = null) private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string $marker = null, ?string $srgbClr = null, ?string $pointSize = null)
{ {
if (isset($seriesDetail->strRef)) { if (isset($seriesDetail->strRef)) {
$seriesSource = (string) $seriesDetail->strRef->f; $seriesSource = (string) $seriesDetail->strRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker); $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->strRef->strCache)) { if (isset($seriesDetail->strRef->strCache)) {
$seriesData = self::chartDataSeriesValues($seriesDetail->strRef->strCache->children($namespacesChartMeta['c']), 's'); $seriesData = $this->chartDataSeriesValues($seriesDetail->strRef->strCache->children($this->cNamespace), 's');
$seriesValues $seriesValues
->setFormatCode($seriesData['formatCode']) ->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']); ->setDataValues($seriesData['dataValues']);
@ -356,9 +465,9 @@ class Chart
return $seriesValues; return $seriesValues;
} elseif (isset($seriesDetail->numRef)) { } elseif (isset($seriesDetail->numRef)) {
$seriesSource = (string) $seriesDetail->numRef->f; $seriesSource = (string) $seriesDetail->numRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, null, null, $marker); $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->numRef->numCache)) { if (isset($seriesDetail->numRef->numCache)) {
$seriesData = self::chartDataSeriesValues($seriesDetail->numRef->numCache->children($namespacesChartMeta['c'])); $seriesData = $this->chartDataSeriesValues($seriesDetail->numRef->numCache->children($this->cNamespace));
$seriesValues $seriesValues
->setFormatCode($seriesData['formatCode']) ->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']); ->setDataValues($seriesData['dataValues']);
@ -367,10 +476,10 @@ class Chart
return $seriesValues; return $seriesValues;
} elseif (isset($seriesDetail->multiLvlStrRef)) { } elseif (isset($seriesDetail->multiLvlStrRef)) {
$seriesSource = (string) $seriesDetail->multiLvlStrRef->f; $seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker); $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) { if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) {
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($namespacesChartMeta['c']), 's'); $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($this->cNamespace), 's');
$seriesValues $seriesValues
->setFormatCode($seriesData['formatCode']) ->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']); ->setDataValues($seriesData['dataValues']);
@ -379,10 +488,10 @@ class Chart
return $seriesValues; return $seriesValues;
} elseif (isset($seriesDetail->multiLvlNumRef)) { } elseif (isset($seriesDetail->multiLvlNumRef)) {
$seriesSource = (string) $seriesDetail->multiLvlNumRef->f; $seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
$seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker); $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize");
if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) { if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) {
$seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($namespacesChartMeta['c']), 's'); $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($this->cNamespace), 's');
$seriesValues $seriesValues
->setFormatCode($seriesData['formatCode']) ->setFormatCode($seriesData['formatCode'])
->setDataValues($seriesData['dataValues']); ->setDataValues($seriesData['dataValues']);
@ -391,10 +500,20 @@ class Chart
return $seriesValues; return $seriesValues;
} }
if (isset($seriesDetail->v)) {
return new DataSeriesValues(
DataSeriesValues::DATASERIES_TYPE_STRING,
null,
null,
1,
[(string) $seriesDetail->v]
);
}
return null; return null;
} }
private static function chartDataSeriesValues($seriesValueSet, $dataType = 'n') private function chartDataSeriesValues(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
{ {
$seriesVal = []; $seriesVal = [];
$formatCode = ''; $formatCode = '';
@ -414,7 +533,7 @@ class Chart
$pointVal = self::getAttribute($seriesValue, 'idx', 'integer'); $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
if ($dataType == 's') { if ($dataType == 's') {
$seriesVal[$pointVal] = (string) $seriesValue->v; $seriesVal[$pointVal] = (string) $seriesValue->v;
} elseif ($seriesValue->v === ExcelError::NA()) { } elseif ((string) $seriesValue->v === ExcelError::NA()) {
$seriesVal[$pointVal] = null; $seriesVal[$pointVal] = null;
} else { } else {
$seriesVal[$pointVal] = (float) $seriesValue->v; $seriesVal[$pointVal] = (float) $seriesValue->v;
@ -431,7 +550,7 @@ class Chart
]; ];
} }
private static function chartDataSeriesValuesMultiLevel($seriesValueSet, $dataType = 'n') private function chartDataSeriesValuesMultiLevel(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
{ {
$seriesVal = []; $seriesVal = [];
$formatCode = ''; $formatCode = '';
@ -452,7 +571,7 @@ class Chart
$pointVal = self::getAttribute($seriesValue, 'idx', 'integer'); $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
if ($dataType == 's') { if ($dataType == 's') {
$seriesVal[$pointVal][] = (string) $seriesValue->v; $seriesVal[$pointVal][] = (string) $seriesValue->v;
} elseif ($seriesValue->v === ExcelError::NA()) { } elseif ((string) $seriesValue->v === ExcelError::NA()) {
$seriesVal[$pointVal] = null; $seriesVal[$pointVal] = null;
} else { } else {
$seriesVal[$pointVal][] = (float) $seriesValue->v; $seriesVal[$pointVal][] = (float) $seriesValue->v;
@ -470,74 +589,227 @@ class Chart
]; ];
} }
private static function parseRichText(SimpleXMLElement $titleDetailPart) private function parseRichText(SimpleXMLElement $titleDetailPart): RichText
{ {
$value = new RichText(); $value = new RichText();
$objText = null; $objText = null;
foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) { $defaultFontSize = null;
if (isset($titleDetailElement->t)) { $defaultBold = null;
$objText = $value->createTextRun((string) $titleDetailElement->t); $defaultItalic = null;
$defaultUnderscore = null;
$defaultStrikethrough = null;
$defaultBaseline = null;
$defaultFontName = null;
$defaultLatin = null;
$defaultEastAsian = null;
$defaultComplexScript = null;
$defaultColor = null;
if (isset($titleDetailPart->pPr->defRPr)) {
/** @var ?int */
$defaultFontSize = self::getAttribute($titleDetailPart->pPr->defRPr, 'sz', 'integer');
/** @var ?bool */
$defaultBold = self::getAttribute($titleDetailPart->pPr->defRPr, 'b', 'boolean');
/** @var ?bool */
$defaultItalic = self::getAttribute($titleDetailPart->pPr->defRPr, 'i', 'boolean');
/** @var ?string */
$defaultUnderscore = self::getAttribute($titleDetailPart->pPr->defRPr, 'u', 'string');
/** @var ?string */
$defaultStrikethrough = self::getAttribute($titleDetailPart->pPr->defRPr, 'strike', 'string');
/** @var ?int */
$defaultBaseline = self::getAttribute($titleDetailPart->pPr->defRPr, 'baseline', 'integer');
if (isset($titleDetailPart->defRPr->rFont['val'])) {
$defaultFontName = (string) $titleDetailPart->defRPr->rFont['val'];
} }
if (isset($titleDetailPart->pPr->defRPr->latin)) {
/** @var ?string */
$defaultLatin = self::getAttribute($titleDetailPart->pPr->defRPr->latin, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->ea)) {
/** @var ?string */
$defaultEastAsian = self::getAttribute($titleDetailPart->pPr->defRPr->ea, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->cs)) {
/** @var ?string */
$defaultComplexScript = self::getAttribute($titleDetailPart->pPr->defRPr->cs, 'typeface', 'string');
}
if (isset($titleDetailPart->pPr->defRPr->solidFill->srgbClr)) {
/** @var ?string */
$defaultColor = self::getAttribute($titleDetailPart->pPr->defRPr->solidFill->srgbClr, 'val', 'string');
}
}
foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
if (
(string) $titleDetailElementKey !== 'r'
|| !isset($titleDetailElement->t)
) {
continue;
}
$objText = $value->createTextRun((string) $titleDetailElement->t);
if ($objText->getFont() === null) {
// @codeCoverageIgnoreStart
continue;
// @codeCoverageIgnoreEnd
}
$fontSize = null;
$bold = null;
$italic = null;
$underscore = null;
$strikethrough = null;
$baseline = null;
$fontName = null;
$latinName = null;
$eastAsian = null;
$complexScript = null;
$fontColor = null;
$uSchemeClr = null;
if (isset($titleDetailElement->rPr)) { if (isset($titleDetailElement->rPr)) {
// not used now, not sure it ever was, grandfathering
if (isset($titleDetailElement->rPr->rFont['val'])) { if (isset($titleDetailElement->rPr->rFont['val'])) {
$objText->getFont()->setName((string) $titleDetailElement->rPr->rFont['val']); // @codeCoverageIgnoreStart
$fontName = (string) $titleDetailElement->rPr->rFont['val'];
// @codeCoverageIgnoreEnd
}
if (isset($titleDetailElement->rPr->latin)) {
/** @var ?string */
$latinName = self::getAttribute($titleDetailElement->rPr->latin, 'typeface', 'string');
}
if (isset($titleDetailElement->rPr->ea)) {
/** @var ?string */
$eastAsian = self::getAttribute($titleDetailElement->rPr->ea, 'typeface', 'string');
}
if (isset($titleDetailElement->rPr->cs)) {
/** @var ?string */
$complexScript = self::getAttribute($titleDetailElement->rPr->cs, 'typeface', 'string');
}
/** @var ?int */
$fontSize = self::getAttribute($titleDetailElement->rPr, 'sz', 'integer');
// not used now, not sure it ever was, grandfathering
/** @var ?string */
$fontColor = self::getAttribute($titleDetailElement->rPr, 'color', 'string');
if (isset($titleDetailElement->rPr->solidFill->srgbClr)) {
/** @var ?string */
$fontColor = self::getAttribute($titleDetailElement->rPr->solidFill->srgbClr, 'val', 'string');
} }
$fontSize = (self::getAttribute($titleDetailElement->rPr, 'sz', 'integer')); /** @var ?bool */
$bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean');
/** @var ?bool */
$italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean');
/** @var ?int */
$baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer');
/** @var ?string */
$underscore = self::getAttribute($titleDetailElement->rPr, 'u', 'string');
if (isset($titleDetailElement->rPr->uFill->solidFill->schemeClr)) {
/** @var ?string */
$uSchemeClr = self::getAttribute($titleDetailElement->rPr->uFill->solidFill->schemeClr, 'val', 'string');
}
/** @var ?string */
$strikethrough = self::getAttribute($titleDetailElement->rPr, 'strike', 'string');
}
$fontFound = false;
$latinName = $latinName ?? $defaultLatin;
if ($latinName !== null) {
$objText->getFont()->setLatin($latinName);
$fontFound = true;
}
$eastAsian = $eastAsian ?? $defaultEastAsian;
if ($eastAsian !== null) {
$objText->getFont()->setEastAsian($eastAsian);
$fontFound = true;
}
$complexScript = $complexScript ?? $defaultComplexScript;
if ($complexScript !== null) {
$objText->getFont()->setComplexScript($complexScript);
$fontFound = true;
}
$fontName = $fontName ?? $defaultFontName;
if ($fontName !== null) {
// @codeCoverageIgnoreStart
$objText->getFont()->setName($fontName);
$fontFound = true;
// @codeCoverageIgnoreEnd
}
$fontSize = $fontSize ?? $defaultFontSize;
if (is_int($fontSize)) { if (is_int($fontSize)) {
$objText->getFont()->setSize(floor($fontSize / 100)); $objText->getFont()->setSize(floor($fontSize / 100));
$fontFound = true;
} }
$fontColor = (self::getAttribute($titleDetailElement->rPr, 'color', 'string')); $fontColor = $fontColor ?? $defaultColor;
if ($fontColor !== null) { if ($fontColor !== null) {
$objText->getFont()->setColor(new Color(self::readColor($fontColor))); $objText->getFont()->setColor(new Color($fontColor));
$fontFound = true;
} }
$bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean'); $bold = $bold ?? $defaultBold;
if ($bold !== null) { if ($bold !== null) {
$objText->getFont()->setBold($bold); $objText->getFont()->setBold($bold);
$fontFound = true;
} }
$italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean'); $italic = $italic ?? $defaultItalic;
if ($italic !== null) { if ($italic !== null) {
$objText->getFont()->setItalic($italic); $objText->getFont()->setItalic($italic);
$fontFound = true;
} }
$baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer'); $baseline = $baseline ?? $defaultBaseline;
if ($baseline !== null) { if ($baseline !== null) {
$objText->getFont()->setBaseLine($baseline);
if ($baseline > 0) { if ($baseline > 0) {
$objText->getFont()->setSuperscript(true); $objText->getFont()->setSuperscript(true);
} elseif ($baseline < 0) { } elseif ($baseline < 0) {
$objText->getFont()->setSubscript(true); $objText->getFont()->setSubscript(true);
} }
$fontFound = true;
} }
$underscore = (self::getAttribute($titleDetailElement->rPr, 'u', 'string')); $underscore = $underscore ?? $defaultUnderscore;
if ($underscore !== null) { if ($underscore !== null) {
if ($underscore == 'sng') { if ($underscore == 'sng') {
$objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE); $objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
} elseif ($underscore == 'dbl') { } elseif ($underscore == 'dbl') {
$objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE); $objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
} elseif ($underscore !== '') {
$objText->getFont()->setUnderline($underscore);
} else { } else {
$objText->getFont()->setUnderline(Font::UNDERLINE_NONE); $objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
} }
$fontFound = true;
if ($uSchemeClr) {
$objText->getFont()->setUSchemeClr($uSchemeClr);
}
} }
$strikethrough = (self::getAttribute($titleDetailElement->rPr, 's', 'string')); $strikethrough = $strikethrough ?? $defaultStrikethrough;
if ($strikethrough !== null) { if ($strikethrough !== null) {
$objText->getFont()->setStrikeType($strikethrough);
if ($strikethrough == 'noStrike') { if ($strikethrough == 'noStrike') {
$objText->getFont()->setStrikethrough(false); $objText->getFont()->setStrikethrough(false);
} else { } else {
$objText->getFont()->setStrikethrough(true); $objText->getFont()->setStrikethrough(true);
} }
$fontFound = true;
} }
if ($fontFound === false) {
$objText->setFont(null);
} }
} }
return $value; return $value;
} }
private static function readChartAttributes($chartDetail) /**
* @param null|Layout|SimpleXMLElement $chartDetail
*/
private function readChartAttributes($chartDetail): array
{ {
$plotAttributes = []; $plotAttributes = [];
if (isset($chartDetail->dLbls)) { if (isset($chartDetail->dLbls)) {
@ -570,7 +842,7 @@ class Chart
/** /**
* @param mixed $plotAttributes * @param mixed $plotAttributes
*/ */
private static function setChartAttributes(Layout $plotArea, $plotAttributes): void private function setChartAttributes(Layout $plotArea, $plotAttributes): void
{ {
foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) { foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
switch ($plotAttributeKey) { switch ($plotAttributeKey) {

View File

@ -50,6 +50,8 @@ class Namespaces
const WORKSHEET = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet'; const WORKSHEET = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet';
const CHARTSHEET = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet';
const SCHEMA_MICROSOFT = 'http://schemas.microsoft.com/office/2006/relationships'; const SCHEMA_MICROSOFT = 'http://schemas.microsoft.com/office/2006/relationships';
const EXTENSIBILITY = 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility'; const EXTENSIBILITY = 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility';
@ -74,5 +76,7 @@ class Namespaces
const PURL_DRAWING = 'http://purl.oclc.org/ooxml/drawingml/main'; const PURL_DRAWING = 'http://purl.oclc.org/ooxml/drawingml/main';
const PURL_CHART = 'http://purl.oclc.org/ooxml/drawingml/chart';
const PURL_WORKSHEET = 'http://purl.oclc.org/ooxml/officeDocument/relationships/worksheet'; const PURL_WORKSHEET = 'http://purl.oclc.org/ooxml/officeDocument/relationships/worksheet';
} }

View File

@ -90,9 +90,9 @@ class Xml extends BaseReader
// Retrieve charset encoding // Retrieve charset encoding
if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $data, $matches)) { if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $data, $matches)) {
$charSet = strtoupper($matches[1]); $charSet = strtoupper($matches[1]);
if (1 == preg_match('/^ISO-8859-\d[\dL]?$/i', $charSet)) { if (preg_match('/^ISO-8859-\d[\dL]?$/i', $charSet) === 1) {
$data = StringHelper::convertEncoding($data, 'UTF-8', $charSet); $data = StringHelper::convertEncoding($data, 'UTF-8', $charSet);
$data = preg_replace('/(<?xml.*encoding=[\'"]).*?([\'"].*?>)/um', '$1' . 'UTF-8' . '$2', $data, 1); $data = (string) preg_replace('/(<?xml.*encoding=[\'"]).*?([\'"].*?>)/um', '$1' . 'UTF-8' . '$2', $data, 1);
} }
} }
$this->fileContents = $data; $this->fileContents = $data;

View File

@ -44,7 +44,7 @@ class Properties
foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) { foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
$propertyAttributes = self::getAttributes($propertyValue, $namespaces['dt']); $propertyAttributes = self::getAttributes($propertyValue, $namespaces['dt']);
$propertyName = preg_replace_callback('/_x([0-9a-f]{4})_/i', [$this, 'hex2str'], $propertyName); $propertyName = (string) preg_replace_callback('/_x([0-9a-f]{4})_/i', [$this, 'hex2str'], $propertyName);
$this->processCustomProperty($docProps, $propertyName, $propertyValue, $propertyAttributes); $this->processCustomProperty($docProps, $propertyName, $propertyValue, $propertyAttributes);
} }

View File

@ -687,7 +687,7 @@ class ReferenceHelper
ksort($cellTokens); ksort($cellTokens);
ksort($newCellTokens); ksort($newCellTokens);
} // Update cell references in the formula } // Update cell references in the formula
$formulaBlock = str_replace('\\', '', preg_replace($cellTokens, $newCellTokens, $formulaBlock)); $formulaBlock = str_replace('\\', '', (string) preg_replace($cellTokens, $newCellTokens, $formulaBlock));
} }
} }
} }

View File

@ -219,7 +219,7 @@ class CellMatcher
foreach ($splitCondition as &$value) { foreach ($splitCondition as &$value) {
// Only count/replace in alternating array entries (ie. not in quoted strings) // Only count/replace in alternating array entries (ie. not in quoted strings)
if ($i = !$i) { if ($i = !$i) {
$value = preg_replace_callback( $value = (string) preg_replace_callback(
'/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
[$this, 'conditionCellAdjustment'], [$this, 'conditionCellAdjustment'],
$value $value
@ -287,7 +287,7 @@ class CellMatcher
$conditions = $this->adjustConditionsForCellReferences($conditional->getConditions()); $conditions = $this->adjustConditionsForCellReferences($conditional->getConditions());
$expression = array_pop($conditions); $expression = array_pop($conditions);
$expression = preg_replace( $expression = (string) preg_replace(
'/\b' . $this->referenceCell . '\b/i', '/\b' . $this->referenceCell . '\b/i',
(string) $this->wrapCellValue(), (string) $this->wrapCellValue(),
$expression $expression

View File

@ -133,7 +133,7 @@ abstract class WizardAbstract
foreach ($splitCondition as &$value) { foreach ($splitCondition as &$value) {
// Only count/replace in alternating array entries (ie. not in quoted strings) // Only count/replace in alternating array entries (ie. not in quoted strings)
if ($i = !$i) { if ($i = !$i) {
$value = preg_replace_callback( $value = (string) preg_replace_callback(
'/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
function ($matches) use ($referenceColumnIndex, $referenceRow) { function ($matches) use ($referenceColumnIndex, $referenceRow) {
return self::reverseCellAdjustment($matches, $referenceColumnIndex, $referenceRow); return self::reverseCellAdjustment($matches, $referenceColumnIndex, $referenceRow);
@ -174,7 +174,7 @@ abstract class WizardAbstract
foreach ($splitCondition as &$value) { foreach ($splitCondition as &$value) {
// Only count/replace in alternating array entries (ie. not in quoted strings) // Only count/replace in alternating array entries (ie. not in quoted strings)
if ($i = !$i) { if ($i = !$i) {
$value = preg_replace_callback( $value = (string) preg_replace_callback(
'/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i', '/' . Calculation::CALCULATION_REGEXP_CELLREF_RELATIVE . '/i',
[$this, 'conditionCellAdjustment'], [$this, 'conditionCellAdjustment'],
$value $value

View File

@ -18,6 +18,29 @@ class Font extends Supervisor
*/ */
protected $name = 'Calibri'; protected $name = 'Calibri';
/**
* The following 6 are used only for chart titles, I think.
*
*@var string
*/
private $latin = '';
/** @var string */
private $eastAsian = '';
/** @var string */
private $complexScript = '';
/** @var int */
private $baseLine = 0;
/** @var string */
private $strikeType = '';
/** @var string */
private $uSchemeClr = '';
// end of chart title items
/** /**
* Font Size. * Font Size.
* *
@ -170,6 +193,15 @@ class Font extends Supervisor
if (isset($styleArray['name'])) { if (isset($styleArray['name'])) {
$this->setName($styleArray['name']); $this->setName($styleArray['name']);
} }
if (isset($styleArray['latin'])) {
$this->setLatin($styleArray['latin']);
}
if (isset($styleArray['eastAsian'])) {
$this->setEastAsian($styleArray['eastAsian']);
}
if (isset($styleArray['complexScript'])) {
$this->setComplexScript($styleArray['complexScript']);
}
if (isset($styleArray['bold'])) { if (isset($styleArray['bold'])) {
$this->setBold($styleArray['bold']); $this->setBold($styleArray['bold']);
} }
@ -213,6 +245,33 @@ class Font extends Supervisor
return $this->name; return $this->name;
} }
public function getLatin(): string
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getLatin();
}
return $this->latin;
}
public function getEastAsian(): string
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getEastAsian();
}
return $this->eastAsian;
}
public function getComplexScript(): string
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getComplexScript();
}
return $this->complexScript;
}
/** /**
* Set Name. * Set Name.
* *
@ -235,6 +294,51 @@ class Font extends Supervisor
return $this; return $this;
} }
public function setLatin(string $fontname): self
{
if ($fontname == '') {
$fontname = 'Calibri';
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['latin' => $fontname]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->latin = $fontname;
}
return $this;
}
public function setEastAsian(string $fontname): self
{
if ($fontname == '') {
$fontname = 'Calibri';
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['eastAsian' => $fontname]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->eastAsian = $fontname;
}
return $this;
}
public function setComplexScript(string $fontname): self
{
if ($fontname == '') {
$fontname = 'Calibri';
}
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['complexScript' => $fontname]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->complexScript = $fontname;
}
return $this;
}
/** /**
* Get Size. * Get Size.
* *
@ -418,6 +522,69 @@ class Font extends Supervisor
return $this; return $this;
} }
public function getBaseLine(): int
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getBaseLine();
}
return $this->baseLine;
}
public function setBaseLine(int $baseLine): self
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['baseLine' => $baseLine]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->baseLine = $baseLine;
}
return $this;
}
public function getStrikeType(): string
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getStrikeType();
}
return $this->strikeType;
}
public function setStrikeType(string $strikeType): self
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['strikeType' => $strikeType]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->strikeType = $strikeType;
}
return $this;
}
public function getUSchemeClr(): string
{
if ($this->isSupervisor) {
return $this->getSharedComponent()->getUSchemeClr();
}
return $this->uSchemeClr;
}
public function setUSchemeClr(string $uSchemeClr): self
{
if ($this->isSupervisor) {
$styleArray = $this->getStyleArray(['uSchemeClr' => $uSchemeClr]);
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
} else {
$this->uSchemeClr = $uSchemeClr;
}
return $this;
}
/** /**
* Get Underline. * Get Underline.
* *
@ -546,6 +713,15 @@ class Font extends Supervisor
$this->underline . $this->underline .
($this->strikethrough ? 't' : 'f') . ($this->strikethrough ? 't' : 'f') .
$this->color->getHashCode() . $this->color->getHashCode() .
'*' .
$this->latin .
'*' .
$this->eastAsian .
'*' .
$this->complexScript .
$this->strikeType .
$this->uSchemeClr .
(string) $this->baseLine .
__CLASS__ __CLASS__
); );
} }
@ -553,12 +729,17 @@ class Font extends Supervisor
protected function exportArray1(): array protected function exportArray1(): array
{ {
$exportedArray = []; $exportedArray = [];
$this->exportArray2($exportedArray, 'baseLine', $this->getBaseLine());
$this->exportArray2($exportedArray, 'bold', $this->getBold()); $this->exportArray2($exportedArray, 'bold', $this->getBold());
$this->exportArray2($exportedArray, 'color', $this->getColor()); $this->exportArray2($exportedArray, 'color', $this->getColor());
$this->exportArray2($exportedArray, 'complexScript', $this->getComplexScript());
$this->exportArray2($exportedArray, 'eastAsian', $this->getEastAsian());
$this->exportArray2($exportedArray, 'italic', $this->getItalic()); $this->exportArray2($exportedArray, 'italic', $this->getItalic());
$this->exportArray2($exportedArray, 'latin', $this->getLatin());
$this->exportArray2($exportedArray, 'name', $this->getName()); $this->exportArray2($exportedArray, 'name', $this->getName());
$this->exportArray2($exportedArray, 'size', $this->getSize()); $this->exportArray2($exportedArray, 'size', $this->getSize());
$this->exportArray2($exportedArray, 'strikethrough', $this->getStrikethrough()); $this->exportArray2($exportedArray, 'strikethrough', $this->getStrikethrough());
$this->exportArray2($exportedArray, 'strikeType', $this->getStrikeType());
$this->exportArray2($exportedArray, 'subscript', $this->getSubscript()); $this->exportArray2($exportedArray, 'subscript', $this->getSubscript());
$this->exportArray2($exportedArray, 'superscript', $this->getSuperscript()); $this->exportArray2($exportedArray, 'superscript', $this->getSuperscript());
$this->exportArray2($exportedArray, 'underline', $this->getUnderline()); $this->exportArray2($exportedArray, 'underline', $this->getUnderline());

View File

@ -129,11 +129,10 @@ class DateFormatter
// but we don't want to change any quoted strings // but we don't want to change any quoted strings
/** @var callable */ /** @var callable */
$callable = [self::class, 'setLowercaseCallback']; $callable = [self::class, 'setLowercaseCallback'];
$format = preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', $callable, $format); $format = (string) preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', $callable, $format);
// Only process the non-quoted blocks for date format characters // Only process the non-quoted blocks for date format characters
/** @phpstan-ignore-next-line */
$blocks = explode('"', $format); $blocks = explode('"', $format);
foreach ($blocks as $key => &$block) { foreach ($blocks as $key => &$block) {
if ($key % 2 == 0) { if ($key % 2 == 0) {

View File

@ -51,12 +51,12 @@ class Formatter
for ($idx = 0; $idx < $cnt; ++$idx) { for ($idx = 0; $idx < $cnt; ++$idx) {
if (preg_match($color_regex, $sections[$idx], $matches)) { if (preg_match($color_regex, $sections[$idx], $matches)) {
$colors[$idx] = $matches[0]; $colors[$idx] = $matches[0];
$sections[$idx] = preg_replace($color_regex, '', $sections[$idx]); $sections[$idx] = (string) preg_replace($color_regex, '', $sections[$idx]);
} }
if (preg_match($cond_regex, $sections[$idx], $matches)) { if (preg_match($cond_regex, $sections[$idx], $matches)) {
$condops[$idx] = $matches[1]; $condops[$idx] = $matches[1];
$condvals[$idx] = $matches[2]; $condvals[$idx] = $matches[2];
$sections[$idx] = preg_replace($cond_regex, '', $sections[$idx]); $sections[$idx] = (string) preg_replace($cond_regex, '', $sections[$idx]);
} }
} }
$color = $colors[0]; $color = $colors[0];
@ -112,7 +112,7 @@ class Formatter
return $value; return $value;
} }
$format = preg_replace_callback( $format = (string) preg_replace_callback(
'/(["])(?:(?=(\\\\?))\\2.)*?\\1/u', '/(["])(?:(?=(\\\\?))\\2.)*?\\1/u',
function ($matches) { function ($matches) {
return str_replace('.', chr(0x00), $matches[0]); return str_replace('.', chr(0x00), $matches[0]);
@ -121,7 +121,7 @@ class Formatter
); );
// Convert any other escaped characters to quoted strings, e.g. (\T to "T") // Convert any other escaped characters to quoted strings, e.g. (\T to "T")
$format = preg_replace('/(\\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/ui', '"${2}"', $format); $format = (string) preg_replace('/(\\\(((.)(?!((AM\/PM)|(A\/P))))|([^ ])))(?=(?:[^"]|"[^"]*")*$)/ui', '"${2}"', $format);
// Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal) // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal)
$sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format); $sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format);
@ -130,7 +130,7 @@ class Formatter
// In Excel formats, "_" is used to add spacing, // In Excel formats, "_" is used to add spacing,
// The following character indicates the size of the spacing, which we can't do in HTML, so we just use a standard space // The following character indicates the size of the spacing, which we can't do in HTML, so we just use a standard space
$format = preg_replace('/_.?/ui', ' ', $format); $format = (string) preg_replace('/_.?/ui', ' ', $format);
// Let's begin inspecting the format and converting the value to a formatted string // Let's begin inspecting the format and converting the value to a formatted string

View File

@ -35,7 +35,7 @@ class PercentageFormatter extends BaseFormatter
$wholePartSize += $decimalPartSize; $wholePartSize += $decimalPartSize;
$replacement = "{$wholePartSize}.{$decimalPartSize}"; $replacement = "{$wholePartSize}.{$decimalPartSize}";
$mask = preg_replace('/[#0,]+\.?[?#0,]*/ui', "%{$replacement}f{$placeHolders}", $format); $mask = (string) preg_replace('/[#0,]+\.?[?#0,]*/ui', "%{$replacement}f{$placeHolders}", $format);
/** @var float */ /** @var float */
$valueFloat = $value; $valueFloat = $value;

View File

@ -145,7 +145,7 @@ class AutoFilter
$this->evaluated = false; $this->evaluated = false;
if ($this->workSheet !== null) { if ($this->workSheet !== null) {
$thisrange = $this->range; $thisrange = $this->range;
$range = preg_replace('/\\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange) ?? ''; $range = (string) preg_replace('/\\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange);
if ($range !== $thisrange) { if ($range !== $thisrange) {
$this->setRange($range); $this->setRange($range);
} }

View File

@ -212,7 +212,7 @@ class Table
{ {
if ($this->workSheet !== null) { if ($this->workSheet !== null) {
$thisrange = $this->range; $thisrange = $this->range;
$range = preg_replace('/\\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange) ?? ''; $range = (string) preg_replace('/\\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange);
if ($range !== $thisrange) { if ($range !== $thisrange) {
$this->setRange($range); $this->setRange($range);
} }

View File

@ -42,10 +42,9 @@ class Validations
public static function validateCellOrCellRange($cellRange): string public static function validateCellOrCellRange($cellRange): string
{ {
if (is_string($cellRange) || is_numeric($cellRange)) { if (is_string($cellRange) || is_numeric($cellRange)) {
// Convert a single column reference like 'A' to 'A:A' // Convert a single column reference like 'A' to 'A:A',
$cellRange = (string) preg_replace('/^([A-Z]+)$/', '${1}:${1}', (string) $cellRange); // a single row reference like '1' to '1:1'
// Convert a single row reference like '1' to '1:1' $cellRange = (string) preg_replace('/^([A-Z]+|\d+)$/', '${1}:${1}', (string) $cellRange);
$cellRange = (string) preg_replace('/^(\d+)$/', '${1}:${1}', $cellRange);
} elseif (is_object($cellRange) && $cellRange instanceof CellAddress) { } elseif (is_object($cellRange) && $cellRange instanceof CellAddress) {
$cellRange = new CellRange($cellRange, $cellRange); $cellRange = new CellRange($cellRange, $cellRange);
} }
@ -66,9 +65,12 @@ class Validations
[$worksheet, $addressRange] = Worksheet::extractSheetTitle($cellRange, true); [$worksheet, $addressRange] = Worksheet::extractSheetTitle($cellRange, true);
// Convert Column ranges like 'A:C' to 'A1:C1048576' // Convert Column ranges like 'A:C' to 'A1:C1048576'
$addressRange = (string) preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $addressRange); // or Row ranges like '1:3' to 'A1:XFD3'
// Convert Row ranges like '1:3' to 'A1:XFD3' $addressRange = (string) preg_replace(
$addressRange = (string) preg_replace('/^(\\d+):(\\d+)$/', 'A${1}:XFD${2}', $addressRange); ['/^([A-Z]+):([A-Z]+)$/i', '/^(\\d+):(\\d+)$/'],
['${1}1:${2}1048576', 'A${1}:XFD${2}'],
$addressRange
);
return empty($worksheet) ? strtoupper($addressRange) : $worksheet . '!' . strtoupper($addressRange); return empty($worksheet) ? strtoupper($addressRange) : $worksheet . '!' . strtoupper($addressRange);
} }

View File

@ -662,13 +662,13 @@ class Html extends BaseWriter
$filename = $drawing->getPath(); $filename = $drawing->getPath();
// Strip off eventual '.' // Strip off eventual '.'
$filename = preg_replace('/^[.]/', '', $filename); $filename = (string) preg_replace('/^[.]/', '', $filename);
// Prepend images root // Prepend images root
$filename = $this->getImagesRoot() . $filename; $filename = $this->getImagesRoot() . $filename;
// Strip off eventual '.' if followed by non-/ // Strip off eventual '.' if followed by non-/
$filename = preg_replace('@^[.]([^/])@', '$1', $filename); $filename = (string) preg_replace('@^[.]([^/])@', '$1', $filename);
// Convert UTF8 data to PCDATA // Convert UTF8 data to PCDATA
$filename = htmlspecialchars($filename, Settings::htmlEntityFlags()); $filename = htmlspecialchars($filename, Settings::htmlEntityFlags());
@ -1326,7 +1326,7 @@ class Html extends BaseWriter
// Converts the cell content so that spaces occuring at beginning of each new line are replaced by &nbsp; // Converts the cell content so that spaces occuring at beginning of each new line are replaced by &nbsp;
// Example: " Hello\n to the world" is converted to "&nbsp;&nbsp;Hello\n&nbsp;to the world" // Example: " Hello\n to the world" is converted to "&nbsp;&nbsp;Hello\n&nbsp;to the world"
$cellData = preg_replace('/(?m)(?:^|\\G) /', '&nbsp;', $cellData); $cellData = (string) preg_replace('/(?m)(?:^|\\G) /', '&nbsp;', $cellData);
// convert newline "\n" to '<br>' // convert newline "\n" to '<br>'
$cellData = nl2br($cellData); $cellData = nl2br($cellData);

View File

@ -10,12 +10,14 @@ use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\Style as CellStyle; use PhpOffice\PhpSpreadsheet\Style\Style as CellStyle;
use PhpOffice\PhpSpreadsheet\Worksheet\ColumnDimension; use PhpOffice\PhpSpreadsheet\Worksheet\ColumnDimension;
use PhpOffice\PhpSpreadsheet\Worksheet\RowDimension; use PhpOffice\PhpSpreadsheet\Worksheet\RowDimension;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Style class Style
{ {
public const CELL_STYLE_PREFIX = 'ce'; public const CELL_STYLE_PREFIX = 'ce';
public const COLUMN_STYLE_PREFIX = 'co'; public const COLUMN_STYLE_PREFIX = 'co';
public const ROW_STYLE_PREFIX = 'ro'; public const ROW_STYLE_PREFIX = 'ro';
public const TABLE_STYLE_PREFIX = 'ta';
private $writer; private $writer;
@ -221,6 +223,26 @@ class Style
$this->writer->endElement(); // Close style:style $this->writer->endElement(); // Close style:style
} }
public function writeTableStyle(Worksheet $worksheet, int $sheetId): void
{
$this->writer->startElement('style:style');
$this->writer->writeAttribute('style:family', 'table');
$this->writer->writeAttribute(
'style:name',
sprintf('%s%d', self::TABLE_STYLE_PREFIX, $sheetId)
);
$this->writer->startElement('style:table-properties');
$this->writer->writeAttribute(
'table:display',
$worksheet->getSheetState() === Worksheet::SHEETSTATE_VISIBLE ? 'true' : 'false'
);
$this->writer->endElement(); // Close style:table-properties
$this->writer->endElement(); // Close style:style
}
public function write(CellStyle $style): void public function write(CellStyle $style): void
{ {
$this->writer->startElement('style:style'); $this->writer->startElement('style:style');

View File

@ -123,6 +123,7 @@ class Content extends WriterPart
for ($sheetIndex = 0; $sheetIndex < $sheetCount; ++$sheetIndex) { for ($sheetIndex = 0; $sheetIndex < $sheetCount; ++$sheetIndex) {
$objWriter->startElement('table:table'); $objWriter->startElement('table:table');
$objWriter->writeAttribute('table:name', $spreadsheet->getSheet($sheetIndex)->getTitle()); $objWriter->writeAttribute('table:name', $spreadsheet->getSheet($sheetIndex)->getTitle());
$objWriter->writeAttribute('table:style-name', Style::TABLE_STYLE_PREFIX . (string) ($sheetIndex + 1));
$objWriter->writeElement('office:forms'); $objWriter->writeElement('office:forms');
foreach ($spreadsheet->getSheet($sheetIndex)->getColumnDimensions() as $columnDimension) { foreach ($spreadsheet->getSheet($sheetIndex)->getColumnDimensions() as $columnDimension) {
$objWriter->startElement('table:table-column'); $objWriter->startElement('table:table-column');
@ -289,6 +290,8 @@ class Content extends WriterPart
$sheetCount = $spreadsheet->getSheetCount(); $sheetCount = $spreadsheet->getSheetCount();
for ($i = 0; $i < $sheetCount; ++$i) { for ($i = 0; $i < $sheetCount; ++$i) {
$worksheet = $spreadsheet->getSheet($i); $worksheet = $spreadsheet->getSheet($i);
$styleWriter->writeTableStyle($worksheet, $i + 1);
$worksheet->calculateColumnWidths(); $worksheet->calculateColumnWidths();
foreach ($worksheet->getColumnDimensions() as $columnDimension) { foreach ($worksheet->getColumnDimensions() as $columnDimension) {
if ($columnDimension->getWidth() !== -1.0) { if ($columnDimension->getWidth() !== -1.0) {

View File

@ -778,8 +778,7 @@ class Parser
*/ */
private function getRefIndex($ext_ref) private function getRefIndex($ext_ref)
{ {
$ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any. $ext_ref = (string) preg_replace(["/^'/", "/'$/"], ['', ''], $ext_ref); // Remove leading and trailing ' if any.
$ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any.
$ext_ref = str_replace('\'\'', '\'', $ext_ref); // Replace escaped '' with ' $ext_ref = str_replace('\'\'', '\'', $ext_ref); // Replace escaped '' with '
// Check if there is a sheet range eg., Sheet1:Sheet2. // Check if there is a sheet range eg., Sheet1:Sheet2.

View File

@ -1039,7 +1039,7 @@ class Worksheet extends BIFFwriter
$record = 0x01B8; // Record identifier $record = 0x01B8; // Record identifier
// Strip URL type // Strip URL type
$url = preg_replace('/^internal:/', '', $url); $url = (string) preg_replace('/^internal:/', '', $url);
// Pack the undocumented parts of the hyperlink stream // Pack the undocumented parts of the hyperlink stream
$unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000'); $unknown1 = pack('H*', 'D0C9EA79F9BACE118C8200AA004BA90B02000000');
@ -1095,8 +1095,7 @@ class Worksheet extends BIFFwriter
// Strip URL type and change Unix dir separator to Dos style (if needed) // Strip URL type and change Unix dir separator to Dos style (if needed)
// //
$url = preg_replace('/^external:/', '', $url); $url = (string) preg_replace(['/^external:/', '/\//'], ['', '\\'], $url);
$url = preg_replace('/\//', '\\', $url);
// Determine if the link is relative or absolute: // Determine if the link is relative or absolute:
// relative if link contains no dir separator, "somefile.xls" // relative if link contains no dir separator, "somefile.xls"
@ -1125,7 +1124,7 @@ class Worksheet extends BIFFwriter
$up_count = pack('v', $up_count); $up_count = pack('v', $up_count);
// Store the short dos dir name (null terminated) // Store the short dos dir name (null terminated)
$dir_short = preg_replace('/\\.\\.\\\\/', '', $dir_long) . "\0"; $dir_short = (string) preg_replace('/\\.\\.\\\\/', '', $dir_long) . "\0";
// Store the long dir name as a wchar string (non-null terminated) // Store the long dir name as a wchar string (non-null terminated)
$dir_long = $dir_long . "\0"; $dir_long = $dir_long . "\0";

View File

@ -10,7 +10,6 @@ use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend; use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea; use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter; use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
@ -75,6 +74,33 @@ class Chart extends WriterPart
$objWriter->writeAttribute('val', 0); $objWriter->writeAttribute('val', 0);
$objWriter->endElement(); $objWriter->endElement();
$objWriter->startElement('c:view3D');
$rotX = $chart->getRotX();
if (is_int($rotX)) {
$objWriter->startElement('c:rotX');
$objWriter->writeAttribute('val', "$rotX");
$objWriter->endElement();
}
$rotY = $chart->getRotY();
if (is_int($rotY)) {
$objWriter->startElement('c:rotY');
$objWriter->writeAttribute('val', "$rotY");
$objWriter->endElement();
}
$rAngAx = $chart->getRAngAx();
if (is_int($rAngAx)) {
$objWriter->startElement('c:rAngAx');
$objWriter->writeAttribute('val', "$rAngAx");
$objWriter->endElement();
}
$perspective = $chart->getPerspective();
if (is_int($perspective)) {
$objWriter->startElement('c:perspective');
$objWriter->writeAttribute('val', "$perspective");
$objWriter->endElement();
}
$objWriter->endElement(); // view3D
$this->writePlotArea($objWriter, $chart->getPlotArea(), $chart->getXAxisLabel(), $chart->getYAxisLabel(), $chart->getChartAxisX(), $chart->getChartAxisY(), $chart->getMajorGridlines(), $chart->getMinorGridlines()); $this->writePlotArea($objWriter, $chart->getPlotArea(), $chart->getXAxisLabel(), $chart->getYAxisLabel(), $chart->getChartAxisX(), $chart->getChartAxisY(), $chart->getMajorGridlines(), $chart->getMinorGridlines());
$this->writeLegend($objWriter, $chart->getLegend()); $this->writeLegend($objWriter, $chart->getLegend());
@ -121,6 +147,10 @@ class Chart extends WriterPart
$objWriter->endElement(); $objWriter->endElement();
$objWriter->startElement('a:p'); $objWriter->startElement('a:p');
$objWriter->startElement('a:pPr');
$objWriter->startElement('a:defRPr');
$objWriter->endElement();
$objWriter->endElement();
$caption = $title->getCaption(); $caption = $title->getCaption();
if ((is_array($caption)) && (count($caption) > 0)) { if ((is_array($caption)) && (count($caption) > 0)) {
@ -196,7 +226,7 @@ class Chart extends WriterPart
return; return;
} }
$id1 = $id2 = 0; $id1 = $id2 = $id3 = '0';
$this->seriesIndex = 0; $this->seriesIndex = 0;
$objWriter->startElement('c:plotArea'); $objWriter->startElement('c:plotArea');
@ -226,6 +256,10 @@ class Chart extends WriterPart
$objWriter->startElement('c:scatterStyle'); $objWriter->startElement('c:scatterStyle');
$objWriter->writeAttribute('val', $plotStyle); $objWriter->writeAttribute('val', $plotStyle);
$objWriter->endElement(); $objWriter->endElement();
} elseif ($groupType === DataSeries::TYPE_SURFACECHART_3D || $groupType === DataSeries::TYPE_SURFACECHART) {
$objWriter->startElement('c:wireframe');
$objWriter->writeAttribute('val', $plotStyle ? '1' : '0');
$objWriter->endElement();
} }
$this->writePlotGroup($plotGroup, $chartType, $objWriter, $catIsMultiLevelSeries, $valIsMultiLevelSeries, $plotGroupingType); $this->writePlotGroup($plotGroup, $chartType, $objWriter, $catIsMultiLevelSeries, $valIsMultiLevelSeries, $plotGroupingType);
@ -250,9 +284,12 @@ class Chart extends WriterPart
$objWriter->endElement(); $objWriter->endElement();
} }
} elseif ($chartType === DataSeries::TYPE_BUBBLECHART) { } elseif ($chartType === DataSeries::TYPE_BUBBLECHART) {
$scale = ($plotGroup === null) ? '' : (string) $plotGroup->getPlotStyle();
if ($scale !== '') {
$objWriter->startElement('c:bubbleScale'); $objWriter->startElement('c:bubbleScale');
$objWriter->writeAttribute('val', 25); $objWriter->writeAttribute('val', $scale);
$objWriter->endElement(); $objWriter->endElement();
}
$objWriter->startElement('c:showNegBubbles'); $objWriter->startElement('c:showNegBubbles');
$objWriter->writeAttribute('val', 0); $objWriter->writeAttribute('val', 0);
@ -276,9 +313,10 @@ class Chart extends WriterPart
$objWriter->endElement(); $objWriter->endElement();
} }
// Generate 2 unique numbers to use for axId values // Generate 3 unique numbers to use for axId values
$id1 = '75091328'; $id1 = '110438656';
$id2 = '75089408'; $id2 = '110444544';
$id3 = '110365312'; // used in Surface Chart
if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) { if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) {
$objWriter->startElement('c:axId'); $objWriter->startElement('c:axId');
@ -287,6 +325,11 @@ class Chart extends WriterPart
$objWriter->startElement('c:axId'); $objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id2); $objWriter->writeAttribute('val', $id2);
$objWriter->endElement(); $objWriter->endElement();
if ($chartType === DataSeries::TYPE_SURFACECHART_3D || $chartType === DataSeries::TYPE_SURFACECHART) {
$objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id3);
$objWriter->endElement();
}
} else { } else {
$objWriter->startElement('c:firstSliceAng'); $objWriter->startElement('c:firstSliceAng');
$objWriter->writeAttribute('val', 0); $objWriter->writeAttribute('val', 0);
@ -304,12 +347,15 @@ class Chart extends WriterPart
if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) { if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) {
if ($chartType === DataSeries::TYPE_BUBBLECHART) { if ($chartType === DataSeries::TYPE_BUBBLECHART) {
$this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id1, $id2, $catIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines); $this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id2, $id1, $catIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines);
} else { } else {
$this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis); $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis);
} }
$this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis, $majorGridlines, $minorGridlines); $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis, $majorGridlines, $minorGridlines);
if ($chartType === DataSeries::TYPE_SURFACECHART_3D || $chartType === DataSeries::TYPE_SURFACECHART) {
$this->writeSerAxis($objWriter, $id2, $id3);
}
} }
$objWriter->endElement(); $objWriter->endElement();
@ -369,9 +415,15 @@ class Chart extends WriterPart
*/ */
private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis): void private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis): void
{ {
// N.B. writeCategoryAxis may be invoked with the last parameter($yAxis) using $xAxis for ScatterChart, etc
// In that case, xAxis is NOT a category.
if ($yAxis->getAxisIsNumericFormat()) {
$objWriter->startElement('c:valAx');
} else {
$objWriter->startElement('c:catAx'); $objWriter->startElement('c:catAx');
}
if ($id1 > 0) { if ($id1 !== '0') {
$objWriter->startElement('c:axId'); $objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id1); $objWriter->writeAttribute('val', $id1);
$objWriter->endElement(); $objWriter->endElement();
@ -403,20 +455,20 @@ class Chart extends WriterPart
$objWriter->endElement(); $objWriter->endElement();
$objWriter->startElement('a:p'); $objWriter->startElement('a:p');
$objWriter->startElement('a:r'); $objWriter->startElement('a:pPr');
$objWriter->startElement('a:defRPr');
$objWriter->endElement();
$objWriter->endElement();
$caption = $xAxisLabel->getCaption(); $caption = $xAxisLabel->getCaption();
if (is_array($caption)) { if (is_array($caption)) {
$caption = $caption[0]; $caption = $caption[0];
} }
$objWriter->startElement('a:t'); $this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a');
$objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($caption));
$objWriter->endElement();
$objWriter->endElement(); $objWriter->endElement();
$objWriter->endElement(); $objWriter->endElement();
$objWriter->endElement(); $objWriter->endElement();
$objWriter->endElement();
$layout = $xAxisLabel->getLayout(); $layout = $xAxisLabel->getLayout();
$this->writeLayout($objWriter, $layout); $this->writeLayout($objWriter, $layout);
@ -445,7 +497,7 @@ class Chart extends WriterPart
$objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('axis_labels')); $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('axis_labels'));
$objWriter->endElement(); $objWriter->endElement();
if ($id2 > 0) { if ($id2 !== '0') {
$objWriter->startElement('c:crossAx'); $objWriter->startElement('c:crossAx');
$objWriter->writeAttribute('val', $id2); $objWriter->writeAttribute('val', $id2);
$objWriter->endElement(); $objWriter->endElement();
@ -487,7 +539,7 @@ class Chart extends WriterPart
{ {
$objWriter->startElement('c:valAx'); $objWriter->startElement('c:valAx');
if ($id2 > 0) { if ($id2 !== '0') {
$objWriter->startElement('c:axId'); $objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id2); $objWriter->writeAttribute('val', $id2);
$objWriter->endElement(); $objWriter->endElement();
@ -746,18 +798,17 @@ class Chart extends WriterPart
$objWriter->endElement(); $objWriter->endElement();
$objWriter->startElement('a:p'); $objWriter->startElement('a:p');
$objWriter->startElement('a:r'); $objWriter->startElement('a:pPr');
$objWriter->startElement('a:defRPr');
$objWriter->endElement();
$objWriter->endElement();
$caption = $yAxisLabel->getCaption(); $caption = $yAxisLabel->getCaption();
if (is_array($caption)) { if (is_array($caption)) {
$caption = $caption[0]; $caption = $caption[0];
} }
$this->getParentWriter()->getWriterPartstringtable()->writeRichTextForCharts($objWriter, $caption, 'a');
$objWriter->startElement('a:t');
$objWriter->writeRawData(StringHelper::controlCharacterPHP2OOXML($caption));
$objWriter->endElement();
$objWriter->endElement();
$objWriter->endElement(); $objWriter->endElement();
$objWriter->endElement(); $objWriter->endElement();
$objWriter->endElement(); $objWriter->endElement();
@ -913,9 +964,9 @@ class Chart extends WriterPart
$objWriter->endElement(); //effectList $objWriter->endElement(); //effectList
$objWriter->endElement(); //end spPr $objWriter->endElement(); //end spPr
if ($id1 > 0) { if ($id1 !== '0') {
$objWriter->startElement('c:crossAx'); $objWriter->startElement('c:crossAx');
$objWriter->writeAttribute('val', $id2); $objWriter->writeAttribute('val', $id1);
$objWriter->endElement(); $objWriter->endElement();
if ($xAxis->getAxisOptionsProperty('horizontal_crosses_value') !== null) { if ($xAxis->getAxisOptionsProperty('horizontal_crosses_value') !== null) {
@ -956,6 +1007,54 @@ class Chart extends WriterPart
$objWriter->endElement(); $objWriter->endElement();
} }
/**
* Write Ser Axis, for Surface chart.
*/
private function writeSerAxis(XMLWriter $objWriter, string $id2, string $id3): void
{
$objWriter->startElement('c:serAx');
$objWriter->startElement('c:axId');
$objWriter->writeAttribute('val', $id3);
$objWriter->endElement(); // axId
$objWriter->startElement('c:scaling');
$objWriter->startElement('c:orientation');
$objWriter->writeAttribute('val', 'minMax');
$objWriter->endElement(); // orientation
$objWriter->endElement(); // scaling
$objWriter->startElement('c:delete');
$objWriter->writeAttribute('val', '0');
$objWriter->endElement(); // delete
$objWriter->startElement('c:axPos');
$objWriter->writeAttribute('val', 'b');
$objWriter->endElement(); // axPos
$objWriter->startElement('c:majorTickMark');
$objWriter->writeAttribute('val', 'out');
$objWriter->endElement(); // majorTickMark
$objWriter->startElement('c:minorTickMark');
$objWriter->writeAttribute('val', 'none');
$objWriter->endElement(); // minorTickMark
$objWriter->startElement('c:tickLblPos');
$objWriter->writeAttribute('val', 'nextTo');
$objWriter->endElement(); // tickLblPos
$objWriter->startElement('c:crossAx');
$objWriter->writeAttribute('val', $id2);
$objWriter->endElement(); // crossAx
$objWriter->startElement('c:crosses');
$objWriter->writeAttribute('val', 'autoZero');
$objWriter->endElement(); // crosses
$objWriter->endElement(); //serAx
}
/** /**
* Get the data series type(s) for a chart plot series. * Get the data series type(s) for a chart plot series.
* *
@ -1016,7 +1115,7 @@ class Chart extends WriterPart
* @param bool $valIsMultiLevelSeries Is value set a multi-series set * @param bool $valIsMultiLevelSeries Is value set a multi-series set
* @param string $plotGroupingType Type of grouping for multi-series values * @param string $plotGroupingType Type of grouping for multi-series values
*/ */
private function writePlotGroup(?DataSeries $plotGroup, $groupType, XMLWriter $objWriter, &$catIsMultiLevelSeries, &$valIsMultiLevelSeries, &$plotGroupingType): void private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWriter $objWriter, &$catIsMultiLevelSeries, &$valIsMultiLevelSeries, &$plotGroupingType): void
{ {
if ($plotGroup === null) { if ($plotGroup === null) {
return; return;
@ -1104,16 +1203,29 @@ class Chart extends WriterPart
} }
// Formatting for the points // Formatting for the points
if (($groupType == DataSeries::TYPE_LINECHART) || ($groupType == DataSeries::TYPE_STOCKCHART)) { if (
$groupType == DataSeries::TYPE_LINECHART
|| $groupType == DataSeries::TYPE_STOCKCHART
|| ($groupType === DataSeries::TYPE_SCATTERCHART && $plotSeriesValues !== false && !$plotSeriesValues->getScatterLines())
|| ($plotSeriesValues !== false && $plotSeriesValues->getSchemeClr())
) {
$plotLineWidth = 12700; $plotLineWidth = 12700;
if ($plotSeriesValues) { if ($plotSeriesValues) {
$plotLineWidth = $plotSeriesValues->getLineWidth(); $plotLineWidth = $plotSeriesValues->getLineWidth();
} }
$objWriter->startElement('c:spPr'); $objWriter->startElement('c:spPr');
$schemeClr = $plotLabel ? $plotLabel->getSchemeClr() : null;
if ($schemeClr) {
$objWriter->startElement('a:solidFill');
$objWriter->startElement('a:schemeClr');
$objWriter->writeAttribute('val', $schemeClr);
$objWriter->endElement();
$objWriter->endElement();
}
$objWriter->startElement('a:ln'); $objWriter->startElement('a:ln');
$objWriter->writeAttribute('w', $plotLineWidth); $objWriter->writeAttribute('w', $plotLineWidth);
if ($groupType == DataSeries::TYPE_STOCKCHART) { if ($groupType == DataSeries::TYPE_STOCKCHART || $groupType === DataSeries::TYPE_SCATTERCHART) {
$objWriter->startElement('a:noFill'); $objWriter->startElement('a:noFill');
$objWriter->endElement(); $objWriter->endElement();
} elseif ($plotLabel) { } elseif ($plotLabel) {
@ -1142,6 +1254,16 @@ class Chart extends WriterPart
$objWriter->startElement('c:size'); $objWriter->startElement('c:size');
$objWriter->writeAttribute('val', (string) $plotSeriesValues->getPointSize()); $objWriter->writeAttribute('val', (string) $plotSeriesValues->getPointSize());
$objWriter->endElement(); $objWriter->endElement();
$fillColor = $plotSeriesValues->getFillColor();
if (is_string($fillColor) && $fillColor !== '') {
$objWriter->startElement('c:spPr');
$objWriter->startElement('a:solidFill');
$objWriter->startElement('a:srgbClr');
$objWriter->writeAttribute('val', $fillColor);
$objWriter->endElement(); // srgbClr
$objWriter->endElement(); // solidFill
$objWriter->endElement(); // spPr
}
} }
$objWriter->endElement(); $objWriter->endElement();
@ -1176,7 +1298,14 @@ class Chart extends WriterPart
$objWriter->startElement('c:cat'); $objWriter->startElement('c:cat');
} }
// xVals (Categories) are not always 'str'
// Test X-axis Label's Datatype to decide 'str' vs 'num'
$CategoryDatatype = $plotSeriesCategory->getDataType();
if ($CategoryDatatype == DataSeriesValues::DATASERIES_TYPE_NUMBER) {
$this->writePlotSeriesValues($plotSeriesCategory, $objWriter, $groupType, 'num');
} else {
$this->writePlotSeriesValues($plotSeriesCategory, $objWriter, $groupType, 'str'); $this->writePlotSeriesValues($plotSeriesCategory, $objWriter, $groupType, 'str');
}
$objWriter->endElement(); $objWriter->endElement();
} }
@ -1192,11 +1321,32 @@ class Chart extends WriterPart
$this->writePlotSeriesValues($plotSeriesValues, $objWriter, $groupType, 'num'); $this->writePlotSeriesValues($plotSeriesValues, $objWriter, $groupType, 'num');
$objWriter->endElement(); $objWriter->endElement();
if ($groupType === DataSeries::TYPE_SCATTERCHART && $plotGroup->getPlotStyle() === 'smoothMarker') {
$objWriter->startElement('c:smooth');
$objWriter->writeAttribute('val', '1');
$objWriter->endElement();
}
} }
if ($groupType === DataSeries::TYPE_BUBBLECHART) { if ($groupType === DataSeries::TYPE_BUBBLECHART) {
if (!empty($plotGroup->getPlotBubbleSizes()[$plotSeriesIdx])) {
$objWriter->startElement('c:bubbleSize');
$this->writePlotSeriesValues(
$plotGroup->getPlotBubbleSizes()[$plotSeriesIdx],
$objWriter,
$groupType,
'num'
);
$objWriter->endElement();
if ($plotSeriesValues !== false) {
$objWriter->startElement('c:bubble3D');
$objWriter->writeAttribute('val', $plotSeriesValues->getBubble3D() ? '1' : '0');
$objWriter->endElement();
}
} else {
$this->writeBubbles($plotSeriesValues, $objWriter); $this->writeBubbles($plotSeriesValues, $objWriter);
} }
}
$objWriter->endElement(); $objWriter->endElement();
} }
@ -1289,6 +1439,10 @@ class Chart extends WriterPart
$objWriter->writeRawData($plotSeriesValues->getDataSource()); $objWriter->writeRawData($plotSeriesValues->getDataSource());
$objWriter->endElement(); $objWriter->endElement();
$count = $plotSeriesValues->getPointCount();
$source = $plotSeriesValues->getDataSource();
$values = $plotSeriesValues->getDataValues();
if ($count > 1 || ($count === 1 && "=$source" !== (string) $values[0])) {
$objWriter->startElement('c:' . $dataType . 'Cache'); $objWriter->startElement('c:' . $dataType . 'Cache');
if (($groupType != DataSeries::TYPE_PIECHART) && ($groupType != DataSeries::TYPE_PIECHART_3D) && ($groupType != DataSeries::TYPE_DONUTCHART)) { if (($groupType != DataSeries::TYPE_PIECHART) && ($groupType != DataSeries::TYPE_PIECHART_3D) && ($groupType != DataSeries::TYPE_DONUTCHART)) {
@ -1318,9 +1472,10 @@ class Chart extends WriterPart
} }
} }
$objWriter->endElement(); $objWriter->endElement(); // *Cache
}
$objWriter->endElement(); $objWriter->endElement(); // *Ref
} }
} }
@ -1362,7 +1517,7 @@ class Chart extends WriterPart
$objWriter->endElement(); $objWriter->endElement();
$objWriter->startElement('c:bubble3D'); $objWriter->startElement('c:bubble3D');
$objWriter->writeAttribute('val', 0); $objWriter->writeAttribute('val', $plotSeriesValues->getBubble3D() ? '1' : '0');
$objWriter->endElement(); $objWriter->endElement();
} }

View File

@ -89,6 +89,9 @@ class Drawing extends WriterPart
$tl = $chart->getTopLeftPosition(); $tl = $chart->getTopLeftPosition();
$tlColRow = Coordinate::indexesFromString($tl['cell']); $tlColRow = Coordinate::indexesFromString($tl['cell']);
$br = $chart->getBottomRightPosition(); $br = $chart->getBottomRightPosition();
$isTwoCellAnchor = $br['cell'] !== '';
if ($isTwoCellAnchor) {
$brColRow = Coordinate::indexesFromString($br['cell']); $brColRow = Coordinate::indexesFromString($br['cell']);
$objWriter->startElement('xdr:twoCellAnchor'); $objWriter->startElement('xdr:twoCellAnchor');
@ -105,6 +108,30 @@ class Drawing extends WriterPart
$objWriter->writeElement('xdr:row', (string) ($brColRow[1] - 1)); $objWriter->writeElement('xdr:row', (string) ($brColRow[1] - 1));
$objWriter->writeElement('xdr:rowOff', self::stringEmu($br['yOffset'])); $objWriter->writeElement('xdr:rowOff', self::stringEmu($br['yOffset']));
$objWriter->endElement(); $objWriter->endElement();
} elseif ($chart->getOneCellAnchor()) {
$objWriter->startElement('xdr:oneCellAnchor');
$objWriter->startElement('xdr:from');
$objWriter->writeElement('xdr:col', (string) ($tlColRow[0] - 1));
$objWriter->writeElement('xdr:colOff', self::stringEmu($tl['xOffset']));
$objWriter->writeElement('xdr:row', (string) ($tlColRow[1] - 1));
$objWriter->writeElement('xdr:rowOff', self::stringEmu($tl['yOffset']));
$objWriter->endElement();
$objWriter->startElement('xdr:ext');
$objWriter->writeAttribute('cx', self::stringEmu($br['xOffset']));
$objWriter->writeAttribute('cy', self::stringEmu($br['yOffset']));
$objWriter->endElement();
} else {
$objWriter->startElement('xdr:absoluteAnchor');
$objWriter->startElement('xdr:pos');
$objWriter->writeAttribute('x', '0');
$objWriter->writeAttribute('y', '0');
$objWriter->endElement();
$objWriter->startElement('xdr:ext');
$objWriter->writeAttribute('cx', self::stringEmu($br['xOffset']));
$objWriter->writeAttribute('cy', self::stringEmu($br['yOffset']));
$objWriter->endElement();
}
$objWriter->startElement('xdr:graphicFrame'); $objWriter->startElement('xdr:graphicFrame');
$objWriter->writeAttribute('macro', ''); $objWriter->writeAttribute('macro', '');

View File

@ -203,7 +203,8 @@ class StringTable extends WriterPart
if (!$richText instanceof RichText) { if (!$richText instanceof RichText) {
$textRun = $richText; $textRun = $richText;
$richText = new RichText(); $richText = new RichText();
$richText->createTextRun($textRun); $run = $richText->createTextRun($textRun);
$run->setFont(null);
} }
if ($prefix !== null) { if ($prefix !== null) {
@ -215,9 +216,13 @@ class StringTable extends WriterPart
foreach ($elements as $element) { foreach ($elements as $element) {
// r // r
$objWriter->startElement($prefix . 'r'); $objWriter->startElement($prefix . 'r');
if ($element->getFont() !== null) {
// rPr // rPr
$objWriter->startElement($prefix . 'rPr'); $objWriter->startElement($prefix . 'rPr');
$size = $element->getFont()->getSize();
if (is_numeric($size)) {
$objWriter->writeAttribute('sz', (string) (int) ($size * 100));
}
// Bold // Bold
$objWriter->writeAttribute('b', ($element->getFont()->getBold() ? 1 : 0)); $objWriter->writeAttribute('b', ($element->getFont()->getBold() ? 1 : 0));
@ -237,14 +242,49 @@ class StringTable extends WriterPart
} }
$objWriter->writeAttribute('u', $underlineType); $objWriter->writeAttribute('u', $underlineType);
// Strikethrough // Strikethrough
$objWriter->writeAttribute('strike', ($element->getFont()->getStrikethrough() ? 'sngStrike' : 'noStrike')); $objWriter->writeAttribute('strike', ($element->getFont()->getStriketype() ?: 'noStrike'));
// Superscript/subscript
if ($element->getFont()->getBaseLine()) {
$objWriter->writeAttribute('baseline', (string) $element->getFont()->getBaseLine());
}
// rFont // Color
$objWriter->startElement($prefix . 'solidFill');
$objWriter->startElement($prefix . 'srgbClr');
$objWriter->writeAttribute('val', $element->getFont()->getColor()->getRGB());
$objWriter->endElement(); // srgbClr
$objWriter->endElement(); // solidFill
// Underscore Color
if ($element->getFont()->getUSchemeClr()) {
$objWriter->startElement($prefix . 'uFill');
$objWriter->startElement($prefix . 'solidFill');
$objWriter->startElement($prefix . 'schemeClr');
$objWriter->writeAttribute('val', $element->getFont()->getUSchemeClr());
$objWriter->endElement(); // schemeClr
$objWriter->endElement(); // solidFill
$objWriter->endElement(); // uFill
}
// fontName
if ($element->getFont()->getLatin()) {
$objWriter->startElement($prefix . 'latin'); $objWriter->startElement($prefix . 'latin');
$objWriter->writeAttribute('typeface', $element->getFont()->getName()); $objWriter->writeAttribute('typeface', $element->getFont()->getLatin());
$objWriter->endElement(); $objWriter->endElement();
}
if ($element->getFont()->getEastAsian()) {
$objWriter->startElement($prefix . 'ea');
$objWriter->writeAttribute('typeface', $element->getFont()->getEastAsian());
$objWriter->endElement();
}
if ($element->getFont()->getComplexScript()) {
$objWriter->startElement($prefix . 'cs');
$objWriter->writeAttribute('typeface', $element->getFont()->getComplexScript());
$objWriter->endElement();
}
$objWriter->endElement(); $objWriter->endElement();
}
// t // t
$objWriter->startElement($prefix . 't'); $objWriter->startElement($prefix . 't');

View File

@ -152,7 +152,7 @@ class Xlfn
*/ */
public static function addXlfn(string $funcstring): string public static function addXlfn(string $funcstring): string
{ {
return preg_replace(self::XLFNREGEXP, '_xlfn.$1', $funcstring); return (string) preg_replace(self::XLFNREGEXP, '_xlfn.$1', $funcstring);
} }
/** /**

View File

@ -19,10 +19,13 @@ abstract class AbstractFunctional extends TestCase
* *
* @return Spreadsheet * @return Spreadsheet
*/ */
protected function writeAndReload(Spreadsheet $spreadsheet, $format, ?callable $readerCustomizer = null) protected function writeAndReload(Spreadsheet $spreadsheet, $format, ?callable $readerCustomizer = null, ?callable $writerCustomizer = null)
{ {
$filename = File::temporaryFilename(); $filename = File::temporaryFilename();
$writer = IOFactory::createWriter($spreadsheet, $format); $writer = IOFactory::createWriter($spreadsheet, $format);
if ($writerCustomizer) {
$writerCustomizer($writer);
}
$writer->save($filename); $writer->save($filename);
$reader = IOFactory::createReader($format); $reader = IOFactory::createReader($format);

View File

@ -0,0 +1,45 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Csv;
use PhpOffice\PhpSpreadsheet\Reader\Csv;
use PHPUnit\Framework\TestCase;
class CsvIssue2840Test extends TestCase
{
public function testNullStringIgnore(): void
{
$reader = new Csv();
self::assertFalse($reader->getPreserveNullString());
$inputData = <<<EOF
john,,doe,,
mary,,jane,,
EOF;
$expected = [
['john', null, 'doe'],
['mary', null, 'jane'],
];
$spreadsheet = $reader->loadSpreadsheetFromString($inputData);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame($expected, $sheet->toArray());
$spreadsheet->disconnectWorksheets();
}
public function testNullStringLoad(): void
{
$reader = new Csv();
$reader->setPreserveNullString(true);
$inputData = <<<EOF
john,,doe,,
mary,,jane,,
EOF;
$expected = [
['john', '', 'doe', '', ''],
['mary', '', 'jane', '', ''],
];
$spreadsheet = $reader->loadSpreadsheetFromString($inputData);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame($expected, $sheet->toArray());
$spreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Gnumeric;
use PhpOffice\PhpSpreadsheet\Reader\Gnumeric;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class HiddenWorksheetTest extends TestCase
{
/**
* @var Spreadsheet
*/
private $spreadsheet;
protected function setup(): void
{
$filename = 'tests/data/Reader/Gnumeric/HiddenSheet.gnumeric';
$reader = new Gnumeric();
$this->spreadsheet = $reader->load($filename);
}
public function testPageSetup(): void
{
$assertions = $this->worksheetAssertions();
foreach ($this->spreadsheet->getAllSheets() as $worksheet) {
if (!array_key_exists($worksheet->getTitle(), $assertions)) {
continue;
}
$sheetAssertions = $assertions[$worksheet->getTitle()];
foreach ($sheetAssertions as $test => $expectedResult) {
$actualResult = $worksheet->getSheetState();
self::assertSame(
$expectedResult,
$actualResult,
"Failed asserting sheet state {$expectedResult} for Worksheet '{$worksheet->getTitle()}' {$test}"
);
}
}
}
private function worksheetAssertions(): array
{
return [
'Sheet1' => [
'sheetState' => Worksheet::SHEETSTATE_VISIBLE,
],
'Sheet2' => [
'sheetState' => Worksheet::SHEETSTATE_HIDDEN,
],
];
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Ods;
use PhpOffice\PhpSpreadsheet\Reader\Ods;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class HiddenWorksheetTest extends TestCase
{
/**
* @var Spreadsheet
*/
private $spreadsheet;
protected function setup(): void
{
$filename = 'tests/data/Reader/Ods/HiddenSheet.ods';
$reader = new Ods();
$this->spreadsheet = $reader->load($filename);
}
public function testPageSetup(): void
{
$assertions = $this->worksheetAssertions();
foreach ($this->spreadsheet->getAllSheets() as $worksheet) {
if (!array_key_exists($worksheet->getTitle(), $assertions)) {
continue;
}
$sheetAssertions = $assertions[$worksheet->getTitle()];
foreach ($sheetAssertions as $test => $expectedResult) {
$actualResult = $worksheet->getSheetState();
self::assertSame(
$expectedResult,
$actualResult,
"Failed asserting sheet state {$expectedResult} for Worksheet '{$worksheet->getTitle()}' {$test}"
);
}
}
}
private function worksheetAssertions(): array
{
return [
'Sheet1' => [
'sheetState' => Worksheet::SHEETSTATE_VISIBLE,
],
'Sheet2' => [
'sheetState' => Worksheet::SHEETSTATE_HIDDEN,
],
];
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls;
use PhpOffice\PhpSpreadsheet\Reader\Xls;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class HiddenWorksheetTest extends TestCase
{
/**
* @var Spreadsheet
*/
private $spreadsheet;
protected function setup(): void
{
$filename = 'tests/data/Reader/XLS/HiddenSheet.xls';
$reader = new Xls();
$this->spreadsheet = $reader->load($filename);
}
public function testPageSetup(): void
{
$assertions = $this->worksheetAssertions();
foreach ($this->spreadsheet->getAllSheets() as $worksheet) {
if (!array_key_exists($worksheet->getTitle(), $assertions)) {
continue;
}
$sheetAssertions = $assertions[$worksheet->getTitle()];
foreach ($sheetAssertions as $test => $expectedResult) {
$actualResult = $worksheet->getSheetState();
self::assertSame(
$expectedResult,
$actualResult,
"Failed asserting sheet state {$expectedResult} for Worksheet '{$worksheet->getTitle()}' {$test}"
);
}
}
}
private function worksheetAssertions(): array
{
return [
'Sheet1' => [
'sheetState' => Worksheet::SHEETSTATE_VISIBLE,
],
'Sheet2' => [
'sheetState' => Worksheet::SHEETSTATE_HIDDEN,
],
];
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class ChartSheetTest extends TestCase
{
public function testLoadChartSheetWithCharts(): void
{
$filename = 'tests/data/Reader/XLSX/ChartSheet.xlsx';
$reader = new Xlsx();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($filename);
self::assertCount(2, $spreadsheet->getAllSheets());
$chartSheet = $spreadsheet->getSheetByName('Chart1');
self::assertInstanceOf(Worksheet::class, $chartSheet);
self::assertSame(1, $chartSheet->getChartCount());
}
public function testLoadChartSheetWithoutCharts(): void
{
$filename = 'tests/data/Reader/XLSX/ChartSheet.xlsx';
$reader = new Xlsx();
$reader->setIncludeCharts(false);
$spreadsheet = $reader->load($filename);
self::assertCount(1, $spreadsheet->getAllSheets());
$chartSheet = $spreadsheet->getSheetByName('Chart1');
self::assertNull($chartSheet);
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PHPUnit\Framework\TestCase;
class ChartsOpenpyxlTest extends TestCase
{
private const DIRECTORY = 'samples' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR;
public function testBubble2(): void
{
$file = self::DIRECTORY . '32readwriteBubbleChart2.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame(1, $sheet->getChartCount());
self::assertSame('Sheet', $sheet->getTitle());
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
self::assertEmpty($chart->getTitle());
self::assertTrue($chart->getOneCellAnchor());
$plotArea = $chart->getPlotArea();
$plotSeries = $plotArea->getPlotGroup();
self::assertCount(1, $plotSeries);
$dataSeries = $plotSeries[0];
$labels = $dataSeries->getPlotLabels();
self::assertCount(2, $labels);
self::assertSame(['2013'], $labels[0]->getDataValues());
self::assertSame(['2014'], $labels[1]->getDataValues());
$plotCategories = $dataSeries->getPlotCategories();
self::assertCount(2, $plotCategories);
$categories = $plotCategories[0];
self::assertSame('Number', $categories->getDataType());
self::assertSame('\'Sheet\'!$A$2:$A$5', $categories->getDataSource());
self::assertFalse($categories->getBubble3D());
$categories = $plotCategories[1];
self::assertCount(2, $plotCategories);
self::assertSame('Number', $categories->getDataType());
self::assertSame('\'Sheet\'!$A$7:$A$10', $categories->getDataSource());
self::assertFalse($categories->getBubble3D());
$plotValues = $dataSeries->getPlotValues();
self::assertCount(2, $plotValues);
$values = $plotValues[0];
self::assertSame('Number', $values->getDataType());
self::assertSame('\'Sheet\'!$B$2:$B$5', $values->getDataSource());
self::assertFalse($values->getBubble3D());
$values = $plotValues[1];
self::assertCount(2, $plotValues);
self::assertSame('Number', $values->getDataType());
self::assertSame('\'Sheet\'!$B$7:$B$10', $values->getDataSource());
self::assertFalse($values->getBubble3D());
$plotValues = $dataSeries->getPlotBubbleSizes();
self::assertCount(2, $plotValues);
$values = $plotValues[0];
self::assertSame('Number', $values->getDataType());
self::assertSame('\'Sheet\'!$C$2:$C$5', $values->getDataSource());
self::assertFalse($values->getBubble3D());
$values = $plotValues[1];
self::assertCount(2, $plotValues);
self::assertSame('Number', $values->getDataType());
self::assertSame('\'Sheet\'!$C$7:$C$10', $values->getDataSource());
self::assertFalse($values->getBubble3D());
$spreadsheet->disconnectWorksheets();
}
public function testXml(): void
{
$infile = self::DIRECTORY . '32readwriteBubbleChart2.xlsx';
$file = 'zip://';
$file .= $infile;
$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:'), 'unusual choice of prefix');
self::assertSame(0, substr_count($data, 'bubbleScale'));
self::assertSame(1, substr_count($data, '<tx><v>2013</v></tx>'), 'v tag for 2013');
self::assertSame(1, substr_count($data, '<tx><v>2014</v></tx>'), 'v tag for 2014');
self::assertSame(0, substr_count($data, 'numCache'), 'no cached values');
}
$file = 'zip://';
$file .= $infile;
$file .= '#xl/drawings/_rels/drawing1.xml.rels';
$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, 'Target="/xl/charts/chart1.xml"'), 'Unusual absolute address in drawing rels file');
}
$file = 'zip://';
$file .= $infile;
$file .= '#xl/worksheets/_rels/sheet1.xml.rels';
$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, 'Target="/xl/drawings/drawing1.xml"'), 'Unusual absolute address in worksheet rels file');
}
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class HiddenWorksheetTest extends TestCase
{
/**
* @var Spreadsheet
*/
private $spreadsheet;
protected function setup(): void
{
$filename = 'tests/data/Reader/XLSX/HiddenSheet.xlsx';
$reader = new Xlsx();
$this->spreadsheet = $reader->load($filename);
}
public function testPageSetup(): void
{
$assertions = $this->worksheetAssertions();
foreach ($this->spreadsheet->getAllSheets() as $worksheet) {
if (!array_key_exists($worksheet->getTitle(), $assertions)) {
continue;
}
$sheetAssertions = $assertions[$worksheet->getTitle()];
foreach ($sheetAssertions as $test => $expectedResult) {
$actualResult = $worksheet->getSheetState();
self::assertSame(
$expectedResult,
$actualResult,
"Failed asserting sheet state {$expectedResult} for Worksheet '{$worksheet->getTitle()}' {$test}"
);
}
}
}
private function worksheetAssertions(): array
{
return [
'Sheet1' => [
'sheetState' => Worksheet::SHEETSTATE_VISIBLE,
],
'Sheet2' => [
'sheetState' => Worksheet::SHEETSTATE_HIDDEN,
],
];
}
}

View File

@ -26,7 +26,7 @@ class URLImageTest extends TestCase
self::assertInstanceOf(Drawing::class, $drawing); self::assertInstanceOf(Drawing::class, $drawing);
// Check if the source is a URL or a file path // Check if the source is a URL or a file path
self::assertTrue($drawing->getIsURL()); self::assertTrue($drawing->getIsURL());
self::assertSame('https://www.globalipmanager.com/DataFiles/Pics/20/Berniaga.comahp2.jpg', $drawing->getPath()); self::assertSame('https://phpspreadsheet.readthedocs.io/en/latest/topics/images/01-03-filter-icon-1.png', $drawing->getPath());
$imageContents = file_get_contents($drawing->getPath()); $imageContents = file_get_contents($drawing->getPath());
self::assertNotFalse($imageContents); self::assertNotFalse($imageContents);
$filePath = tempnam(sys_get_temp_dir(), 'Drawing'); $filePath = tempnam(sys_get_temp_dir(), 'Drawing');
@ -36,7 +36,7 @@ class URLImageTest extends TestCase
unlink($filePath); unlink($filePath);
self::assertNotFalse($mimeType); self::assertNotFalse($mimeType);
$extension = File::mime2ext($mimeType); $extension = File::mime2ext($mimeType);
self::assertSame('jpeg', $extension); self::assertSame('png', $extension);
} }
} }
} }

View File

@ -77,4 +77,29 @@ class WorksheetInfoNamesTest extends TestCase
self::assertEquals($expected, $actual); self::assertEquals($expected, $actual);
} }
public function testListWorksheetNamesChartSheet(): void
{
$filename = 'tests/data/Reader/XLSX/ChartSheet.xlsx';
$reader = new Xlsx();
$actual = $reader->listWorksheetNames($filename);
$expected = ['Sheet1', 'Chart1'];
self::assertEquals($expected, $actual);
}
public function testListWorksheetInfoChartSheet(): void
{
$filename = 'tests/data/Reader/XLSX/ChartSheet.xlsx';
$reader = new Xlsx();
$actual = $reader->listWorksheetInfo($filename);
$chartSheetInfo = $actual[1];
self::assertSame('Chart1', $chartSheetInfo['worksheetName']);
self::assertSame(-1, $chartSheetInfo['lastColumnIndex']);
self::assertSame(0, $chartSheetInfo['totalRows']);
self::assertSame(0, $chartSheetInfo['totalColumns']);
}
} }

View File

@ -10,6 +10,7 @@ use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font; use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Ods; use PhpOffice\PhpSpreadsheet\Writer\Ods;
use PhpOffice\PhpSpreadsheet\Writer\Ods\Content; use PhpOffice\PhpSpreadsheet\Writer\Ods\Content;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -106,4 +107,26 @@ class ContentTest extends TestCase
self::assertXmlStringEqualsXmlFile($this->samplesPath . '/content-with-data.xml', $xml); self::assertXmlStringEqualsXmlFile($this->samplesPath . '/content-with-data.xml', $xml);
} }
public function testWriteWithHiddenWorksheet(): void
{
$workbook = new Spreadsheet();
// Worksheet 1
$worksheet1 = $workbook->getActiveSheet();
$worksheet1->setCellValue('A1', 1);
// Worksheet 2
$worksheet2 = $workbook->createSheet();
$worksheet2->setTitle('New Worksheet');
$worksheet2->setCellValue('A1', 2);
$worksheet2->setSheetState(Worksheet::SHEETSTATE_HIDDEN);
// Write
$content = new Content(new Ods($workbook));
$xml = $content->write();
self::assertXmlStringEqualsXmlFile($this->samplesPath . '/content-hidden-worksheet.xml', $xml);
}
} }

View File

@ -0,0 +1,171 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
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\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PHPUnit\Framework\TestCase;
class Charts32CatAxValAxTest extends TestCase
{
// These tests can only be performed by examining xml.
// They are based on sample 33_Chart_Create_Scatter2.
/** @var string */
private $outputFileName = '';
protected function tearDown(): void
{
if ($this->outputFileName !== '') {
unlink($this->outputFileName);
$this->outputFileName = '';
}
}
/**
* @dataProvider providerCatAxValAx
*/
public function test1CatAx1ValAx(?bool $numeric): void
{
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
// changed data to simulate a trend chart - Xaxis are dates; Yaxis are 3 meausurements from each date
$worksheet->fromArray(
[
['', 'metric1', 'metric2', 'metric3'],
['=DATEVALUE("2021-01-01")', 12.1, 15.1, 21.1],
['=DATEVALUE("2021-01-04")', 56.2, 73.2, 86.2],
['=DATEVALUE("2021-01-07")', 52.2, 61.2, 69.2],
['=DATEVALUE("2021-01-10")', 30.2, 32.2, 0.2],
]
);
$worksheet->getStyle('A2:A5')->getNumberFormat()->setFormatCode(Properties::FORMAT_CODE_DATE_ISO8601);
$worksheet->getColumnDimension('A')->setAutoSize(true);
$worksheet->setSelectedCells('A1');
// 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), // was 2010
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // was 2011
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // was 2012
];
// Set the X-Axis Labels
// changed from STRING to NUMBER
// added 2 additional x-axis values associated with each of the 3 metrics
// added FORMATE_CODE_NUMBER
$xAxisTickValues = [
//new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', Properties::FORMAT_CODE_DATE, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', Properties::FORMAT_CODE_DATE, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$5', Properties::FORMAT_CODE_DATE, 4),
];
// 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
// added FORMAT_CODE_NUMBER
$dataSeriesValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', Properties::FORMAT_CODE_NUMBER, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', Properties::FORMAT_CODE_NUMBER, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', Properties::FORMAT_CODE_NUMBER, 4),
];
// Added so that Xaxis shows dates instead of Excel-equivalent-year1900-numbers
$xAxis = new Axis();
//$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE );
if (is_bool($numeric)) {
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601, $numeric);
} else {
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601);
}
// Build the dataseries
$series = new DataSeries(
DataSeries::TYPE_SCATTERCHART, // plotType
null, // plotGrouping (Scatter charts don't have any grouping)
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues, // plotValues
null, // plotDirection
false, // smooth line
//DataSeries::STYLE_LINEMARKER // plotStyle
DataSeries::STYLE_MARKER // plotStyle
);
// 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 Scatter Trend 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
// added xAxis for correct date display
$xAxis, // xAxis
);
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
$chart->setBottomRightPosition('P20');
// Add the chart to the worksheet
$worksheet->addChart($chart);
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$this->outputFileName = File::temporaryFilename();
$writer->save($this->outputFileName);
$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');
} elseif ($numeric === true) {
self::assertSame(0, substr_count($data, '<c:catAx'));
self::assertSame(2, substr_count($data, '<c:valAx'));
} else {
self::assertSame(1, substr_count($data, '<c:catAx'));
self::assertSame(1, substr_count($data, '<c:valAx'));
}
}
public function providerCatAxValAx(): array
{
return [
[true],
[false],
[null],
];
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\RichText\Run;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class Charts32ColoredAxisLabelTest extends AbstractFunctional
{
private const DIRECTORY = 'samples' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR;
public function readCharts(XlsxReader $reader): void
{
$reader->setIncludeCharts(true);
}
public function writeCharts(XlsxWriter $writer): void
{
$writer->setIncludeCharts(true);
}
public function testStock5(): void
{
$file = self::DIRECTORY . '32readwriteStockChart5.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame(1, $sheet->getChartCount());
/** @var callable */
$callableReader = [$this, 'readCharts'];
/** @var callable */
$callableWriter = [$this, 'writeCharts'];
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter);
$spreadsheet->disconnectWorksheets();
$sheet = $reloadedSpreadsheet->getActiveSheet();
self::assertSame('Charts', $sheet->getTitle());
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
$xAxisLabel = $chart->getXAxisLabel();
$captionArray = $xAxisLabel->getCaption();
self::assertIsArray($captionArray);
self::assertCount(1, $captionArray);
$caption = $captionArray[0];
self::assertInstanceOf(RichText::class, $caption);
self::assertSame('X-Axis Title in Green', $caption->getPlainText());
$elements = $caption->getRichTextElements();
self::assertCount(1, $elements);
$run = $elements[0];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('00B050', $font->getColor()->getRGB());
$yAxisLabel = $chart->getYAxisLabel();
$captionArray = $yAxisLabel->getCaption();
self::assertIsArray($captionArray);
self::assertCount(1, $captionArray);
$caption = $captionArray[0];
self::assertInstanceOf(RichText::class, $caption);
self::assertSame('Y-Axis Title in Red', $caption->getPlainText());
$elements = $caption->getRichTextElements();
self::assertCount(1, $elements);
$run = $elements[0];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('FF0000', $font->getColor()->getRGB());
$reloadedSpreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,333 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\RichText\Run;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class Charts32ScatterTest extends AbstractFunctional
{
private const DIRECTORY = 'samples' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR;
public function readCharts(XlsxReader $reader): void
{
$reader->setIncludeCharts(true);
}
public function writeCharts(XlsxWriter $writer): void
{
$writer->setIncludeCharts(true);
}
public function testScatter1(): void
{
$file = self::DIRECTORY . '32readwriteScatterChart1.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame(1, $sheet->getChartCount());
/** @var callable */
$callableReader = [$this, 'readCharts'];
/** @var callable */
$callableWriter = [$this, 'writeCharts'];
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter);
$spreadsheet->disconnectWorksheets();
$sheet = $reloadedSpreadsheet->getActiveSheet();
self::assertSame('Charts', $sheet->getTitle());
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
$title = $chart->getTitle();
$captionArray = $title->getCaption();
self::assertIsArray($captionArray);
self::assertCount(1, $captionArray);
$caption = $captionArray[0];
self::assertInstanceOf(RichText::class, $caption);
self::assertSame('Scatter - No Join and Markers', $caption->getPlainText());
$elements = $caption->getRichTextElements();
self::assertCount(1, $elements);
$run = $elements[0];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('Calibri', $font->getLatin());
self::assertEquals(12, $font->getSize());
self::assertTrue($font->getBold());
self::assertFalse($font->getItalic());
self::assertFalse($font->getSuperscript());
self::assertFalse($font->getSubscript());
self::assertFalse($font->getStrikethrough());
self::assertSame('none', $font->getUnderline());
self::assertSame('000000', $font->getColor()->getRGB());
$plotArea = $chart->getPlotArea();
$plotSeries = $plotArea->getPlotGroup();
self::assertCount(1, $plotSeries);
$dataSeries = $plotSeries[0];
$plotValues = $dataSeries->getPlotValues();
self::assertCount(3, $plotValues);
$values = $plotValues[0];
self::assertFalse($values->getScatterLines());
self::assertSame(28575, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[1];
self::assertFalse($values->getScatterLines());
self::assertSame(28575, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[2];
self::assertFalse($values->getScatterLines());
self::assertSame(28575, $values->getLineWidth());
self::assertSame(7, $values->getPointSize());
self::assertSame('FFFF00', $values->getFillColor());
$reloadedSpreadsheet->disconnectWorksheets();
}
public function testScatter6(): void
{
$file = self::DIRECTORY . '32readwriteScatterChart6.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame(1, $sheet->getChartCount());
/** @var callable */
$callableReader = [$this, 'readCharts'];
/** @var callable */
$callableWriter = [$this, 'writeCharts'];
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter);
$spreadsheet->disconnectWorksheets();
$sheet = $reloadedSpreadsheet->getActiveSheet();
self::assertSame('Charts', $sheet->getTitle());
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
$title = $chart->getTitle();
$captionArray = $title->getCaption();
self::assertIsArray($captionArray);
self::assertCount(1, $captionArray);
$caption = $captionArray[0];
self::assertInstanceOf(RichText::class, $caption);
self::assertSame('Scatter - Rich Text Title No Join and Markers', $caption->getPlainText());
$elements = $caption->getRichTextElements();
self::assertCount(3, $elements);
$run = $elements[0];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('Calibri', $font->getLatin());
self::assertEquals(12, $font->getSize());
self::assertTrue($font->getBold());
self::assertFalse($font->getItalic());
self::assertFalse($font->getSuperscript());
self::assertFalse($font->getSubscript());
self::assertFalse($font->getStrikethrough());
self::assertSame('none', $font->getUnderline());
self::assertSame('000000', $font->getColor()->getRGB());
$run = $elements[1];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('Courier New', $font->getLatin());
self::assertEquals(10, $font->getSize());
self::assertFalse($font->getBold());
self::assertFalse($font->getItalic());
self::assertFalse($font->getSuperscript());
self::assertFalse($font->getSubscript());
self::assertFalse($font->getStrikethrough());
self::assertSame('single', $font->getUnderline());
self::assertSame('00B0F0', $font->getColor()->getRGB());
$run = $elements[2];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('Calibri', $font->getLatin());
self::assertEquals(12, $font->getSize());
self::assertTrue($font->getBold());
self::assertFalse($font->getItalic());
self::assertFalse($font->getSuperscript());
self::assertFalse($font->getSubscript());
self::assertFalse($font->getStrikethrough());
self::assertSame('none', $font->getUnderline());
self::assertSame('000000', $font->getColor()->getRGB());
$plotArea = $chart->getPlotArea();
$plotSeries = $plotArea->getPlotGroup();
self::assertCount(1, $plotSeries);
$dataSeries = $plotSeries[0];
$plotValues = $dataSeries->getPlotValues();
self::assertCount(3, $plotValues);
$values = $plotValues[0];
self::assertFalse($values->getScatterLines());
self::assertSame(28575, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[1];
self::assertFalse($values->getScatterLines());
self::assertSame(28575, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[2];
self::assertFalse($values->getScatterLines());
self::assertSame(28575, $values->getLineWidth());
self::assertSame(7, $values->getPointSize());
self::assertSame('FFFF00', $values->getFillColor());
$reloadedSpreadsheet->disconnectWorksheets();
}
public function testScatter3(): void
{
$file = self::DIRECTORY . '32readwriteScatterChart3.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame(1, $sheet->getChartCount());
/** @var callable */
$callableReader = [$this, 'readCharts'];
/** @var callable */
$callableWriter = [$this, 'writeCharts'];
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter);
$spreadsheet->disconnectWorksheets();
$sheet = $reloadedSpreadsheet->getActiveSheet();
self::assertSame('Charts', $sheet->getTitle());
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
$title = $chart->getTitle();
$captionArray = $title->getCaption();
self::assertIsArray($captionArray);
self::assertCount(1, $captionArray);
$caption = $captionArray[0];
self::assertInstanceOf(RichText::class, $caption);
self::assertSame('Scatter - Join Straight Lines and Markers', $caption->getPlainText());
$elements = $caption->getRichTextElements();
self::assertCount(1, $elements);
$run = $elements[0];
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('Calibri', $font->getLatin());
self::assertEquals(12, $font->getSize());
self::assertTrue($font->getBold());
self::assertFalse($font->getItalic());
self::assertFalse($font->getSuperscript());
self::assertFalse($font->getSubscript());
self::assertFalse($font->getStrikethrough());
self::assertSame('none', $font->getUnderline());
self::assertSame('000000', $font->getColor()->getRGB());
$plotArea = $chart->getPlotArea();
$plotSeries = $plotArea->getPlotGroup();
self::assertCount(1, $plotSeries);
$dataSeries = $plotSeries[0];
$plotValues = $dataSeries->getPlotValues();
self::assertCount(3, $plotValues);
$values = $plotValues[0];
self::assertTrue($values->getScatterLines());
self::assertSame(12700, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[1];
self::assertTrue($values->getScatterLines());
self::assertSame(12700, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[2];
self::assertTrue($values->getScatterLines());
self::assertSame(12700, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$reloadedSpreadsheet->disconnectWorksheets();
}
public function testScatter7(): void
{
$file = self::DIRECTORY . '32readwriteScatterChart7.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$sheet = $spreadsheet->getActiveSheet();
self::assertSame(1, $sheet->getChartCount());
/** @var callable */
$callableReader = [$this, 'readCharts'];
/** @var callable */
$callableWriter = [$this, 'writeCharts'];
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter);
$spreadsheet->disconnectWorksheets();
$sheet = $reloadedSpreadsheet->getActiveSheet();
self::assertSame('Charts', $sheet->getTitle());
$charts = $sheet->getChartCollection();
self::assertCount(1, $charts);
$chart = $charts[0];
self::assertNotNull($chart);
$title = $chart->getTitle();
$captionArray = $title->getCaption();
self::assertIsArray($captionArray);
self::assertCount(1, $captionArray);
$caption = $captionArray[0];
self::assertInstanceOf(RichText::class, $caption);
self::assertSame('Latin/EA/CS Title ABCאבגDEFァ', $caption->getPlainText());
$elements = $caption->getRichTextElements();
self::assertGreaterThan(0, count($elements));
foreach ($elements as $run) {
self::assertInstanceOf(Run::class, $run);
$font = $run->getFont();
self::assertInstanceOf(Font::class, $font);
self::assertSame('Times New Roman', $font->getLatin());
self::assertSame('Malgun Gothic', $font->getEastAsian());
self::assertSame('Courier New', $font->getComplexScript());
self::assertEquals(12, $font->getSize());
self::assertTrue($font->getBold());
self::assertFalse($font->getItalic());
self::assertFalse($font->getSuperscript());
self::assertFalse($font->getSubscript());
self::assertFalse($font->getStrikethrough());
self::assertSame('none', $font->getUnderline());
self::assertSame('000000', $font->getColor()->getRGB());
}
$plotArea = $chart->getPlotArea();
$plotSeries = $plotArea->getPlotGroup();
self::assertCount(1, $plotSeries);
$dataSeries = $plotSeries[0];
$plotValues = $dataSeries->getPlotValues();
self::assertCount(3, $plotValues);
$values = $plotValues[0];
self::assertFalse($values->getScatterLines());
self::assertSame(28575, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[1];
self::assertFalse($values->getScatterLines());
self::assertSame(28575, $values->getLineWidth());
self::assertSame(3, $values->getPointSize());
self::assertSame('', $values->getFillColor());
$values = $plotValues[2];
self::assertFalse($values->getScatterLines());
self::assertSame(28575, $values->getLineWidth());
self::assertSame(7, $values->getPointSize());
self::assertSame('FFFF00', $values->getFillColor());
$reloadedSpreadsheet->disconnectWorksheets();
}
}

View File

@ -0,0 +1,147 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
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;
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
*/
public function testBezierCount(int $expectedCount, string $inputFile): void
{
$file = self::DIRECTORY . $inputFile;
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$this->outputFileName = File::temporaryFilename();
$writer->save($this->outputFileName);
$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"/>'));
}
}
public function providerScatterCharts(): array
{
return [
'no line' => [0, '32readwriteScatterChart1.xlsx'],
'smooth line (Bezier)' => [3, '32readwriteScatterChart2.xlsx'],
'straight line' => [0, '32readwriteScatterChart3.xlsx'],
];
}
public function testAreaPercentageNoCat(): void
{
$file = self::DIRECTORY . '32readwriteAreaPercentageChart1.xlsx';
$reader = new XlsxReader();
$reader->setIncludeCharts(true);
$spreadsheet = $reader->load($file);
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$this->outputFileName = File::temporaryFilename();
$writer->save($this->outputFileName);
$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>'));
}
}
/**
* @dataProvider providerCatAxValAx
*/
public function testCatAxValAx(?bool $numeric): void
{
$file = self::DIRECTORY . '32readwriteScatterChart1.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);
$xAxis = $chart->getChartAxisX();
self::assertSame(Properties::FORMAT_CODE_GENERAL, $xAxis->getAxisNumberFormat());
if (is_bool($numeric)) {
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL, true);
}
$yAxis = $chart->getChartAxisY();
self::assertSame(Properties::FORMAT_CODE_GENERAL, $yAxis->getAxisNumberFormat());
if (is_bool($numeric)) {
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL, $numeric);
$yAxis->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL, $numeric);
}
$writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true);
$this->outputFileName = File::temporaryFilename();
$writer->save($this->outputFileName);
$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) {
self::assertSame(0, substr_count($data, '<c:catAx>'));
self::assertSame(2, substr_count($data, '<c:valAx>'));
} else {
self::assertSame(1, substr_count($data, '<c:catAx>'));
self::assertSame(1, substr_count($data, '<c:valAx>'));
}
}
public function providerCatAxValAx(): array
{
return [
[true],
[false],
[null],
];
}
}

View File

@ -48,6 +48,30 @@ return [
-6.7800000000000002, -6.7800000000000002,
-2, -2,
], ],
[
31.25,
12.5,
null,
2.5,
],
[
31.25,
12.5,
2.5,
null,
],
[
12.5,
12.5,
null,
null,
],
[
0.0,
null,
null,
null,
],
['#VALUE!', 1, 'y', 3], ['#VALUE!', 1, 'y', 3],
[6, 1, '2', 3], [6, 1, '2', 3],
]; ];

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,6 +3,9 @@
<office:scripts /> <office:scripts />
<office:font-face-decls /> <office:font-face-decls />
<office:automatic-styles> <office:automatic-styles>
<style:style style:family="table" style:name="ta1">
<style:table-properties table:display="true" />
</style:style>
<style:style style:name="ce0" style:family="table-cell" style:parent-style-name="Default"> <style:style style:name="ce0" style:family="table-cell" style:parent-style-name="Default">
<style:table-cell-properties style:rotation-align="none" style:vertical-align="bottom" /> <style:table-cell-properties style:rotation-align="none" style:vertical-align="bottom" />
<style:paragraph-properties fo:text-align="start" /> <style:paragraph-properties fo:text-align="start" />
@ -12,7 +15,7 @@
<office:body> <office:body>
<office:spreadsheet> <office:spreadsheet>
<table:calculation-settings /> <table:calculation-settings />
<table:table table:name="Worksheet"> <table:table table:name="Worksheet" table:style-name="ta1">
<office:forms /> <office:forms />
<table:table-row> <table:table-row>
<table:table-cell table:style-name="ce0" /> <table:table-cell table:style-name="ce0" />

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2">
<office:scripts />
<office:font-face-decls />
<office:automatic-styles>
<style:style style:family="table" style:name="ta1">
<style:table-properties table:display="true" />
</style:style>
<style:style style:family="table" style:name="ta2">
<style:table-properties table:display="false"/>
</style:style>
<style:style style:name="ce0" style:family="table-cell" style:parent-style-name="Default">
<style:table-cell-properties style:rotation-align="none" style:vertical-align="bottom" />
<style:paragraph-properties fo:text-align="start" />
<style:text-properties fo:color="#000000" fo:font-family="Calibri" fo:font-size="11.0pt" />
</style:style>
</office:automatic-styles>
<office:body>
<office:spreadsheet>
<table:calculation-settings />
<table:table table:name="Worksheet" table:style-name="ta1">
<office:forms />
<table:table-row>
<table:table-cell office:value="1" office:value-type="float" table:style-name="ce0">
<text:p>1</text:p>
</table:table-cell>
<table:table-cell table:number-columns-repeated="1023"/>
</table:table-row>
</table:table>
<table:table table:name="New Worksheet" table:style-name="ta2">
<office:forms/>
<table:table-row>
<table:table-cell office:value="2" office:value-type="float" table:style-name="ce0">
<text:p>2</text:p>
</table:table-cell>
<table:table-cell table:number-columns-repeated="1023" />
</table:table-row>
</table:table>
<table:named-expressions />
</office:spreadsheet>
</office:body>
</office:document-content>

View File

@ -3,6 +3,12 @@
<office:scripts/> <office:scripts/>
<office:font-face-decls/> <office:font-face-decls/>
<office:automatic-styles> <office:automatic-styles>
<style:style style:family="table" style:name="ta1">
<style:table-properties table:display="true" />
</style:style>
<style:style style:family="table" style:name="ta2">
<style:table-properties table:display="true" />
</style:style>
<style:style style:family="table-cell" style:name="ce0" style:parent-style-name="Default"> <style:style style:family="table-cell" style:name="ce0" style:parent-style-name="Default">
<style:table-cell-properties style:rotation-align="none" style:vertical-align="bottom" /> <style:table-cell-properties style:rotation-align="none" style:vertical-align="bottom" />
<style:paragraph-properties fo:text-align="start" /> <style:paragraph-properties fo:text-align="start" />
@ -62,7 +68,7 @@
<office:body> <office:body>
<office:spreadsheet> <office:spreadsheet>
<table:calculation-settings/> <table:calculation-settings/>
<table:table table:name="Worksheet"> <table:table table:name="Worksheet" table:style-name="ta1">
<office:forms/> <office:forms/>
<table:table-row> <table:table-row>
<table:table-cell office:value="1" office:value-type="float" table:style-name="ce2"> <table:table-cell office:value="1" office:value-type="float" table:style-name="ce2">
@ -107,7 +113,7 @@
<table:table-cell table:number-columns-repeated="1017"/> <table:table-cell table:number-columns-repeated="1017"/>
</table:table-row> </table:table-row>
</table:table> </table:table>
<table:table table:name="New Worksheet"> <table:table table:name="New Worksheet" table:style-name="ta2">
<office:forms/> <office:forms/>
<table:table-row> <table:table-row>
<table:table-cell office:value="2" office:value-type="float" table:style-name="ce0"> <table:table-cell office:value="2" office:value-type="float" table:style-name="ce0">