Merge branch 'master' into PHP8-Sane-Property-Names

# Conflicts:
#	CHANGELOG.md
#	src/PhpSpreadsheet/Shared/Drawing.php
#	src/PhpSpreadsheet/Spreadsheet.php
#	src/PhpSpreadsheet/Style/Conditional.php
This commit is contained in:
MarkBaker 2020-12-27 15:07:50 +01:00
commit 11522afee0
68 changed files with 1495 additions and 696 deletions

1
.gitattributes vendored
View File

@ -5,7 +5,6 @@
/.php_cs.dist export-ignore
/.sami.php export-ignore
/.scrutinizer.yml export-ignore
/.travis.yml export-ignore
/CHANGELOG.PHPExcel.md export-ignore
/bin export-ignore
/composer.lock export-ignore

View File

@ -1,4 +1,4 @@
name: Build
name: main
on: [ push, pull_request ]
jobs:
test:
@ -10,6 +10,7 @@ jobs:
- '7.3'
- '7.4'
- '8.0'
- '8.1'
name: PHP ${{ matrix.php-version }}
@ -37,7 +38,7 @@ jobs:
- name: Delete composer lock file
id: composer-lock
if: ${{ matrix.php-version == '8.0' }}
if: ${{ matrix.php-version == '8.0' || matrix.php-version == '8.1' }}
run: |
rm composer.lock
echo "::set-output name=flags::--ignore-platform-reqs"
@ -148,3 +149,25 @@ jobs:
./vendor/bin/phpunit --coverage-clover coverage-clover.xml
curl -LO https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover coverage-clover.xml
release:
runs-on: ubuntu-latest
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.ref }} # Otherwise our annotated tag is not fetched and we cannot get correct version
# Create release
- name: Get release info
id: release-info
run: |
echo "::set-output name=subject::$(git tag --format '%(contents:subject)' --points-at)"
git tag --format '%(contents:body)' --points-at > release-body.txt
- uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ github.ref }}
release_name: ${{ steps.release-info.outputs.subject }}
body_path: release-body.txt

View File

@ -1,34 +0,0 @@
language: php
dist: bionic
php:
- 7.2
- 7.3
- 7.4
- nightly
cache:
directories:
- vendor
- $HOME/.composer/cache
before_script:
# Deactivate xdebug
- if [[ $TRAVIS_PHP_VERSION != nightly ]]; then phpenv config-rm xdebug.ini; fi
- if [[ $TRAVIS_PHP_VERSION == nightly ]]; then rm composer.lock; fi
- composer install --ignore-platform-reqs
script:
- ./vendor/bin/phpunit --color=always --coverage-text
allow_failures:
- php: nightly
jobs:
include:
- stage: Code style
php: 7.4
script:
- ./vendor/bin/php-cs-fixer fix --diff --verbose --dry-run
- ./vendor/bin/phpcs

View File

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added
- CSV Reader - Best Guess for Encoding, and Handle Null-string Escape [#1647](https://github.com/PHPOffice/PhpSpreadsheet/issues/1647)
- Provided a Size Helper class to validate size values (pt, px, em)
### Changed
@ -25,8 +26,17 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed
- Fix for Xls Reader when SST has a bad length [#1592](https://github.com/PHPOffice/PhpSpreadsheet/issues/1592)
- Resolve Xlsx loader issue whe hyperlinks don't have a destination
- Resolve issues when printer settings resources IDs clash with drawing IDs
- Resolve issue with SLK long filenames [#1612](https://github.com/PHPOffice/PhpSpreadsheet/issues/1612)
- ROUNDUP and ROUNDDOWN return incorrect results for values of 0 [#1627](https://github.com/phpoffice/phpspreadsheet/pull/1627)
- Apply Column and Row Styles to Existing Cells [#1712](https://github.com/PHPOffice/PhpSpreadsheet/issues/1712) [PR #1721](https://github.com/PHPOffice/PhpSpreadsheet/pull/1721)
- Resolve issues with defined names where worksheet doesn't exist (#1686)[https://github.com/PHPOffice/PhpSpreadsheet/issues/1686] and [#1723](https://github.com/PHPOffice/PhpSpreadsheet/issues/1723) - [PR #1742](https://github.com/PHPOffice/PhpSpreadsheet/pull/1742)
- Fix for issue [#1735](https://github.com/PHPOffice/PhpSpreadsheet/issues/1735) Incorrect activeSheetIndex after RemoveSheetByIndex - [PR #1743](https://github.com/PHPOffice/PhpSpreadsheet/pull/1743)
- Ensure that the list of shared formulae is maintained when an xlsx file is chunked with readFilter[Issue #169](https://github.com/PHPOffice/PhpSpreadsheet/issues/1669).
- Fix for notice during accessing "cached magnification factor" offset [#1354](https://github.com/PHPOffice/PhpSpreadsheet/pull/1354)
- Fix compatibility with ext-gd on php 8
### Security Fix (CVE-2020-7776)
@ -78,7 +88,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Fixed
- PrintArea causes exception [#1544](https://github.com/phpoffice/phpspreadsheet/pull/1544)
- PrintArea causes exception [#1544](https://github.com/phpoffice/phpspreadsheet/pull/1544)
- Calculation/DateTime Failure With PHP8 [#1661](https://github.com/phpoffice/phpspreadsheet/pull/1661)
- Reader/Gnumeric Failure with PHP8 [#1662](https://github.com/phpoffice/phpspreadsheet/pull/1662)
- ReverseSort bug, exposed but not caused by PHP8 [#1660](https://github.com/phpoffice/phpspreadsheet/pull/1660)

View File

@ -1,6 +1,6 @@
# PhpSpreadsheet
[![Build Status](https://travis-ci.org/PHPOffice/PhpSpreadsheet.svg?branch=master)](https://travis-ci.org/PHPOffice/PhpSpreadsheet)
[![Build Status](https://github.com/PHPOffice/PhpSpreadsheet/workflows/main/badge.svg)](https://github.com/PHPOffice/PhpSpreadsheet/actions)
[![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
[![Total Downloads](https://img.shields.io/packagist/dt/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet)

View File

@ -59,7 +59,7 @@
"psr/simple-cache": "^1.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
"voku/anti-xss": "^4.1"
"ezyang/htmlpurifier": "^4.13"
},
"require-dev": {
"dompdf/dompdf": "^0.8.5",

845
composer.lock generated
View File

@ -4,8 +4,62 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "209605c0b9329968170279f40db65d22",
"content-hash": "458fe4e974b469230da589a8781d1e0e",
"packages": [
{
"name": "ezyang/htmlpurifier",
"version": "v4.13.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
"reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"require-dev": {
"simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
},
"type": "library",
"autoload": {
"psr-0": {
"HTMLPurifier": "library/"
},
"files": [
"library/HTMLPurifier.composer.php"
],
"exclude-from-classmap": [
"/library/HTMLPurifier/Language/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "Edward Z. Yang",
"email": "admin@htmlpurifier.org",
"homepage": "http://ezyang.com"
}
],
"description": "Standards compliant HTML filter written in PHP",
"homepage": "http://htmlpurifier.org/",
"keywords": [
"html"
],
"support": {
"issues": "https://github.com/ezyang/htmlpurifier/issues",
"source": "https://github.com/ezyang/htmlpurifier/tree/master"
},
"time": "2020-06-29T00:56:53+00:00"
},
{
"name": "maennchen/zipstream-php",
"version": "2.1.0",
@ -477,242 +531,6 @@
],
"time": "2017-10-23T01:57:42+00:00"
},
{
"name": "symfony/polyfill-iconv",
"version": "v1.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-iconv.git",
"reference": "c536646fdb4f29104dd26effc2fdcb9a5b085024"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c536646fdb4f29104dd26effc2fdcb9a5b085024",
"reference": "c536646fdb4f29104dd26effc2fdcb9a5b085024",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"suggest": {
"ext-iconv": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Iconv\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Iconv extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"iconv",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-10-23T14:02:19+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b740103edbdcc39602239ee8860f0f45a8eb9aa5",
"reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's grapheme_* functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"grapheme",
"intl",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"files": [
"bootstrap.php"
],
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's Normalizer class and related functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"intl",
"normalizer",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.18.1",
@ -789,323 +607,6 @@
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "639447d008615574653fb3bc60d1986d7172eaae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae",
"reference": "639447d008615574653fb3bc60d1986d7172eaae",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "voku/anti-xss",
"version": "4.1.30",
"source": {
"type": "git",
"url": "https://github.com/voku/anti-xss.git",
"reference": "ff6e54f4a98ad1cd28f8b4a0f3c3f92f3c421f0a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/voku/anti-xss/zipball/ff6e54f4a98ad1cd28f8b4a0f3c3f92f3c421f0a",
"reference": "ff6e54f4a98ad1cd28f8b4a0f3c3f92f3c421f0a",
"shasum": ""
},
"require": {
"php": ">=7.0.0",
"voku/portable-utf8": "~5.4.50"
},
"require-dev": {
"phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.1.x-dev"
}
},
"autoload": {
"psr-4": {
"voku\\helper\\": "src/voku/helper/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "EllisLab Dev Team",
"homepage": "http://ellislab.com/"
},
{
"name": "Lars Moelleken",
"email": "lars@moelleken.org",
"homepage": "http://www.moelleken.org/"
}
],
"description": "anti xss-library",
"homepage": "https://github.com/voku/anti-xss",
"keywords": [
"anti-xss",
"clean",
"security",
"xss"
],
"funding": [
{
"url": "https://www.paypal.me/moelleken",
"type": "custom"
},
{
"url": "https://github.com/voku",
"type": "github"
},
{
"url": "https://opencollective.com/anti-xss",
"type": "open_collective"
},
{
"url": "https://www.patreon.com/voku",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/voku/anti-xss",
"type": "tidelift"
}
],
"time": "2020-11-12T00:30:57+00:00"
},
{
"name": "voku/portable-ascii",
"version": "1.5.6",
"source": {
"type": "git",
"url": "https://github.com/voku/portable-ascii.git",
"reference": "80953678b19901e5165c56752d087fc11526017c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/voku/portable-ascii/zipball/80953678b19901e5165c56752d087fc11526017c",
"reference": "80953678b19901e5165c56752d087fc11526017c",
"shasum": ""
},
"require": {
"php": ">=7.0.0"
},
"require-dev": {
"phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
},
"suggest": {
"ext-intl": "Use Intl for transliterator_transliterate() support"
},
"type": "library",
"autoload": {
"psr-4": {
"voku\\": "src/voku/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Lars Moelleken",
"homepage": "http://www.moelleken.org/"
}
],
"description": "Portable ASCII library - performance optimized (ascii) string functions for php.",
"homepage": "https://github.com/voku/portable-ascii",
"keywords": [
"ascii",
"clean",
"php"
],
"funding": [
{
"url": "https://www.paypal.me/moelleken",
"type": "custom"
},
{
"url": "https://github.com/voku",
"type": "github"
},
{
"url": "https://opencollective.com/portable-ascii",
"type": "open_collective"
},
{
"url": "https://www.patreon.com/voku",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii",
"type": "tidelift"
}
],
"time": "2020-11-12T00:07:28+00:00"
},
{
"name": "voku/portable-utf8",
"version": "5.4.50",
"source": {
"type": "git",
"url": "https://github.com/voku/portable-utf8.git",
"reference": "f14ed68ea9ced6639e71ca989c6d907892115ba0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/voku/portable-utf8/zipball/f14ed68ea9ced6639e71ca989c6d907892115ba0",
"reference": "f14ed68ea9ced6639e71ca989c6d907892115ba0",
"shasum": ""
},
"require": {
"php": ">=7.0.0",
"symfony/polyfill-iconv": "~1.0",
"symfony/polyfill-intl-grapheme": "~1.0",
"symfony/polyfill-intl-normalizer": "~1.0",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php72": "~1.0",
"voku/portable-ascii": "~1.5.6"
},
"require-dev": {
"phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
},
"suggest": {
"ext-ctype": "Use Ctype for e.g. hexadecimal digit detection",
"ext-fileinfo": "Use Fileinfo for better binary file detection",
"ext-iconv": "Use iconv for best performance",
"ext-intl": "Use Intl for best performance",
"ext-json": "Use JSON for string detection",
"ext-mbstring": "Use Mbstring for best performance"
},
"type": "library",
"autoload": {
"psr-4": {
"voku\\": "src/voku/"
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"(Apache-2.0 or GPL-2.0)"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Hamid Sarfraz",
"homepage": "http://pageconfig.com/"
},
{
"name": "Lars Moelleken",
"homepage": "http://www.moelleken.org/"
}
],
"description": "Portable UTF-8 library - performance optimized (unicode) string functions for php.",
"homepage": "https://github.com/voku/portable-utf8",
"keywords": [
"UTF",
"clean",
"php",
"unicode",
"utf-8",
"utf8"
],
"funding": [
{
"url": "https://www.paypal.me/moelleken",
"type": "custom"
},
{
"url": "https://github.com/voku",
"type": "github"
},
{
"url": "https://opencollective.com/portable-utf8",
"type": "open_collective"
},
{
"url": "https://www.patreon.com/voku",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/voku/portable-utf8",
"type": "tidelift"
}
],
"time": "2020-11-12T00:17:47+00:00"
}
],
"packages-dev": [
@ -4150,6 +3651,165 @@
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
"reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b740103edbdcc39602239ee8860f0f45a8eb9aa5",
"reference": "b740103edbdcc39602239ee8860f0f45a8eb9aa5",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Grapheme\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's grapheme_* functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"grapheme",
"intl",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"files": [
"bootstrap.php"
],
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's Normalizer class and related functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"intl",
"normalizer",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-php70",
"version": "v1.18.1",
@ -4227,6 +3887,79 @@
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.18.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "639447d008615574653fb3bc60d1986d7172eaae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae",
"reference": "639447d008615574653fb3bc60d1986d7172eaae",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-php73",
"version": "v1.18.1",
@ -4836,7 +4569,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "^7.2|^8.0",
"php": "^7.2||^8.0",
"ext-ctype": "*",
"ext-dom": "*",
"ext-gd": "*",
@ -4852,5 +4585,5 @@
"ext-zlib": "*"
},
"platform-dev": [],
"plugin-api-version": "1.1.0"
"plugin-api-version": "2.0.0"
}

View File

@ -422,8 +422,10 @@ foreach ($worksheet->getRowIterator() as $row) {
$cellIterator = $row->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(FALSE); // This loops through all cells,
// even if a cell value is not set.
// By default, only cells that have a value
// set will be iterated.
// For 'TRUE', we loop through cells
// only when their value is set.
// If this method is not called,
// the default value is 'false'.
foreach ($cellIterator as $cell) {
echo '<td>' .
$cell->getValue() .

View File

@ -458,6 +458,24 @@ $reader->setSheetIndex(0);
$spreadsheet = $reader->load("sample.csv");
```
You may also let PhpSpreadsheet attempt to guess the input encoding.
It will do so based on a test for BOM (UTF-8, UTF-16BE, UTF-16LE, UTF-32BE,
or UTF-32LE),
or by doing heuristic tests for those encodings, falling back to a
specifiable encoding (default is CP1252) if all of those tests fail.
```php
$reader = new \PhpOffice\PhpSpreadsheet\Reader\Csv();
$encoding = \PhpOffice\PhpSpreadsheet\Reader\Csv::guessEncoding('sample.csv');
// or, e.g. $encoding = \PhpOffice\PhpSpreadsheet\Reader\Csv::guessEncoding(
// 'sample.csv', 'ISO-8859-2');
$reader->setInputEncoding($encoding);
$reader->setDelimiter(';');
$reader->setEnclosure('');
$reader->setSheetIndex(0);
$spreadsheet = $reader->load('sample.csv');
```
#### Read a specific worksheet

Binary file not shown.

View File

@ -1108,6 +1108,10 @@ class MathTrig
$digits = Functions::flattenSingleValue($digits);
if ((is_numeric($number)) && (is_numeric($digits))) {
if ($number == 0.0) {
return 0.0;
}
if ($number < 0.0) {
return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
}
@ -1134,6 +1138,10 @@ class MathTrig
$digits = Functions::flattenSingleValue($digits);
if ((is_numeric($number)) && (is_numeric($digits))) {
if ($number == 0.0) {
return 0.0;
}
if ($number < 0.0) {
return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
}

View File

@ -27,15 +27,15 @@ class TextData
{
$character = Functions::flattenSingleValue($character);
if ((!is_numeric($character)) || ($character < 0)) {
if (!is_numeric($character)) {
return Functions::VALUE();
}
$character = (int) $character;
if ($character < 1 || $character > 255) {
return Functions::VALUE();
}
if (function_exists('iconv')) {
return iconv('UCS-4LE', 'UTF-8', pack('V', $character));
}
return mb_convert_encoding('&#' . (int) $character . ';', 'UTF-8', 'HTML-ENTITIES');
return iconv('UCS-4LE', 'UTF-8', pack('V', $character));
}
/**
@ -160,7 +160,7 @@ class TextData
// Validate parameters
if (!is_numeric($value) || !is_numeric($decimals)) {
return Functions::NAN();
return Functions::VALUE();
}
$decimals = floor($decimals);
@ -174,6 +174,7 @@ class TextData
}
$value = MathTrig::MROUND($value, $round);
}
$mask = "$mask;($mask)";
return NumberFormat::toFormattedString($value, $mask);
}
@ -265,7 +266,7 @@ class TextData
// Validate parameters
if (!is_numeric($value) || !is_numeric($decimals)) {
return Functions::NAN();
return Functions::VALUE();
}
$decimals = (int) floor($decimals);

View File

@ -18,7 +18,7 @@ class NamedFormula extends DefinedName
) {
// Validate data
if (empty($formula)) {
throw new Exception('Tou must specify a Formula value for a Named Formula');
throw new Exception('You must specify a Formula value for a Named Formula');
}
parent::__construct($name, $worksheet, $formula, $localOnly, $scope);
}

View File

@ -9,6 +9,21 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet;
class Csv extends BaseReader
{
const UTF8_BOM = "\xEF\xBB\xBF";
const UTF8_BOM_LEN = 3;
const UTF16BE_BOM = "\xfe\xff";
const UTF16BE_BOM_LEN = 2;
const UTF16BE_LF = "\x00\x0a";
const UTF16LE_BOM = "\xff\xfe";
const UTF16LE_BOM_LEN = 2;
const UTF16LE_LF = "\x0a\x00";
const UTF32BE_BOM = "\x00\x00\xfe\xff";
const UTF32BE_BOM_LEN = 4;
const UTF32BE_LF = "\x00\x00\x00\x0a";
const UTF32LE_BOM = "\xff\xfe\x00\x00";
const UTF32LE_BOM_LEN = 4;
const UTF32LE_LF = "\x0a\x00\x00\x00";
/**
* Input encoding.
*
@ -90,12 +105,8 @@ class Csv extends BaseReader
{
rewind($this->fileHandle);
switch ($this->inputEncoding) {
case 'UTF-8':
fgets($this->fileHandle, 4) == "\xEF\xBB\xBF" ?
fseek($this->fileHandle, 3) : fseek($this->fileHandle, 0);
break;
if (fgets($this->fileHandle, self::UTF8_BOM_LEN + 1) !== self::UTF8_BOM) {
rewind($this->fileHandle);
}
}
@ -213,7 +224,9 @@ class Csv extends BaseReader
private function getNextLine()
{
$line = '';
$enclosure = '(?<!' . preg_quote($this->escapeCharacter, '/') . ')' . preg_quote($this->enclosure, '/');
$enclosure = ($this->escapeCharacter === '' ? ''
: ('(?<!' . preg_quote($this->escapeCharacter, '/') . ')'))
. preg_quote($this->enclosure, '/');
do {
// Get the next line in the file
@ -307,7 +320,7 @@ class Csv extends BaseReader
$this->fileHandle = fopen('php://memory', 'r+b');
$data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
fwrite($this->fileHandle, $data);
rewind($this->fileHandle);
$this->skipBOM();
}
}
@ -531,4 +544,63 @@ class Csv extends BaseReader
return in_array($type, $supportedTypes, true);
}
private static function guessEncodingTestNoBom(string &$encoding, string &$contents, string $compare, string $setEncoding): void
{
if ($encoding === '') {
$pos = strpos($contents, $compare);
if ($pos !== false && $pos % strlen($compare) === 0) {
$encoding = $setEncoding;
}
}
}
private static function guessEncodingNoBom(string $filename): string
{
$encoding = '';
$contents = file_get_contents($filename);
self::guessEncodingTestNoBom($encoding, $contents, self::UTF32BE_LF, 'UTF-32BE');
self::guessEncodingTestNoBom($encoding, $contents, self::UTF32LE_LF, 'UTF-32LE');
self::guessEncodingTestNoBom($encoding, $contents, self::UTF16BE_LF, 'UTF-16BE');
self::guessEncodingTestNoBom($encoding, $contents, self::UTF16LE_LF, 'UTF-16LE');
if ($encoding === '' && preg_match('//u', $contents) === 1) {
$encoding = 'UTF-8';
}
return $encoding;
}
private static function guessEncodingTestBom(string &$encoding, string $first4, string $compare, string $setEncoding): void
{
if ($encoding === '') {
if ($compare === substr($first4, 0, strlen($compare))) {
$encoding = $setEncoding;
}
}
}
private static function guessEncodingBom(string $filename): string
{
$encoding = '';
$first4 = file_get_contents($filename, false, null, 0, 4);
if ($first4 !== false) {
self::guessEncodingTestBom($encoding, $first4, self::UTF8_BOM, 'UTF-8');
self::guessEncodingTestBom($encoding, $first4, self::UTF16BE_BOM, 'UTF-16BE');
self::guessEncodingTestBom($encoding, $first4, self::UTF32BE_BOM, 'UTF-32BE');
self::guessEncodingTestBom($encoding, $first4, self::UTF32LE_BOM, 'UTF-32LE');
self::guessEncodingTestBom($encoding, $first4, self::UTF16LE_BOM, 'UTF-16LE');
}
return $encoding;
}
public static function guessEncoding(string $filename, string $dflt = 'CP1252'): string
{
$encoding = self::guessEncodingBom($filename);
if ($encoding === '') {
$encoding = self::guessEncodingNoBom($filename);
}
return ($encoding === '') ? $dflt : $encoding;
}
}

View File

@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Slk extends BaseReader
{
@ -516,7 +517,7 @@ class Slk extends BaseReader
$spreadsheet->createSheet();
}
$spreadsheet->setActiveSheetIndex($this->sheetIndex);
$spreadsheet->getActiveSheet()->setTitle(basename($pFilename, '.slk'));
$spreadsheet->getActiveSheet()->setTitle(substr(basename($pFilename, '.slk'), 0, Worksheet::SHEET_TITLE_MAXIMUM_LENGTH));
// Loop through file
$column = $row = '';

View File

@ -2973,6 +2973,9 @@ class Xls extends BaseReader
// offset within (spliced) record data
$pos = 0;
// Limit global SST position, further control for bad SST Length in BIFF8 data
$limitposSST = 0;
// get spliced record data
$splicedRecordData = $this->getSplicedRecordData();
@ -2986,8 +2989,17 @@ class Xls extends BaseReader
$nm = self::getInt4d($recordData, 4);
$pos += 4;
// look up limit position
foreach ($spliceOffsets as $spliceOffset) {
// it can happen that the string is empty, therefore we need
// <= and not just <
if ($pos <= $spliceOffset) {
$limitposSST = $spliceOffset;
}
}
// loop through the Unicode strings (16-bit length)
for ($i = 0; $i < $nm; ++$i) {
for ($i = 0; $i < $nm && $pos < $limitposSST; ++$i) {
// number of characters in the Unicode string
$numChars = self::getUInt2d($recordData, $pos);
$pos += 2;
@ -3020,7 +3032,7 @@ class Xls extends BaseReader
// expected byte length of character array if not split
$len = ($isCompressed) ? $numChars : $numChars * 2;
// look up limit position
// look up limit position - Check it again to be sure that no error occurs when parsing SST structure
foreach ($spliceOffsets as $spliceOffset) {
// it can happen that the string is empty, therefore we need
// <= and not just <

View File

@ -1280,7 +1280,7 @@ class Xlsx extends BaseReader
}
// Valid range?
if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') {
if ($extractedRange == '') {
continue;
}
@ -1350,7 +1350,7 @@ class Xlsx extends BaseReader
$extractedRange = (string) $definedName;
// Valid range?
if (stripos((string) $definedName, '#REF!') !== false || $extractedRange == '') {
if ($extractedRange == '') {
continue;
}
@ -1398,6 +1398,9 @@ class Xlsx extends BaseReader
$locatedSheet = $excel->getSheetByName($extractedSheetName);
}
if ($locatedSheet === null && !DefinedName::testIfFormula($definedRange)) {
$definedRange = '#REF!';
}
$excel->addDefinedName(DefinedName::createInstance((string) $definedName['name'], $locatedSheet, $definedRange, false));
}
}
@ -1968,7 +1971,7 @@ class Xlsx extends BaseReader
$unparsedPrinterSettings = &$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['printerSettings'];
foreach ($sheetPrinterSettings as $rId => $printerSettings) {
$rId = substr($rId, 3); // rIdXXX
$rId = substr($rId, 3) . 'ps'; // rIdXXX, add 'ps' suffix to avoid identical resource identifier collision with unparsed vmlDrawing
$unparsedPrinterSettings[$rId] = [];
$unparsedPrinterSettings[$rId]['filePath'] = self::dirAdd("$dir/$fileWorksheet", $printerSettings['Target']);
$unparsedPrinterSettings[$rId]['relFilePath'] = (string) $printerSettings['Target'];

View File

@ -41,7 +41,7 @@ class Hyperlinks
foreach (Coordinate::extractAllCellReferencesInRange($hyperlink['ref']) as $cellReference) {
$cell = $worksheet->getCell($cellReference);
if (isset($linkRel['id'])) {
$hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']];
$hyperlinkUrl = $this->hyperlinks[(string) $linkRel['id']] ?? null;
if (isset($hyperlink['location'])) {
$hyperlinkUrl .= '#' . (string) $hyperlink['location'];
}

View File

@ -617,9 +617,11 @@ class Xml extends BaseReader
++$rowID;
}
$xmlX = $worksheet->children($namespaces['x']);
if (isset($xmlX->WorksheetOptions)) {
(new PageSettings($xmlX, $namespaces))->loadPageSettings($spreadsheet);
if (isset($namespaces['x'])) {
$xmlX = $worksheet->children($namespaces['x']);
if (isset($xmlX->WorksheetOptions)) {
(new PageSettings($xmlX, $namespaces))->loadPageSettings($spreadsheet);
}
}
}
++$worksheetID;

View File

@ -2,6 +2,8 @@
namespace PhpOffice\PhpSpreadsheet\Shared;
use GdImage;
class Drawing
{
/**
@ -98,7 +100,7 @@ class Drawing
*/
public static function pixelsToPoints($pxValue)
{
return $pxValue * 0.67777777;
return $pxValue * 0.75;
}
/**
@ -111,7 +113,7 @@ class Drawing
public static function pointsToPixels($ptValue)
{
if ($ptValue != 0) {
return (int) ceil($ptValue * 1.333333333);
return (int) ceil($ptValue / 0.75);
}
return 0;
@ -152,7 +154,7 @@ class Drawing
*
* @param string $bmpFilename Path to Windows DIB (BMP) image
*
* @return resource
* @return GdImage|resource
*/
public static function imagecreatefrombmp($bmpFilename)
{

View File

@ -666,7 +666,7 @@ class Spreadsheet
// Adjust active sheet index if necessary
if (
($this->activeSheetIndex >= $sheetIndex) &&
($sheetIndex > count($this->workSheetCollection) - 1)
($this->activeSheetIndex > 0 || $numSheets <= 1)
) {
--$this->activeSheetIndex;
}

View File

@ -189,7 +189,7 @@ class Conditional implements IComparable
/**
* Set Conditions.
*
* @param string[] $conditions Condition
* @param bool|float|int|string|string[] $conditions Condition
*
* @return $this
*/

View File

@ -42,13 +42,6 @@ class Style extends Supervisor
*/
protected $numberFormat;
/**
* Conditional styles.
*
* @var Conditional[]
*/
protected $conditionalStyles;
/**
* Protection.
*
@ -85,7 +78,6 @@ class Style extends Supervisor
parent::__construct($isSupervisor);
// Initialise values
$this->conditionalStyles = [];
$this->font = new Font($isSupervisor, $isConditional);
$this->fill = new Fill($isSupervisor, $isConditional);
$this->borders = new Borders($isSupervisor, $isConditional);
@ -210,6 +202,8 @@ class Style extends Supervisor
$rangeEnd = Coordinate::coordinateFromString($rangeB);
// Translate column into index
$rangeStart0 = $rangeStart[0];
$rangeEnd0 = $rangeEnd[0];
$rangeStart[0] = Coordinate::columnIndexFromString($rangeStart[0]);
$rangeEnd[0] = Coordinate::columnIndexFromString($rangeEnd[0]);
@ -359,6 +353,13 @@ class Style extends Supervisor
for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
$oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
}
foreach ($this->getActiveSheet()->getColumnIterator($rangeStart0, $rangeEnd0) as $columnIterator) {
$cellIterator = $columnIterator->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $columnCell) {
$columnCell->getStyle()->applyFromArray($pStyles);
}
}
break;
case 'ROW':
@ -370,6 +371,13 @@ class Style extends Supervisor
$oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
}
}
foreach ($this->getActiveSheet()->getRowIterator((int) $rangeStart[1], (int) $rangeEnd[1]) as $rowIterator) {
$cellIterator = $rowIterator->getCellIterator();
$cellIterator->setIterateOnlyExistingCells(true);
foreach ($cellIterator as $rowCell) {
$rowCell->getStyle()->applyFromArray($pStyles);
}
}
break;
case 'CELL':
@ -597,18 +605,12 @@ class Style extends Supervisor
*/
public function getHashCode()
{
$hashConditionals = '';
foreach ($this->conditionalStyles as $conditional) {
$hashConditionals .= $conditional->getHashCode();
}
return md5(
$this->fill->getHashCode() .
$this->font->getHashCode() .
$this->borders->getHashCode() .
$this->alignment->getHashCode() .
$this->numberFormat->getHashCode() .
$hashConditionals .
$this->protection->getHashCode() .
($this->quotePrefix ? 't' : 'f') .
__CLASS__

View File

@ -92,10 +92,11 @@ class ColumnCellIterator extends CellIterator
*/
public function seek($row = 1)
{
if ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $row))) {
throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
}
if (($row < $this->startRow) || ($row > $this->endRow)) {
throw new PhpSpreadsheetException("Row $row is out of range ({$this->startRow} - {$this->endRow})");
} elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $row))) {
throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
}
$this->currentRow = $row;
@ -113,7 +114,7 @@ class ColumnCellIterator extends CellIterator
/**
* Return the current cell in this worksheet column.
*
* @return null|\PhpOffice\PhpSpreadsheet\Cell\Cell
* @return \PhpOffice\PhpSpreadsheet\Cell\Cell
*/
public function current()
{
@ -180,18 +181,12 @@ class ColumnCellIterator extends CellIterator
) {
++$this->startRow;
}
if ($this->startRow > $this->endRow) {
throw new PhpSpreadsheetException('No cells exist within the specified range');
}
while (
(!$this->worksheet->cellExistsByColumnAndRow($this->columnIndex, $this->endRow)) &&
($this->endRow >= $this->startRow)
) {
--$this->endRow;
}
if ($this->endRow < $this->startRow) {
throw new PhpSpreadsheetException('No cells exist within the specified range');
}
}
}
}

View File

@ -2,6 +2,8 @@
namespace PhpOffice\PhpSpreadsheet\Worksheet;
use GdImage;
class MemoryDrawing extends BaseDrawing
{
// Rendering functions
@ -19,7 +21,7 @@ class MemoryDrawing extends BaseDrawing
/**
* Image resource.
*
* @var resource
* @var GdImage|resource
*/
private $imageResource;
@ -62,7 +64,7 @@ class MemoryDrawing extends BaseDrawing
/**
* Get image resource.
*
* @return resource
* @return GdImage|resource
*/
public function getImageResource()
{
@ -72,7 +74,7 @@ class MemoryDrawing extends BaseDrawing
/**
* Set image resource.
*
* @param resource $value
* @param GdImage|resource $value
*
* @return $this
*/

View File

@ -93,12 +93,14 @@ class RowCellIterator extends CellIterator
*/
public function seek($column = 'A')
{
$columnx = $column;
$column = Coordinate::columnIndexFromString($column);
if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) {
throw new PhpSpreadsheetException("Column $column is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})");
} elseif ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($column, $this->rowIndex))) {
if ($this->onlyExistingCells && !($this->worksheet->cellExistsByColumnAndRow($column, $this->rowIndex))) {
throw new PhpSpreadsheetException('In "IterateOnlyExistingCells" mode and Cell does not exist');
}
if (($column < $this->startColumnIndex) || ($column > $this->endColumnIndex)) {
throw new PhpSpreadsheetException("Column $columnx is out of range ({$this->startColumnIndex} - {$this->endColumnIndex})");
}
$this->currentColumnIndex = $column;
return $this;
@ -181,15 +183,9 @@ class RowCellIterator extends CellIterator
while ((!$this->worksheet->cellExistsByColumnAndRow($this->startColumnIndex, $this->rowIndex)) && ($this->startColumnIndex <= $this->endColumnIndex)) {
++$this->startColumnIndex;
}
if ($this->startColumnIndex > $this->endColumnIndex) {
throw new PhpSpreadsheetException('No cells exist within the specified range');
}
while ((!$this->worksheet->cellExistsByColumnAndRow($this->endColumnIndex, $this->rowIndex)) && ($this->endColumnIndex >= $this->startColumnIndex)) {
--$this->endColumnIndex;
}
if ($this->endColumnIndex < $this->startColumnIndex) {
throw new PhpSpreadsheetException('No cells exist within the specified range');
}
}
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Writer;
use HTMLPurifier;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
@ -23,7 +24,6 @@ use PhpOffice\PhpSpreadsheet\Style\Style;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use voku\helper\AntiXSS;
class Html extends BaseWriter
{
@ -1789,9 +1789,9 @@ class Html extends BaseWriter
{
$result = '';
if (!$this->isPdf && isset($pSheet->getComments()[$coordinate])) {
$sanitizer = new AntiXSS();
$sanitizedString = $sanitizer->xss_clean($pSheet->getComment($coordinate)->getText()->getPlainText());
if (!$sanitizer->isXssFound()) {
$sanitizer = new HTMLPurifier();
$sanitizedString = $sanitizer->purify($pSheet->getComment($coordinate)->getText()->getPlainText());
if ($sanitizedString !== '') {
$result .= '<a class="comment-indicator"></a>';
$result .= '<div class="comment">' . nl2br($sanitizedString) . '</div>';
$result .= PHP_EOL;

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
use GdImage;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
@ -2254,7 +2255,7 @@ class Worksheet extends BIFFwriter
*/
public function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1): void
{
$bitmap_array = (is_resource($bitmap) ? $this->processBitmapGd($bitmap) : $this->processBitmap($bitmap));
$bitmap_array = (is_resource($bitmap) || $bitmap instanceof GdImage ? $this->processBitmapGd($bitmap) : $this->processBitmap($bitmap));
[$width, $height, $size, $data] = $bitmap_array;
// Scale the frame of the image.
@ -2460,7 +2461,7 @@ class Worksheet extends BIFFwriter
/**
* Convert a GD-image into the internal format.
*
* @param resource $image The image to process
* @param GdImage|resource $image The image to process
*
* @return array Array with data and properties of the bitmap
*/

View File

@ -1079,7 +1079,7 @@ class Worksheet extends WriterPart
{
$objWriter->writeAttribute('t', $mappedType);
if (!$cellValue instanceof RichText) {
self::writeElementIf($objWriter, isset($pFlippedStringTable[$cellValue]), 'v', $pFlippedStringTable[$cellValue]);
self::writeElementIf($objWriter, isset($pFlippedStringTable[$cellValue]), 'v', $pFlippedStringTable[$cellValue] ?? '');
} else {
$objWriter->writeElement('v', $pFlippedStringTable[$cellValue->getHashCode()]);
}

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests;
use PhpOffice\PhpSpreadsheet\DefinedName;
use PhpOffice\PhpSpreadsheet\NamedFormula;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;
@ -164,4 +165,24 @@ class DefinedNameFormulaTest extends TestCase
'utf-8 named ranges in a formula' => ['Здравствуй+мир', true],
];
}
public function testEmptyNamedFormula(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$spreadSheet = new Spreadsheet();
$workSheet1 = $spreadSheet->getActiveSheet();
new NamedFormula('namedformula', $workSheet1);
}
public function testChangeFormula(): void
{
$spreadSheet = new Spreadsheet();
$workSheet1 = $spreadSheet->getActiveSheet();
$namedFormula = new NamedFormula('namedformula', $workSheet1, '=1');
self::assertEquals('=1', $namedFormula->getFormula());
$namedFormula->setFormula('=2');
self::assertEquals('=2', $namedFormula->getFormula());
$namedFormula->setFormula('');
self::assertEquals('=2', $namedFormula->getFormula());
}
}

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests;
use PhpOffice\PhpSpreadsheet\DefinedName;
use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
@ -121,4 +122,83 @@ class DefinedNameTest extends TestCase
$this->spreadsheet->getDefinedName('foo')->getValue()
);
}
public function testDefinedNameNoWorksheetNoScope(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
new NamedRange('xyz');
}
public function testSetAndGetRange(): void
{
$this->spreadsheet->addDefinedName(
DefinedName::createInstance('xyz', $this->spreadsheet->getActiveSheet(), 'A1')
);
$namedRange = $this->spreadsheet->getDefinedName('XYZ');
self::assertInstanceOf(NamedRange::class, $namedRange);
self::assertEquals('A1', $namedRange->getRange());
self::assertEquals('A1', $namedRange->getValue());
$namedRange->setRange('A2');
self::assertEquals('A2', $namedRange->getValue());
}
public function testChangeWorksheet(): void
{
$sheet1 = $this->spreadsheet->getSheetByName('Sheet #1');
$sheet2 = $this->spreadsheet->getSheetByName('Sheet #2');
$sheet1->getCell('A1')->setValue(1);
$sheet2->getCell('A1')->setValue(2);
$namedRange = new NamedRange('xyz', $sheet2, '$A$1');
$namedRange->setWorksheet($sheet1);
$this->spreadsheet->addNamedRange($namedRange);
$sheet1->getCell('B2')->setValue('=XYZ');
self::assertEquals(1, $sheet1->getCell('B2')->getCalculatedValue());
$sheet2->getCell('B2')->setValue('=XYZ');
self::assertEquals(1, $sheet2->getCell('B2')->getCalculatedValue());
}
public function testLocalOnly(): void
{
$sheet1 = $this->spreadsheet->getSheetByName('Sheet #1');
$sheet2 = $this->spreadsheet->getSheetByName('Sheet #2');
$sheet1->getCell('A1')->setValue(1);
$sheet2->getCell('A1')->setValue(2);
$namedRange = new NamedRange('abc', $sheet2, '$A$1');
$namedRange->setWorksheet($sheet1)->setLocalOnly(true);
$this->spreadsheet->addNamedRange($namedRange);
$sheet1->getCell('C2')->setValue('=ABC');
self::assertEquals(1, $sheet1->getCell('C2')->getCalculatedValue());
$sheet2->getCell('C2')->setValue('=ABC');
self::assertEquals('#NAME?', $sheet2->getCell('C2')->getCalculatedValue());
}
public function testScope(): void
{
$sheet1 = $this->spreadsheet->getSheetByName('Sheet #1');
$sheet2 = $this->spreadsheet->getSheetByName('Sheet #2');
$sheet1->getCell('A1')->setValue(1);
$sheet2->getCell('A1')->setValue(2);
$namedRange = new NamedRange('abc', $sheet2, '$A$1');
$namedRange->setScope($sheet1);
$this->spreadsheet->addNamedRange($namedRange);
$sheet1->getCell('C2')->setValue('=ABC');
self::assertEquals(2, $sheet1->getCell('C2')->getCalculatedValue());
$sheet2->getCell('C2')->setValue('=ABC');
self::assertEquals('#NAME?', $sheet2->getCell('C2')->getCalculatedValue());
}
public function testClone(): void
{
$sheet1 = $this->spreadsheet->getSheetByName('Sheet #1');
$sheet2 = $this->spreadsheet->getSheetByName('Sheet #2');
$sheet1->getCell('A1')->setValue(1);
$sheet2->getCell('A1')->setValue(2);
$namedRange = new NamedRange('abc', $sheet2, '$A$1');
$namedRangeClone = clone $namedRange;
$ss1 = $namedRange->getWorksheet();
$ss2 = $namedRangeClone->getWorksheet();
self::assertNotSame($ss1, $ss2);
self::assertEquals($ss1->getTitle(), $ss2->getTitle());
}
}

View File

@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Logical;
use PhpOffice\PhpSpreadsheet\DocumentGenerator;
use PHPUnit\Framework\TestCase;
use UnexpectedValueException;
class DocumentGeneratorTest extends TestCase
{
@ -137,4 +138,13 @@ EXPECTED
],
];
}
public function testGenerateFunctionBadArray(): void
{
$this->expectException(UnexpectedValueException::class);
$phpSpreadsheetFunctions = [
'ABS' => ['category' => Cat::CATEGORY_MATH_AND_TRIG, 'functionCall' => 1],
];
DocumentGenerator::generateFunctionListByName($phpSpreadsheetFunctions);
}
}

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Functional;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
class CommentsTest extends AbstractFunctional
{
@ -35,10 +36,22 @@ class CommentsTest extends AbstractFunctional
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $format);
$commentsLoaded = $reloadedSpreadsheet->getSheet(0)->getComments();
$sheet = $reloadedSpreadsheet->getSheet(0);
$commentsLoaded = $sheet->getComments();
self::assertCount(1, $commentsLoaded);
$commentCoordinate = key($commentsLoaded);
self::assertSame('E10', $commentCoordinate);
$comment = $commentsLoaded[$commentCoordinate];
self::assertEquals('Comment to test', (string) $comment);
$commentClone = clone $comment;
self::assertEquals($comment, $commentClone);
self::assertNotSame($comment, $commentClone);
if ($format === 'Xlsx') {
self::assertEquals('feb0c24b880a8130262dadf801f85e94', $comment->getHashCode());
self::assertEquals(Alignment::HORIZONTAL_GENERAL, $comment->getAlignment());
$comment->setAlignment(Alignment::HORIZONTAL_RIGHT);
self::assertEquals(Alignment::HORIZONTAL_RIGHT, $comment->getAlignment());
}
}
}

View File

@ -43,7 +43,7 @@ class SampleTest extends TestCase
}
// Unfortunately some tests are too long be ran with code-coverage
// analysis on Travis, so we need to exclude them
// analysis on GitHub Actions, so we need to exclude them
global $argv;
if (in_array('--coverage-clover', $argv)) {
$tooLongToBeCovered = [

View File

@ -161,4 +161,45 @@ class IOFactoryTest extends TestCase
IOFactory::registerReader('foo', 'bar');
}
public function testCreateInvalidWriter(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Writer\Exception::class);
$spreadsheet = new Spreadsheet();
IOFactory::createWriter($spreadsheet, 'bad');
}
public function testCreateInvalidReader(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Reader\Exception::class);
IOFactory::createReader('bad');
}
public function testCreateReaderUnknownExtension(): void
{
$filename = 'samples/Reader/sampleData/example1.tsv';
$reader = IOFactory::createReaderForFile($filename);
self::assertEquals('PhpOffice\\PhpSpreadsheet\\Reader\\Csv', get_class($reader));
}
public function testCreateReaderCsvExtension(): void
{
$filename = 'samples/Reader/sampleData/example1.csv';
$reader = IOFactory::createReaderForFile($filename);
self::assertEquals('PhpOffice\\PhpSpreadsheet\\Reader\\Csv', get_class($reader));
}
public function testCreateReaderNoExtension(): void
{
$filename = 'samples/Reader/sampleData/example1xls';
$reader = IOFactory::createReaderForFile($filename);
self::assertEquals('PhpOffice\\PhpSpreadsheet\\Reader\\Xls', get_class($reader));
}
public function testCreateReaderNotSpreadsheet(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Reader\Exception::class);
$filename = __FILE__;
IOFactory::createReaderForFile($filename);
}
}

View File

@ -275,4 +275,66 @@ EOF;
$reader = new Csv();
$reader->load('tests/data/Reader/CSV/encoding.utf8.csvxxx');
}
/**
* @dataProvider providerEscapes
*/
public function testInferSeparator(string $escape, string $delimiter): void
{
$reader = new Csv();
$reader->setEscapeCharacter($escape);
$filename = 'tests/data/Reader/CSV/escape.csv';
$reader->listWorksheetInfo($filename);
self::assertEquals($delimiter, $reader->getDelimiter());
}
public function providerEscapes()
{
return [
['\\', ';'],
["\x0", ','],
[(version_compare(PHP_VERSION, '7.4') < 0) ? "\x0" : '', ','],
];
}
/**
* @dataProvider providerGuessEncoding
*/
public function testGuessEncoding(string $filename): void
{
$reader = new Csv();
$reader->setInputEncoding(Csv::guessEncoding($filename));
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('première', $sheet->getCell('A1')->getValue());
self::assertEquals('sixième', $sheet->getCell('C2')->getValue());
}
public function providerGuessEncoding()
{
return [
['tests/data/Reader/CSV/premiere.utf8.csv'],
['tests/data/Reader/CSV/premiere.utf8bom.csv'],
['tests/data/Reader/CSV/premiere.utf16be.csv'],
['tests/data/Reader/CSV/premiere.utf16bebom.csv'],
['tests/data/Reader/CSV/premiere.utf16le.csv'],
['tests/data/Reader/CSV/premiere.utf16lebom.csv'],
['tests/data/Reader/CSV/premiere.utf32be.csv'],
['tests/data/Reader/CSV/premiere.utf32bebom.csv'],
['tests/data/Reader/CSV/premiere.utf32le.csv'],
['tests/data/Reader/CSV/premiere.utf32lebom.csv'],
['tests/data/Reader/CSV/premiere.win1252.csv'],
];
}
public function testGuessEncodingDefltIso2(): void
{
$filename = 'tests/data/Reader/CSV/premiere.win1252.csv';
$reader = new Csv();
$reader->setInputEncoding(Csv::guessEncoding($filename, 'ISO-8859-2'));
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('premičre', $sheet->getCell('A1')->getValue());
self::assertEquals('sixičme', $sheet->getCell('C2')->getValue());
}
}

View File

@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Reader\Slk;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
@ -12,6 +13,16 @@ class SlkTest extends \PHPUnit\Framework\TestCase
{
private static $testbook = __DIR__ . '/../../../samples/templates/SylkTest.slk';
private $filename = '';
protected function teardown(): void
{
if ($this->filename) {
unlink($this->filename);
$this->filename = '';
}
}
public function testInfo(): void
{
$reader = new Slk();
@ -131,4 +142,17 @@ class SlkTest extends \PHPUnit\Framework\TestCase
self::assertEquals('FFFF0000', $sheet->getCell('A1')->getStyle()->getFont()->getColor()->getARGB());
}
public function testLongName(): void
{
$contents = file_get_contents(self::$testbook);
$this->filename = File::sysGetTempDir()
. '/123456789a123456789b123456789c12345.slk';
file_put_contents($this->filename, $contents);
$reader = new Slk();
$spreadsheet = $reader->load($this->filename);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('123456789a123456789b123456789c1', $sheet->getTitle());
self::assertEquals('FFFF0000', $sheet->getCell('A1')->getStyle()->getFont()->getColor()->getARGB());
}
}

View File

@ -43,4 +43,39 @@ class XlsTest extends AbstractFunctional
self::assertEquals($sheet->getCell('A1')->getFormattedValue(), $newsheet->getCell('A1')->getFormattedValue());
self::assertEquals($sheet->getCell("$col$row")->getFormattedValue(), $newsheet->getCell("$col$row")->getFormattedValue());
}
/**
* Test load Xls file with invalid length in SST map.
*/
public function testLoadXlsBug1592(): void
{
$filename = 'tests/data/Reader/XLS/bug1592.xls';
$reader = new Xls();
// When no fix applied, spreadsheet is not loaded
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
$col = $sheet->getHighestColumn();
$row = $sheet->getHighestRow();
$newspreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
$newsheet = $newspreadsheet->getActiveSheet();
$newcol = $newsheet->getHighestColumn();
$newrow = $newsheet->getHighestRow();
self::assertEquals($spreadsheet->getSheetCount(), $newspreadsheet->getSheetCount());
self::assertEquals($sheet->getTitle(), $newsheet->getTitle());
self::assertEquals($sheet->getColumnDimensions(), $newsheet->getColumnDimensions());
self::assertEquals($col, $newcol);
self::assertEquals($row, $newrow);
$rowIterator = $sheet->getRowIterator();
foreach ($rowIterator as $row) {
foreach ($row->getCellIterator() as $cell) {
$valOld = $cell->getFormattedValue();
$valNew = $newsheet->getCell($cell->getCoordinate())->getFormattedValue();
self::assertEquals($valOld, $valNew);
}
}
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PHPUnit\Framework\TestCase;
class NamedRangeTest extends TestCase
{
public static function testBug1686b(): void
{
$xlsxFile = 'tests/data/Reader/XLSX/bug1686b.xlsx';
$reader = new Xlsx();
$spreadsheet = $reader->load($xlsxFile);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals(2.1, $sheet->getCell('A1')->getCalculatedValue());
self::assertEquals('#REF!', $sheet->getCell('A2')->getCalculatedValue());
self::assertEquals('#REF!', $sheet->getCell('A3')->getCalculatedValue());
self::assertEquals('#NAME?', $sheet->getCell('A4')->getCalculatedValue());
self::assertEquals('#REF!', $sheet->getCell('A5')->getCalculatedValue());
}
}

View File

@ -82,14 +82,9 @@ class XmlLoadTest extends TestCase
public function testLoadUnusableSample(): void
{
// Sample spreadsheet is not readable by Excel.
// But PhpSpreadsheet can load it except for coverage test.
//global $argv;
//if (in_array('--coverage-clover', $argv)) {
// self::markTestSkipped('Mysterious Travis coverage failure IOFactoryTest');
//}
// But PhpSpreadsheet can load it.
$filename = __DIR__
. '/../../../..'
//. '/samples/templates/Excel2003XMLTest.xml';
. '/samples/templates/excel2003.short.bad.xml';
$reader = new Xml();
$spreadsheet = $reader->load($filename);

View File

@ -0,0 +1,70 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PHPUnit\Framework\TestCase;
class XmlOddTest extends TestCase
{
private $filename = '';
protected function teardown(): void
{
if ($this->filename) {
unlink($this->filename);
$this->filename = '';
}
}
public function testWriteThenRead(): void
{
$xmldata = <<< 'EOT'
<?xml version="1.0" encoding="UTF-8"?><?mso-application progid="Excel.Sheet"?>
<Workbook xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
>
<DocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<Title>Xml2003 Short Workbook</Title>
</DocumentProperties>
<CustomDocumentProperties xmlns="urn:schemas-microsoft-com:office:office">
<my_x05d0_Int dt:dt="integer">2</my_x05d0_Int>
</CustomDocumentProperties>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>9000</WindowHeight>
<WindowWidth>13860</WindowWidth>
<WindowTopX>240</WindowTopX>
<WindowTopY>75</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<ss:Worksheet ss:Name="Sample Data">
<Table>
<Column ss:Width="96.4913"/>
<Row ss:Index="8" ss:AutoFitHeight="0" ss:Height="14.9953">
<Cell>
<ss:Data ss:Type="String">Test String 1</ss:Data>
</Cell>
</Row>
</Table>
</ss:Worksheet>
</Workbook>
EOT;
$this->filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test');
file_put_contents($this->filename, $xmldata);
$reader = new Xml();
$spreadsheet = $reader->load($this->filename);
self::assertEquals(1, $spreadsheet->getSheetCount());
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('Sample Data', $sheet->getTitle());
self::assertEquals('Test String 1', $sheet->getCell('A8')->getValue());
$props = $spreadsheet->getProperties();
self::assertEquals('Xml2003 Short Workbook', $props->getTitle());
self::assertEquals('2', $props->getCustomPropertyValue('myאInt'));
}
}

View File

@ -51,6 +51,147 @@ class SpreadsheetTest extends TestCase
*/
public function testGetSheetByName($index, $sheetName): void
{
self::assertEquals($this->object->getSheet($index), $this->object->getSheetByName($sheetName));
self::assertSame($this->object->getSheet($index), $this->object->getSheetByName($sheetName));
}
public function testAddSheetDuplicateTitle(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$sheet = new Worksheet();
$sheet->setTitle('someSheet2');
$this->object->addSheet($sheet);
}
public function testAddSheetNoAdjustActive(): void
{
$this->object->setActiveSheetIndex(2);
self::assertEquals(2, $this->object->getActiveSheetIndex());
$sheet = new Worksheet();
$sheet->setTitle('someSheet4');
$this->object->addSheet($sheet);
self::assertEquals(2, $this->object->getActiveSheetIndex());
}
public function testAddSheetAdjustActive(): void
{
$this->object->setActiveSheetIndex(2);
self::assertEquals(2, $this->object->getActiveSheetIndex());
$sheet = new Worksheet();
$sheet->setTitle('someSheet0');
$this->object->addSheet($sheet, 0);
self::assertEquals(3, $this->object->getActiveSheetIndex());
}
public function testRemoveSheetIndexTooHigh(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->object->removeSheetByIndex(4);
}
public function testRemoveSheetNoAdjustActive(): void
{
$this->object->setActiveSheetIndex(1);
self::assertEquals(1, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(2);
self::assertEquals(1, $this->object->getActiveSheetIndex());
}
public function testRemoveSheetAdjustActive(): void
{
$this->object->setActiveSheetIndex(2);
self::assertEquals(2, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(1);
self::assertEquals(1, $this->object->getActiveSheetIndex());
}
public function testGetSheetIndexTooHigh(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->object->getSheet(4);
}
public function testGetIndexNonExistent(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$sheet = new Worksheet();
$sheet->setTitle('someSheet4');
$this->object->getIndex($sheet);
}
public function testSetIndexByName(): void
{
$this->object->setIndexByName('someSheet1', 1);
self::assertEquals('someSheet2', $this->object->getSheet(0)->getTitle());
self::assertEquals('someSheet1', $this->object->getSheet(1)->getTitle());
self::assertEquals('someSheet 3', $this->object->getSheet(2)->getTitle());
}
public function testRemoveAllSheets(): void
{
$this->object->setActiveSheetIndex(2);
self::assertEquals(2, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(0);
self::assertEquals(1, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(0);
self::assertEquals(0, $this->object->getActiveSheetIndex());
$this->object->removeSheetByIndex(0);
self::assertEquals(-1, $this->object->getActiveSheetIndex());
$sheet = new Worksheet();
$sheet->setTitle('someSheet4');
$this->object->addSheet($sheet);
self::assertEquals(0, $this->object->getActiveSheetIndex());
}
public function testBug1735(): void
{
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$spreadsheet->createSheet()->setTitle('addedsheet');
$spreadsheet->setActiveSheetIndex(1);
$spreadsheet->removeSheetByIndex(0);
$sheet = $spreadsheet->getActiveSheet();
self::assertEquals('addedsheet', $sheet->getTitle());
}
public function testSetActiveSheetIndexTooHigh(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->object->setActiveSheetIndex(4);
}
public function testSetActiveSheetNoSuchName(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->object->setActiveSheetIndexByName('unknown');
}
public function testAddExternal(): void
{
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->createSheet()->setTitle('someSheet19');
$sheet->getCell('A1')->setValue(1);
$sheet->getCell('A1')->getStyle()->getFont()->setBold(true);
$sheet->getCell('B1')->getStyle()->getFont()->setSuperscript(true);
$sheet->getCell('C1')->getStyle()->getFont()->setSubscript(true);
self::assertCount(4, $spreadsheet->getCellXfCollection());
self::assertEquals(1, $sheet->getCell('A1')->getXfIndex());
$this->object->getActiveSheet()->getCell('A1')->getStyle()->getFont()->setBold(true);
self::assertCount(2, $this->object->getCellXfCollection());
$sheet3 = $this->object->addExternalSheet($sheet);
self::assertCount(6, $this->object->getCellXfCollection());
self::assertEquals('someSheet19', $sheet3->getTitle());
self::assertEquals(1, $sheet3->getCell('A1')->getValue());
self::assertTrue($sheet3->getCell('A1')->getStyle()->getFont()->getBold());
// Prove Xf index changed although style is same.
self::assertEquals(3, $sheet3->getCell('A1')->getXfIndex());
}
public function testAddExternalDuplicateName(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->createSheet()->setTitle('someSheet1');
$sheet->getCell('A1')->setValue(1);
$sheet->getCell('A1')->getStyle()->getFont()->setBold(true);
$this->object->addExternalSheet($sheet);
}
}

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Style;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PHPUnit\Framework\TestCase;
class StyleTest extends TestCase
@ -19,4 +20,141 @@ class StyleTest extends TestCase
$outArray = $cell1style->getStyleArray($styleArray);
self::assertEquals($styleArray, $outArray['quotePrefix']);
}
public function testStyleColumn(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cellCoordinates = 'A:B';
$styleArray = [
'font' => [
'bold' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
$sheet->setCellValue('A1', 'xxxa1');
$sheet->setCellValue('A2', 'xxxa2');
$sheet->setCellValue('A3', 'xxxa3');
$sheet->setCellValue('B1', 'xxxa1');
$sheet->setCellValue('B2', 'xxxa2');
$sheet->setCellValue('B3', 'xxxa3');
$sheet->setCellValue('C1', 'xxxc1');
$sheet->setCellValue('C2', 'xxxc2');
$sheet->setCellValue('C3', 'xxxc3');
$styleArray = [
'font' => [
'italic' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
self::assertTrue($sheet->getStyle('A1')->getFont()->getBold());
self::assertTrue($sheet->getStyle('B2')->getFont()->getBold());
self::assertFalse($sheet->getStyle('C3')->getFont()->getBold());
self::assertTrue($sheet->getStyle('A1')->getFont()->getItalic());
self::assertTrue($sheet->getStyle('B2')->getFont()->getItalic());
self::assertFalse($sheet->getStyle('C3')->getFont()->getItalic());
}
public function testStyleRow(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cellCoordinates = '2:3';
$styleArray = [
'font' => [
'bold' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
$sheet->setCellValue('A1', 'xxxa1');
$sheet->setCellValue('A2', 'xxxa2');
$sheet->setCellValue('A3', 'xxxa3');
$sheet->setCellValue('B1', 'xxxa1');
$sheet->setCellValue('B2', 'xxxa2');
$sheet->setCellValue('B3', 'xxxa3');
$sheet->setCellValue('C1', 'xxxc1');
$sheet->setCellValue('C2', 'xxxc2');
$sheet->setCellValue('C3', 'xxxc3');
$styleArray = [
'font' => [
'italic' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
self::assertFalse($sheet->getStyle('A1')->getFont()->getBold());
self::assertTrue($sheet->getStyle('B2')->getFont()->getBold());
self::assertTrue($sheet->getStyle('C3')->getFont()->getBold());
self::assertFalse($sheet->getStyle('A1')->getFont()->getItalic());
self::assertTrue($sheet->getStyle('B2')->getFont()->getItalic());
self::assertTrue($sheet->getStyle('C3')->getFont()->getItalic());
}
public function testIssue1712A(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$rgb = '4467b8';
$sheet->fromArray(['OK', 'KO']);
$spreadsheet->getActiveSheet()
->getStyle('A1')
->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()
->setRGB($rgb);
$spreadsheet->getActiveSheet()
->getStyle('B')
->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()
->setRGB($rgb);
self::assertEquals($rgb, $sheet->getCell('A1')->getStyle()->getFill()->getStartColor()->getRGB());
self::assertEquals($rgb, $sheet->getCell('B1')->getStyle()->getFill()->getStartColor()->getRGB());
}
public function testIssue1712B(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$rgb = '4467b8';
$spreadsheet->getActiveSheet()
->getStyle('A1')
->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()
->setRGB($rgb);
$spreadsheet->getActiveSheet()
->getStyle('B')
->getFill()
->setFillType(Fill::FILL_SOLID)
->getStartColor()
->setRGB($rgb);
$sheet->fromArray(['OK', 'KO']);
self::assertEquals($rgb, $sheet->getCell('A1')->getStyle()->getFill()->getStartColor()->getRGB());
self::assertEquals($rgb, $sheet->getCell('B1')->getStyle()->getFill()->getStartColor()->getRGB());
}
public function testStyleLoopUpwards(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$cellCoordinates = 'C5:A3';
$styleArray = [
'font' => [
'bold' => true,
],
];
$sheet->getStyle($cellCoordinates)->applyFromArray($styleArray);
$sheet->setCellValue('A1', 'xxxa1');
$sheet->setCellValue('A2', 'xxxa2');
$sheet->setCellValue('A3', 'xxxa3');
$sheet->setCellValue('B1', 'xxxa1');
$sheet->setCellValue('B2', 'xxxa2');
$sheet->setCellValue('B3', 'xxxa3');
$sheet->setCellValue('C1', 'xxxc1');
$sheet->setCellValue('C2', 'xxxc2');
$sheet->setCellValue('C3', 'xxxc3');
self::assertFalse($sheet->getStyle('A1')->getFont()->getBold());
self::assertFalse($sheet->getStyle('B2')->getFont()->getBold());
self::assertTrue($sheet->getStyle('C3')->getFont()->getBold());
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\ColumnCellIterator;
use PHPUnit\Framework\TestCase;
class ColumnCellIterator2Test extends TestCase
{
/**
* @dataProvider providerExistingCell
*/
public function testEndRange(?bool $existing, string $expectedResultFirst, string $expectedResultLast): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('B2')->setValue('cellb2');
$sheet->getCell('B6')->setValue('cellb6');
$iterator = new ColumnCellIterator($sheet, 'B', 1, 8);
if (isset($existing)) {
$iterator->setIterateOnlyExistingCells($existing);
}
$lastCoordinate = '';
$firstCoordinate = '';
foreach ($iterator as $cell) {
$lastCoordinate = $cell->getCoordinate();
if (!$firstCoordinate) {
$firstCoordinate = $lastCoordinate;
}
}
self::assertEquals($expectedResultFirst, $firstCoordinate);
self::assertEquals($expectedResultLast, $lastCoordinate);
}
public function providerExistingCell(): array
{
return [
[null, 'B1', 'B8'],
[false, 'B1', 'B8'],
[true, 'B2', 'B6'],
];
}
/**
* @dataProvider providerEmptyColumn
*/
public function testEmptyColumn(?bool $existing, int $expectedResult): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('B2')->setValue('cellb2');
$sheet->getCell('B6')->setValue('cellb6');
$iterator = new ColumnCellIterator($sheet, 'C');
if (isset($existing)) {
$iterator->setIterateOnlyExistingCells($existing);
}
$numCells = 0;
foreach ($iterator as $cell) {
++$numCells;
}
self::assertEquals($expectedResult, $numCells);
}
public function providerEmptyColumn(): array
{
return [
[null, 6],
[false, 6],
[true, 0],
];
}
}

View File

@ -71,11 +71,21 @@ class ColumnCellIteratorTest extends TestCase
public function testSeekOutOfRange(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->expectExceptionMessage('Row 1 is out of range');
$iterator = new ColumnCellIterator($this->mockWorksheet, 'A', 2, 4);
$iterator->seek(1);
}
public function testSeekNotExisting(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->expectExceptionMessage('Cell does not exist');
$iterator = new ColumnCellIterator($this->mockWorksheet, 'A', 2, 4);
$iterator->setIterateOnlyExistingCells(true);
$iterator->seek(2);
}
public function testPrevOutOfRange(): void
{
$iterator = new ColumnCellIterator($this->mockWorksheet, 'A', 2, 4);

View File

@ -0,0 +1,75 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\RowCellIterator;
use PHPUnit\Framework\TestCase;
class RowCellIterator2Test extends TestCase
{
/**
* @dataProvider providerExistingCell
*/
public function testEndRangeTrue(?bool $existing, string $expectedResultFirst, string $expectedResultLast): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('C2')->setValue('cellb2');
$sheet->getCell('F2')->setValue('cellf2');
$iterator = new RowCellIterator($sheet, 2, 'B', 'H');
if (isset($existing)) {
$iterator->setIterateOnlyExistingCells($existing);
}
$lastCoordinate = '';
$firstCoordinate = '';
foreach ($iterator as $cell) {
$lastCoordinate = $cell->getCoordinate();
if (!$firstCoordinate) {
$firstCoordinate = $lastCoordinate;
}
}
self::assertEquals($expectedResultFirst, $firstCoordinate);
self::assertEquals($expectedResultLast, $lastCoordinate);
}
public function providerExistingCell(): array
{
return [
[null, 'B2', 'H2'],
[false, 'B2', 'H2'],
[true, 'C2', 'F2'],
];
}
/**
* @dataProvider providerEmptyRow
*/
public function testEmptyRow(?bool $existing, int $expectedResult): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('B2')->setValue('cellb2');
$sheet->getCell('F2')->setValue('cellf2');
$iterator = new RowCellIterator($sheet, '3');
if (isset($existing)) {
$iterator->setIterateOnlyExistingCells($existing);
}
$numCells = 0;
foreach ($iterator as $cell) {
++$numCells;
}
self::assertEquals($expectedResult, $numCells);
}
public function providerEmptyRow(): array
{
return [
[null, 6],
[false, 6],
[true, 0],
];
}
}

View File

@ -73,9 +73,22 @@ class RowCellIteratorTest extends TestCase
public function testSeekOutOfRange(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->expectExceptionMessage('Column A is out of range');
$iterator = new RowCellIterator($this->mockWorksheet, 2, 'B', 'D');
$iterator->seek(1);
self::assertFalse($iterator->getIterateOnlyExistingCells());
self::assertEquals(2, $iterator->getCurrentColumnIndex());
$iterator->seek('A');
}
public function testSeekNotExisting(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$this->expectExceptionMessage('Cell does not exist');
$iterator = new RowCellIterator($this->mockWorksheet, 2, 'B', 'D');
$iterator->setIterateOnlyExistingCells(true);
$iterator->seek('B');
}
public function testPrevOutOfRange(): void

View File

@ -10,15 +10,56 @@ use PhpOffice\PhpSpreadsheetTests\Functional;
class XssVulnerabilityTest extends Functional\AbstractFunctional
{
public function providerAcceptableMarkupRichText()
{
return [
'basic text' => ['Hello, I am safely viewing your site', 'Hello, I am safely viewing your site'],
'link' => ["<a href='Visit Google'>Google is here</a>", '<a href="Visit%20Google">Google is here</a>'],
];
}
/**
* @dataProvider providerAcceptableMarkupRichText
*
* @param string $safeTextString
* @param string $adjustedTextString
*/
public function testMarkupInComment($safeTextString, $adjustedTextString): void
{
$spreadsheet = new Spreadsheet();
$richText = new RichText();
$richText->createText($safeTextString);
$spreadsheet->getActiveSheet()->getCell('A1')->setValue('XSS Test');
$spreadsheet->getActiveSheet()
->getComment('A1')
->setText($richText);
$filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test');
$writer = IOFactory::createWriter($spreadsheet, 'Html');
$writer->save($filename);
$verify = file_get_contents($filename);
// Ensure that executable js has been stripped from the comments
self::assertStringContainsString($adjustedTextString, $verify);
}
public function providerXssRichText()
{
return [
'script tag' => ['<script>alert(1)</script>'],
'javascript tag' => ['javascript:alert(1)'],
'with unicode' => ['java\u0003script:alert(1)'],
'script tag' => ["Hello, I am trying to <script>alert('Hack');</script> your site"],
'javascript tag' => ["<a href='&#x2000;javascript:alert(1)'>CLICK</a>"],
'with unicode' => ['<a href="\\u0001java\\u0003script:alert(1)">CLICK<a>'],
'inline css' => ['<li style="list-style-image: url(javascript:alert(0))">'],
'char value chevron' => ["\x3cscript src=http://www.example.com/malicious-code.js\x3e\x3c/script\x3e"],
];
}
private static $counter = 0;
/**
* @dataProvider providerXssRichText
*
@ -43,6 +84,8 @@ class XssVulnerabilityTest extends Functional\AbstractFunctional
$writer->save($filename);
$verify = file_get_contents($filename);
$counter = self::$counter++;
file_put_contents("verify{$counter}.html", $verify);
// Ensure that executable js has been stripped from the comments
self::assertStringNotContainsString($xssTextString, $verify);
}

View File

@ -1,6 +1,11 @@
<?php
return [
[
0,
0,
2,
],
[
662,
662.78999999999996,

View File

@ -1,6 +1,11 @@
<?php
return [
[
0,
0,
2,
],
[
663,
662.78999999999996,

View File

@ -9,6 +9,10 @@ return [
'#VALUE!',
-5,
],
[
'#VALUE!',
0,
],
[
'A',
65,
@ -22,27 +26,39 @@ return [
126,
],
[
'⽇',
'Á',
193,
],
[
'ÿ',
255,
],
[
'#VALUE!',
256,
],
[
'#VALUE!', // '⽇',
12103,
],
[
'œ',
'#VALUE!', // 'œ',
0x153,
],
[
'ƒ',
'#VALUE!', // 'ƒ',
0x192,
],
[
'℅',
'#VALUE!', // '℅',
0x2105,
],
[
'∑',
'#VALUE!', // '∑',
0x2211,
],
[
'†',
'#VALUE!', // '†',
0x2020,
],
];

View File

@ -6,11 +6,20 @@ return [
123.456,
2,
],
[
'$123.46',
123.456,
],
[
'$123.32',
123.321,
2,
],
[
'($123.32)',
-123.321,
2,
],
[
'$1,235,000',
1234567,
@ -22,12 +31,17 @@ return [
-5,
],
[
'#NUM!',
'($1,200,000)',
-1234567,
-5,
],
[
'#VALUE!',
'ABC',
2,
],
[
'#NUM!',
'#VALUE!',
123.456,
'ABC',
],

View File

@ -20,13 +20,41 @@ return [
true,
],
[
'#NUM!',
'-123456.79',
-123456.789,
2,
true,
],
[
'123500',
123456.789,
-2,
true,
],
[
'123,500',
123456.789,
-2,
],
[
'-123500',
-123456.789,
-2,
true,
],
[
'-123,500',
-123456.789,
-2,
],
[
'#VALUE!',
'ABC',
2,
null,
],
[
'#NUM!',
'#VALUE!',
123.456,
'ABC',
null,

View File

@ -54,6 +54,11 @@ return [
'Mark Baker',
2,
],
[
1,
'',
'Mark Baker',
],
[
'#VALUE!',
'BITE',

View File

@ -0,0 +1,4 @@
a\"hello;hello;hello;\",b\"hello;hello;hello;\",c\"\hello;hello;hello;\"
a\"hello;hello;hello;\",b\"hello;hello;hello;\",c\"\hello;hello;hello;\",d
a\"hello;hello;hello;\",b\"hello;hello;hello;\",c\"\hello;hello;hello;\"
a\"hello;hello;hello;\",b\"hello;hello;hello;\",c\"\hello;hello;hello;\"
Can't render this file because it contains an unexpected character in line 1 and column 3.

Binary file not shown.
1 �p�r�e�m�i�è�r�e� �s�e�c�o�n�d� �t�r�o�i�s�i�è�m�e� �
2 �Q�u�a�t�r�i�è�m�e� �c�i�n�q�u�i�è�m�e� �s�i�x�i�è�m�e� �

Binary file not shown.
1 première second troisième
2 Quatrième cinquième sixième

Binary file not shown.
1 p�r�e�m�i�è�r�e�,�s�e�c�o�n�d�,�t�r�o�i�s�i�è�m�e� �
2 �Q�u�a�t�r�i�è�m�e�,�c�i�n�q�u�i�è�m�e�,�s�i�x�i�è�m�e� �
3

Binary file not shown.
1 première second troisième
2 Quatrième cinquième sixième

Binary file not shown.
1 ���p���r���e���m���i������r���e��� ���s���e���c���o���n���d��� ���t���r���o���i���s���i������m���e��� ���
2 ���Q���u���a���t���r���i������m���e��� ���c���i���n���q���u���i������m���e��� ���s���i���x���i������m���e��� ���

Binary file not shown.
1 �����p���r���e���m���i������r���e��� ���s���e���c���o���n���d��� ���t���r���o���i���s���i������m���e��� ���
2 ���Q���u���a���t���r���i������m���e��� ���c���i���n���q���u���i������m���e��� ���s���i���x���i������m���e��� ���

Binary file not shown.
1 p���r���e���m���i������r���e���,���s���e���c���o���n���d���,���t���r���o���i���s���i������m���e��� ���
2 ���Q���u���a���t���r���i������m���e���,���c���i���n���q���u���i������m���e���,���s���i���x���i������m���e��� ���
3 ���

Binary file not shown.
1 ��p���r���e���m���i������r���e���,���s���e���c���o���n���d���,���t���r���o���i���s���i������m���e��� ���
2 ���Q���u���a���t���r���i������m���e���,���c���i���n���q���u���i������m���e���,���s���i���x���i������m���e��� ���
3 ���

View File

@ -0,0 +1,2 @@
première,second,troisième
Quatrième,cinquième,sixième
1 première second troisième
2 Quatrième cinquième sixième

View File

@ -0,0 +1,2 @@
première,second,troisième
Quatrième,cinquième,sixième
1 première second troisième
2 Quatrième cinquième sixième

View File

@ -0,0 +1,2 @@
première,second,troisième
Quatrième,cinquième,sixième
1 première second troisième
2 Quatrième cinquième sixième

Binary file not shown.

Binary file not shown.