From 86f34af6f6dd521d529411f647eb2c77098c59db Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Fri, 29 Nov 2024 07:30:23 +0400 Subject: [PATCH] supprimer upstream-3.x --- upstream-3.x/LICENSE | 21 - upstream-3.x/LICENSE-for-cc42c1d | 166 ------ upstream-3.x/README.md | 54 -- upstream-3.x/UPGRADE-3.0.md | 89 --- upstream-3.x/composer.json | 69 --- .../src/Autoloader/Psr4Autoloader.php | 147 ----- upstream-3.x/src/Autoloader/autoload.php | 15 - .../src/Common/Creator/HelperFactory.php | 48 -- upstream-3.x/src/Common/Entity/Cell.php | 227 -------- upstream-3.x/src/Common/Entity/Row.php | 166 ------ .../src/Common/Entity/Style/Border.php | 80 --- .../src/Common/Entity/Style/BorderPart.php | 181 ------ .../src/Common/Entity/Style/CellAlignment.php | 31 - .../src/Common/Entity/Style/Color.php | 86 --- .../src/Common/Entity/Style/Style.php | 549 ------------------ .../Exception/EncodingConversionException.php | 7 - .../src/Common/Exception/IOException.php | 7 - .../Exception/InvalidArgumentException.php | 7 - .../Exception/InvalidColorException.php | 7 - .../src/Common/Exception/SpoutException.php | 7 - .../Exception/UnsupportedTypeException.php | 7 - .../src/Common/Helper/CellTypeHelper.php | 82 --- .../src/Common/Helper/EncodingHelper.php | 180 ------ .../src/Common/Helper/Escaper/CSV.php | 37 -- .../Helper/Escaper/EscaperInterface.php | 27 - .../src/Common/Helper/Escaper/ODS.php | 63 -- .../src/Common/Helper/Escaper/XLSX.php | 194 ------- .../src/Common/Helper/FileSystemHelper.php | 138 ----- .../Helper/FileSystemHelperInterface.php | 54 -- .../Common/Helper/GlobalFunctionsHelper.php | 371 ------------ .../src/Common/Helper/StringHelper.php | 108 ---- .../Common/Manager/OptionsManagerAbstract.php | 82 --- .../Manager/OptionsManagerInterface.php | 31 - upstream-3.x/src/Common/Type.php | 13 - .../CSV/Creator/InternalEntityFactory.php | 98 ---- .../src/Reader/CSV/Manager/OptionsManager.php | 39 -- upstream-3.x/src/Reader/CSV/Reader.php | 149 ----- upstream-3.x/src/Reader/CSV/RowIterator.php | 249 -------- upstream-3.x/src/Reader/CSV/Sheet.php | 59 -- upstream-3.x/src/Reader/CSV/SheetIterator.php | 89 --- .../InternalEntityFactoryInterface.php | 26 - .../Common/Creator/ReaderEntityFactory.php | 72 --- .../Reader/Common/Creator/ReaderFactory.php | 109 ---- .../src/Reader/Common/Entity/Options.php | 22 - .../src/Reader/Common/Manager/RowManager.php | 78 --- .../src/Reader/Common/XMLProcessor.php | 151 ----- .../Exception/InvalidValueException.php | 30 - .../IteratorNotRewindableException.php | 7 - .../Exception/NoSheetsFoundException.php | 7 - .../src/Reader/Exception/ReaderException.php | 9 - .../Exception/ReaderNotOpenedException.php | 7 - .../SharedStringNotFoundException.php | 7 - .../Exception/XMLProcessingException.php | 7 - upstream-3.x/src/Reader/IteratorInterface.php | 14 - .../src/Reader/ODS/Creator/HelperFactory.php | 43 -- .../ODS/Creator/InternalEntityFactory.php | 124 ---- .../src/Reader/ODS/Creator/ManagerFactory.php | 21 - .../Reader/ODS/Helper/CellValueFormatter.php | 311 ---------- .../src/Reader/ODS/Helper/SettingsHelper.php | 61 -- .../src/Reader/ODS/Manager/OptionsManager.php | 32 - upstream-3.x/src/Reader/ODS/Reader.php | 73 --- upstream-3.x/src/Reader/ODS/RowIterator.php | 388 ------------- upstream-3.x/src/Reader/ODS/Sheet.php | 85 --- upstream-3.x/src/Reader/ODS/SheetIterator.php | 239 -------- upstream-3.x/src/Reader/ReaderAbstract.php | 236 -------- upstream-3.x/src/Reader/ReaderInterface.php | 33 -- .../src/Reader/RowIteratorInterface.php | 22 - upstream-3.x/src/Reader/SheetInterface.php | 34 -- .../src/Reader/SheetIteratorInterface.php | 23 - .../Wrapper/XMLInternalErrorsHelper.php | 75 --- upstream-3.x/src/Reader/Wrapper/XMLReader.php | 192 ------ .../src/Reader/XLSX/Creator/HelperFactory.php | 38 -- .../XLSX/Creator/InternalEntityFactory.php | 163 ------ .../Reader/XLSX/Creator/ManagerFactory.php | 109 ---- .../src/Reader/XLSX/Helper/CellHelper.php | 87 --- .../Reader/XLSX/Helper/CellValueFormatter.php | 319 ---------- .../Reader/XLSX/Helper/DateFormatHelper.php | 122 ---- .../Reader/XLSX/Manager/OptionsManager.php | 36 -- .../CachingStrategyFactory.php | 141 ----- .../CachingStrategyInterface.php | 39 -- .../FileBasedStrategy.php | 188 ------ .../SharedStringsCaching/InMemoryStrategy.php | 76 --- .../XLSX/Manager/SharedStringsManager.php | 252 -------- .../src/Reader/XLSX/Manager/SheetManager.php | 232 -------- .../src/Reader/XLSX/Manager/StyleManager.php | 349 ----------- .../Manager/WorkbookRelationshipsManager.php | 150 ----- upstream-3.x/src/Reader/XLSX/Reader.php | 122 ---- upstream-3.x/src/Reader/XLSX/RowIterator.php | 429 -------------- upstream-3.x/src/Reader/XLSX/Sheet.php | 82 --- .../src/Reader/XLSX/SheetIterator.php | 113 ---- .../src/Writer/CSV/Manager/OptionsManager.php | 34 -- upstream-3.x/src/Writer/CSV/Writer.php | 109 ---- .../Common/Creator/InternalEntityFactory.php | 52 -- .../Creator/ManagerFactoryInterface.php | 23 - .../Common/Creator/Style/BorderBuilder.php | 84 --- .../Common/Creator/Style/StyleBuilder.php | 214 ------- .../Common/Creator/WriterEntityFactory.php | 121 ---- .../Writer/Common/Creator/WriterFactory.php | 109 ---- .../src/Writer/Common/Entity/Options.php | 30 - .../src/Writer/Common/Entity/Sheet.php | 137 ----- .../src/Writer/Common/Entity/Workbook.php | 47 -- .../src/Writer/Common/Entity/Worksheet.php | 131 ----- .../src/Writer/Common/Helper/CellHelper.php | 45 -- ...ileSystemWithRootFolderHelperInterface.php | 24 - .../src/Writer/Common/Helper/ZipHelper.php | 214 ------- .../src/Writer/Common/Manager/CellManager.php | 29 - .../Writer/Common/Manager/ManagesCellSize.php | 62 -- .../Writer/Common/Manager/RegisteredStyle.php | 37 -- .../src/Writer/Common/Manager/RowManager.php | 25 - .../Writer/Common/Manager/SheetManager.php | 146 ----- .../Manager/Style/PossiblyUpdatedStyle.php | 31 - .../Common/Manager/Style/StyleManager.php | 82 --- .../Manager/Style/StyleManagerInterface.php | 30 - .../Common/Manager/Style/StyleMerger.php | 92 --- .../Common/Manager/Style/StyleRegistry.php | 111 ---- .../Manager/WorkbookManagerAbstract.php | 352 ----------- .../Manager/WorkbookManagerInterface.php | 97 ---- .../Manager/WorksheetManagerInterface.php | 60 -- .../Exception/Border/InvalidNameException.php | 16 - .../Border/InvalidStyleException.php | 16 - .../Border/InvalidWidthException.php | 16 - .../Exception/InvalidSheetNameException.php | 7 - .../Exception/SheetNotFoundException.php | 7 - .../WriterAlreadyOpenedException.php | 7 - .../src/Writer/Exception/WriterException.php | 9 - .../Exception/WriterNotOpenedException.php | 7 - .../src/Writer/ODS/Creator/HelperFactory.php | 54 -- .../src/Writer/ODS/Creator/ManagerFactory.php | 107 ---- .../src/Writer/ODS/Helper/BorderHelper.php | 65 --- .../Writer/ODS/Helper/FileSystemHelper.php | 304 ---------- .../src/Writer/ODS/Manager/OptionsManager.php | 50 -- .../Writer/ODS/Manager/Style/StyleManager.php | 462 --------------- .../ODS/Manager/Style/StyleRegistry.php | 42 -- .../Writer/ODS/Manager/WorkbookManager.php | 66 --- .../Writer/ODS/Manager/WorksheetManager.php | 308 ---------- upstream-3.x/src/Writer/ODS/Writer.php | 34 -- upstream-3.x/src/Writer/WriterAbstract.php | 270 --------- upstream-3.x/src/Writer/WriterInterface.php | 77 --- .../src/Writer/WriterMultiSheetsAbstract.php | 216 ------- .../src/Writer/XLSX/Creator/HelperFactory.php | 53 -- .../Writer/XLSX/Creator/ManagerFactory.php | 145 ----- .../src/Writer/XLSX/Entity/SheetView.php | 289 --------- .../src/Writer/XLSX/Helper/BorderHelper.php | 66 --- .../src/Writer/XLSX/Helper/DateHelper.php | 45 -- .../Writer/XLSX/Helper/FileSystemHelper.php | 403 ------------- .../Writer/XLSX/Manager/OptionsManager.php | 64 -- .../XLSX/Manager/SharedStringsManager.php | 103 ---- .../XLSX/Manager/Style/StyleManager.php | 342 ----------- .../XLSX/Manager/Style/StyleRegistry.php | 276 --------- .../Writer/XLSX/Manager/WorkbookManager.php | 74 --- .../Writer/XLSX/Manager/WorksheetManager.php | 376 ------------ upstream-3.x/src/Writer/XLSX/Writer.php | 72 --- 152 files changed, 16595 deletions(-) delete mode 100644 upstream-3.x/LICENSE delete mode 100644 upstream-3.x/LICENSE-for-cc42c1d delete mode 100644 upstream-3.x/README.md delete mode 100644 upstream-3.x/UPGRADE-3.0.md delete mode 100644 upstream-3.x/composer.json delete mode 100644 upstream-3.x/src/Autoloader/Psr4Autoloader.php delete mode 100644 upstream-3.x/src/Autoloader/autoload.php delete mode 100644 upstream-3.x/src/Common/Creator/HelperFactory.php delete mode 100644 upstream-3.x/src/Common/Entity/Cell.php delete mode 100644 upstream-3.x/src/Common/Entity/Row.php delete mode 100644 upstream-3.x/src/Common/Entity/Style/Border.php delete mode 100644 upstream-3.x/src/Common/Entity/Style/BorderPart.php delete mode 100644 upstream-3.x/src/Common/Entity/Style/CellAlignment.php delete mode 100644 upstream-3.x/src/Common/Entity/Style/Color.php delete mode 100644 upstream-3.x/src/Common/Entity/Style/Style.php delete mode 100644 upstream-3.x/src/Common/Exception/EncodingConversionException.php delete mode 100644 upstream-3.x/src/Common/Exception/IOException.php delete mode 100644 upstream-3.x/src/Common/Exception/InvalidArgumentException.php delete mode 100644 upstream-3.x/src/Common/Exception/InvalidColorException.php delete mode 100644 upstream-3.x/src/Common/Exception/SpoutException.php delete mode 100644 upstream-3.x/src/Common/Exception/UnsupportedTypeException.php delete mode 100644 upstream-3.x/src/Common/Helper/CellTypeHelper.php delete mode 100644 upstream-3.x/src/Common/Helper/EncodingHelper.php delete mode 100644 upstream-3.x/src/Common/Helper/Escaper/CSV.php delete mode 100644 upstream-3.x/src/Common/Helper/Escaper/EscaperInterface.php delete mode 100644 upstream-3.x/src/Common/Helper/Escaper/ODS.php delete mode 100644 upstream-3.x/src/Common/Helper/Escaper/XLSX.php delete mode 100644 upstream-3.x/src/Common/Helper/FileSystemHelper.php delete mode 100644 upstream-3.x/src/Common/Helper/FileSystemHelperInterface.php delete mode 100644 upstream-3.x/src/Common/Helper/GlobalFunctionsHelper.php delete mode 100644 upstream-3.x/src/Common/Helper/StringHelper.php delete mode 100644 upstream-3.x/src/Common/Manager/OptionsManagerAbstract.php delete mode 100644 upstream-3.x/src/Common/Manager/OptionsManagerInterface.php delete mode 100644 upstream-3.x/src/Common/Type.php delete mode 100644 upstream-3.x/src/Reader/CSV/Creator/InternalEntityFactory.php delete mode 100644 upstream-3.x/src/Reader/CSV/Manager/OptionsManager.php delete mode 100644 upstream-3.x/src/Reader/CSV/Reader.php delete mode 100644 upstream-3.x/src/Reader/CSV/RowIterator.php delete mode 100644 upstream-3.x/src/Reader/CSV/Sheet.php delete mode 100644 upstream-3.x/src/Reader/CSV/SheetIterator.php delete mode 100644 upstream-3.x/src/Reader/Common/Creator/InternalEntityFactoryInterface.php delete mode 100644 upstream-3.x/src/Reader/Common/Creator/ReaderEntityFactory.php delete mode 100644 upstream-3.x/src/Reader/Common/Creator/ReaderFactory.php delete mode 100644 upstream-3.x/src/Reader/Common/Entity/Options.php delete mode 100644 upstream-3.x/src/Reader/Common/Manager/RowManager.php delete mode 100644 upstream-3.x/src/Reader/Common/XMLProcessor.php delete mode 100644 upstream-3.x/src/Reader/Exception/InvalidValueException.php delete mode 100644 upstream-3.x/src/Reader/Exception/IteratorNotRewindableException.php delete mode 100644 upstream-3.x/src/Reader/Exception/NoSheetsFoundException.php delete mode 100644 upstream-3.x/src/Reader/Exception/ReaderException.php delete mode 100644 upstream-3.x/src/Reader/Exception/ReaderNotOpenedException.php delete mode 100644 upstream-3.x/src/Reader/Exception/SharedStringNotFoundException.php delete mode 100644 upstream-3.x/src/Reader/Exception/XMLProcessingException.php delete mode 100644 upstream-3.x/src/Reader/IteratorInterface.php delete mode 100644 upstream-3.x/src/Reader/ODS/Creator/HelperFactory.php delete mode 100644 upstream-3.x/src/Reader/ODS/Creator/InternalEntityFactory.php delete mode 100644 upstream-3.x/src/Reader/ODS/Creator/ManagerFactory.php delete mode 100644 upstream-3.x/src/Reader/ODS/Helper/CellValueFormatter.php delete mode 100644 upstream-3.x/src/Reader/ODS/Helper/SettingsHelper.php delete mode 100644 upstream-3.x/src/Reader/ODS/Manager/OptionsManager.php delete mode 100644 upstream-3.x/src/Reader/ODS/Reader.php delete mode 100644 upstream-3.x/src/Reader/ODS/RowIterator.php delete mode 100644 upstream-3.x/src/Reader/ODS/Sheet.php delete mode 100644 upstream-3.x/src/Reader/ODS/SheetIterator.php delete mode 100644 upstream-3.x/src/Reader/ReaderAbstract.php delete mode 100644 upstream-3.x/src/Reader/ReaderInterface.php delete mode 100644 upstream-3.x/src/Reader/RowIteratorInterface.php delete mode 100644 upstream-3.x/src/Reader/SheetInterface.php delete mode 100644 upstream-3.x/src/Reader/SheetIteratorInterface.php delete mode 100644 upstream-3.x/src/Reader/Wrapper/XMLInternalErrorsHelper.php delete mode 100644 upstream-3.x/src/Reader/Wrapper/XMLReader.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Creator/HelperFactory.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Creator/InternalEntityFactory.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Creator/ManagerFactory.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Helper/CellHelper.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Helper/CellValueFormatter.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Helper/DateFormatHelper.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Manager/OptionsManager.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/FileBasedStrategy.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Manager/SharedStringsManager.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Manager/SheetManager.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Manager/StyleManager.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Manager/WorkbookRelationshipsManager.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Reader.php delete mode 100644 upstream-3.x/src/Reader/XLSX/RowIterator.php delete mode 100644 upstream-3.x/src/Reader/XLSX/Sheet.php delete mode 100644 upstream-3.x/src/Reader/XLSX/SheetIterator.php delete mode 100644 upstream-3.x/src/Writer/CSV/Manager/OptionsManager.php delete mode 100644 upstream-3.x/src/Writer/CSV/Writer.php delete mode 100644 upstream-3.x/src/Writer/Common/Creator/InternalEntityFactory.php delete mode 100644 upstream-3.x/src/Writer/Common/Creator/ManagerFactoryInterface.php delete mode 100644 upstream-3.x/src/Writer/Common/Creator/Style/BorderBuilder.php delete mode 100644 upstream-3.x/src/Writer/Common/Creator/Style/StyleBuilder.php delete mode 100644 upstream-3.x/src/Writer/Common/Creator/WriterEntityFactory.php delete mode 100644 upstream-3.x/src/Writer/Common/Creator/WriterFactory.php delete mode 100644 upstream-3.x/src/Writer/Common/Entity/Options.php delete mode 100644 upstream-3.x/src/Writer/Common/Entity/Sheet.php delete mode 100644 upstream-3.x/src/Writer/Common/Entity/Workbook.php delete mode 100644 upstream-3.x/src/Writer/Common/Entity/Worksheet.php delete mode 100644 upstream-3.x/src/Writer/Common/Helper/CellHelper.php delete mode 100644 upstream-3.x/src/Writer/Common/Helper/FileSystemWithRootFolderHelperInterface.php delete mode 100644 upstream-3.x/src/Writer/Common/Helper/ZipHelper.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/CellManager.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/ManagesCellSize.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/RegisteredStyle.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/RowManager.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/SheetManager.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/Style/StyleManager.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/Style/StyleManagerInterface.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/Style/StyleMerger.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/Style/StyleRegistry.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/WorkbookManagerAbstract.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/WorkbookManagerInterface.php delete mode 100644 upstream-3.x/src/Writer/Common/Manager/WorksheetManagerInterface.php delete mode 100644 upstream-3.x/src/Writer/Exception/Border/InvalidNameException.php delete mode 100644 upstream-3.x/src/Writer/Exception/Border/InvalidStyleException.php delete mode 100644 upstream-3.x/src/Writer/Exception/Border/InvalidWidthException.php delete mode 100644 upstream-3.x/src/Writer/Exception/InvalidSheetNameException.php delete mode 100644 upstream-3.x/src/Writer/Exception/SheetNotFoundException.php delete mode 100644 upstream-3.x/src/Writer/Exception/WriterAlreadyOpenedException.php delete mode 100644 upstream-3.x/src/Writer/Exception/WriterException.php delete mode 100644 upstream-3.x/src/Writer/Exception/WriterNotOpenedException.php delete mode 100644 upstream-3.x/src/Writer/ODS/Creator/HelperFactory.php delete mode 100644 upstream-3.x/src/Writer/ODS/Creator/ManagerFactory.php delete mode 100644 upstream-3.x/src/Writer/ODS/Helper/BorderHelper.php delete mode 100644 upstream-3.x/src/Writer/ODS/Helper/FileSystemHelper.php delete mode 100644 upstream-3.x/src/Writer/ODS/Manager/OptionsManager.php delete mode 100644 upstream-3.x/src/Writer/ODS/Manager/Style/StyleManager.php delete mode 100644 upstream-3.x/src/Writer/ODS/Manager/Style/StyleRegistry.php delete mode 100644 upstream-3.x/src/Writer/ODS/Manager/WorkbookManager.php delete mode 100644 upstream-3.x/src/Writer/ODS/Manager/WorksheetManager.php delete mode 100644 upstream-3.x/src/Writer/ODS/Writer.php delete mode 100644 upstream-3.x/src/Writer/WriterAbstract.php delete mode 100644 upstream-3.x/src/Writer/WriterInterface.php delete mode 100644 upstream-3.x/src/Writer/WriterMultiSheetsAbstract.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Creator/HelperFactory.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Creator/ManagerFactory.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Entity/SheetView.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Helper/BorderHelper.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Helper/DateHelper.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Helper/FileSystemHelper.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Manager/OptionsManager.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Manager/SharedStringsManager.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Manager/Style/StyleManager.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Manager/Style/StyleRegistry.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Manager/WorkbookManager.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Manager/WorksheetManager.php delete mode 100644 upstream-3.x/src/Writer/XLSX/Writer.php diff --git a/upstream-3.x/LICENSE b/upstream-3.x/LICENSE deleted file mode 100644 index 38ce746..0000000 --- a/upstream-3.x/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 openspout - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/upstream-3.x/LICENSE-for-cc42c1d b/upstream-3.x/LICENSE-for-cc42c1d deleted file mode 100644 index 167ec4d..0000000 --- a/upstream-3.x/LICENSE-for-cc42c1d +++ /dev/null @@ -1,166 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/upstream-3.x/README.md b/upstream-3.x/README.md deleted file mode 100644 index 0ceb637..0000000 --- a/upstream-3.x/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# OpenSpout - -[![Latest Stable Version](https://poser.pugx.org/openspout/openspout/v/stable)](https://packagist.org/packages/openspout/openspout) -[![Build Status](https://github.com/openspout/openspout/actions/workflows/ci.yml/badge.svg)](https://github.com/openspout/openspout/actions/workflows/ci.yml) -[![Code Coverage](https://codecov.io/gh/openspout/openspout/coverage.svg?branch=main)](https://codecov.io/gh/openspout/openspout?branch=main) -[![Total Downloads](https://poser.pugx.org/openspout/openspout/downloads)](https://packagist.org/packages/openspout/openspout) - -OpenSpout is a community driven fork of `box/spout`, a PHP library to read and write spreadsheet files (CSV, XLSX and ODS), in a fast and scalable way. -Unlike other file readers or writers, it is capable of processing very large files, while keeping the memory usage really low (less than 3MB). - -## Documentation - -Documentation can be found at [https://openspout.readthedocs.io/en/latest/](https://openspout.readthedocs.io/en/latest/). - -## Requirements - -* PHP version 7.3 or higher -* PHP extension `php_zip` enabled -* PHP extension `php_xmlreader` enabled - -## Upgrade from `box/spout` - -1. Replace `box/spout` with `openspout/openspout` in your `composer.json` -2. Replace `Box\Spout` with `OpenSpout` in your code - -## Upgrade guide - -Version 3 introduced new functionality but also some breaking changes. If you want to upgrade your Spout codebase from version 2 please consult the [Upgrade guide](UPGRADE-3.0.md). - -## Running tests - -The `main` branch includes unit, functional and performance tests. -If you just want to check that everything is working as expected, executing the unit and functional tests is enough. - -* `phpunit` - runs unit and functional tests -* `phpunit --group perf-tests` - only runs the performance tests - -For information, the performance tests take about 10 minutes to run (processing 1 million rows files is not a quick thing). - -> Performance tests status: [![Build Status](https://travis-ci.org/box/spout.svg?branch=perf-tests)](https://travis-ci.org/box/spout) - -## Copyright and License - -This is a fork of Box's Spout library: https://github.com/box/spout - -Code until and directly descending from commit [`cc42c1d`](https://github.com/openspout/openspout/commit/cc42c1d29fc5d29f07caeace99bd29dbb6d7c2f8) -is copyright of _Box, Inc._ and licensed under the Apache License, Version 2.0: - -https://github.com/openspout/openspout/blob/cc42c1d29fc5d29f07caeace99bd29dbb6d7c2f8/LICENSE - -Code created, edited and released after the commit mentioned above -is copyright of _openspout_ Github organization and licensed under MIT License. - -https://github.com/openspout/openspout/blob/main/LICENSE diff --git a/upstream-3.x/UPGRADE-3.0.md b/upstream-3.x/UPGRADE-3.0.md deleted file mode 100644 index 8f6ae91..0000000 --- a/upstream-3.x/UPGRADE-3.0.md +++ /dev/null @@ -1,89 +0,0 @@ -Upgrading from 2.x to 3.0 -========================= - -Spout 3.0 introduced several backwards-incompatible changes. The upgrade from Spout 2.x to 3.0 must therefore be done with caution. -This guide is meant to ease this process. - -Most notable changes --------------------- -In 2.x, styles were applied per row; it was therefore impossible to apply different styles to cells in the same row. -With the 3.0 version, this is now possible: each cell can have its own style. - -Spout 3.0 tries to enforce better typing. For instance, instead of using/returning generic arrays, Spout now makes use of specific `Row` and `Cell` objects that can encapsulate more data such as type, style, value. - -Finally, **_Spout 3.2 only supports PHP 7.2 and above_**, as other PHP versions are no longer supported by the community. - -Reader changes --------------- -Creating a reader should now be done through the Reader `ReaderEntityFactory`, instead of using the `ReaderFactory`. -Also, the `ReaderFactory::create($type)` method was removed and replaced by methods for each reader: -```php -use OpenSpout\Reader\Common\Creator\ReaderEntityFactory; // namespace is no longer "OpenSpout\Reader" -... -$reader = ReaderEntityFactory::createXLSXReader(); // replaces ReaderFactory::create(Type::XLSX) -$reader = ReaderEntityFactory::createCSVReader(); // replaces ReaderFactory::create(Type::CSV) -$reader = ReaderEntityFactory::createODSReader(); // replaces ReaderFactory::create(Type::ODS) -``` - -When iterating over the spreadsheet rows, Spout now returns `Row` objects, instead of an array containing row values. Accessing the row values should now be done this way: -```php -... -foreach ($reader->getSheetIterator() as $sheet) { - foreach ($sheet->getRowIterator() as $row) { // $row is a "Row" object, not an array - $rowAsArray = $row->toArray(); // this is the 2.x equivalent - // OR - $cellsArray = $row->getCells(); // this can be used to get access to cells' details - ... - } -} -``` - -Writer changes --------------- -Writer creation follows the same change as the reader. It should now be done through the Writer `WriterEntityFactory`, instead of using the `WriterFactory`. -Also, the `WriterFactory::create($type)` method was removed and replaced by methods for each writer: - -```php -use OpenSpout\Writer\Common\Creator\WriterEntityFactory; // namespace is no longer "OpenSpout\Writer" -... -$writer = WriterEntityFactory::createXLSXWriter(); // replaces WriterFactory::create(Type::XLSX) -$writer = WriterEntityFactory::createCSVWriter(); // replaces WriterFactory::create(Type::CSV) -$writer = WriterEntityFactory::createODSWriter(); // replaces WriterFactory::create(Type::ODS) -``` - -Adding rows is also done differently: instead of passing an array, the writer now takes in a `Row` object (or an array of `Row`). Creating such objects can easily be done this way: -```php -// Adding a row from an array of values (2.x equivalent) -$cellValues = ['foo', 12345]; -$row1 = WriterEntityFactory::createRowFromArray($cellValues, $rowStyle); - -// Adding a row from an array of Cell -$cell1 = WriterEntityFactory::createCell('foo', $cellStyle1); // this cell has its own style -$cell2 = WriterEntityFactory::createCell(12345, $cellStyle2); // this cell has its own style -$row2 = WriterEntityFactory::createRow([$cell1, $cell2]); - -$writer->addRows([$row1, $row2]); -``` - -Namespace changes for styles ------------------ -The namespaces for styles have changed. Styles are still created by using a `builder` class. - -For the builder, please update your import statements to use the following namespaces: - - OpenSpout\Writer\Common\Creator\Style\StyleBuilder - OpenSpout\Writer\Common\Creator\Style\BorderBuilder - -The `Style` base class and style definitions like `Border`, `BorderPart` and `Color` also have a new namespace. - -If your are using these classes directly via an import statement in your code, please use the following namespaces: - - OpenSpout\Common\Entity\Style\Border - OpenSpout\Common\Entity\Style\BorderPart - OpenSpout\Common\Entity\Style\Color - OpenSpout\Common\Entity\Style\Style - -Handling of empty rows ----------------------- -In 2.x, empty rows were not added to the spreadsheet. -In 3.0, `addRow` now always writes a row to the spreadsheet: when the row does not contain any cells, an empty row is created in the sheet. diff --git a/upstream-3.x/composer.json b/upstream-3.x/composer.json deleted file mode 100644 index 1a2a8ef..0000000 --- a/upstream-3.x/composer.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "openspout/openspout", - "description": "PHP Library to read and write spreadsheet files (CSV, XLSX and ODS), in a fast and scalable way", - "license": "MIT", - "type": "library", - "keywords": [ - "php", - "read", - "write", - "csv", - "xlsx", - "ods", - "odf", - "open", - "office", - "excel", - "spreadsheet", - "scale", - "memory", - "stream", - "ooxml" - ], - "authors": [ - { - "name": "Adrien Loison", - "email": "adrien@box.com" - } - ], - "homepage": "https://github.com/openspout/openspout", - "require": { - "php": "~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0", - "ext-dom": "*", - "ext-filter": "*", - "ext-libxml": "*", - "ext-xmlreader": "*", - "ext-zip": "*" - }, - "require-dev": { - "ext-zlib": "*", - "friendsofphp/php-cs-fixer": "^3.4", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-iconv": "To handle non UTF-8 CSV files (if \"php-intl\" is not already installed or is too limited)", - "ext-intl": "To handle non UTF-8 CSV files (if \"iconv\" is not already installed)" - }, - "autoload": { - "psr-4": { - "OpenSpout\\": "src/" - } - }, - "autoload-dev": { - "classmap": [ - "tests/" - ] - }, - "config": { - "platform": { - "php": "7.3" - } - }, - "extra": { - "branch-alias": { - "dev-master": "3.3.x-dev" - } - } -} diff --git a/upstream-3.x/src/Autoloader/Psr4Autoloader.php b/upstream-3.x/src/Autoloader/Psr4Autoloader.php deleted file mode 100644 index 59eac31..0000000 --- a/upstream-3.x/src/Autoloader/Psr4Autoloader.php +++ /dev/null @@ -1,147 +0,0 @@ -prefixes[$prefix])) { - $this->prefixes[$prefix] = []; - } - - // retain the base directory for the namespace prefix - if ($prepend) { - array_unshift($this->prefixes[$prefix], $baseDir); - } else { - $this->prefixes[$prefix][] = $baseDir; - } - } - - /** - * Loads the class file for a given class name. - * - * @param string $class the fully-qualified class name - * - * @return mixed the mapped file name on success, or boolean false on - * failure - */ - public function loadClass($class) - { - // the current namespace prefix - $prefix = $class; - - // work backwards through the namespace names of the fully-qualified - // class name to find a mapped file name - while (($pos = strrpos($prefix, '\\')) !== false) { - // retain the trailing namespace separator in the prefix - $prefix = substr($class, 0, $pos + 1); - - // the rest is the relative class name - $relativeClass = substr($class, $pos + 1); - - // try to load a mapped file for the prefix and relative class - $mappedFile = $this->loadMappedFile($prefix, $relativeClass); - if (false !== $mappedFile) { - return $mappedFile; - } - - // remove the trailing namespace separator for the next iteration - // of strrpos() - $prefix = rtrim($prefix, '\\'); - } - - // never found a mapped file - return false; - } - - /** - * Load the mapped file for a namespace prefix and relative class. - * - * @param string $prefix the namespace prefix - * @param string $relativeClass the relative class name - * - * @return mixed boolean false if no mapped file can be loaded, or the - * name of the mapped file that was loaded - */ - protected function loadMappedFile($prefix, $relativeClass) - { - // are there any base directories for this namespace prefix? - if (false === isset($this->prefixes[$prefix])) { - return false; - } - - // look through base directories for this namespace prefix - foreach ($this->prefixes[$prefix] as $baseDir) { - // replace the namespace prefix with the base directory, - // replace namespace separators with directory separators - // in the relative class name, append with .php - $file = $baseDir - .str_replace('\\', '/', $relativeClass) - .'.php'; - - // if the mapped file exists, require it - if ($this->requireFile($file)) { - // yes, we're done - return $file; - } - } - - // never found it - return false; - } - - /** - * If a file exists, require it from the file system. - * - * @param string $file the file to require - * - * @return bool true if the file exists, false if not - */ - protected function requireFile($file) - { - if (file_exists($file)) { - require $file; - - return true; - } - - return false; - } -} diff --git a/upstream-3.x/src/Autoloader/autoload.php b/upstream-3.x/src/Autoloader/autoload.php deleted file mode 100644 index a6768d4..0000000 --- a/upstream-3.x/src/Autoloader/autoload.php +++ /dev/null @@ -1,15 +0,0 @@ -register(); -$loader->addNamespace('OpenSpout', $srcBaseDirectory); diff --git a/upstream-3.x/src/Common/Creator/HelperFactory.php b/upstream-3.x/src/Common/Creator/HelperFactory.php deleted file mode 100644 index 55f1f57..0000000 --- a/upstream-3.x/src/Common/Creator/HelperFactory.php +++ /dev/null @@ -1,48 +0,0 @@ -setValue($value); - $this->setStyle($style); - } - - /** - * @return string - */ - public function __toString() - { - return (string) $this->getValue(); - } - - /** - * @param null|mixed $value - */ - public function setValue($value) - { - $this->value = $value; - $this->type = $this->detectType($value); - } - - /** - * @return null|mixed - */ - public function getValue() - { - return !$this->isError() ? $this->value : null; - } - - /** - * @return mixed - */ - public function getValueEvenIfError() - { - return $this->value; - } - - /** - * @param null|Style $style - */ - public function setStyle($style) - { - $this->style = $style ?: new Style(); - } - - /** - * @return Style - */ - public function getStyle() - { - return $this->style; - } - - /** - * @return null|int - */ - public function getType() - { - return $this->type; - } - - /** - * @param int $type - */ - public function setType($type) - { - $this->type = $type; - } - - /** - * @return bool - */ - public function isBoolean() - { - return self::TYPE_BOOLEAN === $this->type; - } - - /** - * @return bool - */ - public function isEmpty() - { - return self::TYPE_EMPTY === $this->type; - } - - /** - * @return bool - */ - public function isNumeric() - { - return self::TYPE_NUMERIC === $this->type; - } - - /** - * @return bool - */ - public function isString() - { - return self::TYPE_STRING === $this->type; - } - - /** - * @return bool - */ - public function isDate() - { - return self::TYPE_DATE === $this->type; - } - - /** - * @return bool - */ - public function isFormula() - { - return self::TYPE_FORMULA === $this->type; - } - - /** - * @return bool - */ - public function isError() - { - return self::TYPE_ERROR === $this->type; - } - - /** - * Get the current value type. - * - * @param null|mixed $value - * - * @return int - */ - protected function detectType($value) - { - if (CellTypeHelper::isBoolean($value)) { - return self::TYPE_BOOLEAN; - } - if (CellTypeHelper::isEmpty($value)) { - return self::TYPE_EMPTY; - } - if (CellTypeHelper::isNumeric($value)) { - return self::TYPE_NUMERIC; - } - if (CellTypeHelper::isDateTimeOrDateInterval($value)) { - return self::TYPE_DATE; - } - if (CellTypeHelper::isFormula($value)) { - return self::TYPE_FORMULA; - } - if (CellTypeHelper::isNonEmptyString($value)) { - return self::TYPE_STRING; - } - - return self::TYPE_ERROR; - } -} diff --git a/upstream-3.x/src/Common/Entity/Row.php b/upstream-3.x/src/Common/Entity/Row.php deleted file mode 100644 index db6481f..0000000 --- a/upstream-3.x/src/Common/Entity/Row.php +++ /dev/null @@ -1,166 +0,0 @@ -setCells($cells) - ->setStyle($style) - ; - } - - /** - * @return Cell[] $cells - */ - public function getCells() - { - return $this->cells; - } - - /** - * @param Cell[] $cells - * - * @return Row - */ - public function setCells(array $cells) - { - $this->cells = []; - foreach ($cells as $cell) { - $this->addCell($cell); - } - - return $this; - } - - /** - * @param int $cellIndex - * - * @return Row - */ - public function setCellAtIndex(Cell $cell, $cellIndex) - { - $this->cells[$cellIndex] = $cell; - - return $this; - } - - /** - * @param int $cellIndex - * - * @return null|Cell - */ - public function getCellAtIndex($cellIndex) - { - return $this->cells[$cellIndex] ?? null; - } - - /** - * @return Row - */ - public function addCell(Cell $cell) - { - $this->cells[] = $cell; - - return $this; - } - - /** - * @return int - */ - public function getNumCells() - { - // When using "setCellAtIndex", it's possible to - // have "$this->cells" contain holes. - if (empty($this->cells)) { - return 0; - } - - return max(array_keys($this->cells)) + 1; - } - - /** - * @return Style - */ - public function getStyle() - { - return $this->style; - } - - /** - * @param null|Style $style - * - * @return Row - */ - public function setStyle($style) - { - $this->style = $style ?: new Style(); - - return $this; - } - - /** - * @return array The row values, as array - */ - public function toArray() - { - return array_map(function (Cell $cell) { - return $cell->getValue(); - }, $this->cells); - } - - /** - * Set row height. - * - * @param string $height - * - * @return Row - */ - public function setHeight($height) - { - $this->height = $height; - - return $this; - } - - /** - * Returns row height. - * - * @return string - */ - public function getHeight() - { - return $this->height; - } -} diff --git a/upstream-3.x/src/Common/Entity/Style/Border.php b/upstream-3.x/src/Common/Entity/Style/Border.php deleted file mode 100644 index 53b59da..0000000 --- a/upstream-3.x/src/Common/Entity/Style/Border.php +++ /dev/null @@ -1,80 +0,0 @@ -setParts($borderParts); - } - - /** - * @param string $name The name of the border part - * - * @return null|BorderPart - */ - public function getPart($name) - { - return $this->hasPart($name) ? $this->parts[$name] : null; - } - - /** - * @param string $name The name of the border part - * - * @return bool - */ - public function hasPart($name) - { - return isset($this->parts[$name]); - } - - /** - * @return array - */ - public function getParts() - { - return $this->parts; - } - - /** - * Set BorderParts. - * - * @param array $parts - */ - public function setParts($parts) - { - $this->parts = []; - foreach ($parts as $part) { - $this->addPart($part); - } - } - - /** - * @return Border - */ - public function addPart(BorderPart $borderPart) - { - $this->parts[$borderPart->getName()] = $borderPart; - - return $this; - } -} diff --git a/upstream-3.x/src/Common/Entity/Style/BorderPart.php b/upstream-3.x/src/Common/Entity/Style/BorderPart.php deleted file mode 100644 index afee776..0000000 --- a/upstream-3.x/src/Common/Entity/Style/BorderPart.php +++ /dev/null @@ -1,181 +0,0 @@ -setName($name); - $this->setColor($color); - $this->setWidth($width); - $this->setStyle($style); - } - - /** - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * @param string $name The name of the border part @see BorderPart::$allowedNames - * - * @throws InvalidNameException - */ - public function setName($name) - { - if (!\in_array($name, self::$allowedNames, true)) { - throw new InvalidNameException($name); - } - $this->name = $name; - } - - /** - * @return string - */ - public function getStyle() - { - return $this->style; - } - - /** - * @param string $style The style of the border part @see BorderPart::$allowedStyles - * - * @throws InvalidStyleException - */ - public function setStyle($style) - { - if (!\in_array($style, self::$allowedStyles, true)) { - throw new InvalidStyleException($style); - } - $this->style = $style; - } - - /** - * @return string - */ - public function getColor() - { - return $this->color; - } - - /** - * @param string $color The color of the border part @see Color::rgb() - */ - public function setColor($color) - { - $this->color = $color; - } - - /** - * @return string - */ - public function getWidth() - { - return $this->width; - } - - /** - * @param string $width The width of the border part @see BorderPart::$allowedWidths - * - * @throws InvalidWidthException - */ - public function setWidth($width) - { - if (!\in_array($width, self::$allowedWidths, true)) { - throw new InvalidWidthException($width); - } - $this->width = $width; - } - - /** - * @return array - */ - public static function getAllowedStyles() - { - return self::$allowedStyles; - } - - /** - * @return array - */ - public static function getAllowedNames() - { - return self::$allowedNames; - } - - /** - * @return array - */ - public static function getAllowedWidths() - { - return self::$allowedWidths; - } -} diff --git a/upstream-3.x/src/Common/Entity/Style/CellAlignment.php b/upstream-3.x/src/Common/Entity/Style/CellAlignment.php deleted file mode 100644 index ecfa6d7..0000000 --- a/upstream-3.x/src/Common/Entity/Style/CellAlignment.php +++ /dev/null @@ -1,31 +0,0 @@ - 1, - self::RIGHT => 1, - self::CENTER => 1, - self::JUSTIFY => 1, - ]; - - /** - * @param string $cellAlignment - * - * @return bool Whether the given cell alignment is valid - */ - public static function isValid($cellAlignment) - { - return isset(self::$VALID_ALIGNMENTS[$cellAlignment]); - } -} diff --git a/upstream-3.x/src/Common/Entity/Style/Color.php b/upstream-3.x/src/Common/Entity/Style/Color.php deleted file mode 100644 index cd9bdfd..0000000 --- a/upstream-3.x/src/Common/Entity/Style/Color.php +++ /dev/null @@ -1,86 +0,0 @@ - 255) { - throw new InvalidColorException("The RGB components must be between 0 and 255. Received: {$colorComponent}"); - } - } - - /** - * Converts the color component to its corresponding hexadecimal value. - * - * @param int $colorComponent Color component, 0 - 255 - * - * @return string Corresponding hexadecimal value, with a leading 0 if needed. E.g "0f", "2d" - */ - protected static function convertColorComponentToHex($colorComponent) - { - return str_pad(dechex($colorComponent), 2, '0', STR_PAD_LEFT); - } -} diff --git a/upstream-3.x/src/Common/Entity/Style/Style.php b/upstream-3.x/src/Common/Entity/Style/Style.php deleted file mode 100644 index 28f1597..0000000 --- a/upstream-3.x/src/Common/Entity/Style/Style.php +++ /dev/null @@ -1,549 +0,0 @@ -id; - } - - /** - * @param int $id - * - * @return Style - */ - public function setId($id) - { - $this->id = $id; - - return $this; - } - - /** - * @return null|Border - */ - public function getBorder() - { - return $this->border; - } - - /** - * @return Style - */ - public function setBorder(Border $border) - { - $this->shouldApplyBorder = true; - $this->border = $border; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function shouldApplyBorder() - { - return $this->shouldApplyBorder; - } - - /** - * @return bool - */ - public function isFontBold() - { - return $this->fontBold; - } - - /** - * @return Style - */ - public function setFontBold() - { - $this->fontBold = true; - $this->hasSetFontBold = true; - $this->shouldApplyFont = true; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function hasSetFontBold() - { - return $this->hasSetFontBold; - } - - /** - * @return bool - */ - public function isFontItalic() - { - return $this->fontItalic; - } - - /** - * @return Style - */ - public function setFontItalic() - { - $this->fontItalic = true; - $this->hasSetFontItalic = true; - $this->shouldApplyFont = true; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function hasSetFontItalic() - { - return $this->hasSetFontItalic; - } - - /** - * @return bool - */ - public function isFontUnderline() - { - return $this->fontUnderline; - } - - /** - * @return Style - */ - public function setFontUnderline() - { - $this->fontUnderline = true; - $this->hasSetFontUnderline = true; - $this->shouldApplyFont = true; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function hasSetFontUnderline() - { - return $this->hasSetFontUnderline; - } - - /** - * @return bool - */ - public function isFontStrikethrough() - { - return $this->fontStrikethrough; - } - - /** - * @return Style - */ - public function setFontStrikethrough() - { - $this->fontStrikethrough = true; - $this->hasSetFontStrikethrough = true; - $this->shouldApplyFont = true; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function hasSetFontStrikethrough() - { - return $this->hasSetFontStrikethrough; - } - - /** - * @return int - */ - public function getFontSize() - { - return $this->fontSize; - } - - /** - * @param int $fontSize Font size, in pixels - * - * @return Style - */ - public function setFontSize($fontSize) - { - $this->fontSize = $fontSize; - $this->hasSetFontSize = true; - $this->shouldApplyFont = true; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function hasSetFontSize() - { - return $this->hasSetFontSize; - } - - /** - * @return string - */ - public function getFontColor() - { - return $this->fontColor; - } - - /** - * Sets the font color. - * - * @param string $fontColor ARGB color (@see Color) - * - * @return Style - */ - public function setFontColor($fontColor) - { - $this->fontColor = $fontColor; - $this->hasSetFontColor = true; - $this->shouldApplyFont = true; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function hasSetFontColor() - { - return $this->hasSetFontColor; - } - - /** - * @return string - */ - public function getFontName() - { - return $this->fontName; - } - - /** - * @param string $fontName Name of the font to use - * - * @return Style - */ - public function setFontName($fontName) - { - $this->fontName = $fontName; - $this->hasSetFontName = true; - $this->shouldApplyFont = true; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function hasSetFontName() - { - return $this->hasSetFontName; - } - - /** - * @return string - */ - public function getCellAlignment() - { - return $this->cellAlignment; - } - - /** - * @param string $cellAlignment The cell alignment - * - * @return Style - */ - public function setCellAlignment($cellAlignment) - { - $this->cellAlignment = $cellAlignment; - $this->hasSetCellAlignment = true; - $this->shouldApplyCellAlignment = true; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function hasSetCellAlignment() - { - return $this->hasSetCellAlignment; - } - - /** - * @return bool Whether specific cell alignment should be applied - */ - public function shouldApplyCellAlignment() - { - return $this->shouldApplyCellAlignment; - } - - /** - * @return bool - */ - public function shouldWrapText() - { - return $this->shouldWrapText; - } - - /** - * @param bool $shouldWrap Should the text be wrapped - * - * @return Style - */ - public function setShouldWrapText($shouldWrap = true) - { - $this->shouldWrapText = $shouldWrap; - $this->hasSetWrapText = true; - $this->isEmpty = false; - - return $this; - } - - /** - * @return bool - */ - public function hasSetWrapText() - { - return $this->hasSetWrapText; - } - - /** - * @return bool Whether specific font properties should be applied - */ - public function shouldApplyFont() - { - return $this->shouldApplyFont; - } - - /** - * Sets the background color. - * - * @param string $color ARGB color (@see Color) - * - * @return Style - */ - public function setBackgroundColor($color) - { - $this->hasSetBackgroundColor = true; - $this->backgroundColor = $color; - $this->isEmpty = false; - - return $this; - } - - /** - * @return null|string - */ - public function getBackgroundColor() - { - return $this->backgroundColor; - } - - /** - * @return bool Whether the background color should be applied - */ - public function shouldApplyBackgroundColor() - { - return $this->hasSetBackgroundColor; - } - - /** - * Sets format. - * - * @param string $format - * - * @return Style - */ - public function setFormat($format) - { - $this->hasSetFormat = true; - $this->format = $format; - $this->isEmpty = false; - - return $this; - } - - /** - * @return null|string - */ - public function getFormat() - { - return $this->format; - } - - /** - * @return bool Whether format should be applied - */ - public function shouldApplyFormat() - { - return $this->hasSetFormat; - } - - public function isRegistered(): bool - { - return $this->isRegistered; - } - - public function markAsRegistered(?int $id): void - { - $this->setId($id); - $this->isRegistered = true; - } - - public function unmarkAsRegistered(): void - { - $this->setId(0); - $this->isRegistered = false; - } - - public function isEmpty(): bool - { - return $this->isEmpty; - } - - /** - * Sets should shrink to fit. - * - * @param bool $shrinkToFit - * - * @return Style - */ - public function setShouldShrinkToFit($shrinkToFit = true) - { - $this->hasSetShrinkToFit = true; - $this->shouldShrinkToFit = $shrinkToFit; - - return $this; - } - - /** - * @return bool Whether format should be applied - */ - public function shouldShrinkToFit() - { - return $this->shouldShrinkToFit; - } - - /** - * @return bool - */ - public function hasSetShrinkToFit() - { - return $this->hasSetShrinkToFit; - } -} diff --git a/upstream-3.x/src/Common/Exception/EncodingConversionException.php b/upstream-3.x/src/Common/Exception/EncodingConversionException.php deleted file mode 100644 index ef0cdc6..0000000 --- a/upstream-3.x/src/Common/Exception/EncodingConversionException.php +++ /dev/null @@ -1,7 +0,0 @@ -globalFunctionsHelper = $globalFunctionsHelper; - - $this->supportedEncodingsWithBom = [ - self::ENCODING_UTF8 => self::BOM_UTF8, - self::ENCODING_UTF16_LE => self::BOM_UTF16_LE, - self::ENCODING_UTF16_BE => self::BOM_UTF16_BE, - self::ENCODING_UTF32_LE => self::BOM_UTF32_LE, - self::ENCODING_UTF32_BE => self::BOM_UTF32_BE, - ]; - } - - /** - * Returns the number of bytes to use as offset in order to skip the BOM. - * - * @param resource $filePointer Pointer to the file to check - * @param string $encoding Encoding of the file to check - * - * @return int Bytes offset to apply to skip the BOM (0 means no BOM) - */ - public function getBytesOffsetToSkipBOM($filePointer, $encoding) - { - $byteOffsetToSkipBom = 0; - - if ($this->hasBOM($filePointer, $encoding)) { - $bomUsed = $this->supportedEncodingsWithBom[$encoding]; - - // we skip the N first bytes - $byteOffsetToSkipBom = \strlen($bomUsed); - } - - return $byteOffsetToSkipBom; - } - - /** - * Attempts to convert a non UTF-8 string into UTF-8. - * - * @param string $string Non UTF-8 string to be converted - * @param string $sourceEncoding The encoding used to encode the source string - * - * @throws \OpenSpout\Common\Exception\EncodingConversionException If conversion is not supported or if the conversion failed - * - * @return string The converted, UTF-8 string - */ - public function attemptConversionToUTF8($string, $sourceEncoding) - { - return $this->attemptConversion($string, $sourceEncoding, self::ENCODING_UTF8); - } - - /** - * Attempts to convert a UTF-8 string into the given encoding. - * - * @param string $string UTF-8 string to be converted - * @param string $targetEncoding The encoding the string should be re-encoded into - * - * @throws \OpenSpout\Common\Exception\EncodingConversionException If conversion is not supported or if the conversion failed - * - * @return string The converted string, encoded with the given encoding - */ - public function attemptConversionFromUTF8($string, $targetEncoding) - { - return $this->attemptConversion($string, self::ENCODING_UTF8, $targetEncoding); - } - - /** - * Returns whether the file identified by the given pointer has a BOM. - * - * @param resource $filePointer Pointer to the file to check - * @param string $encoding Encoding of the file to check - * - * @return bool TRUE if the file has a BOM, FALSE otherwise - */ - protected function hasBOM($filePointer, $encoding) - { - $hasBOM = false; - - $this->globalFunctionsHelper->rewind($filePointer); - - if (\array_key_exists($encoding, $this->supportedEncodingsWithBom)) { - $potentialBom = $this->supportedEncodingsWithBom[$encoding]; - $numBytesInBom = \strlen($potentialBom); - - $hasBOM = ($this->globalFunctionsHelper->fgets($filePointer, $numBytesInBom + 1) === $potentialBom); - } - - return $hasBOM; - } - - /** - * Attempts to convert the given string to the given encoding. - * Depending on what is installed on the server, we will try to iconv or mbstring. - * - * @param string $string string to be converted - * @param string $sourceEncoding The encoding used to encode the source string - * @param string $targetEncoding The encoding the string should be re-encoded into - * - * @throws \OpenSpout\Common\Exception\EncodingConversionException If conversion is not supported or if the conversion failed - * - * @return string The converted string, encoded with the given encoding - */ - protected function attemptConversion($string, $sourceEncoding, $targetEncoding) - { - // if source and target encodings are the same, it's a no-op - if ($sourceEncoding === $targetEncoding) { - return $string; - } - - $convertedString = null; - - if ($this->canUseIconv()) { - $convertedString = $this->globalFunctionsHelper->iconv($string, $sourceEncoding, $targetEncoding); - } elseif ($this->canUseMbString()) { - $convertedString = $this->globalFunctionsHelper->mb_convert_encoding($string, $sourceEncoding, $targetEncoding); - } else { - throw new EncodingConversionException("The conversion from {$sourceEncoding} to {$targetEncoding} is not supported. Please install \"iconv\" or \"PHP Intl\"."); - } - - if (false === $convertedString) { - throw new EncodingConversionException("The conversion from {$sourceEncoding} to {$targetEncoding} failed."); - } - - return $convertedString; - } - - /** - * Returns whether "iconv" can be used. - * - * @return bool TRUE if "iconv" is available and can be used, FALSE otherwise - */ - protected function canUseIconv() - { - return $this->globalFunctionsHelper->function_exists('iconv'); - } - - /** - * Returns whether "mb_string" functions can be used. - * These functions come with the PHP Intl package. - * - * @return bool TRUE if "mb_string" functions are available and can be used, FALSE otherwise - */ - protected function canUseMbString() - { - return $this->globalFunctionsHelper->function_exists('mb_convert_encoding'); - } -} diff --git a/upstream-3.x/src/Common/Helper/Escaper/CSV.php b/upstream-3.x/src/Common/Helper/Escaper/CSV.php deleted file mode 100644 index d68199a..0000000 --- a/upstream-3.x/src/Common/Helper/Escaper/CSV.php +++ /dev/null @@ -1,37 +0,0 @@ -', '&') as well as - // single/double quotes (for XML attributes) need to be encoded. - if (\defined('ENT_DISALLOWED')) { - /** - * 'ENT_DISALLOWED' ensures that invalid characters in the given document type are replaced. - * Otherwise control characters like a vertical tab "\v" will make the XML document unreadable by the XML processor. - * - * @see https://github.com/box/spout/issues/329 - */ - $replacedString = htmlspecialchars($string, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8'); - } else { - // We are on hhvm or any other engine that does not support ENT_DISALLOWED. - $escapedString = htmlspecialchars($string, ENT_QUOTES, 'UTF-8'); - - // control characters values are from 0 to 1F (hex values) in the ASCII table - // some characters should not be escaped though: "\t", "\r" and "\n". - $regexPattern = '[\x00-\x08'. - // skipping "\t" (0x9) and "\n" (0xA) - '\x0B-\x0C'. - // skipping "\r" (0xD) - '\x0E-\x1F]'; - $replacedString = preg_replace("/{$regexPattern}/", '�', $escapedString); - } - - return $replacedString; - } - - /** - * Unescapes the given string to make it compatible with XLSX. - * - * @param string $string The string to unescape - * - * @return string The unescaped string - */ - public function unescape($string) - { - // ============== - // = WARNING = - // ============== - // It is assumed that the given string has already had its XML entities decoded. - // This is true if the string is coming from a DOMNode (as DOMNode already decode XML entities on creation). - // Therefore there is no need to call "htmlspecialchars_decode()". - return $string; - } -} diff --git a/upstream-3.x/src/Common/Helper/Escaper/XLSX.php b/upstream-3.x/src/Common/Helper/Escaper/XLSX.php deleted file mode 100644 index 16bb162..0000000 --- a/upstream-3.x/src/Common/Helper/Escaper/XLSX.php +++ /dev/null @@ -1,194 +0,0 @@ -initIfNeeded(); - - $escapedString = $this->escapeControlCharacters($string); - // @NOTE: Using ENT_QUOTES as XML entities ('<', '>', '&') as well as - // single/double quotes (for XML attributes) need to be encoded. - return htmlspecialchars($escapedString, ENT_QUOTES, 'UTF-8'); - } - - /** - * Unescapes the given string to make it compatible with XLSX. - * - * @param string $string The string to unescape - * - * @return string The unescaped string - */ - public function unescape($string) - { - $this->initIfNeeded(); - - // ============== - // = WARNING = - // ============== - // It is assumed that the given string has already had its XML entities decoded. - // This is true if the string is coming from a DOMNode (as DOMNode already decode XML entities on creation). - // Therefore there is no need to call "htmlspecialchars_decode()". - return $this->unescapeControlCharacters($string); - } - - /** - * Initializes the control characters if not already done. - */ - protected function initIfNeeded() - { - if (!$this->isAlreadyInitialized) { - $this->escapableControlCharactersPattern = $this->getEscapableControlCharactersPattern(); - $this->controlCharactersEscapingMap = $this->getControlCharactersEscapingMap(); - $this->controlCharactersEscapingReverseMap = array_flip($this->controlCharactersEscapingMap); - - $this->isAlreadyInitialized = true; - } - } - - /** - * @return string Regex pattern containing all escapable control characters - */ - protected function getEscapableControlCharactersPattern() - { - // control characters values are from 0 to 1F (hex values) in the ASCII table - // some characters should not be escaped though: "\t", "\r" and "\n". - return '[\x00-\x08'. - // skipping "\t" (0x9) and "\n" (0xA) - '\x0B-\x0C'. - // skipping "\r" (0xD) - '\x0E-\x1F]'; - } - - /** - * Builds the map containing control characters to be escaped - * mapped to their escaped values. - * "\t", "\r" and "\n" don't need to be escaped. - * - * NOTE: the logic has been adapted from the XlsxWriter library (BSD License) - * - * @see https://github.com/jmcnamara/XlsxWriter/blob/f1e610f29/xlsxwriter/sharedstrings.py#L89 - * - * @return string[] - */ - protected function getControlCharactersEscapingMap() - { - $controlCharactersEscapingMap = []; - - // control characters values are from 0 to 1F (hex values) in the ASCII table - for ($charValue = 0x00; $charValue <= 0x1F; ++$charValue) { - $character = \chr($charValue); - if (preg_match("/{$this->escapableControlCharactersPattern}/", $character)) { - $charHexValue = dechex($charValue); - $escapedChar = '_x'.sprintf('%04s', strtoupper($charHexValue)).'_'; - $controlCharactersEscapingMap[$escapedChar] = $character; - } - } - - return $controlCharactersEscapingMap; - } - - /** - * Converts PHP control characters from the given string to OpenXML escaped control characters. - * - * Excel escapes control characters with _xHHHH_ and also escapes any - * literal strings of that type by encoding the leading underscore. - * So "\0" -> _x0000_ and "_x0000_" -> _x005F_x0000_. - * - * NOTE: the logic has been adapted from the XlsxWriter library (BSD License) - * - * @see https://github.com/jmcnamara/XlsxWriter/blob/f1e610f29/xlsxwriter/sharedstrings.py#L89 - * - * @param string $string String to escape - * - * @return string - */ - protected function escapeControlCharacters($string) - { - $escapedString = $this->escapeEscapeCharacter($string); - - // if no control characters - if (!preg_match("/{$this->escapableControlCharactersPattern}/", $escapedString)) { - return $escapedString; - } - - return preg_replace_callback("/({$this->escapableControlCharactersPattern})/", function ($matches) { - return $this->controlCharactersEscapingReverseMap[$matches[0]]; - }, $escapedString); - } - - /** - * Escapes the escape character: "_x0000_" -> "_x005F_x0000_". - * - * @param string $string String to escape - * - * @return string The escaped string - */ - protected function escapeEscapeCharacter($string) - { - return preg_replace('/_(x[\dA-F]{4})_/', '_x005F_$1_', $string); - } - - /** - * Converts OpenXML escaped control characters from the given string to PHP control characters. - * - * Excel escapes control characters with _xHHHH_ and also escapes any - * literal strings of that type by encoding the leading underscore. - * So "_x0000_" -> "\0" and "_x005F_x0000_" -> "_x0000_" - * - * NOTE: the logic has been adapted from the XlsxWriter library (BSD License) - * - * @see https://github.com/jmcnamara/XlsxWriter/blob/f1e610f29/xlsxwriter/sharedstrings.py#L89 - * - * @param string $string String to unescape - * - * @return string - */ - protected function unescapeControlCharacters($string) - { - $unescapedString = $string; - - foreach ($this->controlCharactersEscapingMap as $escapedCharValue => $charValue) { - // only unescape characters that don't contain the escaped escape character for now - $unescapedString = preg_replace("/(?unescapeEscapeCharacter($unescapedString); - } - - /** - * Unecapes the escape character: "_x005F_x0000_" => "_x0000_". - * - * @param string $string String to unescape - * - * @return string The unescaped string - */ - protected function unescapeEscapeCharacter($string) - { - return preg_replace('/_x005F(_x[\dA-F]{4}_)/', '$1', $string); - } -} diff --git a/upstream-3.x/src/Common/Helper/FileSystemHelper.php b/upstream-3.x/src/Common/Helper/FileSystemHelper.php deleted file mode 100644 index 130f1f8..0000000 --- a/upstream-3.x/src/Common/Helper/FileSystemHelper.php +++ /dev/null @@ -1,138 +0,0 @@ -baseFolderRealPath = realpath($baseFolderPath); - } - - /** - * Creates an empty folder with the given name under the given parent folder. - * - * @param string $parentFolderPath The parent folder path under which the folder is going to be created - * @param string $folderName The name of the folder to create - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or if the folder path is not inside of the base folder - * - * @return string Path of the created folder - */ - public function createFolder($parentFolderPath, $folderName) - { - $this->throwIfOperationNotInBaseFolder($parentFolderPath); - - $folderPath = $parentFolderPath.'/'.$folderName; - - $wasCreationSuccessful = mkdir($folderPath, 0777, true); - if (!$wasCreationSuccessful) { - throw new IOException("Unable to create folder: {$folderPath}"); - } - - return $folderPath; - } - - /** - * Creates a file with the given name and content in the given folder. - * The parent folder must exist. - * - * @param string $parentFolderPath The parent folder path where the file is going to be created - * @param string $fileName The name of the file to create - * @param string $fileContents The contents of the file to create - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the file or if the file path is not inside of the base folder - * - * @return string Path of the created file - */ - public function createFileWithContents($parentFolderPath, $fileName, $fileContents) - { - $this->throwIfOperationNotInBaseFolder($parentFolderPath); - - $filePath = $parentFolderPath.'/'.$fileName; - - $wasCreationSuccessful = file_put_contents($filePath, $fileContents); - if (false === $wasCreationSuccessful) { - throw new IOException("Unable to create file: {$filePath}"); - } - - return $filePath; - } - - /** - * Delete the file at the given path. - * - * @param string $filePath Path of the file to delete - * - * @throws \OpenSpout\Common\Exception\IOException If the file path is not inside of the base folder - */ - public function deleteFile($filePath) - { - $this->throwIfOperationNotInBaseFolder($filePath); - - if (file_exists($filePath) && is_file($filePath)) { - unlink($filePath); - } - } - - /** - * Delete the folder at the given path as well as all its contents. - * - * @param string $folderPath Path of the folder to delete - * - * @throws \OpenSpout\Common\Exception\IOException If the folder path is not inside of the base folder - */ - public function deleteFolderRecursively($folderPath) - { - $this->throwIfOperationNotInBaseFolder($folderPath); - - $itemIterator = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($folderPath, \RecursiveDirectoryIterator::SKIP_DOTS), - \RecursiveIteratorIterator::CHILD_FIRST - ); - - foreach ($itemIterator as $item) { - if ($item->isDir()) { - rmdir($item->getPathname()); - } else { - unlink($item->getPathname()); - } - } - - rmdir($folderPath); - } - - /** - * All I/O operations must occur inside the base folder, for security reasons. - * This function will throw an exception if the folder where the I/O operation - * should occur is not inside the base folder. - * - * @param string $operationFolderPath The path of the folder where the I/O operation should occur - * - * @throws \OpenSpout\Common\Exception\IOException If the folder where the I/O operation should occur - * is not inside the base folder or the base folder does not exist - */ - protected function throwIfOperationNotInBaseFolder(string $operationFolderPath) - { - $operationFolderRealPath = realpath($operationFolderPath); - if (!$this->baseFolderRealPath) { - throw new IOException("The base folder path is invalid: {$this->baseFolderRealPath}"); - } - $isInBaseFolder = (0 === strpos($operationFolderRealPath, $this->baseFolderRealPath)); - if (!$isInBaseFolder) { - throw new IOException("Cannot perform I/O operation outside of the base folder: {$this->baseFolderRealPath}"); - } - } -} diff --git a/upstream-3.x/src/Common/Helper/FileSystemHelperInterface.php b/upstream-3.x/src/Common/Helper/FileSystemHelperInterface.php deleted file mode 100644 index 90082d2..0000000 --- a/upstream-3.x/src/Common/Helper/FileSystemHelperInterface.php +++ /dev/null @@ -1,54 +0,0 @@ -= 70400 ? '' : "\0"; - - return fgetcsv($handle, $length, $delimiter, $enclosure, $escapeCharacter); - } - - /** - * Wrapper around global function fputcsv(). - * - * @see fputcsv() - * - * @param resource $handle - * @param null|string $delimiter - * @param null|string $enclosure - * - * @return false|int - */ - public function fputcsv($handle, array $fields, $delimiter = null, $enclosure = null) - { - /** - * PHP uses '\' as the default escape character. This is not RFC-4180 compliant... - * To fix that, simply disable the escape character. - * - * @see https://bugs.php.net/bug.php?id=43225 - * @see http://tools.ietf.org/html/rfc4180 - */ - $escapeCharacter = \PHP_VERSION_ID >= 70400 ? '' : "\0"; - - return fputcsv($handle, $fields, $delimiter, $enclosure, $escapeCharacter); - } - - /** - * Wrapper around global function fwrite(). - * - * @see fwrite() - * - * @param resource $handle - * @param string $string - * - * @return int - */ - public function fwrite($handle, $string) - { - return fwrite($handle, $string); - } - - /** - * Wrapper around global function fclose(). - * - * @see fclose() - * - * @param resource $handle - * - * @return bool - */ - public function fclose($handle) - { - return fclose($handle); - } - - /** - * Wrapper around global function rewind(). - * - * @see rewind() - * - * @param resource $handle - * - * @return bool - */ - public function rewind($handle) - { - return rewind($handle); - } - - /** - * Wrapper around global function file_exists(). - * - * @see file_exists() - * - * @param string $fileName - * - * @return bool - */ - public function file_exists($fileName) - { - return file_exists($fileName); - } - - /** - * Wrapper around global function file_get_contents(). - * - * @see file_get_contents() - * - * @param string $filePath - * - * @return string - */ - public function file_get_contents($filePath) - { - $realFilePath = $this->convertToUseRealPath($filePath); - - return file_get_contents($realFilePath); - } - - /** - * Wrapper around global function feof(). - * - * @see feof() - * - * @param resource $handle - * - * @return bool - */ - public function feof($handle) - { - return feof($handle); - } - - /** - * Wrapper around global function is_readable(). - * - * @see is_readable() - * - * @param string $fileName - * - * @return bool - */ - public function is_readable($fileName) - { - return is_readable($fileName); - } - - /** - * Wrapper around global function basename(). - * - * @see basename() - * - * @param string $path - * @param string $suffix - * - * @return string - */ - public function basename($path, $suffix = '') - { - return basename($path, $suffix); - } - - /** - * Wrapper around global function header(). - * - * @see header() - * - * @param string $string - */ - public function header($string) - { - header($string); - } - - /** - * Wrapper around global function ob_end_clean(). - * - * @see ob_end_clean() - */ - public function ob_end_clean() - { - if (ob_get_length() > 0) { - ob_end_clean(); - } - } - - /** - * Wrapper around global function iconv(). - * - * @see iconv() - * - * @param string $string The string to be converted - * @param string $sourceEncoding The encoding of the source string - * @param string $targetEncoding The encoding the source string should be converted to - * - * @return bool|string the converted string or FALSE on failure - */ - public function iconv($string, $sourceEncoding, $targetEncoding) - { - return iconv($sourceEncoding, $targetEncoding, $string); - } - - /** - * Wrapper around global function mb_convert_encoding(). - * - * @see mb_convert_encoding() - * - * @param string $string The string to be converted - * @param string $sourceEncoding The encoding of the source string - * @param string $targetEncoding The encoding the source string should be converted to - * - * @return bool|string the converted string or FALSE on failure - */ - public function mb_convert_encoding($string, $sourceEncoding, $targetEncoding) - { - return mb_convert_encoding($string, $targetEncoding, $sourceEncoding); - } - - /** - * Wrapper around global function stream_get_wrappers(). - * - * @see stream_get_wrappers() - * - * @return array - */ - public function stream_get_wrappers() - { - return stream_get_wrappers(); - } - - /** - * Wrapper around global function function_exists(). - * - * @see function_exists() - * - * @param string $functionName - * - * @return bool - */ - public function function_exists($functionName) - { - return \function_exists($functionName); - } - - /** - * Updates the given file path to use a real path. - * This is to avoid issues on some Windows setup. - * - * @param string $filePath File path - * - * @return string The file path using a real path - */ - protected function convertToUseRealPath($filePath) - { - $realFilePath = $filePath; - - if ($this->isZipStream($filePath)) { - if (preg_match('/zip:\/\/(.*)#(.*)/', $filePath, $matches)) { - $documentPath = $matches[1]; - $documentInsideZipPath = $matches[2]; - $realFilePath = 'zip://'.realpath($documentPath).'#'.$documentInsideZipPath; - } - } else { - $realFilePath = realpath($filePath); - } - - return $realFilePath; - } - - /** - * Returns whether the given path is a zip stream. - * - * @param string $path Path pointing to a document - * - * @return bool TRUE if path is a zip stream, FALSE otherwise - */ - protected function isZipStream($path) - { - return 0 === strpos($path, 'zip://'); - } -} diff --git a/upstream-3.x/src/Common/Helper/StringHelper.php b/upstream-3.x/src/Common/Helper/StringHelper.php deleted file mode 100644 index 96e7f2c..0000000 --- a/upstream-3.x/src/Common/Helper/StringHelper.php +++ /dev/null @@ -1,108 +0,0 @@ -hasMbstringSupport = \extension_loaded('mbstring'); - $this->isRunningPhp7OrOlder = version_compare(PHP_VERSION, '8.0.0') < 0; - $this->localeInfo = localeconv(); - } - - /** - * Returns the length of the given string. - * It uses the multi-bytes function is available. - * - * @see strlen - * @see mb_strlen - * - * @param string $string - * - * @return int - */ - public function getStringLength($string) - { - return $this->hasMbstringSupport ? mb_strlen($string) : \strlen($string); - } - - /** - * Returns the position of the first occurrence of the given character/substring within the given string. - * It uses the multi-bytes function is available. - * - * @see strpos - * @see mb_strpos - * - * @param string $char Needle - * @param string $string Haystack - * - * @return int Char/substring's first occurrence position within the string if found (starts at 0) or -1 if not found - */ - public function getCharFirstOccurrencePosition($char, $string) - { - $position = $this->hasMbstringSupport ? mb_strpos($string, $char) : strpos($string, $char); - - return (false !== $position) ? $position : -1; - } - - /** - * Returns the position of the last occurrence of the given character/substring within the given string. - * It uses the multi-bytes function is available. - * - * @see strrpos - * @see mb_strrpos - * - * @param string $char Needle - * @param string $string Haystack - * - * @return int Char/substring's last occurrence position within the string if found (starts at 0) or -1 if not found - */ - public function getCharLastOccurrencePosition($char, $string) - { - $position = $this->hasMbstringSupport ? mb_strrpos($string, $char) : strrpos($string, $char); - - return (false !== $position) ? $position : -1; - } - - /** - * Formats a numeric value (int or float) in a way that's compatible with the expected spreadsheet format. - * - * Formatting of float values is locale dependent in PHP < 8. - * Thousands separators and decimal points vary from locale to locale (en_US: 12.34 vs pl_PL: 12,34). - * However, float values must be formatted with no thousands separator and a "." as decimal point - * to work properly. This method can be used to convert the value to the correct format before storing it. - * - * @see https://wiki.php.net/rfc/locale_independent_float_to_string for the changed behavior in PHP8. - * - * @param float|int $numericValue - * - * @return float|int|string - */ - public function formatNumericValue($numericValue) - { - if ($this->isRunningPhp7OrOlder && \is_float($numericValue)) { - return str_replace( - [$this->localeInfo['thousands_sep'], $this->localeInfo['decimal_point']], - ['', '.'], - (string) $numericValue - ); - } - - return $numericValue; - } -} diff --git a/upstream-3.x/src/Common/Manager/OptionsManagerAbstract.php b/upstream-3.x/src/Common/Manager/OptionsManagerAbstract.php deleted file mode 100644 index 3bfce39..0000000 --- a/upstream-3.x/src/Common/Manager/OptionsManagerAbstract.php +++ /dev/null @@ -1,82 +0,0 @@ - OPTION_VALUE] */ - private $options = []; - - /** - * OptionsManagerAbstract constructor. - */ - public function __construct() - { - $this->supportedOptions = $this->getSupportedOptions(); - $this->setDefaultOptions(); - } - - /** - * Sets the given option, if this option is supported. - * - * @param string $optionName - * @param mixed $optionValue - */ - public function setOption($optionName, $optionValue) - { - if (\in_array($optionName, $this->supportedOptions, true)) { - $this->options[$optionName] = $optionValue; - } - } - - /** - * Add an option to the internal list of options - * Used only for mergeCells() for now. - * - * @param mixed $optionName - * @param mixed $optionValue - */ - public function addOption($optionName, $optionValue) - { - if (\in_array($optionName, $this->supportedOptions, true)) { - if (!isset($this->options[$optionName])) { - $this->options[$optionName] = []; - } elseif (!\is_array($this->options[$optionName])) { - $this->options[$optionName] = [$this->options[$optionName]]; - } - $this->options[$optionName][] = $optionValue; - } - } - - /** - * @param string $optionName - * - * @return null|mixed The set option or NULL if no option with given name found - */ - public function getOption($optionName) - { - $optionValue = null; - - if (isset($this->options[$optionName])) { - $optionValue = $this->options[$optionName]; - } - - return $optionValue; - } - - /** - * @return array List of supported options - */ - abstract protected function getSupportedOptions(); - - /** - * Sets the default options. - * To be overriden by child classes. - */ - abstract protected function setDefaultOptions(); -} diff --git a/upstream-3.x/src/Common/Manager/OptionsManagerInterface.php b/upstream-3.x/src/Common/Manager/OptionsManagerInterface.php deleted file mode 100644 index 7913017..0000000 --- a/upstream-3.x/src/Common/Manager/OptionsManagerInterface.php +++ /dev/null @@ -1,31 +0,0 @@ -helperFactory = $helperFactory; - } - - /** - * @param resource $filePointer Pointer to the CSV file to read - * @param OptionsManagerInterface $optionsManager - * @param GlobalFunctionsHelper $globalFunctionsHelper - * - * @return SheetIterator - */ - public function createSheetIterator($filePointer, $optionsManager, $globalFunctionsHelper) - { - $rowIterator = $this->createRowIterator($filePointer, $optionsManager, $globalFunctionsHelper); - $sheet = $this->createSheet($rowIterator); - - return new SheetIterator($sheet); - } - - /** - * @param Cell[] $cells - * - * @return Row - */ - public function createRow(array $cells = []) - { - return new Row($cells, null); - } - - /** - * @param mixed $cellValue - * - * @return Cell - */ - public function createCell($cellValue) - { - return new Cell($cellValue); - } - - /** - * @return Row - */ - public function createRowFromArray(array $cellValues = []) - { - $cells = array_map(function ($cellValue) { - return $this->createCell($cellValue); - }, $cellValues); - - return $this->createRow($cells); - } - - /** - * @param RowIterator $rowIterator - * - * @return Sheet - */ - private function createSheet($rowIterator) - { - return new Sheet($rowIterator); - } - - /** - * @param resource $filePointer Pointer to the CSV file to read - * @param OptionsManagerInterface $optionsManager - * @param GlobalFunctionsHelper $globalFunctionsHelper - * - * @return RowIterator - */ - private function createRowIterator($filePointer, $optionsManager, $globalFunctionsHelper) - { - $encodingHelper = $this->helperFactory->createEncodingHelper($globalFunctionsHelper); - - return new RowIterator($filePointer, $optionsManager, $encodingHelper, $this, $globalFunctionsHelper); - } -} diff --git a/upstream-3.x/src/Reader/CSV/Manager/OptionsManager.php b/upstream-3.x/src/Reader/CSV/Manager/OptionsManager.php deleted file mode 100644 index 9772a43..0000000 --- a/upstream-3.x/src/Reader/CSV/Manager/OptionsManager.php +++ /dev/null @@ -1,39 +0,0 @@ -setOption(Options::SHOULD_FORMAT_DATES, false); - $this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false); - $this->setOption(Options::FIELD_DELIMITER, ','); - $this->setOption(Options::FIELD_ENCLOSURE, '"'); - $this->setOption(Options::ENCODING, EncodingHelper::ENCODING_UTF8); - } -} diff --git a/upstream-3.x/src/Reader/CSV/Reader.php b/upstream-3.x/src/Reader/CSV/Reader.php deleted file mode 100644 index d62b933..0000000 --- a/upstream-3.x/src/Reader/CSV/Reader.php +++ /dev/null @@ -1,149 +0,0 @@ -= 8.1 */ - private $isRunningAtLeastPhp81; - - public function __construct( - OptionsManagerInterface $optionsManager, - GlobalFunctionsHelper $globalFunctionsHelper, - InternalEntityFactoryInterface $entityFactory - ) { - parent::__construct($optionsManager, $globalFunctionsHelper, $entityFactory); - $this->isRunningAtLeastPhp81 = version_compare(PHP_VERSION, '8.1.0') >= 0; - } - - /** - * Sets the field delimiter for the CSV. - * Needs to be called before opening the reader. - * - * @param string $fieldDelimiter Character that delimits fields - * - * @return Reader - */ - public function setFieldDelimiter($fieldDelimiter) - { - $this->optionsManager->setOption(Options::FIELD_DELIMITER, $fieldDelimiter); - - return $this; - } - - /** - * Sets the field enclosure for the CSV. - * Needs to be called before opening the reader. - * - * @param string $fieldEnclosure Character that enclose fields - * - * @return Reader - */ - public function setFieldEnclosure($fieldEnclosure) - { - $this->optionsManager->setOption(Options::FIELD_ENCLOSURE, $fieldEnclosure); - - return $this; - } - - /** - * Sets the encoding of the CSV file to be read. - * Needs to be called before opening the reader. - * - * @param string $encoding Encoding of the CSV file to be read - * - * @return Reader - */ - public function setEncoding($encoding) - { - $this->optionsManager->setOption(Options::ENCODING, $encoding); - - return $this; - } - - /** - * Returns whether stream wrappers are supported. - * - * @return bool - */ - protected function doesSupportStreamWrapper() - { - return true; - } - - /** - * Opens the file at the given path to make it ready to be read. - * If setEncoding() was not called, it assumes that the file is encoded in UTF-8. - * - * @param string $filePath Path of the CSV file to be read - * - * @throws \OpenSpout\Common\Exception\IOException - */ - protected function openReader($filePath) - { - // "auto_detect_line_endings" is deprecated in PHP 8.1 - if (!$this->isRunningAtLeastPhp81) { - $this->originalAutoDetectLineEndings = ini_get('auto_detect_line_endings'); - ini_set('auto_detect_line_endings', '1'); - } - - $this->filePointer = $this->globalFunctionsHelper->fopen($filePath, 'r'); - if (!$this->filePointer) { - throw new IOException("Could not open file {$filePath} for reading."); - } - - /** @var InternalEntityFactory $entityFactory */ - $entityFactory = $this->entityFactory; - - $this->sheetIterator = $entityFactory->createSheetIterator( - $this->filePointer, - $this->optionsManager, - $this->globalFunctionsHelper - ); - } - - /** - * Returns an iterator to iterate over sheets. - * - * @return SheetIterator To iterate over sheets - */ - protected function getConcreteSheetIterator() - { - return $this->sheetIterator; - } - - /** - * Closes the reader. To be used after reading the file. - */ - protected function closeReader() - { - if (\is_resource($this->filePointer)) { - $this->globalFunctionsHelper->fclose($this->filePointer); - } - - // "auto_detect_line_endings" is deprecated in PHP 8.1 - if (!$this->isRunningAtLeastPhp81) { - ini_set('auto_detect_line_endings', $this->originalAutoDetectLineEndings); - } - } -} diff --git a/upstream-3.x/src/Reader/CSV/RowIterator.php b/upstream-3.x/src/Reader/CSV/RowIterator.php deleted file mode 100644 index 497d266..0000000 --- a/upstream-3.x/src/Reader/CSV/RowIterator.php +++ /dev/null @@ -1,249 +0,0 @@ -filePointer = $filePointer; - $this->fieldDelimiter = $optionsManager->getOption(Options::FIELD_DELIMITER); - $this->fieldEnclosure = $optionsManager->getOption(Options::FIELD_ENCLOSURE); - $this->encoding = $optionsManager->getOption(Options::ENCODING); - $this->shouldPreserveEmptyRows = $optionsManager->getOption(Options::SHOULD_PRESERVE_EMPTY_ROWS); - $this->encodingHelper = $encodingHelper; - $this->entityFactory = $entityFactory; - $this->globalFunctionsHelper = $globalFunctionsHelper; - } - - /** - * Rewind the Iterator to the first element. - * - * @see http://php.net/manual/en/iterator.rewind.php - */ - #[\ReturnTypeWillChange] - public function rewind(): void - { - $this->rewindAndSkipBom(); - - $this->numReadRows = 0; - $this->rowBuffer = null; - - $this->next(); - } - - /** - * Checks if current position is valid. - * - * @see http://php.net/manual/en/iterator.valid.php - */ - #[\ReturnTypeWillChange] - public function valid(): bool - { - return $this->filePointer && !$this->hasReachedEndOfFile; - } - - /** - * Move forward to next element. Reads data for the next unprocessed row. - * - * @see http://php.net/manual/en/iterator.next.php - * - * @throws \OpenSpout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8 - */ - #[\ReturnTypeWillChange] - public function next(): void - { - $this->hasReachedEndOfFile = $this->globalFunctionsHelper->feof($this->filePointer); - - if (!$this->hasReachedEndOfFile) { - $this->readDataForNextRow(); - } - } - - /** - * Return the current element from the buffer. - * - * @see http://php.net/manual/en/iterator.current.php - */ - #[\ReturnTypeWillChange] - public function current(): ?Row - { - return $this->rowBuffer; - } - - /** - * Return the key of the current element. - * - * @see http://php.net/manual/en/iterator.key.php - */ - #[\ReturnTypeWillChange] - public function key(): int - { - return $this->numReadRows; - } - - /** - * Cleans up what was created to iterate over the object. - */ - #[\ReturnTypeWillChange] - public function end(): void - { - // do nothing - } - - /** - * This rewinds and skips the BOM if inserted at the beginning of the file - * by moving the file pointer after it, so that it is not read. - */ - protected function rewindAndSkipBom() - { - $byteOffsetToSkipBom = $this->encodingHelper->getBytesOffsetToSkipBOM($this->filePointer, $this->encoding); - - // sets the cursor after the BOM (0 means no BOM, so rewind it) - $this->globalFunctionsHelper->fseek($this->filePointer, $byteOffsetToSkipBom); - } - - /** - * @throws \OpenSpout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8 - */ - protected function readDataForNextRow() - { - do { - $rowData = $this->getNextUTF8EncodedRow(); - } while ($this->shouldReadNextRow($rowData)); - - if (false !== $rowData) { - // array_map will replace NULL values by empty strings - $rowDataBufferAsArray = array_map(function ($value) { return (string) $value; }, $rowData); - $this->rowBuffer = $this->entityFactory->createRowFromArray($rowDataBufferAsArray); - ++$this->numReadRows; - } else { - // If we reach this point, it means end of file was reached. - // This happens when the last lines are empty lines. - $this->hasReachedEndOfFile = true; - } - } - - /** - * @param array|bool $currentRowData - * - * @return bool Whether the data for the current row can be returned or if we need to keep reading - */ - protected function shouldReadNextRow($currentRowData) - { - $hasSuccessfullyFetchedRowData = (false !== $currentRowData); - $hasNowReachedEndOfFile = $this->globalFunctionsHelper->feof($this->filePointer); - $isEmptyLine = $this->isEmptyLine($currentRowData); - - return - (!$hasSuccessfullyFetchedRowData && !$hasNowReachedEndOfFile) - || (!$this->shouldPreserveEmptyRows && $isEmptyLine) - ; - } - - /** - * Returns the next row, converted if necessary to UTF-8. - * As fgetcsv() does not manage correctly encoding for non UTF-8 data, - * we remove manually whitespace with ltrim or rtrim (depending on the order of the bytes). - * - * @throws \OpenSpout\Common\Exception\EncodingConversionException If unable to convert data to UTF-8 - * - * @return array|false The row for the current file pointer, encoded in UTF-8 or FALSE if nothing to read - */ - protected function getNextUTF8EncodedRow() - { - $encodedRowData = $this->globalFunctionsHelper->fgetcsv($this->filePointer, self::MAX_READ_BYTES_PER_LINE, $this->fieldDelimiter, $this->fieldEnclosure); - if (false === $encodedRowData) { - return false; - } - - foreach ($encodedRowData as $cellIndex => $cellValue) { - switch ($this->encoding) { - case EncodingHelper::ENCODING_UTF16_LE: - case EncodingHelper::ENCODING_UTF32_LE: - // remove whitespace from the beginning of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data - $cellValue = ltrim($cellValue); - - break; - - case EncodingHelper::ENCODING_UTF16_BE: - case EncodingHelper::ENCODING_UTF32_BE: - // remove whitespace from the end of a string as fgetcsv() add extra whitespace when it try to explode non UTF-8 data - $cellValue = rtrim($cellValue); - - break; - } - - $encodedRowData[$cellIndex] = $this->encodingHelper->attemptConversionToUTF8($cellValue, $this->encoding); - } - - return $encodedRowData; - } - - /** - * @param array|bool $lineData Array containing the cells value for the line - * - * @return bool Whether the given line is empty - */ - protected function isEmptyLine($lineData) - { - return \is_array($lineData) && 1 === \count($lineData) && null === $lineData[0]; - } -} diff --git a/upstream-3.x/src/Reader/CSV/Sheet.php b/upstream-3.x/src/Reader/CSV/Sheet.php deleted file mode 100644 index f64b2ee..0000000 --- a/upstream-3.x/src/Reader/CSV/Sheet.php +++ /dev/null @@ -1,59 +0,0 @@ -rowIterator = $rowIterator; - } - - /** - * @return \OpenSpout\Reader\CSV\RowIterator - */ - public function getRowIterator() - { - return $this->rowIterator; - } - - /** - * @return int Index of the sheet - */ - public function getIndex() - { - return 0; - } - - /** - * @return string Name of the sheet - empty string since CSV does not support that - */ - public function getName() - { - return ''; - } - - /** - * @return bool Always TRUE as there is only one sheet - */ - public function isActive() - { - return true; - } - - /** - * @return bool Always TRUE as the only sheet is always visible - */ - public function isVisible() - { - return true; - } -} diff --git a/upstream-3.x/src/Reader/CSV/SheetIterator.php b/upstream-3.x/src/Reader/CSV/SheetIterator.php deleted file mode 100644 index cbcf7c1..0000000 --- a/upstream-3.x/src/Reader/CSV/SheetIterator.php +++ /dev/null @@ -1,89 +0,0 @@ -sheet = $sheet; - } - - /** - * Rewind the Iterator to the first element. - * - * @see http://php.net/manual/en/iterator.rewind.php - */ - #[\ReturnTypeWillChange] - public function rewind(): void - { - $this->hasReadUniqueSheet = false; - } - - /** - * Checks if current position is valid. - * - * @see http://php.net/manual/en/iterator.valid.php - */ - #[\ReturnTypeWillChange] - public function valid(): bool - { - return !$this->hasReadUniqueSheet; - } - - /** - * Move forward to next element. - * - * @see http://php.net/manual/en/iterator.next.php - */ - #[\ReturnTypeWillChange] - public function next(): void - { - $this->hasReadUniqueSheet = true; - } - - /** - * Return the current element. - * - * @see http://php.net/manual/en/iterator.current.php - */ - #[\ReturnTypeWillChange] - public function current(): Sheet - { - return $this->sheet; - } - - /** - * Return the key of the current element. - * - * @see http://php.net/manual/en/iterator.key.php - */ - #[\ReturnTypeWillChange] - public function key(): int - { - return 1; - } - - /** - * Cleans up what was created to iterate over the object. - */ - #[\ReturnTypeWillChange] - public function end(): void - { - // do nothing - } -} diff --git a/upstream-3.x/src/Reader/Common/Creator/InternalEntityFactoryInterface.php b/upstream-3.x/src/Reader/Common/Creator/InternalEntityFactoryInterface.php deleted file mode 100644 index e9cee4f..0000000 --- a/upstream-3.x/src/Reader/Common/Creator/InternalEntityFactoryInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -createGlobalFunctionsHelper(); - - return new CSVReader($optionsManager, $globalFunctionsHelper, $entityFactory); - } - - /** - * @return XLSXReader - */ - private static function createXLSXReader() - { - $optionsManager = new XLSXOptionsManager(); - $helperFactory = new XLSXHelperFactory(); - $managerFactory = new XLSXManagerFactory($helperFactory, new CachingStrategyFactory()); - $entityFactory = new XLSXInternalEntityFactory($managerFactory, $helperFactory); - $globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper(); - - return new XLSXReader($optionsManager, $globalFunctionsHelper, $entityFactory, $managerFactory); - } - - /** - * @return ODSReader - */ - private static function createODSReader() - { - $optionsManager = new ODSOptionsManager(); - $helperFactory = new ODSHelperFactory(); - $managerFactory = new ODSManagerFactory(); - $entityFactory = new ODSInternalEntityFactory($helperFactory, $managerFactory); - $globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper(); - - return new ODSReader($optionsManager, $globalFunctionsHelper, $entityFactory); - } -} diff --git a/upstream-3.x/src/Reader/Common/Entity/Options.php b/upstream-3.x/src/Reader/Common/Entity/Options.php deleted file mode 100644 index 48e15a1..0000000 --- a/upstream-3.x/src/Reader/Common/Entity/Options.php +++ /dev/null @@ -1,22 +0,0 @@ -entityFactory = $entityFactory; - } - - /** - * Detect whether a row is considered empty. - * An empty row has all of its cells empty. - * - * @return bool - */ - public function isEmpty(Row $row) - { - foreach ($row->getCells() as $cell) { - if (!$cell->isEmpty()) { - return false; - } - } - - return true; - } - - /** - * Fills the missing indexes of a row with empty cells. - * - * @return Row - */ - public function fillMissingIndexesWithEmptyCells(Row $row) - { - $numCells = $row->getNumCells(); - - if (0 === $numCells) { - return $row; - } - - $rowCells = $row->getCells(); - $maxCellIndex = $numCells; - - /** - * If the row has empty cells, calling "setCellAtIndex" will add the cell - * but in the wrong place (the new cell is added at the end of the array). - * Therefore, we need to sort the array using keys to have proper order. - * - * @see https://github.com/box/spout/issues/740 - */ - $needsSorting = false; - - for ($cellIndex = 0; $cellIndex < $maxCellIndex; ++$cellIndex) { - if (!isset($rowCells[$cellIndex])) { - $row->setCellAtIndex($this->entityFactory->createCell(''), $cellIndex); - $needsSorting = true; - } - } - - if ($needsSorting) { - $rowCells = $row->getCells(); - ksort($rowCells); - $row->setCells($rowCells); - } - - return $row; - } -} diff --git a/upstream-3.x/src/Reader/Common/XMLProcessor.php b/upstream-3.x/src/Reader/Common/XMLProcessor.php deleted file mode 100644 index 967c0ff..0000000 --- a/upstream-3.x/src/Reader/Common/XMLProcessor.php +++ /dev/null @@ -1,151 +0,0 @@ -xmlReader = $xmlReader; - } - - /** - * @param string $nodeName A callback may be triggered when a node with this name is read - * @param int $nodeType Type of the node [NODE_TYPE_START || NODE_TYPE_END] - * @param callable $callback Callback to execute when the read node has the given name and type - * - * @return XMLProcessor - */ - public function registerCallback($nodeName, $nodeType, $callback) - { - $callbackKey = $this->getCallbackKey($nodeName, $nodeType); - $this->callbacks[$callbackKey] = $this->getInvokableCallbackData($callback); - - return $this; - } - - /** - * Resumes the reading of the XML file where it was left off. - * Stops whenever a callback indicates that reading should stop or at the end of the file. - * - * @throws \OpenSpout\Reader\Exception\XMLProcessingException - */ - public function readUntilStopped() - { - while ($this->xmlReader->read()) { - $nodeType = $this->xmlReader->nodeType; - $nodeNamePossiblyWithPrefix = $this->xmlReader->name; - $nodeNameWithoutPrefix = $this->xmlReader->localName; - - $callbackData = $this->getRegisteredCallbackData($nodeNamePossiblyWithPrefix, $nodeNameWithoutPrefix, $nodeType); - - if (null !== $callbackData) { - $callbackResponse = $this->invokeCallback($callbackData, [$this->xmlReader]); - - if (self::PROCESSING_STOP === $callbackResponse) { - // stop reading - break; - } - } - } - } - - /** - * @param string $nodeName Name of the node - * @param int $nodeType Type of the node [NODE_TYPE_START || NODE_TYPE_END] - * - * @return string Key used to store the associated callback - */ - private function getCallbackKey($nodeName, $nodeType) - { - return "{$nodeName}{$nodeType}"; - } - - /** - * Because the callback can be a "protected" function, we don't want to use call_user_func() directly - * but instead invoke the callback using Reflection. This allows the invocation of "protected" functions. - * Since some functions can be called a lot, we pre-process the callback to only return the elements that - * will be needed to invoke the callback later. - * - * @param callable $callback Array reference to a callback: [OBJECT, METHOD_NAME] - * - * @return array Associative array containing the elements needed to invoke the callback using Reflection - */ - private function getInvokableCallbackData($callback) - { - $callbackObject = $callback[0]; - $callbackMethodName = $callback[1]; - $reflectionMethod = new \ReflectionMethod(\get_class($callbackObject), $callbackMethodName); - $reflectionMethod->setAccessible(true); - - return [ - self::CALLBACK_REFLECTION_METHOD => $reflectionMethod, - self::CALLBACK_REFLECTION_OBJECT => $callbackObject, - ]; - } - - /** - * @param string $nodeNamePossiblyWithPrefix Name of the node, possibly prefixed - * @param string $nodeNameWithoutPrefix Name of the same node, un-prefixed - * @param int $nodeType Type of the node [NODE_TYPE_START || NODE_TYPE_END] - * - * @return null|array Callback data to be used for execution when a node of the given name/type is read or NULL if none found - */ - private function getRegisteredCallbackData($nodeNamePossiblyWithPrefix, $nodeNameWithoutPrefix, $nodeType) - { - // With prefixed nodes, we should match if (by order of preference): - // 1. the callback was registered with the prefixed node name (e.g. "x:worksheet") - // 2. the callback was registered with the un-prefixed node name (e.g. "worksheet") - $callbackKeyForPossiblyPrefixedName = $this->getCallbackKey($nodeNamePossiblyWithPrefix, $nodeType); - $callbackKeyForUnPrefixedName = $this->getCallbackKey($nodeNameWithoutPrefix, $nodeType); - $hasPrefix = ($nodeNamePossiblyWithPrefix !== $nodeNameWithoutPrefix); - - $callbackKeyToUse = $callbackKeyForUnPrefixedName; - if ($hasPrefix && isset($this->callbacks[$callbackKeyForPossiblyPrefixedName])) { - $callbackKeyToUse = $callbackKeyForPossiblyPrefixedName; - } - - // Using isset here because it is way faster than array_key_exists... - return $this->callbacks[$callbackKeyToUse] ?? null; - } - - /** - * @param array $callbackData Associative array containing data to invoke the callback using Reflection - * @param array $args Arguments to pass to the callback - * - * @return int Callback response - */ - private function invokeCallback($callbackData, $args) - { - $reflectionMethod = $callbackData[self::CALLBACK_REFLECTION_METHOD]; - $callbackObject = $callbackData[self::CALLBACK_REFLECTION_OBJECT]; - - return $reflectionMethod->invokeArgs($callbackObject, $args); - } -} diff --git a/upstream-3.x/src/Reader/Exception/InvalidValueException.php b/upstream-3.x/src/Reader/Exception/InvalidValueException.php deleted file mode 100644 index 9bbcebd..0000000 --- a/upstream-3.x/src/Reader/Exception/InvalidValueException.php +++ /dev/null @@ -1,30 +0,0 @@ -invalidValue = $invalidValue; - parent::__construct($message, $code, $previous); - } - - /** - * @return mixed - */ - public function getInvalidValue() - { - return $this->invalidValue; - } -} diff --git a/upstream-3.x/src/Reader/Exception/IteratorNotRewindableException.php b/upstream-3.x/src/Reader/Exception/IteratorNotRewindableException.php deleted file mode 100644 index 06aac22..0000000 --- a/upstream-3.x/src/Reader/Exception/IteratorNotRewindableException.php +++ /dev/null @@ -1,7 +0,0 @@ -createStringsEscaper(); - - return new CellValueFormatter($shouldFormatDates, $escaper); - } - - /** - * @param InternalEntityFactory $entityFactory - * - * @return SettingsHelper - */ - public function createSettingsHelper($entityFactory) - { - return new SettingsHelper($entityFactory); - } - - /** - * @return \OpenSpout\Common\Helper\Escaper\ODS - */ - public function createStringsEscaper() - { - // @noinspection PhpUnnecessaryFullyQualifiedNameInspection - return new \OpenSpout\Common\Helper\Escaper\ODS(); - } -} diff --git a/upstream-3.x/src/Reader/ODS/Creator/InternalEntityFactory.php b/upstream-3.x/src/Reader/ODS/Creator/InternalEntityFactory.php deleted file mode 100644 index 960fec9..0000000 --- a/upstream-3.x/src/Reader/ODS/Creator/InternalEntityFactory.php +++ /dev/null @@ -1,124 +0,0 @@ -helperFactory = $helperFactory; - $this->managerFactory = $managerFactory; - } - - /** - * @param string $filePath Path of the file to be read - * @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager - * - * @return SheetIterator - */ - public function createSheetIterator($filePath, $optionsManager) - { - $escaper = $this->helperFactory->createStringsEscaper(); - $settingsHelper = $this->helperFactory->createSettingsHelper($this); - - return new SheetIterator($filePath, $optionsManager, $escaper, $settingsHelper, $this); - } - - /** - * @param XMLReader $xmlReader XML Reader - * @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based) - * @param string $sheetName Name of the sheet - * @param bool $isSheetActive Whether the sheet was defined as active - * @param bool $isSheetVisible Whether the sheet is visible - * @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager - * - * @return Sheet - */ - public function createSheet($xmlReader, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible, $optionsManager) - { - $rowIterator = $this->createRowIterator($xmlReader, $optionsManager); - - return new Sheet($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible); - } - - /** - * @param Cell[] $cells - * - * @return Row - */ - public function createRow(array $cells = []) - { - return new Row($cells, null); - } - - /** - * @param mixed $cellValue - * - * @return Cell - */ - public function createCell($cellValue) - { - return new Cell($cellValue); - } - - /** - * @return XMLReader - */ - public function createXMLReader() - { - return new XMLReader(); - } - - /** - * @return \ZipArchive - */ - public function createZipArchive() - { - return new \ZipArchive(); - } - - /** - * @param XMLReader $xmlReader XML Reader - * @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager - * - * @return RowIterator - */ - private function createRowIterator($xmlReader, $optionsManager) - { - $shouldFormatDates = $optionsManager->getOption(Options::SHOULD_FORMAT_DATES); - $cellValueFormatter = $this->helperFactory->createCellValueFormatter($shouldFormatDates); - $xmlProcessor = $this->createXMLProcessor($xmlReader); - $rowManager = $this->managerFactory->createRowManager($this); - - return new RowIterator($xmlReader, $optionsManager, $cellValueFormatter, $xmlProcessor, $rowManager, $this); - } - - /** - * @param XMLReader $xmlReader - * - * @return XMLProcessor - */ - private function createXMLProcessor($xmlReader) - { - return new XMLProcessor($xmlReader); - } -} diff --git a/upstream-3.x/src/Reader/ODS/Creator/ManagerFactory.php b/upstream-3.x/src/Reader/ODS/Creator/ManagerFactory.php deleted file mode 100644 index 546069e..0000000 --- a/upstream-3.x/src/Reader/ODS/Creator/ManagerFactory.php +++ /dev/null @@ -1,21 +0,0 @@ - ' ', - self::XML_NODE_TEXT_TAB => "\t", - self::XML_NODE_TEXT_LINE_BREAK => "\n", - ]; - - /** - * @param bool $shouldFormatDates Whether date/time values should be returned as PHP objects or be formatted as strings - * @param \OpenSpout\Common\Helper\Escaper\ODS $escaper Used to unescape XML data - */ - public function __construct($shouldFormatDates, $escaper) - { - $this->shouldFormatDates = $shouldFormatDates; - $this->escaper = $escaper; - } - - /** - * Returns the (unescaped) correctly marshalled, cell value associated to the given XML node. - * - * @see http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#refTable13 - * - * @param \DOMElement $node - * - * @throws InvalidValueException If the node value is not valid - * - * @return bool|\DateInterval|\DateTime|float|int|string The value associated with the cell, empty string if cell's type is void/undefined - */ - public function extractAndFormatNodeValue($node) - { - $cellType = $node->getAttribute(self::XML_ATTRIBUTE_TYPE); - - switch ($cellType) { - case self::CELL_TYPE_STRING: - return $this->formatStringCellValue($node); - - case self::CELL_TYPE_FLOAT: - return $this->formatFloatCellValue($node); - - case self::CELL_TYPE_BOOLEAN: - return $this->formatBooleanCellValue($node); - - case self::CELL_TYPE_DATE: - return $this->formatDateCellValue($node); - - case self::CELL_TYPE_TIME: - return $this->formatTimeCellValue($node); - - case self::CELL_TYPE_CURRENCY: - return $this->formatCurrencyCellValue($node); - - case self::CELL_TYPE_PERCENTAGE: - return $this->formatPercentageCellValue($node); - - case self::CELL_TYPE_VOID: - default: - return ''; - } - } - - /** - * Returns the cell String value. - * - * @param \DOMElement $node - * - * @return string The value associated with the cell - */ - protected function formatStringCellValue($node) - { - $pNodeValues = []; - $pNodes = $node->getElementsByTagName(self::XML_NODE_P); - - foreach ($pNodes as $pNode) { - $pNodeValues[] = $this->extractTextValueFromNode($pNode); - } - - $escapedCellValue = implode("\n", $pNodeValues); - - return $this->escaper->unescape($escapedCellValue); - } - - /** - * Returns the cell Numeric value from the given node. - * - * @param \DOMElement $node - * - * @return float|int The value associated with the cell - */ - protected function formatFloatCellValue($node) - { - $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_VALUE); - - $nodeIntValue = (int) $nodeValue; - $nodeFloatValue = (float) $nodeValue; - - return ((float) $nodeIntValue === $nodeFloatValue) ? $nodeIntValue : $nodeFloatValue; - } - - /** - * Returns the cell Boolean value from the given node. - * - * @param \DOMElement $node - * - * @return bool The value associated with the cell - */ - protected function formatBooleanCellValue($node) - { - $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_BOOLEAN_VALUE); - - return (bool) $nodeValue; - } - - /** - * Returns the cell Date value from the given node. - * - * @param \DOMElement $node - * - * @throws InvalidValueException If the value is not a valid date - * - * @return \DateTime|string The value associated with the cell - */ - protected function formatDateCellValue($node) - { - // The XML node looks like this: - // - // 05/19/16 04:39 PM - // - - if ($this->shouldFormatDates) { - // The date is already formatted in the "p" tag - $nodeWithValueAlreadyFormatted = $node->getElementsByTagName(self::XML_NODE_P)->item(0); - $cellValue = $nodeWithValueAlreadyFormatted->nodeValue; - } else { - // otherwise, get it from the "date-value" attribute - $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_DATE_VALUE); - - try { - $cellValue = new \DateTime($nodeValue); - } catch (\Exception $e) { - throw new InvalidValueException($nodeValue); - } - } - - return $cellValue; - } - - /** - * Returns the cell Time value from the given node. - * - * @param \DOMElement $node - * - * @throws InvalidValueException If the value is not a valid time - * - * @return \DateInterval|string The value associated with the cell - */ - protected function formatTimeCellValue($node) - { - // The XML node looks like this: - // - // 01:24:00 PM - // - - if ($this->shouldFormatDates) { - // The date is already formatted in the "p" tag - $nodeWithValueAlreadyFormatted = $node->getElementsByTagName(self::XML_NODE_P)->item(0); - $cellValue = $nodeWithValueAlreadyFormatted->nodeValue; - } else { - // otherwise, get it from the "time-value" attribute - $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_TIME_VALUE); - - try { - $cellValue = new \DateInterval($nodeValue); - } catch (\Exception $e) { - throw new InvalidValueException($nodeValue); - } - } - - return $cellValue; - } - - /** - * Returns the cell Currency value from the given node. - * - * @param \DOMElement $node - * - * @return string The value associated with the cell (e.g. "100 USD" or "9.99 EUR") - */ - protected function formatCurrencyCellValue($node) - { - $value = $node->getAttribute(self::XML_ATTRIBUTE_VALUE); - $currency = $node->getAttribute(self::XML_ATTRIBUTE_CURRENCY); - - return "{$value} {$currency}"; - } - - /** - * Returns the cell Percentage value from the given node. - * - * @param \DOMElement $node - * - * @return float|int The value associated with the cell - */ - protected function formatPercentageCellValue($node) - { - // percentages are formatted like floats - return $this->formatFloatCellValue($node); - } - - /** - * @param \DOMNode $pNode - * - * @return string - */ - private function extractTextValueFromNode($pNode) - { - $textValue = ''; - - foreach ($pNode->childNodes as $childNode) { - if ($childNode instanceof \DOMText) { - $textValue .= $childNode->nodeValue; - } elseif ($this->isWhitespaceNode($childNode->nodeName)) { - $textValue .= $this->transformWhitespaceNode($childNode); - } elseif (self::XML_NODE_TEXT_A === $childNode->nodeName || self::XML_NODE_TEXT_SPAN === $childNode->nodeName) { - $textValue .= $this->extractTextValueFromNode($childNode); - } - } - - return $textValue; - } - - /** - * Returns whether the given node is a whitespace node. It must be one of these: - * - - * - - * - . - * - * @param string $nodeName - * - * @return bool - */ - private function isWhitespaceNode($nodeName) - { - return isset(self::$WHITESPACE_XML_NODES[$nodeName]); - } - - /** - * The "" node can contain the string value directly - * or contain child elements. In this case, whitespaces contain in - * the child elements should be replaced by their XML equivalent: - * - space => - * - tab => - * - line break => . - * - * @see https://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#__RefHeading__1415200_253892949 - * - * @param \DOMElement $node The XML node representing a whitespace - * - * @return string The corresponding whitespace value - */ - private function transformWhitespaceNode($node) - { - $countAttribute = $node->getAttribute(self::XML_ATTRIBUTE_C); // only defined for "" - $numWhitespaces = (!empty($countAttribute)) ? (int) $countAttribute : 1; - - return str_repeat(self::$WHITESPACE_XML_NODES[$node->nodeName], $numWhitespaces); - } -} diff --git a/upstream-3.x/src/Reader/ODS/Helper/SettingsHelper.php b/upstream-3.x/src/Reader/ODS/Helper/SettingsHelper.php deleted file mode 100644 index 4463eeb..0000000 --- a/upstream-3.x/src/Reader/ODS/Helper/SettingsHelper.php +++ /dev/null @@ -1,61 +0,0 @@ -entityFactory = $entityFactory; - } - - /** - * @param string $filePath Path of the file to be read - * - * @return null|string Name of the sheet that was defined as active or NULL if none found - */ - public function getActiveSheetName($filePath) - { - $xmlReader = $this->entityFactory->createXMLReader(); - if (false === $xmlReader->openFileInZip($filePath, self::SETTINGS_XML_FILE_PATH)) { - return null; - } - - $activeSheetName = null; - - try { - while ($xmlReader->readUntilNodeFound(self::XML_NODE_CONFIG_ITEM)) { - if (self::XML_ATTRIBUTE_VALUE_ACTIVE_TABLE === $xmlReader->getAttribute(self::XML_ATTRIBUTE_CONFIG_NAME)) { - $activeSheetName = $xmlReader->readString(); - - break; - } - } - } catch (XMLProcessingException $exception) { - // do nothing - } - - $xmlReader->close(); - - return $activeSheetName; - } -} diff --git a/upstream-3.x/src/Reader/ODS/Manager/OptionsManager.php b/upstream-3.x/src/Reader/ODS/Manager/OptionsManager.php deleted file mode 100644 index e13c544..0000000 --- a/upstream-3.x/src/Reader/ODS/Manager/OptionsManager.php +++ /dev/null @@ -1,32 +0,0 @@ -setOption(Options::SHOULD_FORMAT_DATES, false); - $this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false); - } -} diff --git a/upstream-3.x/src/Reader/ODS/Reader.php b/upstream-3.x/src/Reader/ODS/Reader.php deleted file mode 100644 index 8f3970a..0000000 --- a/upstream-3.x/src/Reader/ODS/Reader.php +++ /dev/null @@ -1,73 +0,0 @@ -entityFactory; - - $this->zip = $entityFactory->createZipArchive(); - - if (true === $this->zip->open($filePath)) { - /** @var InternalEntityFactory $entityFactory */ - $entityFactory = $this->entityFactory; - $this->sheetIterator = $entityFactory->createSheetIterator($filePath, $this->optionsManager); - } else { - throw new IOException("Could not open {$filePath} for reading."); - } - } - - /** - * Returns an iterator to iterate over sheets. - * - * @return SheetIterator To iterate over sheets - */ - protected function getConcreteSheetIterator() - { - return $this->sheetIterator; - } - - /** - * Closes the reader. To be used after reading the file. - */ - protected function closeReader() - { - if (null !== $this->zip) { - $this->zip->close(); - } - } -} diff --git a/upstream-3.x/src/Reader/ODS/RowIterator.php b/upstream-3.x/src/Reader/ODS/RowIterator.php deleted file mode 100644 index a3ab7aa..0000000 --- a/upstream-3.x/src/Reader/ODS/RowIterator.php +++ /dev/null @@ -1,388 +0,0 @@ -" element - * @param OptionsManagerInterface $optionsManager Reader's options manager - * @param CellValueFormatter $cellValueFormatter Helper to format cell values - * @param XMLProcessor $xmlProcessor Helper to process XML files - * @param RowManager $rowManager Manages rows - * @param InternalEntityFactory $entityFactory Factory to create entities - */ - public function __construct( - XMLReader $xmlReader, - OptionsManagerInterface $optionsManager, - CellValueFormatter $cellValueFormatter, - XMLProcessor $xmlProcessor, - RowManager $rowManager, - InternalEntityFactory $entityFactory - ) { - $this->xmlReader = $xmlReader; - $this->shouldPreserveEmptyRows = $optionsManager->getOption(Options::SHOULD_PRESERVE_EMPTY_ROWS); - $this->cellValueFormatter = $cellValueFormatter; - $this->entityFactory = $entityFactory; - $this->rowManager = $rowManager; - - // Register all callbacks to process different nodes when reading the XML file - $this->xmlProcessor = $xmlProcessor; - $this->xmlProcessor->registerCallback(self::XML_NODE_ROW, XMLProcessor::NODE_TYPE_START, [$this, 'processRowStartingNode']); - $this->xmlProcessor->registerCallback(self::XML_NODE_CELL, XMLProcessor::NODE_TYPE_START, [$this, 'processCellStartingNode']); - $this->xmlProcessor->registerCallback(self::XML_NODE_ROW, XMLProcessor::NODE_TYPE_END, [$this, 'processRowEndingNode']); - $this->xmlProcessor->registerCallback(self::XML_NODE_TABLE, XMLProcessor::NODE_TYPE_END, [$this, 'processTableEndingNode']); - } - - /** - * Rewind the Iterator to the first element. - * NOTE: It can only be done once, as it is not possible to read an XML file backwards. - * - * @see http://php.net/manual/en/iterator.rewind.php - * - * @throws \OpenSpout\Reader\Exception\IteratorNotRewindableException If the iterator is rewound more than once - */ - #[\ReturnTypeWillChange] - public function rewind(): void - { - // Because sheet and row data is located in the file, we can't rewind both the - // sheet iterator and the row iterator, as XML file cannot be read backwards. - // Therefore, rewinding the row iterator has been disabled. - if ($this->hasAlreadyBeenRewound) { - throw new IteratorNotRewindableException(); - } - - $this->hasAlreadyBeenRewound = true; - $this->lastRowIndexProcessed = 0; - $this->nextRowIndexToBeProcessed = 1; - $this->rowBuffer = null; - $this->hasReachedEndOfFile = false; - - $this->next(); - } - - /** - * Checks if current position is valid. - * - * @see http://php.net/manual/en/iterator.valid.php - */ - #[\ReturnTypeWillChange] - public function valid(): bool - { - return !$this->hasReachedEndOfFile; - } - - /** - * Move forward to next element. Empty rows will be skipped. - * - * @see http://php.net/manual/en/iterator.next.php - * - * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If a shared string was not found - * @throws \OpenSpout\Common\Exception\IOException If unable to read the sheet data XML - */ - #[\ReturnTypeWillChange] - public function next(): void - { - if ($this->doesNeedDataForNextRowToBeProcessed()) { - $this->readDataForNextRow(); - } - - ++$this->lastRowIndexProcessed; - } - - /** - * Return the current element, from the buffer. - * - * @see http://php.net/manual/en/iterator.current.php - */ - #[\ReturnTypeWillChange] - public function current(): Row - { - return $this->rowBuffer; - } - - /** - * Return the key of the current element. - * - * @see http://php.net/manual/en/iterator.key.php - */ - #[\ReturnTypeWillChange] - public function key(): int - { - return $this->lastRowIndexProcessed; - } - - /** - * Cleans up what was created to iterate over the object. - */ - #[\ReturnTypeWillChange] - public function end(): void - { - $this->xmlReader->close(); - } - - /** - * Returns whether we need data for the next row to be processed. - * We DO need to read data if: - * - we have not read any rows yet - * OR - * - the next row to be processed immediately follows the last read row. - * - * @return bool whether we need data for the next row to be processed - */ - protected function doesNeedDataForNextRowToBeProcessed() - { - $hasReadAtLeastOneRow = (0 !== $this->lastRowIndexProcessed); - - return - !$hasReadAtLeastOneRow - || $this->lastRowIndexProcessed === $this->nextRowIndexToBeProcessed - 1 - ; - } - - /** - * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If a shared string was not found - * @throws \OpenSpout\Common\Exception\IOException If unable to read the sheet data XML - */ - protected function readDataForNextRow() - { - $this->currentlyProcessedRow = $this->entityFactory->createRow(); - - try { - $this->xmlProcessor->readUntilStopped(); - } catch (XMLProcessingException $exception) { - throw new IOException("The sheet's data cannot be read. [{$exception->getMessage()}]"); - } - - $this->rowBuffer = $this->currentlyProcessedRow; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int A return code that indicates what action should the processor take next - */ - protected function processRowStartingNode($xmlReader) - { - // Reset data from current row - $this->hasAlreadyReadOneCellInCurrentRow = false; - $this->lastProcessedCell = null; - $this->numColumnsRepeated = 1; - $this->numRowsRepeated = $this->getNumRowsRepeatedForCurrentNode($xmlReader); - - return XMLProcessor::PROCESSING_CONTINUE; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int A return code that indicates what action should the processor take next - */ - protected function processCellStartingNode($xmlReader) - { - $currentNumColumnsRepeated = $this->getNumColumnsRepeatedForCurrentNode($xmlReader); - - // NOTE: expand() will automatically decode all XML entities of the child nodes - /** @var \DOMElement $node */ - $node = $xmlReader->expand(); - $currentCell = $this->getCell($node); - - // process cell N only after having read cell N+1 (see below why) - if ($this->hasAlreadyReadOneCellInCurrentRow) { - for ($i = 0; $i < $this->numColumnsRepeated; ++$i) { - $this->currentlyProcessedRow->addCell($this->lastProcessedCell); - } - } - - $this->hasAlreadyReadOneCellInCurrentRow = true; - $this->lastProcessedCell = $currentCell; - $this->numColumnsRepeated = $currentNumColumnsRepeated; - - return XMLProcessor::PROCESSING_CONTINUE; - } - - /** - * @return int A return code that indicates what action should the processor take next - */ - protected function processRowEndingNode() - { - $isEmptyRow = $this->isEmptyRow($this->currentlyProcessedRow, $this->lastProcessedCell); - - // if the fetched row is empty and we don't want to preserve it... - if (!$this->shouldPreserveEmptyRows && $isEmptyRow) { - // ... skip it - return XMLProcessor::PROCESSING_CONTINUE; - } - - // if the row is empty, we don't want to return more than one cell - $actualNumColumnsRepeated = (!$isEmptyRow) ? $this->numColumnsRepeated : 1; - $numCellsInCurrentlyProcessedRow = $this->currentlyProcessedRow->getNumCells(); - - // Only add the value if the last read cell is not a trailing empty cell repeater in Excel. - // The current count of read columns is determined by counting the values in "$this->currentlyProcessedRowData". - // This is to avoid creating a lot of empty cells, as Excel adds a last empty "" - // with a number-columns-repeated value equals to the number of (supported columns - used columns). - // In Excel, the number of supported columns is 16384, but we don't want to returns rows with - // always 16384 cells. - if (($numCellsInCurrentlyProcessedRow + $actualNumColumnsRepeated) !== self::MAX_COLUMNS_EXCEL) { - for ($i = 0; $i < $actualNumColumnsRepeated; ++$i) { - $this->currentlyProcessedRow->addCell($this->lastProcessedCell); - } - } - - // If we are processing row N and the row is repeated M times, - // then the next row to be processed will be row (N+M). - $this->nextRowIndexToBeProcessed += $this->numRowsRepeated; - - // at this point, we have all the data we need for the row - // so that we can populate the buffer - return XMLProcessor::PROCESSING_STOP; - } - - /** - * @return int A return code that indicates what action should the processor take next - */ - protected function processTableEndingNode() - { - // The closing "" marks the end of the file - $this->hasReachedEndOfFile = true; - - return XMLProcessor::PROCESSING_STOP; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int The value of "table:number-rows-repeated" attribute of the current node, or 1 if attribute missing - */ - protected function getNumRowsRepeatedForCurrentNode($xmlReader) - { - $numRowsRepeated = $xmlReader->getAttribute(self::XML_ATTRIBUTE_NUM_ROWS_REPEATED); - - return (null !== $numRowsRepeated) ? (int) $numRowsRepeated : 1; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int The value of "table:number-columns-repeated" attribute of the current node, or 1 if attribute missing - */ - protected function getNumColumnsRepeatedForCurrentNode($xmlReader) - { - $numColumnsRepeated = $xmlReader->getAttribute(self::XML_ATTRIBUTE_NUM_COLUMNS_REPEATED); - - return (null !== $numColumnsRepeated) ? (int) $numColumnsRepeated : 1; - } - - /** - * Returns the cell with (unescaped) correctly marshalled, cell value associated to the given XML node. - * - * @param \DOMElement $node - * - * @return Cell The cell set with the associated with the cell - */ - protected function getCell($node) - { - try { - $cellValue = $this->cellValueFormatter->extractAndFormatNodeValue($node); - $cell = $this->entityFactory->createCell($cellValue); - } catch (InvalidValueException $exception) { - $cell = $this->entityFactory->createCell($exception->getInvalidValue()); - $cell->setType(Cell::TYPE_ERROR); - } - - return $cell; - } - - /** - * After finishing processing each cell, a row is considered empty if it contains - * no cells or if the last read cell is empty. - * After finishing processing each cell, the last read cell is not part of the - * row data yet (as we still need to apply the "num-columns-repeated" attribute). - * - * @param Row $currentRow - * @param null|Cell $lastReadCell The last read cell - * - * @return bool Whether the row is empty - */ - protected function isEmptyRow($currentRow, $lastReadCell) - { - return - $this->rowManager->isEmpty($currentRow) - && (!isset($lastReadCell) || $lastReadCell->isEmpty()) - ; - } -} diff --git a/upstream-3.x/src/Reader/ODS/Sheet.php b/upstream-3.x/src/Reader/ODS/Sheet.php deleted file mode 100644 index 306f268..0000000 --- a/upstream-3.x/src/Reader/ODS/Sheet.php +++ /dev/null @@ -1,85 +0,0 @@ -rowIterator = $rowIterator; - $this->index = $sheetIndex; - $this->name = $sheetName; - $this->isActive = $isSheetActive; - $this->isVisible = $isSheetVisible; - } - - /** - * @return \OpenSpout\Reader\ODS\RowIterator - */ - public function getRowIterator() - { - return $this->rowIterator; - } - - /** - * @return int Index of the sheet, based on order in the workbook (zero-based) - */ - public function getIndex() - { - return $this->index; - } - - /** - * @return string Name of the sheet - */ - public function getName() - { - return $this->name; - } - - /** - * @return bool Whether the sheet was defined as active - */ - public function isActive() - { - return $this->isActive; - } - - /** - * @return bool Whether the sheet is visible - */ - public function isVisible() - { - return $this->isVisible; - } -} diff --git a/upstream-3.x/src/Reader/ODS/SheetIterator.php b/upstream-3.x/src/Reader/ODS/SheetIterator.php deleted file mode 100644 index 5240e38..0000000 --- a/upstream-3.x/src/Reader/ODS/SheetIterator.php +++ /dev/null @@ -1,239 +0,0 @@ - [IS_SHEET_VISIBLE] */ - protected $sheetsVisibility; - - /** - * @param string $filePath Path of the file to be read - * @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager - * @param \OpenSpout\Common\Helper\Escaper\ODS $escaper Used to unescape XML data - * @param SettingsHelper $settingsHelper Helper to get data from "settings.xml" - * @param InternalEntityFactory $entityFactory Factory to create entities - */ - public function __construct($filePath, $optionsManager, $escaper, $settingsHelper, $entityFactory) - { - $this->filePath = $filePath; - $this->optionsManager = $optionsManager; - $this->entityFactory = $entityFactory; - $this->xmlReader = $entityFactory->createXMLReader(); - $this->escaper = $escaper; - $this->activeSheetName = $settingsHelper->getActiveSheetName($filePath); - } - - /** - * Rewind the Iterator to the first element. - * - * @see http://php.net/manual/en/iterator.rewind.php - * - * @throws \OpenSpout\Common\Exception\IOException If unable to open the XML file containing sheets' data - */ - #[\ReturnTypeWillChange] - public function rewind() - { - $this->xmlReader->close(); - - if (false === $this->xmlReader->openFileInZip($this->filePath, self::CONTENT_XML_FILE_PATH)) { - $contentXmlFilePath = $this->filePath.'#'.self::CONTENT_XML_FILE_PATH; - - throw new IOException("Could not open \"{$contentXmlFilePath}\"."); - } - - try { - $this->sheetsVisibility = $this->readSheetsVisibility(); - $this->hasFoundSheet = $this->xmlReader->readUntilNodeFound(self::XML_NODE_TABLE); - } catch (XMLProcessingException $exception) { - throw new IOException("The content.xml file is invalid and cannot be read. [{$exception->getMessage()}]"); - } - - $this->currentSheetIndex = 0; - } - - /** - * Checks if current position is valid. - * - * @see http://php.net/manual/en/iterator.valid.php - * - * @return bool - */ - #[\ReturnTypeWillChange] - public function valid() - { - return $this->hasFoundSheet; - } - - /** - * Move forward to next element. - * - * @see http://php.net/manual/en/iterator.next.php - */ - #[\ReturnTypeWillChange] - public function next() - { - $this->hasFoundSheet = $this->xmlReader->readUntilNodeFound(self::XML_NODE_TABLE); - - if ($this->hasFoundSheet) { - ++$this->currentSheetIndex; - } - } - - /** - * Return the current element. - * - * @see http://php.net/manual/en/iterator.current.php - * - * @return \OpenSpout\Reader\ODS\Sheet - */ - #[\ReturnTypeWillChange] - public function current() - { - $escapedSheetName = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_TABLE_NAME); - $sheetName = $this->escaper->unescape($escapedSheetName); - - $isSheetActive = $this->isSheetActive($sheetName, $this->currentSheetIndex, $this->activeSheetName); - - $sheetStyleName = $this->xmlReader->getAttribute(self::XML_ATTRIBUTE_TABLE_STYLE_NAME); - $isSheetVisible = $this->isSheetVisible($sheetStyleName); - - return $this->entityFactory->createSheet( - $this->xmlReader, - $this->currentSheetIndex, - $sheetName, - $isSheetActive, - $isSheetVisible, - $this->optionsManager - ); - } - - /** - * Return the key of the current element. - * - * @see http://php.net/manual/en/iterator.key.php - * - * @return int - */ - #[\ReturnTypeWillChange] - public function key() - { - return $this->currentSheetIndex + 1; - } - - /** - * Cleans up what was created to iterate over the object. - */ - #[\ReturnTypeWillChange] - public function end() - { - $this->xmlReader->close(); - } - - /** - * Extracts the visibility of the sheets. - * - * @return array Associative array [STYLE_NAME] => [IS_SHEET_VISIBLE] - */ - private function readSheetsVisibility() - { - $sheetsVisibility = []; - - $this->xmlReader->readUntilNodeFound(self::XML_NODE_AUTOMATIC_STYLES); - /** @var \DOMElement $automaticStylesNode */ - $automaticStylesNode = $this->xmlReader->expand(); - - $tableStyleNodes = $automaticStylesNode->getElementsByTagNameNS(self::XML_STYLE_NAMESPACE, self::XML_NODE_STYLE_TABLE_PROPERTIES); - - /** @var \DOMElement $tableStyleNode */ - foreach ($tableStyleNodes as $tableStyleNode) { - $isSheetVisible = ('false' !== $tableStyleNode->getAttribute(self::XML_ATTRIBUTE_TABLE_DISPLAY)); - - $parentStyleNode = $tableStyleNode->parentNode; - $styleName = $parentStyleNode->getAttribute(self::XML_ATTRIBUTE_STYLE_NAME); - - $sheetsVisibility[$styleName] = $isSheetVisible; - } - - return $sheetsVisibility; - } - - /** - * Returns whether the current sheet was defined as the active one. - * - * @param string $sheetName Name of the current sheet - * @param int $sheetIndex Index of the current sheet - * @param null|string $activeSheetName Name of the sheet that was defined as active or NULL if none defined - * - * @return bool Whether the current sheet was defined as the active one - */ - private function isSheetActive($sheetName, $sheetIndex, $activeSheetName) - { - // The given sheet is active if its name matches the defined active sheet's name - // or if no information about the active sheet was found, it defaults to the first sheet. - return - (null === $activeSheetName && 0 === $sheetIndex) - || ($activeSheetName === $sheetName) - ; - } - - /** - * Returns whether the current sheet is visible. - * - * @param string $sheetStyleName Name of the sheet style - * - * @return bool Whether the current sheet is visible - */ - private function isSheetVisible($sheetStyleName) - { - return $this->sheetsVisibility[$sheetStyleName] ?? - true; - } -} diff --git a/upstream-3.x/src/Reader/ReaderAbstract.php b/upstream-3.x/src/Reader/ReaderAbstract.php deleted file mode 100644 index 2b7c8b4..0000000 --- a/upstream-3.x/src/Reader/ReaderAbstract.php +++ /dev/null @@ -1,236 +0,0 @@ -optionsManager = $optionsManager; - $this->globalFunctionsHelper = $globalFunctionsHelper; - $this->entityFactory = $entityFactory; - } - - /** - * Sets whether date/time values should be returned as PHP objects or be formatted as strings. - * - * @param bool $shouldFormatDates - * - * @return ReaderAbstract - */ - public function setShouldFormatDates($shouldFormatDates) - { - $this->optionsManager->setOption(Options::SHOULD_FORMAT_DATES, $shouldFormatDates); - - return $this; - } - - /** - * Sets whether empty rows should be returned or skipped. - * - * @param bool $shouldPreserveEmptyRows - * - * @return ReaderAbstract - */ - public function setShouldPreserveEmptyRows($shouldPreserveEmptyRows) - { - $this->optionsManager->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, $shouldPreserveEmptyRows); - - return $this; - } - - /** - * Prepares the reader to read the given file. It also makes sure - * that the file exists and is readable. - * - * @param string $filePath Path of the file to be read - * - * @throws \OpenSpout\Common\Exception\IOException If the file at the given path does not exist, is not readable or is corrupted - */ - public function open($filePath) - { - if ($this->isStreamWrapper($filePath) && (!$this->doesSupportStreamWrapper() || !$this->isSupportedStreamWrapper($filePath))) { - throw new IOException("Could not open {$filePath} for reading! Stream wrapper used is not supported for this type of file."); - } - - if (!$this->isPhpStream($filePath)) { - // we skip the checks if the provided file path points to a PHP stream - if (!$this->globalFunctionsHelper->file_exists($filePath)) { - throw new IOException("Could not open {$filePath} for reading! File does not exist."); - } - if (!$this->globalFunctionsHelper->is_readable($filePath)) { - throw new IOException("Could not open {$filePath} for reading! File is not readable."); - } - } - - try { - $fileRealPath = $this->getFileRealPath($filePath); - $this->openReader($fileRealPath); - $this->isStreamOpened = true; - } catch (\Exception $exception) { - throw new IOException("Could not open {$filePath} for reading! ({$exception->getMessage()})"); - } - } - - /** - * Returns an iterator to iterate over sheets. - * - * @throws \OpenSpout\Reader\Exception\ReaderNotOpenedException If called before opening the reader - * - * @return SheetIteratorInterface To iterate over sheets - */ - public function getSheetIterator() - { - if (!$this->isStreamOpened) { - throw new ReaderNotOpenedException('Reader should be opened first.'); - } - - return $this->getConcreteSheetIterator(); - } - - /** - * Closes the reader, preventing any additional reading. - */ - public function close() - { - if ($this->isStreamOpened) { - $this->closeReader(); - - $sheetIterator = $this->getConcreteSheetIterator(); - if (null !== $sheetIterator) { - $sheetIterator->end(); - } - - $this->isStreamOpened = false; - } - } - - /** - * Returns whether stream wrappers are supported. - * - * @return bool - */ - abstract protected function doesSupportStreamWrapper(); - - /** - * Opens the file at the given file path to make it ready to be read. - * - * @param string $filePath Path of the file to be read - */ - abstract protected function openReader($filePath); - - /** - * Returns an iterator to iterate over sheets. - * - * @return SheetIteratorInterface To iterate over sheets - */ - abstract protected function getConcreteSheetIterator(); - - /** - * Closes the reader. To be used after reading the file. - */ - abstract protected function closeReader(); - - /** - * Returns the real path of the given path. - * If the given path is a valid stream wrapper, returns the path unchanged. - * - * @param string $filePath - * - * @return string - */ - protected function getFileRealPath($filePath) - { - if ($this->isSupportedStreamWrapper($filePath)) { - return $filePath; - } - - // Need to use realpath to fix "Can't open file" on some Windows setup - return realpath($filePath); - } - - /** - * Returns the scheme of the custom stream wrapper, if the path indicates a stream wrapper is used. - * For example, php://temp => php, s3://path/to/file => s3... - * - * @param string $filePath Path of the file to be read - * - * @return null|string The stream wrapper scheme or NULL if not a stream wrapper - */ - protected function getStreamWrapperScheme($filePath) - { - $streamScheme = null; - if (preg_match('/^(\w+):\/\//', $filePath, $matches)) { - $streamScheme = $matches[1]; - } - - return $streamScheme; - } - - /** - * Checks if the given path is an unsupported stream wrapper - * (like local path, php://temp, mystream://foo/bar...). - * - * @param string $filePath Path of the file to be read - * - * @return bool Whether the given path is an unsupported stream wrapper - */ - protected function isStreamWrapper($filePath) - { - return null !== $this->getStreamWrapperScheme($filePath); - } - - /** - * Checks if the given path is an supported stream wrapper - * (like php://temp, mystream://foo/bar...). - * If the given path is a local path, returns true. - * - * @param string $filePath Path of the file to be read - * - * @return bool Whether the given path is an supported stream wrapper - */ - protected function isSupportedStreamWrapper($filePath) - { - $streamScheme = $this->getStreamWrapperScheme($filePath); - - return (null !== $streamScheme) ? - \in_array($streamScheme, $this->globalFunctionsHelper->stream_get_wrappers(), true) : - true; - } - - /** - * Checks if a path is a PHP stream (like php://output, php://memory, ...). - * - * @param string $filePath Path of the file to be read - * - * @return bool Whether the given path maps to a PHP stream - */ - protected function isPhpStream($filePath) - { - $streamScheme = $this->getStreamWrapperScheme($filePath); - - return 'php' === $streamScheme; - } -} diff --git a/upstream-3.x/src/Reader/ReaderInterface.php b/upstream-3.x/src/Reader/ReaderInterface.php deleted file mode 100644 index a1d9f74..0000000 --- a/upstream-3.x/src/Reader/ReaderInterface.php +++ /dev/null @@ -1,33 +0,0 @@ -initialUseInternalErrorsValue = libxml_use_internal_errors(true); - } - - /** - * Throws an XMLProcessingException if an error occured. - * It also always resets the "libxml_use_internal_errors" setting back to its initial value. - * - * @throws \OpenSpout\Reader\Exception\XMLProcessingException - */ - protected function resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured() - { - if ($this->hasXMLErrorOccured()) { - $this->resetXMLInternalErrorsSetting(); - - throw new XMLProcessingException($this->getLastXMLErrorMessage()); - } - - $this->resetXMLInternalErrorsSetting(); - } - - protected function resetXMLInternalErrorsSetting() - { - libxml_use_internal_errors($this->initialUseInternalErrorsValue); - } - - /** - * Returns whether the a XML error has occured since the last time errors were cleared. - * - * @return bool TRUE if an error occured, FALSE otherwise - */ - private function hasXMLErrorOccured() - { - return false !== libxml_get_last_error(); - } - - /** - * Returns the error message for the last XML error that occured. - * - * @see libxml_get_last_error - * - * @return null|string Last XML error message or null if no error - */ - private function getLastXMLErrorMessage() - { - $errorMessage = null; - $error = libxml_get_last_error(); - - if (false !== $error) { - $errorMessage = trim($error->message); - } - - return $errorMessage; - } -} diff --git a/upstream-3.x/src/Reader/Wrapper/XMLReader.php b/upstream-3.x/src/Reader/Wrapper/XMLReader.php deleted file mode 100644 index 946ca27..0000000 --- a/upstream-3.x/src/Reader/Wrapper/XMLReader.php +++ /dev/null @@ -1,192 +0,0 @@ -getRealPathURIForFileInZip($zipFilePath, $fileInsideZipPath); - - // We need to check first that the file we are trying to read really exist because: - // - PHP emits a warning when trying to open a file that does not exist. - // - HHVM does not check if file exists within zip file (@link https://github.com/facebook/hhvm/issues/5779) - if ($this->fileExistsWithinZip($realPathURI)) { - $wasOpenSuccessful = $this->open($realPathURI, null, LIBXML_NONET); - } - - return $wasOpenSuccessful; - } - - /** - * Returns the real path for the given path components. - * This is useful to avoid issues on some Windows setup. - * - * @param string $zipFilePath Path to the ZIP file - * @param string $fileInsideZipPath Relative or absolute path of the file inside the zip - * - * @return string The real path URI - */ - public function getRealPathURIForFileInZip($zipFilePath, $fileInsideZipPath) - { - // The file path should not start with a '/', otherwise it won't be found - $fileInsideZipPathWithoutLeadingSlash = ltrim($fileInsideZipPath, '/'); - - return self::ZIP_WRAPPER.realpath($zipFilePath).'#'.$fileInsideZipPathWithoutLeadingSlash; - } - - /** - * Move to next node in document. - * - * @see \XMLReader::read - * - * @throws \OpenSpout\Reader\Exception\XMLProcessingException If an error/warning occurred - * - * @return bool TRUE on success or FALSE on failure - */ - #[\ReturnTypeWillChange] - public function read() - { - $this->useXMLInternalErrors(); - - $wasReadSuccessful = parent::read(); - - $this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured(); - - return $wasReadSuccessful; - } - - /** - * Read until the element with the given name is found, or the end of the file. - * - * @param string $nodeName Name of the node to find - * - * @throws \OpenSpout\Reader\Exception\XMLProcessingException If an error/warning occurred - * - * @return bool TRUE on success or FALSE on failure - */ - public function readUntilNodeFound($nodeName) - { - do { - $wasReadSuccessful = $this->read(); - $isNotPositionedOnStartingNode = !$this->isPositionedOnStartingNode($nodeName); - } while ($wasReadSuccessful && $isNotPositionedOnStartingNode); - - return $wasReadSuccessful; - } - - /** - * Move cursor to next node skipping all subtrees. - * - * @see \XMLReader::next - * - * @param null|string $localName The name of the next node to move to - * - * @throws \OpenSpout\Reader\Exception\XMLProcessingException If an error/warning occurred - * - * @return bool TRUE on success or FALSE on failure - */ - #[\ReturnTypeWillChange] - public function next($localName = null) - { - $this->useXMLInternalErrors(); - - $wasNextSuccessful = parent::next($localName); - - $this->resetXMLInternalErrorsSettingAndThrowIfXMLErrorOccured(); - - return $wasNextSuccessful; - } - - /** - * @param string $nodeName - * - * @return bool Whether the XML Reader is currently positioned on the starting node with given name - */ - public function isPositionedOnStartingNode($nodeName) - { - return $this->isPositionedOnNode($nodeName, self::ELEMENT); - } - - /** - * @param string $nodeName - * - * @return bool Whether the XML Reader is currently positioned on the ending node with given name - */ - public function isPositionedOnEndingNode($nodeName) - { - return $this->isPositionedOnNode($nodeName, self::END_ELEMENT); - } - - /** - * @return string The name of the current node, un-prefixed - */ - public function getCurrentNodeName() - { - return $this->localName; - } - - /** - * Returns whether the file at the given location exists. - * - * @param string $zipStreamURI URI of a zip stream, e.g. "zip://file.zip#path/inside.xml" - * - * @return bool TRUE if the file exists, FALSE otherwise - */ - protected function fileExistsWithinZip($zipStreamURI) - { - $doesFileExists = false; - - $pattern = '/zip:\/\/([^#]+)#(.*)/'; - if (preg_match($pattern, $zipStreamURI, $matches)) { - $zipFilePath = $matches[1]; - $innerFilePath = $matches[2]; - - $zip = new \ZipArchive(); - if (true === $zip->open($zipFilePath)) { - $doesFileExists = (false !== $zip->locateName($innerFilePath)); - $zip->close(); - } - } - - return $doesFileExists; - } - - /** - * @param string $nodeName - * @param int $nodeType - * - * @return bool Whether the XML Reader is currently positioned on the node with given name and type - */ - private function isPositionedOnNode($nodeName, $nodeType) - { - /** - * In some cases, the node has a prefix (for instance, "" can also be ""). - * So if the given node name does not have a prefix, we need to look at the unprefixed name ("localName"). - * - * @see https://github.com/box/spout/issues/233 - */ - $hasPrefix = (false !== strpos($nodeName, ':')); - $currentNodeName = ($hasPrefix) ? $this->name : $this->localName; - - return $this->nodeType === $nodeType && $currentNodeName === $nodeName; - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Creator/HelperFactory.php b/upstream-3.x/src/Reader/XLSX/Creator/HelperFactory.php deleted file mode 100644 index 3528b23..0000000 --- a/upstream-3.x/src/Reader/XLSX/Creator/HelperFactory.php +++ /dev/null @@ -1,38 +0,0 @@ -createStringsEscaper(); - - return new CellValueFormatter($sharedStringsManager, $styleManager, $shouldFormatDates, $shouldUse1904Dates, $escaper); - } - - /** - * @return Escaper\XLSX - */ - public function createStringsEscaper() - { - // @noinspection PhpUnnecessaryFullyQualifiedNameInspection - return new Escaper\XLSX(); - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Creator/InternalEntityFactory.php b/upstream-3.x/src/Reader/XLSX/Creator/InternalEntityFactory.php deleted file mode 100644 index 33b18df..0000000 --- a/upstream-3.x/src/Reader/XLSX/Creator/InternalEntityFactory.php +++ /dev/null @@ -1,163 +0,0 @@ -managerFactory = $managerFactory; - $this->helperFactory = $helperFactory; - } - - /** - * @param string $filePath Path of the file to be read - * @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager - * @param SharedStringsManager $sharedStringsManager Manages shared strings - * - * @return SheetIterator - */ - public function createSheetIterator($filePath, $optionsManager, $sharedStringsManager) - { - $sheetManager = $this->managerFactory->createSheetManager( - $filePath, - $optionsManager, - $sharedStringsManager, - $this - ); - - return new SheetIterator($sheetManager); - } - - /** - * @param string $filePath Path of the XLSX file being read - * @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml - * @param int $sheetIndex Index of the sheet, based on order in the workbook (zero-based) - * @param string $sheetName Name of the sheet - * @param bool $isSheetActive Whether the sheet was defined as active - * @param bool $isSheetVisible Whether the sheet is visible - * @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager - * @param SharedStringsManager $sharedStringsManager Manages shared strings - * - * @return Sheet - */ - public function createSheet( - $filePath, - $sheetDataXMLFilePath, - $sheetIndex, - $sheetName, - $isSheetActive, - $isSheetVisible, - $optionsManager, - $sharedStringsManager - ) { - $rowIterator = $this->createRowIterator($filePath, $sheetDataXMLFilePath, $optionsManager, $sharedStringsManager); - - return new Sheet($rowIterator, $sheetIndex, $sheetName, $isSheetActive, $isSheetVisible); - } - - /** - * @param Cell[] $cells - * - * @return Row - */ - public function createRow(array $cells = []) - { - return new Row($cells, null); - } - - /** - * @param mixed $cellValue - * - * @return Cell - */ - public function createCell($cellValue) - { - return new Cell($cellValue); - } - - /** - * @return \ZipArchive - */ - public function createZipArchive() - { - return new \ZipArchive(); - } - - /** - * @return XMLReader - */ - public function createXMLReader() - { - return new XMLReader(); - } - - /** - * @param XMLReader $xmlReader - * - * @return XMLProcessor - */ - public function createXMLProcessor($xmlReader) - { - return new XMLProcessor($xmlReader); - } - - /** - * @param string $filePath Path of the XLSX file being read - * @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml - * @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager - * @param SharedStringsManager $sharedStringsManager Manages shared strings - * - * @return RowIterator - */ - private function createRowIterator($filePath, $sheetDataXMLFilePath, $optionsManager, $sharedStringsManager) - { - $xmlReader = $this->createXMLReader(); - $xmlProcessor = $this->createXMLProcessor($xmlReader); - - $styleManager = $this->managerFactory->createStyleManager($filePath, $this); - $rowManager = $this->managerFactory->createRowManager($this); - $shouldFormatDates = $optionsManager->getOption(Options::SHOULD_FORMAT_DATES); - $shouldUse1904Dates = $optionsManager->getOption(Options::SHOULD_USE_1904_DATES); - - $cellValueFormatter = $this->helperFactory->createCellValueFormatter( - $sharedStringsManager, - $styleManager, - $shouldFormatDates, - $shouldUse1904Dates - ); - - $shouldPreserveEmptyRows = $optionsManager->getOption(Options::SHOULD_PRESERVE_EMPTY_ROWS); - - return new RowIterator( - $filePath, - $sheetDataXMLFilePath, - $shouldPreserveEmptyRows, - $xmlReader, - $xmlProcessor, - $cellValueFormatter, - $rowManager, - $this - ); - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Creator/ManagerFactory.php b/upstream-3.x/src/Reader/XLSX/Creator/ManagerFactory.php deleted file mode 100644 index 10a8833..0000000 --- a/upstream-3.x/src/Reader/XLSX/Creator/ManagerFactory.php +++ /dev/null @@ -1,109 +0,0 @@ -helperFactory = $helperFactory; - $this->cachingStrategyFactory = $cachingStrategyFactory; - } - - /** - * @param string $filePath Path of the XLSX file being read - * @param string $tempFolder Temporary folder where the temporary files to store shared strings will be stored - * @param InternalEntityFactory $entityFactory Factory to create entities - * - * @return SharedStringsManager - */ - public function createSharedStringsManager($filePath, $tempFolder, $entityFactory) - { - $workbookRelationshipsManager = $this->createWorkbookRelationshipsManager($filePath, $entityFactory); - - return new SharedStringsManager( - $filePath, - $tempFolder, - $workbookRelationshipsManager, - $entityFactory, - $this->helperFactory, - $this->cachingStrategyFactory - ); - } - - /** - * @param string $filePath Path of the XLSX file being read - * @param \OpenSpout\Common\Manager\OptionsManagerInterface $optionsManager Reader's options manager - * @param \OpenSpout\Reader\XLSX\Manager\SharedStringsManager $sharedStringsManager Manages shared strings - * @param InternalEntityFactory $entityFactory Factory to create entities - * - * @return SheetManager - */ - public function createSheetManager($filePath, $optionsManager, $sharedStringsManager, $entityFactory) - { - $escaper = $this->helperFactory->createStringsEscaper(); - - return new SheetManager($filePath, $optionsManager, $sharedStringsManager, $escaper, $entityFactory); - } - - /** - * @param string $filePath Path of the XLSX file being read - * @param InternalEntityFactory $entityFactory Factory to create entities - * - * @return StyleManager - */ - public function createStyleManager($filePath, $entityFactory) - { - $workbookRelationshipsManager = $this->createWorkbookRelationshipsManager($filePath, $entityFactory); - - return new StyleManager($filePath, $workbookRelationshipsManager, $entityFactory); - } - - /** - * @param InternalEntityFactory $entityFactory Factory to create entities - * - * @return RowManager - */ - public function createRowManager($entityFactory) - { - return new RowManager($entityFactory); - } - - /** - * @param string $filePath Path of the XLSX file being read - * @param InternalEntityFactory $entityFactory Factory to create entities - * - * @return WorkbookRelationshipsManager - */ - private function createWorkbookRelationshipsManager($filePath, $entityFactory) - { - if (!isset($this->cachedWorkbookRelationshipsManager)) { - $this->cachedWorkbookRelationshipsManager = new WorkbookRelationshipsManager($filePath, $entityFactory); - } - - return $this->cachedWorkbookRelationshipsManager; - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Helper/CellHelper.php b/upstream-3.x/src/Reader/XLSX/Helper/CellHelper.php deleted file mode 100644 index 827d728..0000000 --- a/upstream-3.x/src/Reader/XLSX/Helper/CellHelper.php +++ /dev/null @@ -1,87 +0,0 @@ - 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, - 'H' => 7, 'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, - 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, - 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, - ]; - - /** - * Returns the base 10 column index associated to the cell index (base 26). - * Excel uses A to Z letters for column indexing, where A is the 1st column, - * Z is the 26th and AA is the 27th. - * The mapping is zero based, so that A1 maps to 0, B2 maps to 1, Z13 to 25 and AA4 to 26. - * - * @param string $cellIndex The Excel cell index ('A1', 'BC13', ...) - * - * @throws \OpenSpout\Common\Exception\InvalidArgumentException When the given cell index is invalid - * - * @return int - */ - public static function getColumnIndexFromCellIndex($cellIndex) - { - if (!self::isValidCellIndex($cellIndex)) { - throw new InvalidArgumentException('Cannot get column index from an invalid cell index.'); - } - - $columnIndex = 0; - - // Remove row information - $columnLetters = preg_replace('/\d/', '', $cellIndex); - - // strlen() is super slow too... Using isset() is way faster and not too unreadable, - // since we checked before that there are between 1 and 3 letters. - $columnLength = isset($columnLetters[1]) ? (isset($columnLetters[2]) ? 3 : 2) : 1; - - // Looping over the different letters of the column is slower than this method. - // Also, not using the pow() function because it's slooooow... - switch ($columnLength) { - case 1: - $columnIndex = (self::$columnLetterToIndexMapping[$columnLetters]); - - break; - - case 2: - $firstLetterIndex = (self::$columnLetterToIndexMapping[$columnLetters[0]] + 1) * 26; - $secondLetterIndex = self::$columnLetterToIndexMapping[$columnLetters[1]]; - $columnIndex = $firstLetterIndex + $secondLetterIndex; - - break; - - case 3: - $firstLetterIndex = (self::$columnLetterToIndexMapping[$columnLetters[0]] + 1) * 676; - $secondLetterIndex = (self::$columnLetterToIndexMapping[$columnLetters[1]] + 1) * 26; - $thirdLetterIndex = self::$columnLetterToIndexMapping[$columnLetters[2]]; - $columnIndex = $firstLetterIndex + $secondLetterIndex + $thirdLetterIndex; - - break; - } - - return $columnIndex; - } - - /** - * Returns whether a cell index is valid, in an Excel world. - * To be valid, the cell index should start with capital letters and be followed by numbers. - * There can only be 3 letters, as there can only be 16,384 rows, which is equivalent to 'XFE'. - * - * @param string $cellIndex The Excel cell index ('A1', 'BC13', ...) - * - * @return bool - */ - protected static function isValidCellIndex($cellIndex) - { - return 1 === preg_match('/^[A-Z]{1,3}\d+$/', $cellIndex); - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Helper/CellValueFormatter.php b/upstream-3.x/src/Reader/XLSX/Helper/CellValueFormatter.php deleted file mode 100644 index 08e5282..0000000 --- a/upstream-3.x/src/Reader/XLSX/Helper/CellValueFormatter.php +++ /dev/null @@ -1,319 +0,0 @@ -sharedStringsManager = $sharedStringsManager; - $this->styleManager = $styleManager; - $this->shouldFormatDates = $shouldFormatDates; - $this->shouldUse1904Dates = $shouldUse1904Dates; - $this->escaper = $escaper; - } - - /** - * Returns the (unescaped) correctly marshalled, cell value associated to the given XML node. - * - * @param \DOMElement $node - * - * @throws InvalidValueException If the value is not valid - * - * @return bool|\DateTime|float|int|string The value associated with the cell - */ - public function extractAndFormatNodeValue($node) - { - // Default cell type is "n" - $cellType = $node->getAttribute(self::XML_ATTRIBUTE_TYPE) ?: self::CELL_TYPE_NUMERIC; - $cellStyleId = (int) $node->getAttribute(self::XML_ATTRIBUTE_STYLE_ID); - $vNodeValue = $this->getVNodeValue($node); - - if (('' === $vNodeValue) && (self::CELL_TYPE_INLINE_STRING !== $cellType)) { - return $vNodeValue; - } - - switch ($cellType) { - case self::CELL_TYPE_INLINE_STRING: - return $this->formatInlineStringCellValue($node); - - case self::CELL_TYPE_SHARED_STRING: - return $this->formatSharedStringCellValue($vNodeValue); - - case self::CELL_TYPE_STR: - return $this->formatStrCellValue($vNodeValue); - - case self::CELL_TYPE_BOOLEAN: - return $this->formatBooleanCellValue($vNodeValue); - - case self::CELL_TYPE_NUMERIC: - return $this->formatNumericCellValue($vNodeValue, $cellStyleId); - - case self::CELL_TYPE_DATE: - return $this->formatDateCellValue($vNodeValue); - - default: - throw new InvalidValueException($vNodeValue); - } - } - - /** - * Returns the cell's string value from a node's nested value node. - * - * @param \DOMElement $node - * - * @return string The value associated with the cell - */ - protected function getVNodeValue($node) - { - // for cell types having a "v" tag containing the value. - // if not, the returned value should be empty string. - $vNode = $node->getElementsByTagName(self::XML_NODE_VALUE)->item(0); - - return (null !== $vNode) ? $vNode->nodeValue : ''; - } - - /** - * Returns the cell String value where string is inline. - * - * @param \DOMElement $node - * - * @return string The value associated with the cell - */ - protected function formatInlineStringCellValue($node) - { - // inline strings are formatted this way (they can contain any number of nodes): - // [INLINE_STRING][INLINE_STRING_2] - $tNodes = $node->getElementsByTagName(self::XML_NODE_INLINE_STRING_VALUE); - - $cellValue = ''; - for ($i = 0; $i < $tNodes->count(); ++$i) { - $tNode = $tNodes->item($i); - $cellValue .= $this->escaper->unescape($tNode->nodeValue); - } - - return $cellValue; - } - - /** - * Returns the cell String value from shared-strings file using nodeValue index. - * - * @param string $nodeValue - * - * @return string The value associated with the cell - */ - protected function formatSharedStringCellValue($nodeValue) - { - // shared strings are formatted this way: - // [SHARED_STRING_INDEX] - $sharedStringIndex = (int) $nodeValue; - $escapedCellValue = $this->sharedStringsManager->getStringAtIndex($sharedStringIndex); - - return $this->escaper->unescape($escapedCellValue); - } - - /** - * Returns the cell String value, where string is stored in value node. - * - * @param string $nodeValue - * - * @return string The value associated with the cell - */ - protected function formatStrCellValue($nodeValue) - { - $escapedCellValue = trim($nodeValue); - - return $this->escaper->unescape($escapedCellValue); - } - - /** - * Returns the cell Numeric value from string of nodeValue. - * The value can also represent a timestamp and a DateTime will be returned. - * - * @param string $nodeValue - * @param int $cellStyleId 0 being the default style - * - * @return \DateTime|float|int The value associated with the cell - */ - protected function formatNumericCellValue($nodeValue, $cellStyleId) - { - // Numeric values can represent numbers as well as timestamps. - // We need to look at the style of the cell to determine whether it is one or the other. - $shouldFormatAsDate = $this->styleManager->shouldFormatNumericValueAsDate($cellStyleId); - - if ($shouldFormatAsDate) { - $cellValue = $this->formatExcelTimestampValue((float) $nodeValue, $cellStyleId); - } else { - $nodeIntValue = (int) $nodeValue; - $nodeFloatValue = (float) $nodeValue; - $cellValue = ((float) $nodeIntValue === $nodeFloatValue) ? $nodeIntValue : $nodeFloatValue; - } - - return $cellValue; - } - - /** - * Returns a cell's PHP Date value, associated to the given timestamp. - * NOTE: The timestamp is a float representing the number of days since the base Excel date: - * Dec 30th 1899, 1900 or Jan 1st, 1904, depending on the Workbook setting. - * NOTE: The timestamp can also represent a time, if it is a value between 0 and 1. - * - * @see ECMA-376 Part 1 - §18.17.4 - * - * @param float $nodeValue - * @param int $cellStyleId 0 being the default style - * - * @throws InvalidValueException If the value is not a valid timestamp - * - * @return \DateTime The value associated with the cell - */ - protected function formatExcelTimestampValue($nodeValue, $cellStyleId) - { - if ($this->isValidTimestampValue($nodeValue)) { - $cellValue = $this->formatExcelTimestampValueAsDateTimeValue($nodeValue, $cellStyleId); - } else { - throw new InvalidValueException($nodeValue); - } - - return $cellValue; - } - - /** - * Returns whether the given timestamp is supported by SpreadsheetML. - * - * @see ECMA-376 Part 1 - §18.17.4 - this specifies the timestamp boundaries. - * - * @param float $timestampValue - * - * @return bool - */ - protected function isValidTimestampValue($timestampValue) - { - // @NOTE: some versions of Excel don't support negative dates (e.g. Excel for Mac 2011) - return - $this->shouldUse1904Dates && $timestampValue >= -695055 && $timestampValue <= 2957003.9999884 - || !$this->shouldUse1904Dates && $timestampValue >= -693593 && $timestampValue <= 2958465.9999884 - ; - } - - /** - * Returns a cell's PHP DateTime value, associated to the given timestamp. - * Only the time value matters. The date part is set to the base Excel date: - * Dec 30th 1899, 1900 or Jan 1st, 1904, depending on the Workbook setting. - * - * @param float $nodeValue - * @param int $cellStyleId 0 being the default style - * - * @return \DateTime|string The value associated with the cell - */ - protected function formatExcelTimestampValueAsDateTimeValue($nodeValue, $cellStyleId) - { - $baseDate = $this->shouldUse1904Dates ? '1904-01-01' : '1899-12-30'; - - $daysSinceBaseDate = (int) $nodeValue; - $timeRemainder = fmod($nodeValue, 1); - $secondsRemainder = round($timeRemainder * self::NUM_SECONDS_IN_ONE_DAY, 0); - - $dateObj = \DateTime::createFromFormat('|Y-m-d', $baseDate); - $dateObj->modify('+'.$daysSinceBaseDate.'days'); - $dateObj->modify('+'.$secondsRemainder.'seconds'); - - if ($this->shouldFormatDates) { - //$styleNumberFormatCode = $this->styleManager->getNumberFormatCode($cellStyleId); - //$phpDateFormat = DateFormatHelper::toPHPDateFormat($styleNumberFormatCode); - // Toujours utiliser le format français complet - $phpDateFormat = "d/m/Y H:i:s"; - $cellValue = $dateObj->format($phpDateFormat); - // Enlever la composante heure si elle n'existe pas - $cellValue = preg_replace('/ 00:00:00$/', "", $cellValue); - } else { - $cellValue = $dateObj; - } - - return $cellValue; - } - - /** - * Returns the cell Boolean value from a specific node's Value. - * - * @param string $nodeValue - * - * @return bool The value associated with the cell - */ - protected function formatBooleanCellValue($nodeValue) - { - return (bool) $nodeValue; - } - - /** - * Returns a cell's PHP Date value, associated to the given stored nodeValue. - * - * @see ECMA-376 Part 1 - §18.17.4 - * - * @param string $nodeValue ISO 8601 Date string - * - * @throws InvalidValueException If the value is not a valid date - * - * @return \DateTime|string The value associated with the cell - */ - protected function formatDateCellValue($nodeValue) - { - // Mitigate thrown Exception on invalid date-time format (http://php.net/manual/en/datetime.construct.php) - try { - $cellValue = ($this->shouldFormatDates) ? $nodeValue : new \DateTime($nodeValue); - } catch (\Exception $e) { - throw new InvalidValueException($nodeValue); - } - - return $cellValue; - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Helper/DateFormatHelper.php b/upstream-3.x/src/Reader/XLSX/Helper/DateFormatHelper.php deleted file mode 100644 index 78fc4f4..0000000 --- a/upstream-3.x/src/Reader/XLSX/Helper/DateFormatHelper.php +++ /dev/null @@ -1,122 +0,0 @@ - [ - // Time - 'am/pm' => 'A', // Uppercase Ante meridiem and Post meridiem - ':mm' => ':i', // Minutes with leading zeros - if preceded by a ":" (otherwise month) - 'mm:' => 'i:', // Minutes with leading zeros - if followed by a ":" (otherwise month) - 'ss' => 's', // Seconds, with leading zeros - '.s' => '', // Ignore (fractional seconds format does not exist in PHP) - - // Date - 'e' => 'Y', // Full numeric representation of a year, 4 digits - 'yyyy' => 'Y', // Full numeric representation of a year, 4 digits - 'yy' => 'y', // Two digit representation of a year - 'mmmmm' => 'M', // Short textual representation of a month, three letters ("mmmmm" should only contain the 1st letter...) - 'mmmm' => 'F', // Full textual representation of a month - 'mmm' => 'M', // Short textual representation of a month, three letters - 'mm' => 'm', // Numeric representation of a month, with leading zeros - 'm' => 'n', // Numeric representation of a month, without leading zeros - 'dddd' => 'l', // Full textual representation of the day of the week - 'ddd' => 'D', // Textual representation of a day, three letters - 'dd' => 'd', // Day of the month, 2 digits with leading zeros - 'd' => 'j', // Day of the month without leading zeros - ], - self::KEY_HOUR_12 => [ - 'hh' => 'h', // 12-hour format of an hour without leading zeros - 'h' => 'g', // 12-hour format of an hour without leading zeros - ], - self::KEY_HOUR_24 => [ - 'hh' => 'H', // 24-hour hours with leading zero - 'h' => 'G', // 24-hour format of an hour without leading zeros - ], - ]; - - /** - * Converts the given Excel date format to a format understandable by the PHP date function. - * - * @param string $excelDateFormat Excel date format - * - * @return string PHP date format (as defined here: http://php.net/manual/en/function.date.php) - */ - public static function toPHPDateFormat($excelDateFormat) - { - // Remove brackets potentially present at the beginning of the format string - // and text portion of the format at the end of it (starting with ";") - // See §18.8.31 of ECMA-376 for more detail. - $dateFormat = preg_replace('/^(?:\[\$[^\]]+?\])?([^;]*).*/', '$1', $excelDateFormat); - - // Double quotes are used to escape characters that must not be interpreted. - // For instance, ["Day " dd] should result in "Day 13" and we should not try to interpret "D", "a", "y" - // By exploding the format string using double quote as a delimiter, we can get all parts - // that must be transformed (even indexes) and all parts that must not be (odd indexes). - $dateFormatParts = explode('"', $dateFormat); - - foreach ($dateFormatParts as $partIndex => $dateFormatPart) { - // do not look at odd indexes - if (1 === $partIndex % 2) { - continue; - } - - // Make sure all characters are lowercase, as the mapping table is using lowercase characters - $transformedPart = strtolower($dateFormatPart); - - // Remove escapes related to non-format characters - $transformedPart = str_replace('\\', '', $transformedPart); - - // Apply general transformation first... - $transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_GENERAL]); - - // ... then apply hour transformation, for 12-hour or 24-hour format - if (self::has12HourFormatMarker($dateFormatPart)) { - $transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_12]); - } else { - $transformedPart = strtr($transformedPart, self::$excelDateFormatToPHPDateFormatMapping[self::KEY_HOUR_24]); - } - - // overwrite the parts array with the new transformed part - $dateFormatParts[$partIndex] = $transformedPart; - } - - // Merge all transformed parts back together - $phpDateFormat = implode('"', $dateFormatParts); - - // Finally, to have the date format compatible with the DateTime::format() function, we need to escape - // all characters that are inside double quotes (and double quotes must be removed). - // For instance, ["Day " dd] should become [\D\a\y\ dd] - return preg_replace_callback('/"(.+?)"/', function ($matches) { - $stringToEscape = $matches[1]; - $letters = preg_split('//u', $stringToEscape, -1, PREG_SPLIT_NO_EMPTY); - - return '\\'.implode('\\', $letters); - }, $phpDateFormat); - } - - /** - * @param string $excelDateFormat Date format as defined by Excel - * - * @return bool Whether the given date format has the 12-hour format marker - */ - private static function has12HourFormatMarker($excelDateFormat) - { - return false !== stripos($excelDateFormat, 'am/pm'); - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Manager/OptionsManager.php b/upstream-3.x/src/Reader/XLSX/Manager/OptionsManager.php deleted file mode 100644 index 5749f65..0000000 --- a/upstream-3.x/src/Reader/XLSX/Manager/OptionsManager.php +++ /dev/null @@ -1,36 +0,0 @@ -setOption(Options::TEMP_FOLDER, sys_get_temp_dir()); - $this->setOption(Options::SHOULD_FORMAT_DATES, true); - $this->setOption(Options::SHOULD_PRESERVE_EMPTY_ROWS, false); - $this->setOption(Options::SHOULD_USE_1904_DATES, false); - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php b/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php deleted file mode 100644 index c2f8c9f..0000000 --- a/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyFactory.php +++ /dev/null @@ -1,141 +0,0 @@ - 20 * 600 ≈ 12KB - */ - public const AMOUNT_MEMORY_NEEDED_PER_STRING_IN_KB = 12; - - /** - * To avoid running out of memory when extracting a huge number of shared strings, they can be saved to temporary files - * instead of in memory. Then, when accessing a string, the corresponding file contents will be loaded in memory - * and the string will be quickly retrieved. - * The performance bottleneck is not when creating these temporary files, but rather when loading their content. - * Because the contents of the last loaded file stays in memory until another file needs to be loaded, it works - * best when the indexes of the shared strings are sorted in the sheet data. - * 10,000 was chosen because it creates small files that are fast to be loaded in memory. - */ - public const MAX_NUM_STRINGS_PER_TEMP_FILE = 10000; - - /** - * Returns the best caching strategy, given the number of unique shared strings - * and the amount of memory available. - * - * @param null|int $sharedStringsUniqueCount Number of unique shared strings (NULL if unknown) - * @param string $tempFolder Temporary folder where the temporary files to store shared strings will be stored - * @param HelperFactory $helperFactory Factory to create helpers - * - * @return CachingStrategyInterface The best caching strategy - */ - public function createBestCachingStrategy($sharedStringsUniqueCount, $tempFolder, $helperFactory) - { - if ($this->isInMemoryStrategyUsageSafe($sharedStringsUniqueCount)) { - return new InMemoryStrategy($sharedStringsUniqueCount); - } - - return new FileBasedStrategy($tempFolder, self::MAX_NUM_STRINGS_PER_TEMP_FILE, $helperFactory); - } - - /** - * Returns whether it is safe to use in-memory caching, given the number of unique shared strings - * and the amount of memory available. - * - * @param null|int $sharedStringsUniqueCount Number of unique shared strings (NULL if unknown) - * - * @return bool - */ - protected function isInMemoryStrategyUsageSafe($sharedStringsUniqueCount) - { - // if the number of shared strings in unknown, do not use "in memory" strategy - if (null === $sharedStringsUniqueCount) { - return false; - } - - $memoryAvailable = $this->getMemoryLimitInKB(); - - if (-1 === (int) $memoryAvailable) { - // if cannot get memory limit or if memory limit set as unlimited, don't trust and play safe - $isInMemoryStrategyUsageSafe = ($sharedStringsUniqueCount < self::MAX_NUM_STRINGS_PER_TEMP_FILE); - } else { - $memoryNeeded = $sharedStringsUniqueCount * self::AMOUNT_MEMORY_NEEDED_PER_STRING_IN_KB; - $isInMemoryStrategyUsageSafe = ($memoryAvailable > $memoryNeeded); - } - - return $isInMemoryStrategyUsageSafe; - } - - /** - * Returns the PHP "memory_limit" in Kilobytes. - * - * @return float - */ - protected function getMemoryLimitInKB() - { - $memoryLimitFormatted = $this->getMemoryLimitFromIni(); - $memoryLimitFormatted = strtolower(trim($memoryLimitFormatted)); - - // No memory limit - if ('-1' === $memoryLimitFormatted) { - return -1; - } - - if (preg_match('/(\d+)([bkmgt])b?/', $memoryLimitFormatted, $matches)) { - $amount = (int) ($matches[1]); - $unit = $matches[2]; - - switch ($unit) { - case 'b': return $amount / 1024; - - case 'k': return $amount; - - case 'm': return $amount * 1024; - - case 'g': return $amount * 1024 * 1024; - - case 't': return $amount * 1024 * 1024 * 1024; - } - } - - return -1; - } - - /** - * Returns the formatted "memory_limit" value. - * - * @return string - */ - protected function getMemoryLimitFromIni() - { - return ini_get('memory_limit'); - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php b/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php deleted file mode 100644 index b314827..0000000 --- a/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/CachingStrategyInterface.php +++ /dev/null @@ -1,39 +0,0 @@ -fileSystemHelper = $helperFactory->createFileSystemHelper($tempFolder); - $this->tempFolder = $this->fileSystemHelper->createFolder($tempFolder, uniqid('sharedstrings')); - - $this->maxNumStringsPerTempFile = $maxNumStringsPerTempFile; - - $this->globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper(); - $this->tempFilePointer = null; - } - - /** - * Adds the given string to the cache. - * - * @param string $sharedString The string to be added to the cache - * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file - */ - public function addStringForIndex($sharedString, $sharedStringIndex) - { - $tempFilePath = $this->getSharedStringTempFilePath($sharedStringIndex); - - if (!$this->globalFunctionsHelper->file_exists($tempFilePath)) { - if ($this->tempFilePointer) { - $this->globalFunctionsHelper->fclose($this->tempFilePointer); - } - $this->tempFilePointer = $this->globalFunctionsHelper->fopen($tempFilePath, 'w'); - } - - // The shared string retrieval logic expects each cell data to be on one line only - // Encoding the line feed character allows to preserve this assumption - $lineFeedEncodedSharedString = $this->escapeLineFeed($sharedString); - - $this->globalFunctionsHelper->fwrite($this->tempFilePointer, $lineFeedEncodedSharedString.PHP_EOL); - } - - /** - * Closes the cache after the last shared string was added. - * This prevents any additional string from being added to the cache. - */ - public function closeCache() - { - // close pointer to the last temp file that was written - if ($this->tempFilePointer) { - $this->globalFunctionsHelper->fclose($this->tempFilePointer); - } - } - - /** - * Returns the string located at the given index from the cache. - * - * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file - * - * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If no shared string found for the given index - * - * @return string The shared string at the given index - */ - public function getStringAtIndex($sharedStringIndex) - { - $tempFilePath = $this->getSharedStringTempFilePath($sharedStringIndex); - $indexInFile = $sharedStringIndex % $this->maxNumStringsPerTempFile; - - if (!$this->globalFunctionsHelper->file_exists($tempFilePath)) { - throw new SharedStringNotFoundException("Shared string temp file not found: {$tempFilePath} ; for index: {$sharedStringIndex}"); - } - - if ($this->inMemoryTempFilePath !== $tempFilePath) { - $this->inMemoryTempFileContents = explode(PHP_EOL, $this->globalFunctionsHelper->file_get_contents($tempFilePath)); - $this->inMemoryTempFilePath = $tempFilePath; - } - - $sharedString = null; - - // Using isset here because it is way faster than array_key_exists... - if (isset($this->inMemoryTempFileContents[$indexInFile])) { - $escapedSharedString = $this->inMemoryTempFileContents[$indexInFile]; - $sharedString = $this->unescapeLineFeed($escapedSharedString); - } - - if (null === $sharedString) { - throw new SharedStringNotFoundException("Shared string not found for index: {$sharedStringIndex}"); - } - - return rtrim($sharedString, PHP_EOL); - } - - /** - * Destroys the cache, freeing memory and removing any created artifacts. - */ - public function clearCache() - { - if ($this->tempFolder) { - $this->fileSystemHelper->deleteFolderRecursively($this->tempFolder); - } - } - - /** - * Returns the path for the temp file that should contain the string for the given index. - * - * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file - * - * @return string The temp file path for the given index - */ - protected function getSharedStringTempFilePath($sharedStringIndex) - { - $numTempFile = (int) ($sharedStringIndex / $this->maxNumStringsPerTempFile); - - return $this->tempFolder.'/sharedstrings'.$numTempFile; - } - - /** - * Escapes the line feed characters (\n). - * - * @param string $unescapedString - * - * @return string - */ - private function escapeLineFeed($unescapedString) - { - return str_replace("\n", self::ESCAPED_LINE_FEED_CHARACTER, $unescapedString); - } - - /** - * Unescapes the line feed characters (\n). - * - * @param string $escapedString - * - * @return string - */ - private function unescapeLineFeed($escapedString) - { - return str_replace(self::ESCAPED_LINE_FEED_CHARACTER, "\n", $escapedString); - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php b/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php deleted file mode 100644 index 04312ad..0000000 --- a/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsCaching/InMemoryStrategy.php +++ /dev/null @@ -1,76 +0,0 @@ -inMemoryCache = new \SplFixedArray($sharedStringsUniqueCount); - $this->isCacheClosed = false; - } - - /** - * Adds the given string to the cache. - * - * @param string $sharedString The string to be added to the cache - * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file - */ - public function addStringForIndex($sharedString, $sharedStringIndex) - { - if (!$this->isCacheClosed) { - $this->inMemoryCache->offsetSet($sharedStringIndex, $sharedString); - } - } - - /** - * Closes the cache after the last shared string was added. - * This prevents any additional string from being added to the cache. - */ - public function closeCache() - { - $this->isCacheClosed = true; - } - - /** - * Returns the string located at the given index from the cache. - * - * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file - * - * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If no shared string found for the given index - * - * @return string The shared string at the given index - */ - public function getStringAtIndex($sharedStringIndex) - { - try { - return $this->inMemoryCache->offsetGet($sharedStringIndex); - } catch (\RuntimeException $e) { - throw new SharedStringNotFoundException("Shared string not found for index: {$sharedStringIndex}"); - } - } - - /** - * Destroys the cache, freeing memory and removing any created artifacts. - */ - public function clearCache() - { - $this->inMemoryCache = new \SplFixedArray(0); - $this->isCacheClosed = false; - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsManager.php b/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsManager.php deleted file mode 100644 index 120ce68..0000000 --- a/upstream-3.x/src/Reader/XLSX/Manager/SharedStringsManager.php +++ /dev/null @@ -1,252 +0,0 @@ -filePath = $filePath; - $this->tempFolder = $tempFolder; - $this->workbookRelationshipsManager = $workbookRelationshipsManager; - $this->entityFactory = $entityFactory; - $this->helperFactory = $helperFactory; - $this->cachingStrategyFactory = $cachingStrategyFactory; - } - - /** - * Returns whether the XLSX file contains a shared strings XML file. - * - * @return bool - */ - public function hasSharedStrings() - { - return $this->workbookRelationshipsManager->hasSharedStringsXMLFile(); - } - - /** - * Builds an in-memory array containing all the shared strings of the sheet. - * All the strings are stored in a XML file, located at 'xl/sharedStrings.xml'. - * It is then accessed by the sheet data, via the string index in the built table. - * - * More documentation available here: http://msdn.microsoft.com/en-us/library/office/gg278314.aspx - * - * The XML file can be really big with sheets containing a lot of data. That is why - * we need to use a XML reader that provides streaming like the XMLReader library. - * - * @throws \OpenSpout\Common\Exception\IOException If shared strings XML file can't be read - */ - public function extractSharedStrings() - { - $sharedStringsXMLFilePath = $this->workbookRelationshipsManager->getSharedStringsXMLFilePath(); - $xmlReader = $this->entityFactory->createXMLReader(); - $sharedStringIndex = 0; - - if (false === $xmlReader->openFileInZip($this->filePath, $sharedStringsXMLFilePath)) { - throw new IOException('Could not open "'.$sharedStringsXMLFilePath.'".'); - } - - try { - $sharedStringsUniqueCount = $this->getSharedStringsUniqueCount($xmlReader); - $this->cachingStrategy = $this->getBestSharedStringsCachingStrategy($sharedStringsUniqueCount); - - $xmlReader->readUntilNodeFound(self::XML_NODE_SI); - - while (self::XML_NODE_SI === $xmlReader->getCurrentNodeName()) { - $this->processSharedStringsItem($xmlReader, $sharedStringIndex); - ++$sharedStringIndex; - - // jump to the next '' tag - $xmlReader->next(self::XML_NODE_SI); - } - - $this->cachingStrategy->closeCache(); - } catch (XMLProcessingException $exception) { - throw new IOException("The sharedStrings.xml file is invalid and cannot be read. [{$exception->getMessage()}]"); - } - - $xmlReader->close(); - } - - /** - * Returns the shared string at the given index, using the previously chosen caching strategy. - * - * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file - * - * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If no shared string found for the given index - * - * @return string The shared string at the given index - */ - public function getStringAtIndex($sharedStringIndex) - { - return $this->cachingStrategy->getStringAtIndex($sharedStringIndex); - } - - /** - * Destroys the cache, freeing memory and removing any created artifacts. - */ - public function cleanup() - { - if (null !== $this->cachingStrategy) { - $this->cachingStrategy->clearCache(); - } - } - - /** - * Returns the shared strings unique count, as specified in tag. - * - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader instance - * - * @throws \OpenSpout\Common\Exception\IOException If sharedStrings.xml is invalid and can't be read - * - * @return null|int Number of unique shared strings in the sharedStrings.xml file - */ - protected function getSharedStringsUniqueCount($xmlReader) - { - $xmlReader->next(self::XML_NODE_SST); - - // Iterate over the "sst" elements to get the actual "sst ELEMENT" (skips any DOCTYPE) - while (self::XML_NODE_SST === $xmlReader->getCurrentNodeName() && XMLReader::ELEMENT !== $xmlReader->nodeType) { - $xmlReader->read(); - } - - $uniqueCount = $xmlReader->getAttribute(self::XML_ATTRIBUTE_UNIQUE_COUNT); - - // some software do not add the "uniqueCount" attribute but only use the "count" one - // @see https://github.com/box/spout/issues/254 - if (null === $uniqueCount) { - $uniqueCount = $xmlReader->getAttribute(self::XML_ATTRIBUTE_COUNT); - } - - return (null !== $uniqueCount) ? (int) $uniqueCount : null; - } - - /** - * Returns the best shared strings caching strategy. - * - * @param null|int $sharedStringsUniqueCount Number of unique shared strings (NULL if unknown) - * - * @return CachingStrategyInterface - */ - protected function getBestSharedStringsCachingStrategy($sharedStringsUniqueCount) - { - return $this->cachingStrategyFactory - ->createBestCachingStrategy($sharedStringsUniqueCount, $this->tempFolder, $this->helperFactory) - ; - } - - /** - * Processes the shared strings item XML node which the given XML reader is positioned on. - * - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XML Reader positioned on a "" node - * @param int $sharedStringIndex Index of the processed shared strings item - */ - protected function processSharedStringsItem($xmlReader, $sharedStringIndex) - { - $sharedStringValue = ''; - - // NOTE: expand() will automatically decode all XML entities of the child nodes - /** @var \DOMElement $siNode */ - $siNode = $xmlReader->expand(); - $textNodes = $siNode->getElementsByTagName(self::XML_NODE_T); - - foreach ($textNodes as $textNode) { - if ($this->shouldExtractTextNodeValue($textNode)) { - $textNodeValue = $textNode->nodeValue; - $shouldPreserveWhitespace = $this->shouldPreserveWhitespace($textNode); - - $sharedStringValue .= ($shouldPreserveWhitespace) ? $textNodeValue : trim($textNodeValue); - } - } - - $this->cachingStrategy->addStringForIndex($sharedStringValue, $sharedStringIndex); - } - - /** - * Not all text nodes' values must be extracted. - * Some text nodes are part of a node describing the pronunciation for instance. - * We'll only consider the nodes whose parents are "" or "". - * - * @param \DOMElement $textNode Text node to check - * - * @return bool Whether the given text node's value must be extracted - */ - protected function shouldExtractTextNodeValue($textNode) - { - $parentTagName = $textNode->parentNode->localName; - - return self::XML_NODE_SI === $parentTagName || self::XML_NODE_R === $parentTagName; - } - - /** - * If the text node has the attribute 'xml:space="preserve"', then preserve whitespace. - * - * @param \DOMElement $textNode The text node element () whose whitespace may be preserved - * - * @return bool Whether whitespace should be preserved - */ - protected function shouldPreserveWhitespace($textNode) - { - $spaceValue = $textNode->getAttribute(self::XML_ATTRIBUTE_XML_SPACE); - - return self::XML_ATTRIBUTE_VALUE_PRESERVE === $spaceValue; - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Manager/SheetManager.php b/upstream-3.x/src/Reader/XLSX/Manager/SheetManager.php deleted file mode 100644 index e2e173e..0000000 --- a/upstream-3.x/src/Reader/XLSX/Manager/SheetManager.php +++ /dev/null @@ -1,232 +0,0 @@ -filePath = $filePath; - $this->optionsManager = $optionsManager; - $this->sharedStringsManager = $sharedStringsManager; - $this->escaper = $escaper; - $this->entityFactory = $entityFactory; - } - - /** - * Returns the sheets metadata of the file located at the previously given file path. - * The paths to the sheets' data are read from the [Content_Types].xml file. - * - * @return Sheet[] Sheets within the XLSX file - */ - public function getSheets() - { - $this->sheets = []; - $this->currentSheetIndex = 0; - $this->activeSheetIndex = 0; // By default, the first sheet is active - - $xmlReader = $this->entityFactory->createXMLReader(); - $xmlProcessor = $this->entityFactory->createXMLProcessor($xmlReader); - - $xmlProcessor->registerCallback(self::XML_NODE_WORKBOOK_PROPERTIES, XMLProcessor::NODE_TYPE_START, [$this, 'processWorkbookPropertiesStartingNode']); - $xmlProcessor->registerCallback(self::XML_NODE_WORKBOOK_VIEW, XMLProcessor::NODE_TYPE_START, [$this, 'processWorkbookViewStartingNode']); - $xmlProcessor->registerCallback(self::XML_NODE_SHEET, XMLProcessor::NODE_TYPE_START, [$this, 'processSheetStartingNode']); - $xmlProcessor->registerCallback(self::XML_NODE_SHEETS, XMLProcessor::NODE_TYPE_END, [$this, 'processSheetsEndingNode']); - - if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_XML_FILE_PATH)) { - $xmlProcessor->readUntilStopped(); - $xmlReader->close(); - } - - return $this->sheets; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int A return code that indicates what action should the processor take next - */ - protected function processWorkbookPropertiesStartingNode($xmlReader) - { - // Using "filter_var($x, FILTER_VALIDATE_BOOLEAN)" here because the value of the "date1904" attribute - // may be the string "false", that is not mapped to the boolean "false" by default... - $shouldUse1904Dates = filter_var($xmlReader->getAttribute(self::XML_ATTRIBUTE_DATE_1904), FILTER_VALIDATE_BOOLEAN); - $this->optionsManager->setOption(Options::SHOULD_USE_1904_DATES, $shouldUse1904Dates); - - return XMLProcessor::PROCESSING_CONTINUE; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int A return code that indicates what action should the processor take next - */ - protected function processWorkbookViewStartingNode($xmlReader) - { - // The "workbookView" node is located before "sheet" nodes, ensuring that - // the active sheet is known before parsing sheets data. - $this->activeSheetIndex = (int) $xmlReader->getAttribute(self::XML_ATTRIBUTE_ACTIVE_TAB); - - return XMLProcessor::PROCESSING_CONTINUE; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int A return code that indicates what action should the processor take next - */ - protected function processSheetStartingNode($xmlReader) - { - $isSheetActive = ($this->currentSheetIndex === $this->activeSheetIndex); - $this->sheets[] = $this->getSheetFromSheetXMLNode($xmlReader, $this->currentSheetIndex, $isSheetActive); - ++$this->currentSheetIndex; - - return XMLProcessor::PROCESSING_CONTINUE; - } - - /** - * @return int A return code that indicates what action should the processor take next - */ - protected function processSheetsEndingNode() - { - return XMLProcessor::PROCESSING_STOP; - } - - /** - * Returns an instance of a sheet, given the XML node describing the sheet - from "workbook.xml". - * We can find the XML file path describing the sheet inside "workbook.xml.res", by mapping with the sheet ID - * ("r:id" in "workbook.xml", "Id" in "workbook.xml.res"). - * - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReaderOnSheetNode XML Reader instance, pointing on the node describing the sheet, as defined in "workbook.xml" - * @param int $sheetIndexZeroBased Index of the sheet, based on order of appearance in the workbook (zero-based) - * @param bool $isSheetActive Whether this sheet was defined as active - * - * @return \OpenSpout\Reader\XLSX\Sheet Sheet instance - */ - protected function getSheetFromSheetXMLNode($xmlReaderOnSheetNode, $sheetIndexZeroBased, $isSheetActive) - { - $sheetId = $xmlReaderOnSheetNode->getAttribute(self::XML_ATTRIBUTE_R_ID); - - $sheetState = $xmlReaderOnSheetNode->getAttribute(self::XML_ATTRIBUTE_STATE); - $isSheetVisible = (self::SHEET_STATE_HIDDEN !== $sheetState); - - $escapedSheetName = $xmlReaderOnSheetNode->getAttribute(self::XML_ATTRIBUTE_NAME); - $sheetName = $this->escaper->unescape($escapedSheetName); - - $sheetDataXMLFilePath = $this->getSheetDataXMLFilePathForSheetId($sheetId); - - return $this->entityFactory->createSheet( - $this->filePath, - $sheetDataXMLFilePath, - $sheetIndexZeroBased, - $sheetName, - $isSheetActive, - $isSheetVisible, - $this->optionsManager, - $this->sharedStringsManager - ); - } - - /** - * @param string $sheetId The sheet ID, as defined in "workbook.xml" - * - * @return string The XML file path describing the sheet inside "workbook.xml.res", for the given sheet ID - */ - protected function getSheetDataXMLFilePathForSheetId($sheetId) - { - $sheetDataXMLFilePath = ''; - - // find the file path of the sheet, by looking at the "workbook.xml.res" file - $xmlReader = $this->entityFactory->createXMLReader(); - if ($xmlReader->openFileInZip($this->filePath, self::WORKBOOK_XML_RELS_FILE_PATH)) { - while ($xmlReader->read()) { - if ($xmlReader->isPositionedOnStartingNode(self::XML_NODE_RELATIONSHIP)) { - $relationshipSheetId = $xmlReader->getAttribute(self::XML_ATTRIBUTE_ID); - - if ($relationshipSheetId === $sheetId) { - // In workbook.xml.rels, it is only "worksheets/sheet1.xml" - // In [Content_Types].xml, the path is "/xl/worksheets/sheet1.xml" - $sheetDataXMLFilePath = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TARGET); - - // sometimes, the sheet data file path already contains "/xl/"... - if (0 !== strpos($sheetDataXMLFilePath, '/xl/')) { - $sheetDataXMLFilePath = '/xl/'.$sheetDataXMLFilePath; - - break; - } - } - } - } - - $xmlReader->close(); - } - - return $sheetDataXMLFilePath; - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Manager/StyleManager.php b/upstream-3.x/src/Reader/XLSX/Manager/StyleManager.php deleted file mode 100644 index 4eb9046..0000000 --- a/upstream-3.x/src/Reader/XLSX/Manager/StyleManager.php +++ /dev/null @@ -1,349 +0,0 @@ - 'm/d/yyyy', // @NOTE: ECMA spec is 'mm-dd-yy' - 15 => 'd-mmm-yy', - 16 => 'd-mmm', - 17 => 'mmm-yy', - 18 => 'h:mm AM/PM', - 19 => 'h:mm:ss AM/PM', - 20 => 'h:mm', - 21 => 'h:mm:ss', - 22 => 'm/d/yyyy h:mm', // @NOTE: ECMA spec is 'm/d/yy h:mm', - 45 => 'mm:ss', - 46 => '[h]:mm:ss', - 47 => 'mm:ss.0', // @NOTE: ECMA spec is 'mmss.0', - ]; - - /** @var string Path of the XLSX file being read */ - protected $filePath; - - /** @var bool Whether the XLSX file contains a styles XML file */ - protected $hasStylesXMLFile; - - /** @var null|string Path of the styles XML file */ - protected $stylesXMLFilePath; - - /** @var InternalEntityFactory Factory to create entities */ - protected $entityFactory; - - /** @var array Array containing the IDs of built-in number formats indicating a date */ - protected $builtinNumFmtIdIndicatingDates; - - /** @var null|array Array containing a mapping NUM_FMT_ID => FORMAT_CODE */ - protected $customNumberFormats; - - /** @var null|array Array containing a mapping STYLE_ID => [STYLE_ATTRIBUTES] */ - protected $stylesAttributes; - - /** @var array Cache containing a mapping NUM_FMT_ID => IS_DATE_FORMAT. Used to avoid lots of recalculations */ - protected $numFmtIdToIsDateFormatCache = []; - - /** - * @param string $filePath Path of the XLSX file being read - * @param WorkbookRelationshipsManager $workbookRelationshipsManager Helps retrieving workbook relationships - * @param InternalEntityFactory $entityFactory Factory to create entities - */ - public function __construct($filePath, $workbookRelationshipsManager, $entityFactory) - { - $this->filePath = $filePath; - $this->entityFactory = $entityFactory; - $this->builtinNumFmtIdIndicatingDates = array_keys(self::$builtinNumFmtIdToNumFormatMapping); - $this->hasStylesXMLFile = $workbookRelationshipsManager->hasStylesXMLFile(); - if ($this->hasStylesXMLFile) { - $this->stylesXMLFilePath = $workbookRelationshipsManager->getStylesXMLFilePath(); - } - } - - /** - * Returns whether the style with the given ID should consider - * numeric values as timestamps and format the cell as a date. - * - * @param int $styleId Zero-based style ID - * - * @return bool Whether the cell with the given cell should display a date instead of a numeric value - */ - public function shouldFormatNumericValueAsDate($styleId) - { - if (!$this->hasStylesXMLFile) { - return false; - } - - $stylesAttributes = $this->getStylesAttributes(); - - // Default style (0) does not format numeric values as timestamps. Only custom styles do. - // Also if the style ID does not exist in the styles.xml file, format as numeric value. - // Using isset here because it is way faster than array_key_exists... - if (self::DEFAULT_STYLE_ID === $styleId || !isset($stylesAttributes[$styleId])) { - return false; - } - - $styleAttributes = $stylesAttributes[$styleId]; - - return $this->doesStyleIndicateDate($styleAttributes); - } - - /** - * Returns the format as defined in "styles.xml" of the given style. - * NOTE: It is assumed that the style DOES have a number format associated to it. - * - * @param int $styleId Zero-based style ID - * - * @return string The number format code associated with the given style - */ - public function getNumberFormatCode($styleId) - { - $stylesAttributes = $this->getStylesAttributes(); - $styleAttributes = $stylesAttributes[$styleId]; - $numFmtId = $styleAttributes[self::XML_ATTRIBUTE_NUM_FMT_ID]; - - if ($this->isNumFmtIdBuiltInDateFormat($numFmtId)) { - $numberFormatCode = self::$builtinNumFmtIdToNumFormatMapping[$numFmtId]; - } else { - $customNumberFormats = $this->getCustomNumberFormats(); - $numberFormatCode = $customNumberFormats[$numFmtId]; - } - - return $numberFormatCode; - } - - /** - * Reads the styles.xml file and extract the relevant information from the file. - */ - protected function extractRelevantInfo() - { - $this->customNumberFormats = []; - $this->stylesAttributes = []; - - $xmlReader = $this->entityFactory->createXMLReader(); - - if ($xmlReader->openFileInZip($this->filePath, $this->stylesXMLFilePath)) { - while ($xmlReader->read()) { - if ($xmlReader->isPositionedOnStartingNode(self::XML_NODE_NUM_FMTS)) { - $this->extractNumberFormats($xmlReader); - } elseif ($xmlReader->isPositionedOnStartingNode(self::XML_NODE_CELL_XFS)) { - $this->extractStyleAttributes($xmlReader); - } - } - - $xmlReader->close(); - } - } - - /** - * Extracts number formats from the "numFmt" nodes. - * For simplicity, the styles attributes are kept in memory. This is possible thanks - * to the reuse of formats. So 1 million cells should not use 1 million formats. - * - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XML Reader positioned on the "numFmts" node - */ - protected function extractNumberFormats($xmlReader) - { - while ($xmlReader->read()) { - if ($xmlReader->isPositionedOnStartingNode(self::XML_NODE_NUM_FMT)) { - $numFmtId = (int) ($xmlReader->getAttribute(self::XML_ATTRIBUTE_NUM_FMT_ID)); - $formatCode = $xmlReader->getAttribute(self::XML_ATTRIBUTE_FORMAT_CODE); - $this->customNumberFormats[$numFmtId] = $formatCode; - } elseif ($xmlReader->isPositionedOnEndingNode(self::XML_NODE_NUM_FMTS)) { - // Once done reading "numFmts" node's children - break; - } - } - } - - /** - * Extracts style attributes from the "xf" nodes, inside the "cellXfs" section. - * For simplicity, the styles attributes are kept in memory. This is possible thanks - * to the reuse of styles. So 1 million cells should not use 1 million styles. - * - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XML Reader positioned on the "cellXfs" node - */ - protected function extractStyleAttributes($xmlReader) - { - while ($xmlReader->read()) { - if ($xmlReader->isPositionedOnStartingNode(self::XML_NODE_XF)) { - $numFmtId = $xmlReader->getAttribute(self::XML_ATTRIBUTE_NUM_FMT_ID); - $normalizedNumFmtId = (null !== $numFmtId) ? (int) $numFmtId : null; - - $applyNumberFormat = $xmlReader->getAttribute(self::XML_ATTRIBUTE_APPLY_NUMBER_FORMAT); - $normalizedApplyNumberFormat = (null !== $applyNumberFormat) ? (bool) $applyNumberFormat : null; - - $this->stylesAttributes[] = [ - self::XML_ATTRIBUTE_NUM_FMT_ID => $normalizedNumFmtId, - self::XML_ATTRIBUTE_APPLY_NUMBER_FORMAT => $normalizedApplyNumberFormat, - ]; - } elseif ($xmlReader->isPositionedOnEndingNode(self::XML_NODE_CELL_XFS)) { - // Once done reading "cellXfs" node's children - break; - } - } - } - - /** - * @return array The custom number formats - */ - protected function getCustomNumberFormats() - { - if (!isset($this->customNumberFormats)) { - $this->extractRelevantInfo(); - } - - return $this->customNumberFormats; - } - - /** - * @return array The styles attributes - */ - protected function getStylesAttributes() - { - if (!isset($this->stylesAttributes)) { - $this->extractRelevantInfo(); - } - - return $this->stylesAttributes; - } - - /** - * @param array $styleAttributes Array containing the style attributes (2 keys: "applyNumberFormat" and "numFmtId") - * - * @return bool Whether the style with the given attributes indicates that the number is a date - */ - protected function doesStyleIndicateDate($styleAttributes) - { - $applyNumberFormat = $styleAttributes[self::XML_ATTRIBUTE_APPLY_NUMBER_FORMAT]; - $numFmtId = $styleAttributes[self::XML_ATTRIBUTE_NUM_FMT_ID]; - - // A style may apply a date format if it has: - // - "applyNumberFormat" attribute not set to "false" - // - "numFmtId" attribute set - // This is a preliminary check, as having "numFmtId" set just means the style should apply a specific number format, - // but this is not necessarily a date. - if (false === $applyNumberFormat || null === $numFmtId) { - return false; - } - - return $this->doesNumFmtIdIndicateDate($numFmtId); - } - - /** - * Returns whether the number format ID indicates that the number is a date. - * The result is cached to avoid recomputing the same thing over and over, as - * "numFmtId" attributes can be shared between multiple styles. - * - * @param int $numFmtId - * - * @return bool Whether the number format ID indicates that the number is a date - */ - protected function doesNumFmtIdIndicateDate($numFmtId) - { - if (!isset($this->numFmtIdToIsDateFormatCache[$numFmtId])) { - $formatCode = $this->getFormatCodeForNumFmtId($numFmtId); - - $this->numFmtIdToIsDateFormatCache[$numFmtId] = ( - $this->isNumFmtIdBuiltInDateFormat($numFmtId) - || $this->isFormatCodeCustomDateFormat($formatCode) - ); - } - - return $this->numFmtIdToIsDateFormatCache[$numFmtId]; - } - - /** - * @param int $numFmtId - * - * @return null|string The custom number format or NULL if none defined for the given numFmtId - */ - protected function getFormatCodeForNumFmtId($numFmtId) - { - $customNumberFormats = $this->getCustomNumberFormats(); - - // Using isset here because it is way faster than array_key_exists... - return (isset($customNumberFormats[$numFmtId])) ? $customNumberFormats[$numFmtId] : null; - } - - /** - * @param int $numFmtId - * - * @return bool Whether the number format ID indicates that the number is a date - */ - protected function isNumFmtIdBuiltInDateFormat($numFmtId) - { - return \in_array($numFmtId, $this->builtinNumFmtIdIndicatingDates, true); - } - - /** - * @param null|string $formatCode - * - * @return bool Whether the given format code indicates that the number is a date - */ - protected function isFormatCodeCustomDateFormat($formatCode) - { - // if no associated format code or if using the default "General" format - if (null === $formatCode || 0 === strcasecmp($formatCode, self::NUMBER_FORMAT_GENERAL)) { - return false; - } - - return $this->isFormatCodeMatchingDateFormatPattern($formatCode); - } - - /** - * @param string $formatCode - * - * @return bool Whether the given format code matches a date format pattern - */ - protected function isFormatCodeMatchingDateFormatPattern($formatCode) - { - // Remove extra formatting (what's between [ ], the brackets should not be preceded by a "\") - $pattern = '((? [FILE_NAME] */ - private $cachedWorkbookRelationships; - - /** - * @param string $filePath Path of the XLSX file being read - * @param InternalEntityFactory $entityFactory Factory to create entities - */ - public function __construct($filePath, $entityFactory) - { - $this->filePath = $filePath; - $this->entityFactory = $entityFactory; - } - - /** - * @return string The path of the shared string XML file - */ - public function getSharedStringsXMLFilePath() - { - $workbookRelationships = $this->getWorkbookRelationships(); - $sharedStringsXMLFilePath = $workbookRelationships[self::RELATIONSHIP_TYPE_SHARED_STRINGS] - ?? $workbookRelationships[self::RELATIONSHIP_TYPE_SHARED_STRINGS_STRICT]; - - // the file path can be relative (e.g. "styles.xml") or absolute (e.g. "/xl/styles.xml") - $doesContainBasePath = (false !== strpos($sharedStringsXMLFilePath, self::BASE_PATH)); - if (!$doesContainBasePath) { - // make sure we return an absolute file path - $sharedStringsXMLFilePath = self::BASE_PATH.$sharedStringsXMLFilePath; - } - - return $sharedStringsXMLFilePath; - } - - /** - * @return bool Whether the XLSX file contains a shared string XML file - */ - public function hasSharedStringsXMLFile() - { - $workbookRelationships = $this->getWorkbookRelationships(); - - return isset($workbookRelationships[self::RELATIONSHIP_TYPE_SHARED_STRINGS]) - || isset($workbookRelationships[self::RELATIONSHIP_TYPE_SHARED_STRINGS_STRICT]); - } - - /** - * @return bool Whether the XLSX file contains a styles XML file - */ - public function hasStylesXMLFile() - { - $workbookRelationships = $this->getWorkbookRelationships(); - - return isset($workbookRelationships[self::RELATIONSHIP_TYPE_STYLES]) - || isset($workbookRelationships[self::RELATIONSHIP_TYPE_STYLES_STRICT]); - } - - /** - * @return string The path of the styles XML file - */ - public function getStylesXMLFilePath() - { - $workbookRelationships = $this->getWorkbookRelationships(); - $stylesXMLFilePath = $workbookRelationships[self::RELATIONSHIP_TYPE_STYLES] - ?? $workbookRelationships[self::RELATIONSHIP_TYPE_STYLES_STRICT]; - - // the file path can be relative (e.g. "styles.xml") or absolute (e.g. "/xl/styles.xml") - $doesContainBasePath = (false !== strpos($stylesXMLFilePath, self::BASE_PATH)); - if (!$doesContainBasePath) { - // make sure we return a full path - $stylesXMLFilePath = self::BASE_PATH.$stylesXMLFilePath; - } - - return $stylesXMLFilePath; - } - - /** - * Reads the workbook.xml.rels and extracts the filename associated to the different types. - * It caches the result so that the file is read only once. - * - * @throws \OpenSpout\Common\Exception\IOException If workbook.xml.rels can't be read - * - * @return array - */ - private function getWorkbookRelationships() - { - if (!isset($this->cachedWorkbookRelationships)) { - $xmlReader = $this->entityFactory->createXMLReader(); - - if (false === $xmlReader->openFileInZip($this->filePath, self::WORKBOOK_RELS_XML_FILE_PATH)) { - throw new IOException('Could not open "'.self::WORKBOOK_RELS_XML_FILE_PATH.'".'); - } - - $this->cachedWorkbookRelationships = []; - - while ($xmlReader->readUntilNodeFound(self::XML_NODE_RELATIONSHIP)) { - $this->processWorkbookRelationship($xmlReader); - } - } - - return $this->cachedWorkbookRelationships; - } - - /** - * Extracts and store the data of the current workbook relationship. - * - * @param XMLReader $xmlReader - */ - private function processWorkbookRelationship($xmlReader) - { - $type = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TYPE); - $target = $xmlReader->getAttribute(self::XML_ATTRIBUTE_TARGET); - - // @NOTE: if a type is defined more than once, we overwrite the previous value - // To be changed if we want to get the file paths of sheet XML files for instance. - $this->cachedWorkbookRelationships[$type] = $target; - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Reader.php b/upstream-3.x/src/Reader/XLSX/Reader.php deleted file mode 100644 index 197c7fc..0000000 --- a/upstream-3.x/src/Reader/XLSX/Reader.php +++ /dev/null @@ -1,122 +0,0 @@ -managerFactory = $managerFactory; - } - - /** - * @param string $tempFolder Temporary folder where the temporary files will be created - * - * @return Reader - */ - public function setTempFolder($tempFolder) - { - $this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder); - - return $this; - } - - /** - * Returns whether stream wrappers are supported. - * - * @return bool - */ - protected function doesSupportStreamWrapper() - { - return false; - } - - /** - * Opens the file at the given file path to make it ready to be read. - * It also parses the sharedStrings.xml file to get all the shared strings available in memory - * and fetches all the available sheets. - * - * @param string $filePath Path of the file to be read - * - * @throws \OpenSpout\Common\Exception\IOException If the file at the given path or its content cannot be read - * @throws \OpenSpout\Reader\Exception\NoSheetsFoundException If there are no sheets in the file - */ - protected function openReader($filePath) - { - /** @var InternalEntityFactory $entityFactory */ - $entityFactory = $this->entityFactory; - - $this->zip = $entityFactory->createZipArchive(); - - if (true === $this->zip->open($filePath)) { - $tempFolder = $this->optionsManager->getOption(Options::TEMP_FOLDER); - $this->sharedStringsManager = $this->managerFactory->createSharedStringsManager($filePath, $tempFolder, $entityFactory); - - if ($this->sharedStringsManager->hasSharedStrings()) { - // Extracts all the strings from the sheets for easy access in the future - $this->sharedStringsManager->extractSharedStrings(); - } - - $this->sheetIterator = $entityFactory->createSheetIterator( - $filePath, - $this->optionsManager, - $this->sharedStringsManager - ); - } else { - throw new IOException("Could not open {$filePath} for reading."); - } - } - - /** - * Returns an iterator to iterate over sheets. - * - * @return SheetIterator To iterate over sheets - */ - protected function getConcreteSheetIterator() - { - return $this->sheetIterator; - } - - /** - * Closes the reader. To be used after reading the file. - */ - protected function closeReader() - { - if (null !== $this->zip) { - $this->zip->close(); - } - - if (null !== $this->sharedStringsManager) { - $this->sharedStringsManager->cleanup(); - } - } -} diff --git a/upstream-3.x/src/Reader/XLSX/RowIterator.php b/upstream-3.x/src/Reader/XLSX/RowIterator.php deleted file mode 100644 index de38370..0000000 --- a/upstream-3.x/src/Reader/XLSX/RowIterator.php +++ /dev/null @@ -1,429 +0,0 @@ -filePath = $filePath; - $this->sheetDataXMLFilePath = $this->normalizeSheetDataXMLFilePath($sheetDataXMLFilePath); - $this->shouldPreserveEmptyRows = $shouldPreserveEmptyRows; - $this->xmlReader = $xmlReader; - $this->cellValueFormatter = $cellValueFormatter; - $this->rowManager = $rowManager; - $this->entityFactory = $entityFactory; - - // Register all callbacks to process different nodes when reading the XML file - $this->xmlProcessor = $xmlProcessor; - $this->xmlProcessor->registerCallback(self::XML_NODE_DIMENSION, XMLProcessor::NODE_TYPE_START, [$this, 'processDimensionStartingNode']); - $this->xmlProcessor->registerCallback(self::XML_NODE_ROW, XMLProcessor::NODE_TYPE_START, [$this, 'processRowStartingNode']); - $this->xmlProcessor->registerCallback(self::XML_NODE_CELL, XMLProcessor::NODE_TYPE_START, [$this, 'processCellStartingNode']); - $this->xmlProcessor->registerCallback(self::XML_NODE_ROW, XMLProcessor::NODE_TYPE_END, [$this, 'processRowEndingNode']); - $this->xmlProcessor->registerCallback(self::XML_NODE_WORKSHEET, XMLProcessor::NODE_TYPE_END, [$this, 'processWorksheetEndingNode']); - } - - /** - * Rewind the Iterator to the first element. - * Initializes the XMLReader object that reads the associated sheet data. - * The XMLReader is configured to be safe from billion laughs attack. - * - * @see http://php.net/manual/en/iterator.rewind.php - * - * @throws \OpenSpout\Common\Exception\IOException If the sheet data XML cannot be read - */ - #[\ReturnTypeWillChange] - public function rewind(): void - { - $this->xmlReader->close(); - - if (false === $this->xmlReader->openFileInZip($this->filePath, $this->sheetDataXMLFilePath)) { - throw new IOException("Could not open \"{$this->sheetDataXMLFilePath}\"."); - } - - $this->numReadRows = 0; - $this->lastRowIndexProcessed = 0; - $this->nextRowIndexToBeProcessed = 0; - $this->rowBuffer = null; - $this->hasReachedEndOfFile = false; - $this->numColumns = 0; - - $this->next(); - } - - /** - * Checks if current position is valid. - * - * @see http://php.net/manual/en/iterator.valid.php - */ - #[\ReturnTypeWillChange] - public function valid(): bool - { - return !$this->hasReachedEndOfFile; - } - - /** - * Move forward to next element. Reads data describing the next unprocessed row. - * - * @see http://php.net/manual/en/iterator.next.php - * - * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If a shared string was not found - * @throws \OpenSpout\Common\Exception\IOException If unable to read the sheet data XML - */ - #[\ReturnTypeWillChange] - public function next(): void - { - ++$this->nextRowIndexToBeProcessed; - - if ($this->doesNeedDataForNextRowToBeProcessed()) { - $this->readDataForNextRow(); - } - } - - /** - * Return the current element, either an empty row or from the buffer. - * - * @see http://php.net/manual/en/iterator.current.php - */ - #[\ReturnTypeWillChange] - public function current(): ?Row - { - $rowToBeProcessed = $this->rowBuffer; - - if ($this->shouldPreserveEmptyRows) { - // when we need to preserve empty rows, we will either return - // an empty row or the last row read. This depends whether the - // index of last row that was read matches the index of the last - // row whose value should be returned. - if ($this->lastRowIndexProcessed !== $this->nextRowIndexToBeProcessed) { - // return empty row if mismatch between last processed row - // and the row that needs to be returned - $rowToBeProcessed = $this->entityFactory->createRow(); - } - } - - return $rowToBeProcessed; - } - - /** - * Return the key of the current element. Here, the row index. - * - * @see http://php.net/manual/en/iterator.key.php - */ - #[\ReturnTypeWillChange] - public function key(): int - { - // TODO: This should return $this->nextRowIndexToBeProcessed - // but to avoid a breaking change, the return value for - // this function has been kept as the number of rows read. - return $this->shouldPreserveEmptyRows ? - $this->nextRowIndexToBeProcessed : - $this->numReadRows; - } - - /** - * Cleans up what was created to iterate over the object. - */ - #[\ReturnTypeWillChange] - public function end(): void - { - $this->xmlReader->close(); - } - - /** - * @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml - * - * @return string path of the XML file containing the sheet data, - * without the leading slash - */ - protected function normalizeSheetDataXMLFilePath($sheetDataXMLFilePath) - { - return ltrim($sheetDataXMLFilePath, '/'); - } - - /** - * Returns whether we need data for the next row to be processed. - * We don't need to read data if: - * we have already read at least one row - * AND - * we need to preserve empty rows - * AND - * the last row that was read is not the row that need to be processed - * (i.e. if we need to return empty rows). - * - * @return bool whether we need data for the next row to be processed - */ - protected function doesNeedDataForNextRowToBeProcessed() - { - $hasReadAtLeastOneRow = (0 !== $this->lastRowIndexProcessed); - - return - !$hasReadAtLeastOneRow - || !$this->shouldPreserveEmptyRows - || $this->lastRowIndexProcessed < $this->nextRowIndexToBeProcessed - ; - } - - /** - * @throws \OpenSpout\Reader\Exception\SharedStringNotFoundException If a shared string was not found - * @throws \OpenSpout\Common\Exception\IOException If unable to read the sheet data XML - */ - protected function readDataForNextRow() - { - $this->currentlyProcessedRow = $this->entityFactory->createRow(); - - try { - $this->xmlProcessor->readUntilStopped(); - } catch (XMLProcessingException $exception) { - throw new IOException("The {$this->sheetDataXMLFilePath} file cannot be read. [{$exception->getMessage()}]"); - } - - $this->rowBuffer = $this->currentlyProcessedRow; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int A return code that indicates what action should the processor take next - */ - protected function processDimensionStartingNode($xmlReader) - { - // Read dimensions of the sheet - $dimensionRef = $xmlReader->getAttribute(self::XML_ATTRIBUTE_REF); // returns 'A1:M13' for instance (or 'A1' for empty sheet) - if (preg_match('/[A-Z]+\d+:([A-Z]+\d+)/', $dimensionRef, $matches)) { - $this->numColumns = CellHelper::getColumnIndexFromCellIndex($matches[1]) + 1; - } - - return XMLProcessor::PROCESSING_CONTINUE; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int A return code that indicates what action should the processor take next - */ - protected function processRowStartingNode($xmlReader) - { - // Reset index of the last processed column - $this->lastColumnIndexProcessed = -1; - - // Mark the last processed row as the one currently being read - $this->lastRowIndexProcessed = $this->getRowIndex($xmlReader); - - // Read spans info if present - $numberOfColumnsForRow = $this->numColumns; - $spans = $xmlReader->getAttribute(self::XML_ATTRIBUTE_SPANS); // returns '1:5' for instance - if ($spans) { - [, $numberOfColumnsForRow] = explode(':', $spans); - $numberOfColumnsForRow = (int) $numberOfColumnsForRow; - } - - $cells = array_fill(0, $numberOfColumnsForRow, $this->entityFactory->createCell('')); - $this->currentlyProcessedRow->setCells($cells); - - return XMLProcessor::PROCESSING_CONTINUE; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" starting node - * - * @return int A return code that indicates what action should the processor take next - */ - protected function processCellStartingNode($xmlReader) - { - $currentColumnIndex = $this->getColumnIndex($xmlReader); - - // NOTE: expand() will automatically decode all XML entities of the child nodes - /** @var \DOMElement $node */ - $node = $xmlReader->expand(); - $cell = $this->getCell($node); - - $this->currentlyProcessedRow->setCellAtIndex($cell, $currentColumnIndex); - $this->lastColumnIndexProcessed = $currentColumnIndex; - - return XMLProcessor::PROCESSING_CONTINUE; - } - - /** - * @return int A return code that indicates what action should the processor take next - */ - protected function processRowEndingNode() - { - // if the fetched row is empty and we don't want to preserve it.., - if (!$this->shouldPreserveEmptyRows && $this->rowManager->isEmpty($this->currentlyProcessedRow)) { - // ... skip it - return XMLProcessor::PROCESSING_CONTINUE; - } - - ++$this->numReadRows; - - // If needed, we fill the empty cells - if (0 === $this->numColumns) { - $this->currentlyProcessedRow = $this->rowManager->fillMissingIndexesWithEmptyCells($this->currentlyProcessedRow); - } - - // at this point, we have all the data we need for the row - // so that we can populate the buffer - return XMLProcessor::PROCESSING_STOP; - } - - /** - * @return int A return code that indicates what action should the processor take next - */ - protected function processWorksheetEndingNode() - { - // The closing "" marks the end of the file - $this->hasReachedEndOfFile = true; - - return XMLProcessor::PROCESSING_STOP; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" node - * - * @throws \OpenSpout\Common\Exception\InvalidArgumentException When the given cell index is invalid - * - * @return int Row index - */ - protected function getRowIndex($xmlReader) - { - // Get "r" attribute if present (from something like - $currentRowIndex = $xmlReader->getAttribute(self::XML_ATTRIBUTE_ROW_INDEX); - - return (null !== $currentRowIndex) ? - (int) $currentRowIndex : - $this->lastRowIndexProcessed + 1; - } - - /** - * @param \OpenSpout\Reader\Wrapper\XMLReader $xmlReader XMLReader object, positioned on a "" node - * - * @throws \OpenSpout\Common\Exception\InvalidArgumentException When the given cell index is invalid - * - * @return int Column index - */ - protected function getColumnIndex($xmlReader) - { - // Get "r" attribute if present (from something like - $currentCellIndex = $xmlReader->getAttribute(self::XML_ATTRIBUTE_CELL_INDEX); - - return (null !== $currentCellIndex) ? - CellHelper::getColumnIndexFromCellIndex($currentCellIndex) : - $this->lastColumnIndexProcessed + 1; - } - - /** - * Returns the cell with (unescaped) correctly marshalled, cell value associated to the given XML node. - * - * @param \DOMElement $node - * - * @return Cell The cell set with the associated with the cell - */ - protected function getCell($node) - { - try { - $cellValue = $this->cellValueFormatter->extractAndFormatNodeValue($node); - $cell = $this->entityFactory->createCell($cellValue); - } catch (InvalidValueException $exception) { - $cell = $this->entityFactory->createCell($exception->getInvalidValue()); - $cell->setType(Cell::TYPE_ERROR); - } - - return $cell; - } -} diff --git a/upstream-3.x/src/Reader/XLSX/Sheet.php b/upstream-3.x/src/Reader/XLSX/Sheet.php deleted file mode 100644 index 64d7637..0000000 --- a/upstream-3.x/src/Reader/XLSX/Sheet.php +++ /dev/null @@ -1,82 +0,0 @@ -rowIterator = $rowIterator; - $this->index = $sheetIndex; - $this->name = $sheetName; - $this->isActive = $isSheetActive; - $this->isVisible = $isSheetVisible; - } - - /** - * @return \OpenSpout\Reader\XLSX\RowIterator - */ - public function getRowIterator() - { - return $this->rowIterator; - } - - /** - * @return int Index of the sheet, based on order in the workbook (zero-based) - */ - public function getIndex() - { - return $this->index; - } - - /** - * @return string Name of the sheet - */ - public function getName() - { - return $this->name; - } - - /** - * @return bool Whether the sheet was defined as active - */ - public function isActive() - { - return $this->isActive; - } - - /** - * @return bool Whether the sheet is visible - */ - public function isVisible() - { - return $this->isVisible; - } -} diff --git a/upstream-3.x/src/Reader/XLSX/SheetIterator.php b/upstream-3.x/src/Reader/XLSX/SheetIterator.php deleted file mode 100644 index f58fc67..0000000 --- a/upstream-3.x/src/Reader/XLSX/SheetIterator.php +++ /dev/null @@ -1,113 +0,0 @@ -sheets = $sheetManager->getSheets(); - - if (0 === \count($this->sheets)) { - throw new NoSheetsFoundException('The file must contain at least one sheet.'); - } - } - - /** - * Rewind the Iterator to the first element. - * - * @see http://php.net/manual/en/iterator.rewind.php - */ - #[\ReturnTypeWillChange] - public function rewind() - { - $this->currentSheetIndex = 0; - } - - /** - * Checks if current position is valid. - * - * @see http://php.net/manual/en/iterator.valid.php - * - * @return bool - */ - #[\ReturnTypeWillChange] - public function valid() - { - return $this->currentSheetIndex < \count($this->sheets); - } - - /** - * Move forward to next element. - * - * @see http://php.net/manual/en/iterator.next.php - */ - #[\ReturnTypeWillChange] - public function next() - { - // Using isset here because it is way faster than array_key_exists... - if (isset($this->sheets[$this->currentSheetIndex])) { - $currentSheet = $this->sheets[$this->currentSheetIndex]; - $currentSheet->getRowIterator()->end(); - - ++$this->currentSheetIndex; - } - } - - /** - * Return the current element. - * - * @see http://php.net/manual/en/iterator.current.php - * - * @return \OpenSpout\Reader\XLSX\Sheet - */ - #[\ReturnTypeWillChange] - public function current() - { - return $this->sheets[$this->currentSheetIndex]; - } - - /** - * Return the key of the current element. - * - * @see http://php.net/manual/en/iterator.key.php - * - * @return int - */ - #[\ReturnTypeWillChange] - public function key() - { - return $this->currentSheetIndex + 1; - } - - /** - * Cleans up what was created to iterate over the object. - */ - #[\ReturnTypeWillChange] - public function end() - { - // make sure we are not leaking memory in case the iteration stopped before the end - foreach ($this->sheets as $sheet) { - $sheet->getRowIterator()->end(); - } - } -} diff --git a/upstream-3.x/src/Writer/CSV/Manager/OptionsManager.php b/upstream-3.x/src/Writer/CSV/Manager/OptionsManager.php deleted file mode 100644 index c6e25a6..0000000 --- a/upstream-3.x/src/Writer/CSV/Manager/OptionsManager.php +++ /dev/null @@ -1,34 +0,0 @@ -setOption(Options::FIELD_DELIMITER, ','); - $this->setOption(Options::FIELD_ENCLOSURE, '"'); - $this->setOption(Options::SHOULD_ADD_BOM, true); - } -} diff --git a/upstream-3.x/src/Writer/CSV/Writer.php b/upstream-3.x/src/Writer/CSV/Writer.php deleted file mode 100644 index 2815d0e..0000000 --- a/upstream-3.x/src/Writer/CSV/Writer.php +++ /dev/null @@ -1,109 +0,0 @@ -optionsManager->setOption(Options::FIELD_DELIMITER, $fieldDelimiter); - - return $this; - } - - /** - * Sets the field enclosure for the CSV. - * - * @param string $fieldEnclosure Character that enclose fields - * - * @return Writer - */ - public function setFieldEnclosure($fieldEnclosure) - { - $this->optionsManager->setOption(Options::FIELD_ENCLOSURE, $fieldEnclosure); - - return $this; - } - - /** - * Set if a BOM has to be added to the file. - * - * @param bool $shouldAddBOM - * - * @return Writer - */ - public function setShouldAddBOM($shouldAddBOM) - { - $this->optionsManager->setOption(Options::SHOULD_ADD_BOM, (bool) $shouldAddBOM); - - return $this; - } - - /** - * Opens the CSV streamer and makes it ready to accept data. - */ - protected function openWriter() - { - if ($this->optionsManager->getOption(Options::SHOULD_ADD_BOM)) { - // Adds UTF-8 BOM for Unicode compatibility - $this->globalFunctionsHelper->fputs($this->filePointer, EncodingHelper::BOM_UTF8); - } - } - - /** - * Adds a row to the currently opened writer. - * - * @param Row $row The row containing cells and styles - * - * @throws IOException If unable to write data - */ - protected function addRowToWriter(Row $row) - { - $fieldDelimiter = $this->optionsManager->getOption(Options::FIELD_DELIMITER); - $fieldEnclosure = $this->optionsManager->getOption(Options::FIELD_ENCLOSURE); - - $wasWriteSuccessful = $this->globalFunctionsHelper->fputcsv($this->filePointer, $row->getCells(), $fieldDelimiter, $fieldEnclosure); - if (false === $wasWriteSuccessful) { - throw new IOException('Unable to write data'); - } - - ++$this->lastWrittenRowIndex; - if (0 === $this->lastWrittenRowIndex % self::FLUSH_THRESHOLD) { - $this->globalFunctionsHelper->fflush($this->filePointer); - } - } - - /** - * Closes the CSV streamer, preventing any additional writing. - * If set, sets the headers and redirects output to the browser. - */ - protected function closeWriter() - { - $this->lastWrittenRowIndex = 0; - } -} diff --git a/upstream-3.x/src/Writer/Common/Creator/InternalEntityFactory.php b/upstream-3.x/src/Writer/Common/Creator/InternalEntityFactory.php deleted file mode 100644 index 63e96d3..0000000 --- a/upstream-3.x/src/Writer/Common/Creator/InternalEntityFactory.php +++ /dev/null @@ -1,52 +0,0 @@ -border = new Border(); - } - - /** - * @param string $color Border A RGB color code - * @param string $width Border width @see BorderPart::allowedWidths - * @param string $style Border style @see BorderPart::allowedStyles - * - * @return BorderBuilder - */ - public function setBorderTop($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID) - { - $this->border->addPart(new BorderPart(Border::TOP, $color, $width, $style)); - - return $this; - } - - /** - * @param string $color Border A RGB color code - * @param string $width Border width @see BorderPart::allowedWidths - * @param string $style Border style @see BorderPart::allowedStyles - * - * @return BorderBuilder - */ - public function setBorderRight($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID) - { - $this->border->addPart(new BorderPart(Border::RIGHT, $color, $width, $style)); - - return $this; - } - - /** - * @param string $color Border A RGB color code - * @param string $width Border width @see BorderPart::allowedWidths - * @param string $style Border style @see BorderPart::allowedStyles - * - * @return BorderBuilder - */ - public function setBorderBottom($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID) - { - $this->border->addPart(new BorderPart(Border::BOTTOM, $color, $width, $style)); - - return $this; - } - - /** - * @param string $color Border A RGB color code - * @param string $width Border width @see BorderPart::allowedWidths - * @param string $style Border style @see BorderPart::allowedStyles - * - * @return BorderBuilder - */ - public function setBorderLeft($color = Color::BLACK, $width = Border::WIDTH_MEDIUM, $style = Border::STYLE_SOLID) - { - $this->border->addPart(new BorderPart(Border::LEFT, $color, $width, $style)); - - return $this; - } - - /** - * @return Border - */ - public function build() - { - return $this->border; - } -} diff --git a/upstream-3.x/src/Writer/Common/Creator/Style/StyleBuilder.php b/upstream-3.x/src/Writer/Common/Creator/Style/StyleBuilder.php deleted file mode 100644 index 2ef0d15..0000000 --- a/upstream-3.x/src/Writer/Common/Creator/Style/StyleBuilder.php +++ /dev/null @@ -1,214 +0,0 @@ -style = new Style(); - } - - /** - * Makes the font bold. - * - * @return StyleBuilder - */ - public function setFontBold() - { - $this->style->setFontBold(); - - return $this; - } - - /** - * Makes the font italic. - * - * @return StyleBuilder - */ - public function setFontItalic() - { - $this->style->setFontItalic(); - - return $this; - } - - /** - * Makes the font underlined. - * - * @return StyleBuilder - */ - public function setFontUnderline() - { - $this->style->setFontUnderline(); - - return $this; - } - - /** - * Makes the font struck through. - * - * @return StyleBuilder - */ - public function setFontStrikethrough() - { - $this->style->setFontStrikethrough(); - - return $this; - } - - /** - * Sets the font size. - * - * @param int $fontSize Font size, in pixels - * - * @return StyleBuilder - */ - public function setFontSize($fontSize) - { - $this->style->setFontSize($fontSize); - - return $this; - } - - /** - * Sets the font color. - * - * @param string $fontColor ARGB color (@see Color) - * - * @return StyleBuilder - */ - public function setFontColor($fontColor) - { - $this->style->setFontColor($fontColor); - - return $this; - } - - /** - * Sets the font name. - * - * @param string $fontName Name of the font to use - * - * @return StyleBuilder - */ - public function setFontName($fontName) - { - $this->style->setFontName($fontName); - - return $this; - } - - /** - * Makes the text wrap in the cell if requested. - * - * @param bool $shouldWrap Should the text be wrapped - * - * @return StyleBuilder - */ - public function setShouldWrapText($shouldWrap = true) - { - $this->style->setShouldWrapText($shouldWrap); - - return $this; - } - - /** - * Sets the cell alignment. - * - * @param string $cellAlignment The cell alignment - * - * @throws InvalidArgumentException If the given cell alignment is not valid - * - * @return StyleBuilder - */ - public function setCellAlignment($cellAlignment) - { - if (!CellAlignment::isValid($cellAlignment)) { - throw new InvalidArgumentException('Invalid cell alignment value'); - } - - $this->style->setCellAlignment($cellAlignment); - - return $this; - } - - /** - * Set a border. - * - * @return $this - */ - public function setBorder(Border $border) - { - $this->style->setBorder($border); - - return $this; - } - - /** - * Sets a background color. - * - * @param string $color ARGB color (@see Color) - * - * @return StyleBuilder - */ - public function setBackgroundColor($color) - { - $this->style->setBackgroundColor($color); - - return $this; - } - - /** - * Sets a format. - * - * @param string $format Format - * - * @return StyleBuilder - * - * @api - */ - public function setFormat($format) - { - $this->style->setFormat($format); - - return $this; - } - - /** - * Set should shrink to fit. - * - * @param bool $shrinkToFit - * - * @return StyleBuilder - * - * @api - */ - public function setShouldShrinkToFit($shrinkToFit = true) - { - $this->style->setShouldShrinkToFit($shrinkToFit); - - return $this; - } - - /** - * Returns the configured style. The style is cached and can be reused. - * - * @return Style - */ - public function build() - { - return $this->style; - } -} diff --git a/upstream-3.x/src/Writer/Common/Creator/WriterEntityFactory.php b/upstream-3.x/src/Writer/Common/Creator/WriterEntityFactory.php deleted file mode 100644 index da6a43a..0000000 --- a/upstream-3.x/src/Writer/Common/Creator/WriterEntityFactory.php +++ /dev/null @@ -1,121 +0,0 @@ -index = $sheetIndex; - $this->associatedWorkbookId = $associatedWorkbookId; - - $this->sheetManager = $sheetManager; - $this->sheetManager->markWorkbookIdAsUsed($associatedWorkbookId); - - $this->setName(self::DEFAULT_SHEET_NAME_PREFIX.($sheetIndex + 1)); - $this->setIsVisible(true); - } - - /** - * @return int Index of the sheet, based on order in the workbook (zero-based) - */ - public function getIndex() - { - return $this->index; - } - - /** - * @return string - */ - public function getAssociatedWorkbookId() - { - return $this->associatedWorkbookId; - } - - /** - * @return string Name of the sheet - */ - public function getName() - { - return $this->name; - } - - /** - * Sets the name of the sheet. Note that Excel has some restrictions on the name: - * - it should not be blank - * - it should not exceed 31 characters - * - it should not contain these characters: \ / ? * : [ or ] - * - it should be unique. - * - * @param string $name Name of the sheet - * - * @throws \OpenSpout\Writer\Exception\InvalidSheetNameException if the sheet's name is invalid - * - * @return Sheet - */ - public function setName($name) - { - $this->sheetManager->throwIfNameIsInvalid($name, $this); - - $this->name = $name; - - $this->sheetManager->markSheetNameAsUsed($this); - - return $this; - } - - /** - * @return bool isVisible Visibility of the sheet - */ - public function isVisible() - { - return $this->isVisible; - } - - /** - * @param bool $isVisible Visibility of the sheet - * - * @return Sheet - */ - public function setIsVisible($isVisible) - { - $this->isVisible = $isVisible; - - return $this; - } - - public function getSheetView(): ?SheetView - { - return $this->sheetView; - } - - /** - * @return $this - */ - public function setSheetView(SheetView $sheetView) - { - $this->sheetView = $sheetView; - - return $this; - } - - public function hasSheetView(): bool - { - return $this->sheetView instanceof SheetView; - } -} diff --git a/upstream-3.x/src/Writer/Common/Entity/Workbook.php b/upstream-3.x/src/Writer/Common/Entity/Workbook.php deleted file mode 100644 index 152ded8..0000000 --- a/upstream-3.x/src/Writer/Common/Entity/Workbook.php +++ /dev/null @@ -1,47 +0,0 @@ -internalId = uniqid(); - } - - /** - * @return Worksheet[] - */ - public function getWorksheets() - { - return $this->worksheets; - } - - /** - * @param Worksheet[] $worksheets - */ - public function setWorksheets($worksheets) - { - $this->worksheets = $worksheets; - } - - /** - * @return string - */ - public function getInternalId() - { - return $this->internalId; - } -} diff --git a/upstream-3.x/src/Writer/Common/Entity/Worksheet.php b/upstream-3.x/src/Writer/Common/Entity/Worksheet.php deleted file mode 100644 index 0263429..0000000 --- a/upstream-3.x/src/Writer/Common/Entity/Worksheet.php +++ /dev/null @@ -1,131 +0,0 @@ -filePath = $worksheetFilePath; - $this->filePointer = null; - $this->externalSheet = $externalSheet; - $this->maxNumColumns = 0; - $this->lastWrittenRowIndex = 0; - $this->sheetDataStarted = false; - } - - /** - * @return string - */ - public function getFilePath() - { - return $this->filePath; - } - - /** - * @return resource - */ - public function getFilePointer() - { - return $this->filePointer; - } - - /** - * @param resource $filePointer - */ - public function setFilePointer($filePointer) - { - $this->filePointer = $filePointer; - } - - /** - * @return Sheet - */ - public function getExternalSheet() - { - return $this->externalSheet; - } - - /** - * @return int - */ - public function getMaxNumColumns() - { - return $this->maxNumColumns; - } - - /** - * @param int $maxNumColumns - */ - public function setMaxNumColumns($maxNumColumns) - { - $this->maxNumColumns = $maxNumColumns; - } - - /** - * @return int - */ - public function getLastWrittenRowIndex() - { - return $this->lastWrittenRowIndex; - } - - /** - * @param int $lastWrittenRowIndex - */ - public function setLastWrittenRowIndex($lastWrittenRowIndex) - { - $this->lastWrittenRowIndex = $lastWrittenRowIndex; - } - - /** - * @return int The ID of the worksheet - */ - public function getId() - { - // sheet index is zero-based, while ID is 1-based - return $this->externalSheet->getIndex() + 1; - } - - /** - * @return bool - */ - public function getSheetDataStarted() - { - return $this->sheetDataStarted; - } - - /** - * @param bool $sheetDataStarted - */ - public function setSheetDataStarted($sheetDataStarted) - { - $this->sheetDataStarted = $sheetDataStarted; - } -} diff --git a/upstream-3.x/src/Writer/Common/Helper/CellHelper.php b/upstream-3.x/src/Writer/Common/Helper/CellHelper.php deleted file mode 100644 index 400e82c..0000000 --- a/upstream-3.x/src/Writer/Common/Helper/CellHelper.php +++ /dev/null @@ -1,45 +0,0 @@ - column letters */ - private static $columnIndexToColumnLettersCache = []; - - /** - * Returns the column letters (base 26) associated to the base 10 column index. - * Excel uses A to Z letters for column indexing, where A is the 1st column, - * Z is the 26th and AA is the 27th. - * The mapping is zero based, so that 0 maps to A, B maps to 1, Z to 25 and AA to 26. - * - * @param int $columnIndexZeroBased The Excel column index (0, 42, ...) - * - * @return string The associated cell index ('A', 'BC', ...) - */ - public static function getColumnLettersFromColumnIndex($columnIndexZeroBased) - { - $originalColumnIndex = $columnIndexZeroBased; - - // Using isset here because it is way faster than array_key_exists... - if (!isset(self::$columnIndexToColumnLettersCache[$originalColumnIndex])) { - $columnLetters = ''; - $capitalAAsciiValue = \ord('A'); - - do { - $modulus = $columnIndexZeroBased % 26; - $columnLetters = \chr($capitalAAsciiValue + $modulus).$columnLetters; - - // substracting 1 because it's zero-based - $columnIndexZeroBased = (int) ($columnIndexZeroBased / 26) - 1; - } while ($columnIndexZeroBased >= 0); - - self::$columnIndexToColumnLettersCache[$originalColumnIndex] = $columnLetters; - } - - return self::$columnIndexToColumnLettersCache[$originalColumnIndex]; - } -} diff --git a/upstream-3.x/src/Writer/Common/Helper/FileSystemWithRootFolderHelperInterface.php b/upstream-3.x/src/Writer/Common/Helper/FileSystemWithRootFolderHelperInterface.php deleted file mode 100644 index f571ebf..0000000 --- a/upstream-3.x/src/Writer/Common/Helper/FileSystemWithRootFolderHelperInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -entityFactory = $entityFactory; - } - - /** - * Returns a new ZipArchive instance pointing at the given path. - * - * @param string $tmpFolderPath Path of the temp folder where the zip file will be created - * - * @return \ZipArchive - */ - public function createZip($tmpFolderPath) - { - $zip = $this->entityFactory->createZipArchive(); - $zipFilePath = $tmpFolderPath.self::ZIP_EXTENSION; - - $zip->open($zipFilePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); - - return $zip; - } - - /** - * @param \ZipArchive $zip An opened zip archive object - * - * @return string Path where the zip file of the given folder will be created - */ - public function getZipFilePath(\ZipArchive $zip) - { - return $zip->filename; - } - - /** - * Adds the given file, located under the given root folder to the archive. - * The file will be compressed. - * - * Example of use: - * addFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml'); - * => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml' - * - * @param \ZipArchive $zip An opened zip archive object - * @param string $rootFolderPath path of the root folder that will be ignored in the archive tree - * @param string $localFilePath Path of the file to be added, under the root folder - * @param string $existingFileMode Controls what to do when trying to add an existing file - */ - public function addFileToArchive($zip, $rootFolderPath, $localFilePath, $existingFileMode = self::EXISTING_FILES_OVERWRITE) - { - $this->addFileToArchiveWithCompressionMethod( - $zip, - $rootFolderPath, - $localFilePath, - $existingFileMode, - \ZipArchive::CM_DEFAULT - ); - } - - /** - * Adds the given file, located under the given root folder to the archive. - * The file will NOT be compressed. - * - * Example of use: - * addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml'); - * => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml' - * - * @param \ZipArchive $zip An opened zip archive object - * @param string $rootFolderPath path of the root folder that will be ignored in the archive tree - * @param string $localFilePath Path of the file to be added, under the root folder - * @param string $existingFileMode Controls what to do when trying to add an existing file - */ - public function addUncompressedFileToArchive($zip, $rootFolderPath, $localFilePath, $existingFileMode = self::EXISTING_FILES_OVERWRITE) - { - $this->addFileToArchiveWithCompressionMethod( - $zip, - $rootFolderPath, - $localFilePath, - $existingFileMode, - \ZipArchive::CM_STORE - ); - } - - /** - * @return bool Whether it is possible to choose the desired compression method to be used - */ - public static function canChooseCompressionMethod() - { - // setCompressionName() is a PHP7+ method... - return method_exists(new \ZipArchive(), 'setCompressionName'); - } - - /** - * @param \ZipArchive $zip An opened zip archive object - * @param string $folderPath Path to the folder to be zipped - * @param string $existingFileMode Controls what to do when trying to add an existing file - */ - public function addFolderToArchive($zip, $folderPath, $existingFileMode = self::EXISTING_FILES_OVERWRITE) - { - $folderRealPath = $this->getNormalizedRealPath($folderPath).'/'; - $itemIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($folderPath, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST); - - foreach ($itemIterator as $itemInfo) { - $itemRealPath = $this->getNormalizedRealPath($itemInfo->getPathname()); - $itemLocalPath = str_replace($folderRealPath, '', $itemRealPath); - - if ($itemInfo->isFile() && !$this->shouldSkipFile($zip, $itemLocalPath, $existingFileMode)) { - $zip->addFile($itemRealPath, $itemLocalPath); - } - } - } - - /** - * Closes the archive and copies it into the given stream. - * - * @param \ZipArchive $zip An opened zip archive object - * @param resource $streamPointer Pointer to the stream to copy the zip - */ - public function closeArchiveAndCopyToStream($zip, $streamPointer) - { - $zipFilePath = $zip->filename; - $zip->close(); - - $this->copyZipToStream($zipFilePath, $streamPointer); - } - - /** - * Adds the given file, located under the given root folder to the archive. - * The file will NOT be compressed. - * - * Example of use: - * addUncompressedFileToArchive($zip, '/tmp/xlsx/foo', 'bar/baz.xml'); - * => will add the file located at '/tmp/xlsx/foo/bar/baz.xml' in the archive, but only as 'bar/baz.xml' - * - * @param \ZipArchive $zip An opened zip archive object - * @param string $rootFolderPath path of the root folder that will be ignored in the archive tree - * @param string $localFilePath Path of the file to be added, under the root folder - * @param string $existingFileMode Controls what to do when trying to add an existing file - * @param int $compressionMethod The compression method - */ - protected function addFileToArchiveWithCompressionMethod($zip, $rootFolderPath, $localFilePath, $existingFileMode, $compressionMethod) - { - if (!$this->shouldSkipFile($zip, $localFilePath, $existingFileMode)) { - $normalizedFullFilePath = $this->getNormalizedRealPath($rootFolderPath.'/'.$localFilePath); - $zip->addFile($normalizedFullFilePath, $localFilePath); - - if (self::canChooseCompressionMethod()) { - $zip->setCompressionName($localFilePath, $compressionMethod); - } - } - } - - /** - * @param \ZipArchive $zip - * @param string $itemLocalPath - * @param string $existingFileMode - * - * @return bool Whether the file should be added to the archive or skipped - */ - protected function shouldSkipFile($zip, $itemLocalPath, $existingFileMode) - { - // Skip files if: - // - EXISTING_FILES_SKIP mode chosen - // - File already exists in the archive - return self::EXISTING_FILES_SKIP === $existingFileMode && false !== $zip->locateName($itemLocalPath); - } - - /** - * Returns canonicalized absolute pathname, containing only forward slashes. - * - * @param string $path Path to normalize - * - * @return string Normalized and canonicalized path - */ - protected function getNormalizedRealPath($path) - { - $realPath = realpath($path); - - return str_replace(\DIRECTORY_SEPARATOR, '/', $realPath); - } - - /** - * Streams the contents of the zip file into the given stream. - * - * @param string $zipFilePath Path of the zip file - * @param resource $pointer Pointer to the stream to copy the zip - */ - protected function copyZipToStream($zipFilePath, $pointer) - { - $zipFilePointer = fopen($zipFilePath, 'r'); - stream_copy_to_stream($zipFilePointer, $pointer); - fclose($zipFilePointer); - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/CellManager.php b/upstream-3.x/src/Writer/Common/Manager/CellManager.php deleted file mode 100644 index 5ce70d4..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/CellManager.php +++ /dev/null @@ -1,29 +0,0 @@ -styleMerger = $styleMerger; - } - - /** - * Merges a Style into a cell's Style. - */ - public function applyStyle(Cell $cell, Style $style) - { - $mergedStyle = $this->styleMerger->merge($cell->getStyle(), $style); - $cell->setStyle($mergedStyle); - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/ManagesCellSize.php b/upstream-3.x/src/Writer/Common/Manager/ManagesCellSize.php deleted file mode 100644 index eb8c4a4..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/ManagesCellSize.php +++ /dev/null @@ -1,62 +0,0 @@ -defaultColumnWidth = $width; - } - - /** - * @param null|float $height - */ - public function setDefaultRowHeight($height) - { - $this->defaultRowHeight = $height; - } - - /** - * @param int ...$columns One or more columns with this width - */ - public function setColumnWidth(float $width, ...$columns) - { - // Gather sequences - $sequence = []; - foreach ($columns as $i) { - $sequenceLength = \count($sequence); - if ($sequenceLength > 0) { - $previousValue = $sequence[$sequenceLength - 1]; - if ($i !== $previousValue + 1) { - $this->setColumnWidthForRange($width, $sequence[0], $previousValue); - $sequence = []; - } - } - $sequence[] = $i; - } - $this->setColumnWidthForRange($width, $sequence[0], $sequence[\count($sequence) - 1]); - } - - /** - * @param float $width The width to set - * @param int $start First column index of the range - * @param int $end Last column index of the range - */ - public function setColumnWidthForRange(float $width, int $start, int $end) - { - $this->columnWidths[] = [$start, $end, $width]; - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/RegisteredStyle.php b/upstream-3.x/src/Writer/Common/Manager/RegisteredStyle.php deleted file mode 100644 index d3ef877..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/RegisteredStyle.php +++ /dev/null @@ -1,37 +0,0 @@ -style = $style; - $this->isMatchingRowStyle = $isMatchingRowStyle; - } - - public function getStyle(): Style - { - return $this->style; - } - - public function isMatchingRowStyle(): bool - { - return $this->isMatchingRowStyle; - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/RowManager.php b/upstream-3.x/src/Writer/Common/Manager/RowManager.php deleted file mode 100644 index e702bf7..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/RowManager.php +++ /dev/null @@ -1,25 +0,0 @@ -getCells() as $cell) { - if (!$cell->isEmpty()) { - return false; - } - } - - return true; - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/SheetManager.php b/upstream-3.x/src/Writer/Common/Manager/SheetManager.php deleted file mode 100644 index 4d6a6c2..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/SheetManager.php +++ /dev/null @@ -1,146 +0,0 @@ - [[SHEET_INDEX] => [SHEET_NAME]] keeping track of sheets' name to enforce uniqueness per workbook */ - private static $SHEETS_NAME_USED = []; - - /** @var StringHelper */ - private $stringHelper; - - /** - * SheetManager constructor. - */ - public function __construct(StringHelper $stringHelper) - { - $this->stringHelper = $stringHelper; - } - - /** - * Throws an exception if the given sheet's name is not valid. - * - * @see Sheet::setName for validity rules. - * - * @param string $name - * @param Sheet $sheet The sheet whose future name is checked - * - * @throws \OpenSpout\Writer\Exception\InvalidSheetNameException if the sheet's name is invalid - */ - public function throwIfNameIsInvalid($name, Sheet $sheet) - { - if (!\is_string($name)) { - $actualType = \gettype($name); - $errorMessage = "The sheet's name is invalid. It must be a string ({$actualType} given)."; - - throw new InvalidSheetNameException($errorMessage); - } - - $failedRequirements = []; - $nameLength = $this->stringHelper->getStringLength($name); - - if (!$this->isNameUnique($name, $sheet)) { - $failedRequirements[] = 'It should be unique'; - } else { - if (0 === $nameLength) { - $failedRequirements[] = 'It should not be blank'; - } else { - if ($nameLength > self::MAX_LENGTH_SHEET_NAME) { - $failedRequirements[] = 'It should not exceed 31 characters'; - } - - if ($this->doesContainInvalidCharacters($name)) { - $failedRequirements[] = 'It should not contain these characters: \\ / ? * : [ or ]'; - } - - if ($this->doesStartOrEndWithSingleQuote($name)) { - $failedRequirements[] = 'It should not start or end with a single quote'; - } - } - } - - if (0 !== \count($failedRequirements)) { - $errorMessage = "The sheet's name (\"{$name}\") is invalid. It did not respect these rules:\n - "; - $errorMessage .= implode("\n - ", $failedRequirements); - - throw new InvalidSheetNameException($errorMessage); - } - } - - /** - * @param string $workbookId Workbook ID associated to a Sheet - */ - public function markWorkbookIdAsUsed($workbookId) - { - if (!isset(self::$SHEETS_NAME_USED[$workbookId])) { - self::$SHEETS_NAME_USED[$workbookId] = []; - } - } - - public function markSheetNameAsUsed(Sheet $sheet) - { - self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()][$sheet->getIndex()] = $sheet->getName(); - } - - /** - * Returns whether the given name contains at least one invalid character. - * - * @see Sheet::$INVALID_CHARACTERS_IN_SHEET_NAME for the full list. - * - * @param string $name - * - * @return bool TRUE if the name contains invalid characters, FALSE otherwise - */ - private function doesContainInvalidCharacters($name) - { - return str_replace(self::$INVALID_CHARACTERS_IN_SHEET_NAME, '', $name) !== $name; - } - - /** - * Returns whether the given name starts or ends with a single quote. - * - * @param string $name - * - * @return bool TRUE if the name starts or ends with a single quote, FALSE otherwise - */ - private function doesStartOrEndWithSingleQuote($name) - { - $startsWithSingleQuote = (0 === $this->stringHelper->getCharFirstOccurrencePosition('\'', $name)); - $endsWithSingleQuote = ($this->stringHelper->getCharLastOccurrencePosition('\'', $name) === ($this->stringHelper->getStringLength($name) - 1)); - - return $startsWithSingleQuote || $endsWithSingleQuote; - } - - /** - * Returns whether the given name is unique. - * - * @param string $name - * @param Sheet $sheet The sheet whose future name is checked - * - * @return bool TRUE if the name is unique, FALSE otherwise - */ - private function isNameUnique($name, Sheet $sheet) - { - foreach (self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()] as $sheetIndex => $sheetName) { - if ($sheetIndex !== $sheet->getIndex() && $sheetName === $name) { - return false; - } - } - - return true; - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php b/upstream-3.x/src/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php deleted file mode 100644 index d78dd4b..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/Style/PossiblyUpdatedStyle.php +++ /dev/null @@ -1,31 +0,0 @@ -style = $style; - $this->isUpdated = $isUpdated; - } - - public function getStyle(): Style - { - return $this->style; - } - - public function isUpdated(): bool - { - return $this->isUpdated; - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/Style/StyleManager.php b/upstream-3.x/src/Writer/Common/Manager/Style/StyleManager.php deleted file mode 100644 index 47f4d34..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/Style/StyleManager.php +++ /dev/null @@ -1,82 +0,0 @@ -styleRegistry = $styleRegistry; - } - - /** - * Registers the given style as a used style. - * Duplicate styles won't be registered more than once. - * - * @param Style $style The style to be registered - * - * @return Style the registered style, updated with an internal ID - */ - public function registerStyle($style) - { - return $this->styleRegistry->registerStyle($style); - } - - /** - * Apply additional styles if the given row needs it. - * Typically, set "wrap text" if a cell contains a new line. - * - * @return PossiblyUpdatedStyle The eventually updated style - */ - public function applyExtraStylesIfNeeded(Cell $cell): PossiblyUpdatedStyle - { - return $this->applyWrapTextIfCellContainsNewLine($cell); - } - - /** - * Returns the default style. - * - * @return Style Default style - */ - protected function getDefaultStyle() - { - // By construction, the default style has ID 0 - return $this->styleRegistry->getRegisteredStyles()[0]; - } - - /** - * Set the "wrap text" option if a cell of the given row contains a new line. - * - * @NOTE: There is a bug on the Mac version of Excel (2011 and below) where new lines - * are ignored even when the "wrap text" option is set. This only occurs with - * inline strings (shared strings do work fine). - * A workaround would be to encode "\n" as "_x000D_" but it does not work - * on the Windows version of Excel... - * - * @param Cell $cell The cell the style should be applied to - * - * @return PossiblyUpdatedStyle The eventually updated style - */ - protected function applyWrapTextIfCellContainsNewLine(Cell $cell): PossiblyUpdatedStyle - { - $cellStyle = $cell->getStyle(); - - // if the "wrap text" option is already set, no-op - if (!$cellStyle->hasSetWrapText() && $cell->isString() && false !== strpos($cell->getValue(), "\n")) { - $cellStyle->setShouldWrapText(); - - return new PossiblyUpdatedStyle($cellStyle, true); - } - - return new PossiblyUpdatedStyle($cellStyle, false); - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/Style/StyleManagerInterface.php b/upstream-3.x/src/Writer/Common/Manager/Style/StyleManagerInterface.php deleted file mode 100644 index 0418f3d..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/Style/StyleManagerInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -mergeFontStyles($mergedStyle, $style, $baseStyle); - $this->mergeOtherFontProperties($mergedStyle, $style, $baseStyle); - $this->mergeCellProperties($mergedStyle, $style, $baseStyle); - - return $mergedStyle; - } - - /** - * @param Style $styleToUpdate (passed as reference) - */ - private function mergeFontStyles(Style $styleToUpdate, Style $style, Style $baseStyle) - { - if (!$style->hasSetFontBold() && $baseStyle->isFontBold()) { - $styleToUpdate->setFontBold(); - } - if (!$style->hasSetFontItalic() && $baseStyle->isFontItalic()) { - $styleToUpdate->setFontItalic(); - } - if (!$style->hasSetFontUnderline() && $baseStyle->isFontUnderline()) { - $styleToUpdate->setFontUnderline(); - } - if (!$style->hasSetFontStrikethrough() && $baseStyle->isFontStrikethrough()) { - $styleToUpdate->setFontStrikethrough(); - } - } - - /** - * @param Style $styleToUpdate Style to update (passed as reference) - */ - private function mergeOtherFontProperties(Style $styleToUpdate, Style $style, Style $baseStyle) - { - if (!$style->hasSetFontSize() && Style::DEFAULT_FONT_SIZE !== $baseStyle->getFontSize()) { - $styleToUpdate->setFontSize($baseStyle->getFontSize()); - } - if (!$style->hasSetFontColor() && Style::DEFAULT_FONT_COLOR !== $baseStyle->getFontColor()) { - $styleToUpdate->setFontColor($baseStyle->getFontColor()); - } - if (!$style->hasSetFontName() && Style::DEFAULT_FONT_NAME !== $baseStyle->getFontName()) { - $styleToUpdate->setFontName($baseStyle->getFontName()); - } - } - - /** - * @param Style $styleToUpdate Style to update (passed as reference) - */ - private function mergeCellProperties(Style $styleToUpdate, Style $style, Style $baseStyle) - { - if (!$style->hasSetWrapText() && $baseStyle->shouldWrapText()) { - $styleToUpdate->setShouldWrapText(); - } - if (!$style->hasSetShrinkToFit() && $baseStyle->shouldShrinkToFit()) { - $styleToUpdate->setShouldShrinkToFit(); - } - if (!$style->hasSetCellAlignment() && $baseStyle->shouldApplyCellAlignment()) { - $styleToUpdate->setCellAlignment($baseStyle->getCellAlignment()); - } - if (null === $style->getBorder() && $baseStyle->shouldApplyBorder()) { - $styleToUpdate->setBorder($baseStyle->getBorder()); - } - if (null === $style->getFormat() && $baseStyle->shouldApplyFormat()) { - $styleToUpdate->setFormat($baseStyle->getFormat()); - } - if (!$style->shouldApplyBackgroundColor() && $baseStyle->shouldApplyBackgroundColor()) { - $styleToUpdate->setBackgroundColor($baseStyle->getBackgroundColor()); - } - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/Style/StyleRegistry.php b/upstream-3.x/src/Writer/Common/Manager/Style/StyleRegistry.php deleted file mode 100644 index b3782a0..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/Style/StyleRegistry.php +++ /dev/null @@ -1,111 +0,0 @@ - [STYLE_ID] mapping table, keeping track of the registered styles */ - protected $serializedStyleToStyleIdMappingTable = []; - - /** @var array [STYLE_ID] => [STYLE] mapping table, keeping track of the registered styles */ - protected $styleIdToStyleMappingTable = []; - - public function __construct(Style $defaultStyle) - { - // This ensures that the default style is the first one to be registered - $this->registerStyle($defaultStyle); - } - - /** - * Registers the given style as a used style. - * Duplicate styles won't be registered more than once. - * - * @param Style $style The style to be registered - * - * @return Style the registered style, updated with an internal ID - */ - public function registerStyle(Style $style) - { - $serializedStyle = $this->serialize($style); - - if (!$this->hasSerializedStyleAlreadyBeenRegistered($serializedStyle)) { - $nextStyleId = \count($this->serializedStyleToStyleIdMappingTable); - $style->markAsRegistered($nextStyleId); - - $this->serializedStyleToStyleIdMappingTable[$serializedStyle] = $nextStyleId; - $this->styleIdToStyleMappingTable[$nextStyleId] = $style; - } - - return $this->getStyleFromSerializedStyle($serializedStyle); - } - - /** - * @return Style[] List of registered styles - */ - public function getRegisteredStyles() - { - return array_values($this->styleIdToStyleMappingTable); - } - - /** - * @param int $styleId - * - * @return Style - */ - public function getStyleFromStyleId($styleId) - { - return $this->styleIdToStyleMappingTable[$styleId]; - } - - /** - * Serializes the style for future comparison with other styles. - * The ID is excluded from the comparison, as we only care about - * actual style properties. - * - * @return string The serialized style - */ - public function serialize(Style $style) - { - // In order to be able to properly compare style, set static ID value and reset registration - $currentId = $style->getId(); - $style->unmarkAsRegistered(); - - $serializedStyle = serialize($style); - - $style->markAsRegistered($currentId); - - return $serializedStyle; - } - - /** - * Returns whether the serialized style has already been registered. - * - * @param string $serializedStyle The serialized style - * - * @return bool - */ - protected function hasSerializedStyleAlreadyBeenRegistered(string $serializedStyle) - { - // Using isset here because it is way faster than array_key_exists... - return isset($this->serializedStyleToStyleIdMappingTable[$serializedStyle]); - } - - /** - * Returns the registered style associated to the given serialization. - * - * @param string $serializedStyle The serialized style from which the actual style should be fetched from - * - * @return Style - */ - protected function getStyleFromSerializedStyle($serializedStyle) - { - $styleId = $this->serializedStyleToStyleIdMappingTable[$serializedStyle]; - - return $this->styleIdToStyleMappingTable[$styleId]; - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/WorkbookManagerAbstract.php b/upstream-3.x/src/Writer/Common/Manager/WorkbookManagerAbstract.php deleted file mode 100644 index 8ffd7e9..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/WorkbookManagerAbstract.php +++ /dev/null @@ -1,352 +0,0 @@ -workbook = $workbook; - $this->optionsManager = $optionsManager; - $this->worksheetManager = $worksheetManager; - $this->styleManager = $styleManager; - $this->styleMerger = $styleMerger; - $this->fileSystemHelper = $fileSystemHelper; - $this->entityFactory = $entityFactory; - $this->managerFactory = $managerFactory; - } - - /** - * @return null|Workbook - */ - public function getWorkbook() - { - return $this->workbook; - } - - /** - * Creates a new sheet in the workbook and make it the current sheet. - * The writing will resume where it stopped (i.e. data won't be truncated). - * - * @return Worksheet The created sheet - */ - public function addNewSheetAndMakeItCurrent() - { - $worksheet = $this->addNewSheet(); - $this->setCurrentWorksheet($worksheet); - - return $worksheet; - } - - /** - * @return Worksheet[] All the workbook's sheets - */ - public function getWorksheets() - { - return $this->workbook->getWorksheets(); - } - - /** - * Returns the current sheet. - * - * @return Worksheet The current sheet - */ - public function getCurrentWorksheet() - { - return $this->currentWorksheet; - } - - /** - * Starts the current sheet and opens the file pointer. - * - * @throws IOException - */ - public function startCurrentSheet() - { - $this->worksheetManager->startSheet($this->getCurrentWorksheet()); - } - - /** - * Sets the given sheet as the current one. New data will be written to this sheet. - * The writing will resume where it stopped (i.e. data won't be truncated). - * - * @param Sheet $sheet The "external" sheet to set as current - * - * @throws SheetNotFoundException If the given sheet does not exist in the workbook - */ - public function setCurrentSheet(Sheet $sheet) - { - $worksheet = $this->getWorksheetFromExternalSheet($sheet); - if (null !== $worksheet) { - $this->currentWorksheet = $worksheet; - } else { - throw new SheetNotFoundException('The given sheet does not exist in the workbook.'); - } - } - - /** - * Adds a row to the current sheet. - * If shouldCreateNewSheetsAutomatically option is set to true, it will handle pagination - * with the creation of new worksheets if one worksheet has reached its maximum capicity. - * - * @param Row $row The row to be added - * - * @throws IOException If trying to create a new sheet and unable to open the sheet for writing - * @throws \OpenSpout\Common\Exception\InvalidArgumentException - */ - public function addRowToCurrentWorksheet(Row $row) - { - $currentWorksheet = $this->getCurrentWorksheet(); - $hasReachedMaxRows = $this->hasCurrentWorksheetReachedMaxRows(); - - // if we reached the maximum number of rows for the current sheet... - if ($hasReachedMaxRows) { - // ... continue writing in a new sheet if option set - if ($this->optionsManager->getOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY)) { - $currentWorksheet = $this->addNewSheetAndMakeItCurrent(); - - $this->addRowToWorksheet($currentWorksheet, $row); - } - // otherwise, do nothing as the data won't be written anyways - } else { - $this->addRowToWorksheet($currentWorksheet, $row); - } - } - - public function setDefaultColumnWidth(float $width) - { - $this->worksheetManager->setDefaultColumnWidth($width); - } - - public function setDefaultRowHeight(float $height) - { - $this->worksheetManager->setDefaultRowHeight($height); - } - - /** - * @param int ...$columns One or more columns with this width - */ - public function setColumnWidth(float $width, ...$columns) - { - $this->worksheetManager->setColumnWidth($width, ...$columns); - } - - /** - * @param float $width The width to set - * @param int $start First column index of the range - * @param int $end Last column index of the range - */ - public function setColumnWidthForRange(float $width, int $start, int $end) - { - $this->worksheetManager->setColumnWidthForRange($width, $start, $end); - } - - /** - * Closes the workbook and all its associated sheets. - * All the necessary files are written to disk and zipped together to create the final file. - * All the temporary files are then deleted. - * - * @param resource $finalFilePointer Pointer to the spreadsheet that will be created - */ - public function close($finalFilePointer) - { - $this->closeAllWorksheets(); - $this->closeRemainingObjects(); - $this->writeAllFilesToDiskAndZipThem($finalFilePointer); - $this->cleanupTempFolder(); - } - - /** - * @return int Maximum number of rows/columns a sheet can contain - */ - abstract protected function getMaxRowsPerWorksheet(); - - /** - * @return string The file path where the data for the given sheet will be stored - */ - abstract protected function getWorksheetFilePath(Sheet $sheet); - - /** - * Closes custom objects that are still opened. - */ - protected function closeRemainingObjects() - { - // do nothing by default - } - - /** - * Writes all the necessary files to disk and zip them together to create the final file. - * - * @param resource $finalFilePointer Pointer to the spreadsheet that will be created - */ - abstract protected function writeAllFilesToDiskAndZipThem($finalFilePointer); - - /** - * Deletes the root folder created in the temp folder and all its contents. - */ - protected function cleanupTempFolder() - { - $rootFolder = $this->fileSystemHelper->getRootFolder(); - $this->fileSystemHelper->deleteFolderRecursively($rootFolder); - } - - /** - * Creates a new sheet in the workbook. The current sheet remains unchanged. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to open the sheet for writing - * - * @return Worksheet The created sheet - */ - private function addNewSheet() - { - $worksheets = $this->getWorksheets(); - - $newSheetIndex = \count($worksheets); - $sheetManager = $this->managerFactory->createSheetManager(); - $sheet = $this->entityFactory->createSheet($newSheetIndex, $this->workbook->getInternalId(), $sheetManager); - - $worksheetFilePath = $this->getWorksheetFilePath($sheet); - $worksheet = $this->entityFactory->createWorksheet($worksheetFilePath, $sheet); - - $this->worksheetManager->startSheet($worksheet); - - $worksheets[] = $worksheet; - $this->workbook->setWorksheets($worksheets); - - return $worksheet; - } - - /** - * @param Worksheet $worksheet - */ - private function setCurrentWorksheet($worksheet) - { - $this->currentWorksheet = $worksheet; - } - - /** - * Returns the worksheet associated to the given external sheet. - * - * @param Sheet $sheet - * - * @return null|Worksheet the worksheet associated to the given external sheet or null if not found - */ - private function getWorksheetFromExternalSheet($sheet) - { - $worksheetFound = null; - - foreach ($this->getWorksheets() as $worksheet) { - if ($worksheet->getExternalSheet() === $sheet) { - $worksheetFound = $worksheet; - - break; - } - } - - return $worksheetFound; - } - - /** - * @return bool whether the current worksheet has reached the maximum number of rows per sheet - */ - private function hasCurrentWorksheetReachedMaxRows() - { - $currentWorksheet = $this->getCurrentWorksheet(); - - return $currentWorksheet->getLastWrittenRowIndex() >= $this->getMaxRowsPerWorksheet(); - } - - /** - * Adds a row to the given sheet. - * - * @param Worksheet $worksheet Worksheet to write the row to - * @param Row $row The row to be added - * - * @throws IOException - * @throws \OpenSpout\Common\Exception\InvalidArgumentException - */ - private function addRowToWorksheet(Worksheet $worksheet, Row $row) - { - $this->applyDefaultRowStyle($row); - $this->worksheetManager->addRow($worksheet, $row); - - // update max num columns for the worksheet - $currentMaxNumColumns = $worksheet->getMaxNumColumns(); - $cellsCount = $row->getNumCells(); - $worksheet->setMaxNumColumns(max($currentMaxNumColumns, $cellsCount)); - } - - private function applyDefaultRowStyle(Row $row) - { - $defaultRowStyle = $this->optionsManager->getOption(Options::DEFAULT_ROW_STYLE); - - if (null !== $defaultRowStyle) { - $mergedStyle = $this->styleMerger->merge($row->getStyle(), $defaultRowStyle); - $row->setStyle($mergedStyle); - } - } - - /** - * Closes all workbook's associated sheets. - */ - private function closeAllWorksheets() - { - $worksheets = $this->getWorksheets(); - - foreach ($worksheets as $worksheet) { - $this->worksheetManager->close($worksheet); - } - } -} diff --git a/upstream-3.x/src/Writer/Common/Manager/WorkbookManagerInterface.php b/upstream-3.x/src/Writer/Common/Manager/WorkbookManagerInterface.php deleted file mode 100644 index 209abc6..0000000 --- a/upstream-3.x/src/Writer/Common/Manager/WorkbookManagerInterface.php +++ /dev/null @@ -1,97 +0,0 @@ -getOption(Options::TEMP_FOLDER); - $zipHelper = $this->createZipHelper($entityFactory); - - return new FileSystemHelper($tempFolder, $zipHelper); - } - - /** - * @return Escaper\ODS - */ - public function createStringsEscaper() - { - return new Escaper\ODS(); - } - - /** - * @return StringHelper - */ - public function createStringHelper() - { - return new StringHelper(); - } - - /** - * @param InternalEntityFactory $entityFactory - * - * @return ZipHelper - */ - private function createZipHelper($entityFactory) - { - return new ZipHelper($entityFactory); - } -} diff --git a/upstream-3.x/src/Writer/ODS/Creator/ManagerFactory.php b/upstream-3.x/src/Writer/ODS/Creator/ManagerFactory.php deleted file mode 100644 index a514286..0000000 --- a/upstream-3.x/src/Writer/ODS/Creator/ManagerFactory.php +++ /dev/null @@ -1,107 +0,0 @@ -entityFactory = $entityFactory; - $this->helperFactory = $helperFactory; - } - - /** - * @return WorkbookManager - */ - public function createWorkbookManager(OptionsManagerInterface $optionsManager) - { - $workbook = $this->entityFactory->createWorkbook(); - - $fileSystemHelper = $this->helperFactory->createSpecificFileSystemHelper($optionsManager, $this->entityFactory); - $fileSystemHelper->createBaseFilesAndFolders(); - - $styleMerger = $this->createStyleMerger(); - $styleManager = $this->createStyleManager($optionsManager); - $worksheetManager = $this->createWorksheetManager($styleManager, $styleMerger); - - return new WorkbookManager( - $workbook, - $optionsManager, - $worksheetManager, - $styleManager, - $styleMerger, - $fileSystemHelper, - $this->entityFactory, - $this - ); - } - - /** - * @return SheetManager - */ - public function createSheetManager() - { - $stringHelper = $this->helperFactory->createStringHelper(); - - return new SheetManager($stringHelper); - } - - /** - * @return WorksheetManager - */ - private function createWorksheetManager(StyleManager $styleManager, StyleMerger $styleMerger) - { - $stringsEscaper = $this->helperFactory->createStringsEscaper(); - $stringsHelper = $this->helperFactory->createStringHelper(); - - return new WorksheetManager($styleManager, $styleMerger, $stringsEscaper, $stringsHelper); - } - - /** - * @return StyleManager - */ - private function createStyleManager(OptionsManagerInterface $optionsManager) - { - $styleRegistry = $this->createStyleRegistry($optionsManager); - - return new StyleManager($styleRegistry, $optionsManager); - } - - /** - * @return StyleRegistry - */ - private function createStyleRegistry(OptionsManagerInterface $optionsManager) - { - $defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE); - - return new StyleRegistry($defaultRowStyle); - } - - /** - * @return StyleMerger - */ - private function createStyleMerger() - { - return new StyleMerger(); - } -} diff --git a/upstream-3.x/src/Writer/ODS/Helper/BorderHelper.php b/upstream-3.x/src/Writer/ODS/Helper/BorderHelper.php deleted file mode 100644 index caaf954..0000000 --- a/upstream-3.x/src/Writer/ODS/Helper/BorderHelper.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ -class BorderHelper -{ - /** - * Width mappings. - * - * @var array - */ - protected static $widthMap = [ - Border::WIDTH_THIN => '0.75pt', - Border::WIDTH_MEDIUM => '1.75pt', - Border::WIDTH_THICK => '2.5pt', - ]; - - /** - * Style mapping. - * - * @var array - */ - protected static $styleMap = [ - Border::STYLE_SOLID => 'solid', - Border::STYLE_DASHED => 'dashed', - Border::STYLE_DOTTED => 'dotted', - Border::STYLE_DOUBLE => 'double', - ]; - - /** - * @return string - */ - public static function serializeBorderPart(BorderPart $borderPart) - { - $definition = 'fo:border-%s="%s"'; - - if (Border::STYLE_NONE === $borderPart->getStyle()) { - $borderPartDefinition = sprintf($definition, $borderPart->getName(), 'none'); - } else { - $attributes = [ - self::$widthMap[$borderPart->getWidth()], - self::$styleMap[$borderPart->getStyle()], - '#'.$borderPart->getColor(), - ]; - $borderPartDefinition = sprintf($definition, $borderPart->getName(), implode(' ', $attributes)); - } - - return $borderPartDefinition; - } -} diff --git a/upstream-3.x/src/Writer/ODS/Helper/FileSystemHelper.php b/upstream-3.x/src/Writer/ODS/Helper/FileSystemHelper.php deleted file mode 100644 index b3280b6..0000000 --- a/upstream-3.x/src/Writer/ODS/Helper/FileSystemHelper.php +++ /dev/null @@ -1,304 +0,0 @@ -zipHelper = $zipHelper; - } - - /** - * @return string - */ - public function getRootFolder() - { - return $this->rootFolder; - } - - /** - * @return string - */ - public function getSheetsContentTempFolder() - { - return $this->sheetsContentTempFolder; - } - - /** - * Creates all the folders needed to create a ODS file, as well as the files that won't change. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create at least one of the base folders - */ - public function createBaseFilesAndFolders() - { - $this - ->createRootFolder() - ->createMetaInfoFolderAndFile() - ->createSheetsContentTempFolder() - ->createMetaFile() - ->createMimetypeFile() - ; - } - - /** - * Creates the "content.xml" file under the root folder. - * - * @param WorksheetManager $worksheetManager - * @param StyleManager $styleManager - * @param Worksheet[] $worksheets - * - * @return FileSystemHelper - */ - public function createContentFile($worksheetManager, $styleManager, $worksheets) - { - $contentXmlFileContents = <<<'EOD' - - - EOD; - - $contentXmlFileContents .= $styleManager->getContentXmlFontFaceSectionContent(); - $contentXmlFileContents .= $styleManager->getContentXmlAutomaticStylesSectionContent($worksheets); - - $contentXmlFileContents .= ''; - - $this->createFileWithContents($this->rootFolder, self::CONTENT_XML_FILE_NAME, $contentXmlFileContents); - - // Append sheets content to "content.xml" - $contentXmlFilePath = $this->rootFolder.'/'.self::CONTENT_XML_FILE_NAME; - $contentXmlHandle = fopen($contentXmlFilePath, 'a'); - - foreach ($worksheets as $worksheet) { - // write the "" node, with the final sheet's name - fwrite($contentXmlHandle, $worksheetManager->getTableElementStartAsString($worksheet)); - - $worksheetFilePath = $worksheet->getFilePath(); - $this->copyFileContentsToTarget($worksheetFilePath, $contentXmlHandle); - - fwrite($contentXmlHandle, ''); - } - - $contentXmlFileContents = ''; - - fwrite($contentXmlHandle, $contentXmlFileContents); - fclose($contentXmlHandle); - - return $this; - } - - /** - * Deletes the temporary folder where sheets content was stored. - * - * @return FileSystemHelper - */ - public function deleteWorksheetTempFolder() - { - $this->deleteFolderRecursively($this->sheetsContentTempFolder); - - return $this; - } - - /** - * Creates the "styles.xml" file under the root folder. - * - * @param StyleManager $styleManager - * @param int $numWorksheets Number of created worksheets - * - * @return FileSystemHelper - */ - public function createStylesFile($styleManager, $numWorksheets) - { - $stylesXmlFileContents = $styleManager->getStylesXMLFileContent($numWorksheets); - $this->createFileWithContents($this->rootFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents); - - return $this; - } - - /** - * Zips the root folder and streams the contents of the zip into the given stream. - * - * @param resource $streamPointer Pointer to the stream to copy the zip - */ - public function zipRootFolderAndCopyToStream($streamPointer) - { - $zip = $this->zipHelper->createZip($this->rootFolder); - - $zipFilePath = $this->zipHelper->getZipFilePath($zip); - - // In order to have the file's mime type detected properly, files need to be added - // to the zip file in a particular order. - // @see http://www.jejik.com/articles/2010/03/how_to_correctly_create_odf_documents_using_zip/ - $this->zipHelper->addUncompressedFileToArchive($zip, $this->rootFolder, self::MIMETYPE_FILE_NAME); - - $this->zipHelper->addFolderToArchive($zip, $this->rootFolder, ZipHelper::EXISTING_FILES_SKIP); - $this->zipHelper->closeArchiveAndCopyToStream($zip, $streamPointer); - - // once the zip is copied, remove it - $this->deleteFile($zipFilePath); - } - - /** - * Creates the folder that will be used as root. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder - * - * @return FileSystemHelper - */ - protected function createRootFolder() - { - $this->rootFolder = $this->createFolder($this->baseFolderRealPath, uniqid('ods')); - - return $this; - } - - /** - * Creates the "META-INF" folder under the root folder as well as the "manifest.xml" file in it. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or the "manifest.xml" file - * - * @return FileSystemHelper - */ - protected function createMetaInfoFolderAndFile() - { - $this->metaInfFolder = $this->createFolder($this->rootFolder, self::META_INF_FOLDER_NAME); - - $this->createManifestFile(); - - return $this; - } - - /** - * Creates the "manifest.xml" file under the "META-INF" folder (under root). - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the file - * - * @return FileSystemHelper - */ - protected function createManifestFile() - { - $manifestXmlFileContents = <<<'EOD' - - - - - - - - EOD; - - $this->createFileWithContents($this->metaInfFolder, self::MANIFEST_XML_FILE_NAME, $manifestXmlFileContents); - - return $this; - } - - /** - * Creates the temp folder where specific sheets content will be written to. - * This folder is not part of the final ODS file and is only used to be able to jump between sheets. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder - * - * @return FileSystemHelper - */ - protected function createSheetsContentTempFolder() - { - $this->sheetsContentTempFolder = $this->createFolder($this->rootFolder, self::SHEETS_CONTENT_TEMP_FOLDER_NAME); - - return $this; - } - - /** - * Creates the "meta.xml" file under the root folder. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the file - * - * @return FileSystemHelper - */ - protected function createMetaFile() - { - $appName = self::APP_NAME; - $createdDate = (new \DateTime())->format(\DateTime::W3C); - - $metaXmlFileContents = << - - - {$appName} - {$createdDate} - {$createdDate} - - - EOD; - - $this->createFileWithContents($this->rootFolder, self::META_XML_FILE_NAME, $metaXmlFileContents); - - return $this; - } - - /** - * Creates the "mimetype" file under the root folder. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the file - * - * @return FileSystemHelper - */ - protected function createMimetypeFile() - { - $this->createFileWithContents($this->rootFolder, self::MIMETYPE_FILE_NAME, self::MIMETYPE); - - return $this; - } - - /** - * Streams the content of the file at the given path into the target resource. - * Depending on which mode the target resource was created with, it will truncate then copy - * or append the content to the target file. - * - * @param string $sourceFilePath Path of the file whose content will be copied - * @param resource $targetResource Target resource that will receive the content - */ - protected function copyFileContentsToTarget($sourceFilePath, $targetResource) - { - $sourceHandle = fopen($sourceFilePath, 'r'); - stream_copy_to_stream($sourceHandle, $targetResource); - fclose($sourceHandle); - } -} diff --git a/upstream-3.x/src/Writer/ODS/Manager/OptionsManager.php b/upstream-3.x/src/Writer/ODS/Manager/OptionsManager.php deleted file mode 100644 index d098abf..0000000 --- a/upstream-3.x/src/Writer/ODS/Manager/OptionsManager.php +++ /dev/null @@ -1,50 +0,0 @@ -styleBuilder = $styleBuilder; - parent::__construct(); - } - - /** - * {@inheritdoc} - */ - protected function getSupportedOptions() - { - return [ - Options::TEMP_FOLDER, - Options::DEFAULT_ROW_STYLE, - Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, - Options::DEFAULT_COLUMN_WIDTH, - Options::DEFAULT_ROW_HEIGHT, - Options::COLUMN_WIDTHS, - ]; - } - - /** - * {@inheritdoc} - */ - protected function setDefaultOptions() - { - $this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir()); - $this->setOption(Options::DEFAULT_ROW_STYLE, $this->styleBuilder->build()); - $this->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, true); - } -} diff --git a/upstream-3.x/src/Writer/ODS/Manager/Style/StyleManager.php b/upstream-3.x/src/Writer/ODS/Manager/Style/StyleManager.php deleted file mode 100644 index e1d9976..0000000 --- a/upstream-3.x/src/Writer/ODS/Manager/Style/StyleManager.php +++ /dev/null @@ -1,462 +0,0 @@ -setDefaultColumnWidth($optionsManager->getOption(Options::DEFAULT_COLUMN_WIDTH)); - $this->setDefaultRowHeight($optionsManager->getOption(Options::DEFAULT_ROW_HEIGHT)); - $this->columnWidths = $optionsManager->getOption(Options::COLUMN_WIDTHS) ?? []; - } - - /** - * Returns the content of the "styles.xml" file, given a list of styles. - * - * @param int $numWorksheets Number of worksheets created - * - * @return string - */ - public function getStylesXMLFileContent($numWorksheets) - { - $content = <<<'EOD' - - - EOD; - - $content .= $this->getFontFaceSectionContent(); - $content .= $this->getStylesSectionContent(); - $content .= $this->getAutomaticStylesSectionContent($numWorksheets); - $content .= $this->getMasterStylesSectionContent($numWorksheets); - - $content .= <<<'EOD' - - EOD; - - return $content; - } - - /** - * Returns the contents of the "" section, inside "content.xml" file. - * - * @return string - */ - public function getContentXmlFontFaceSectionContent() - { - $content = ''; - foreach ($this->styleRegistry->getUsedFonts() as $fontName) { - $content .= ''; - } - $content .= ''; - - return $content; - } - - /** - * Returns the contents of the "" section, inside "content.xml" file. - * - * @param Worksheet[] $worksheets - * - * @return string - */ - public function getContentXmlAutomaticStylesSectionContent($worksheets) - { - $content = ''; - - foreach ($this->styleRegistry->getRegisteredStyles() as $style) { - $content .= $this->getStyleSectionContent($style); - } - - $useOptimalRowHeight = empty($this->defaultRowHeight) ? 'true' : 'false'; - $defaultRowHeight = empty($this->defaultRowHeight) ? '15pt' : "{$this->defaultRowHeight}pt"; - $defaultColumnWidth = empty($this->defaultColumnWidth) ? '' : "style:column-width=\"{$this->defaultColumnWidth}pt\""; - - $content .= << - - - - - - EOD; - - foreach ($worksheets as $worksheet) { - $worksheetId = $worksheet->getId(); - $isSheetVisible = $worksheet->getExternalSheet()->isVisible() ? 'true' : 'false'; - - $content .= << - - - EOD; - } - - // Sort column widths since ODS cares about order - usort($this->columnWidths, function ($a, $b) { - if ($a[0] === $b[0]) { - return 0; - } - - return ($a[0] < $b[0]) ? -1 : 1; - }); - $content .= $this->getTableColumnStylesXMLContent(); - - $content .= ''; - - return $content; - } - - public function getTableColumnStylesXMLContent(): string - { - if (empty($this->columnWidths)) { - return ''; - } - - $content = ''; - foreach ($this->columnWidths as $styleIndex => $entry) { - $content .= << - - - EOD; - } - - return $content; - } - - public function getStyledTableColumnXMLContent(int $maxNumColumns): string - { - if (empty($this->columnWidths)) { - return ''; - } - - $content = ''; - foreach ($this->columnWidths as $styleIndex => $entry) { - $numCols = $entry[1] - $entry[0] + 1; - $content .= << - EOD; - } - // Note: This assumes the column widths are contiguous and default width is - // only applied to columns after the last custom column with a custom width - $content .= ''; - - return $content; - } - - /** - * Returns the content of the "" section, inside "styles.xml" file. - * - * @return string - */ - protected function getFontFaceSectionContent() - { - $content = ''; - foreach ($this->styleRegistry->getUsedFonts() as $fontName) { - $content .= ''; - } - $content .= ''; - - return $content; - } - - /** - * Returns the content of the "" section, inside "styles.xml" file. - * - * @return string - */ - protected function getStylesSectionContent() - { - $defaultStyle = $this->getDefaultStyle(); - - return << - - - - - - - - - EOD; - } - - /** - * Returns the content of the "" section, inside "styles.xml" file. - * - * @param int $numWorksheets Number of worksheets created - * - * @return string - */ - protected function getAutomaticStylesSectionContent($numWorksheets) - { - $content = ''; - - for ($i = 1; $i <= $numWorksheets; ++$i) { - $content .= << - - - - - EOD; - } - - $content .= ''; - - return $content; - } - - /** - * Returns the content of the "" section, inside "styles.xml" file. - * - * @param int $numWorksheets Number of worksheets created - * - * @return string - */ - protected function getMasterStylesSectionContent($numWorksheets) - { - $content = ''; - - for ($i = 1; $i <= $numWorksheets; ++$i) { - $content .= << - - - - - - EOD; - } - - $content .= ''; - - return $content; - } - - /** - * Returns the contents of the "" section, inside "" section. - * - * @param \OpenSpout\Common\Entity\Style\Style $style - * - * @return string - */ - protected function getStyleSectionContent($style) - { - $styleIndex = $style->getId() + 1; // 1-based - - $content = ''; - - $content .= $this->getTextPropertiesSectionContent($style); - $content .= $this->getParagraphPropertiesSectionContent($style); - $content .= $this->getTableCellPropertiesSectionContent($style); - - $content .= ''; - - return $content; - } - - /** - * Returns the contents of the "" section, inside "" section. - * - * @param \OpenSpout\Common\Entity\Style\Style $style - * - * @return string - */ - private function getTextPropertiesSectionContent($style) - { - if (!$style->shouldApplyFont()) { - return ''; - } - - return 'getFontSectionContent($style) - .'/>'; - } - - /** - * Returns the contents of the fonts definition section, inside "" section. - * - * @param \OpenSpout\Common\Entity\Style\Style $style - * - * @return string - */ - private function getFontSectionContent($style) - { - $defaultStyle = $this->getDefaultStyle(); - $content = ''; - - $fontColor = $style->getFontColor(); - if ($fontColor !== $defaultStyle->getFontColor()) { - $content .= ' fo:color="#'.$fontColor.'"'; - } - - $fontName = $style->getFontName(); - if ($fontName !== $defaultStyle->getFontName()) { - $content .= ' style:font-name="'.$fontName.'" style:font-name-asian="'.$fontName.'" style:font-name-complex="'.$fontName.'"'; - } - - $fontSize = $style->getFontSize(); - if ($fontSize !== $defaultStyle->getFontSize()) { - $content .= ' fo:font-size="'.$fontSize.'pt" style:font-size-asian="'.$fontSize.'pt" style:font-size-complex="'.$fontSize.'pt"'; - } - - if ($style->isFontBold()) { - $content .= ' fo:font-weight="bold" style:font-weight-asian="bold" style:font-weight-complex="bold"'; - } - if ($style->isFontItalic()) { - $content .= ' fo:font-style="italic" style:font-style-asian="italic" style:font-style-complex="italic"'; - } - if ($style->isFontUnderline()) { - $content .= ' style:text-underline-style="solid" style:text-underline-type="single"'; - } - if ($style->isFontStrikethrough()) { - $content .= ' style:text-line-through-style="solid"'; - } - - return $content; - } - - /** - * Returns the contents of the "" section, inside "" section. - * - * @param \OpenSpout\Common\Entity\Style\Style $style - * - * @return string - */ - private function getParagraphPropertiesSectionContent($style) - { - if (!$style->shouldApplyCellAlignment()) { - return ''; - } - - return 'getCellAlignmentSectionContent($style) - .'/>'; - } - - /** - * Returns the contents of the cell alignment definition for the "" section. - * - * @param \OpenSpout\Common\Entity\Style\Style $style - * - * @return string - */ - private function getCellAlignmentSectionContent($style) - { - return sprintf( - ' fo:text-align="%s" ', - $this->transformCellAlignment($style->getCellAlignment()) - ); - } - - /** - * Even though "left" and "right" alignments are part of the spec, and interpreted - * respectively as "start" and "end", using the recommended values increase compatibility - * with software that will read the created ODS file. - * - * @param string $cellAlignment - * - * @return string - */ - private function transformCellAlignment($cellAlignment) - { - switch ($cellAlignment) { - case CellAlignment::LEFT: - return 'start'; - - case CellAlignment::RIGHT: - return 'end'; - - default: - return $cellAlignment; - } - } - - /** - * Returns the contents of the "" section, inside "" section. - * - * @param \OpenSpout\Common\Entity\Style\Style $style - * - * @return string - */ - private function getTableCellPropertiesSectionContent($style) - { - $content = 'shouldWrapText()) { - $content .= $this->getWrapTextXMLContent(); - } - - if ($style->shouldApplyBorder()) { - $content .= $this->getBorderXMLContent($style); - } - - if ($style->shouldApplyBackgroundColor()) { - $content .= $this->getBackgroundColorXMLContent($style); - } - - $content .= '/>'; - - return $content; - } - - /** - * Returns the contents of the wrap text definition for the "" section. - * - * @return string - */ - private function getWrapTextXMLContent() - { - return ' fo:wrap-option="wrap" style:vertical-align="automatic" '; - } - - /** - * Returns the contents of the borders definition for the "" section. - * - * @param \OpenSpout\Common\Entity\Style\Style $style - * - * @return string - */ - private function getBorderXMLContent($style) - { - $borders = array_map(function (BorderPart $borderPart) { - return BorderHelper::serializeBorderPart($borderPart); - }, $style->getBorder()->getParts()); - - return sprintf(' %s ', implode(' ', $borders)); - } - - /** - * Returns the contents of the background color definition for the "" section. - * - * @param \OpenSpout\Common\Entity\Style\Style $style - * - * @return string - */ - private function getBackgroundColorXMLContent($style) - { - return sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor()); - } -} diff --git a/upstream-3.x/src/Writer/ODS/Manager/Style/StyleRegistry.php b/upstream-3.x/src/Writer/ODS/Manager/Style/StyleRegistry.php deleted file mode 100644 index e90dc96..0000000 --- a/upstream-3.x/src/Writer/ODS/Manager/Style/StyleRegistry.php +++ /dev/null @@ -1,42 +0,0 @@ - [] Map whose keys contain all the fonts used */ - protected $usedFontsSet = []; - - /** - * Registers the given style as a used style. - * Duplicate styles won't be registered more than once. - * - * @param Style $style The style to be registered - * - * @return Style the registered style, updated with an internal ID - */ - public function registerStyle(Style $style) - { - if ($style->isRegistered()) { - return $style; - } - - $registeredStyle = parent::registerStyle($style); - $this->usedFontsSet[$style->getFontName()] = true; - - return $registeredStyle; - } - - /** - * @return string[] List of used fonts name - */ - public function getUsedFonts() - { - return array_keys($this->usedFontsSet); - } -} diff --git a/upstream-3.x/src/Writer/ODS/Manager/WorkbookManager.php b/upstream-3.x/src/Writer/ODS/Manager/WorkbookManager.php deleted file mode 100644 index 90dc0e4..0000000 --- a/upstream-3.x/src/Writer/ODS/Manager/WorkbookManager.php +++ /dev/null @@ -1,66 +0,0 @@ -fileSystemHelper->getSheetsContentTempFolder(); - - return $sheetsContentTempFolder.'/sheet'.$sheet->getIndex().'.xml'; - } - - /** - * @return int Maximum number of rows/columns a sheet can contain - */ - protected function getMaxRowsPerWorksheet() - { - return self::$maxRowsPerWorksheet; - } - - /** - * Writes all the necessary files to disk and zip them together to create the final file. - * - * @param resource $finalFilePointer Pointer to the spreadsheet that will be created - */ - protected function writeAllFilesToDiskAndZipThem($finalFilePointer) - { - $worksheets = $this->getWorksheets(); - $numWorksheets = \count($worksheets); - - $this->fileSystemHelper - ->createContentFile($this->worksheetManager, $this->styleManager, $worksheets) - ->deleteWorksheetTempFolder() - ->createStylesFile($this->styleManager, $numWorksheets) - ->zipRootFolderAndCopyToStream($finalFilePointer) - ; - } -} diff --git a/upstream-3.x/src/Writer/ODS/Manager/WorksheetManager.php b/upstream-3.x/src/Writer/ODS/Manager/WorksheetManager.php deleted file mode 100644 index 3eeca5f..0000000 --- a/upstream-3.x/src/Writer/ODS/Manager/WorksheetManager.php +++ /dev/null @@ -1,308 +0,0 @@ -styleManager = $styleManager; - $this->styleMerger = $styleMerger; - $this->stringsEscaper = $stringsEscaper; - $this->stringHelper = $stringHelper; - } - - /** - * Prepares the worksheet to accept data. - * - * @param Worksheet $worksheet The worksheet to start - * - * @throws \OpenSpout\Common\Exception\IOException If the sheet data file cannot be opened for writing - */ - public function startSheet(Worksheet $worksheet) - { - $sheetFilePointer = fopen($worksheet->getFilePath(), 'w'); - $this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer); - - $worksheet->setFilePointer($sheetFilePointer); - } - - /** - * Returns the table XML root node as string. - * - * @return string "" node as string - */ - public function getTableElementStartAsString(Worksheet $worksheet) - { - $externalSheet = $worksheet->getExternalSheet(); - $escapedSheetName = $this->stringsEscaper->escape($externalSheet->getName()); - $tableStyleName = 'ta'.($externalSheet->getIndex() + 1); - - $tableElement = ''; - $tableElement .= $this->styleManager->getStyledTableColumnXMLContent($worksheet->getMaxNumColumns()); - - return $tableElement; - } - - /** - * Adds a row to the given worksheet. - * - * @param Worksheet $worksheet The worksheet to add the row to - * @param Row $row The row to be added - * - * @throws InvalidArgumentException If a cell value's type is not supported - * @throws IOException If the data cannot be written - */ - public function addRow(Worksheet $worksheet, Row $row) - { - $cells = $row->getCells(); - $rowStyle = $row->getStyle(); - - $data = ''; - - $currentCellIndex = 0; - $nextCellIndex = 1; - - for ($i = 0; $i < $row->getNumCells(); ++$i) { - /** @var Cell $cell */ - $cell = $cells[$currentCellIndex]; - /** @var null|Cell $nextCell */ - $nextCell = $cells[$nextCellIndex] ?? null; - - if (null === $nextCell || $cell->getValue() !== $nextCell->getValue()) { - $registeredStyle = $this->applyStyleAndRegister($cell, $rowStyle); - $cellStyle = $registeredStyle->getStyle(); - if ($registeredStyle->isMatchingRowStyle()) { - $rowStyle = $cellStyle; // Replace actual rowStyle (possibly with null id) by registered style (with id) - } - - $data .= $this->getCellXMLWithStyle($cell, $cellStyle, $currentCellIndex, $nextCellIndex); - $currentCellIndex = $nextCellIndex; - } - - ++$nextCellIndex; - } - - $data .= ''; - - $wasWriteSuccessful = fwrite($worksheet->getFilePointer(), $data); - if (false === $wasWriteSuccessful) { - throw new IOException("Unable to write data in {$worksheet->getFilePath()}"); - } - - // only update the count if the write worked - $lastWrittenRowIndex = $worksheet->getLastWrittenRowIndex(); - $worksheet->setLastWrittenRowIndex($lastWrittenRowIndex + 1); - } - - /** - * Closes the worksheet. - */ - public function close(Worksheet $worksheet) - { - $worksheetFilePointer = $worksheet->getFilePointer(); - - if (!\is_resource($worksheetFilePointer)) { - return; - } - - fclose($worksheetFilePointer); - } - - /** - * @param null|float $width - */ - public function setDefaultColumnWidth($width) - { - $this->styleManager->setDefaultColumnWidth($width); - } - - /** - * @param null|float $height - */ - public function setDefaultRowHeight($height) - { - $this->styleManager->setDefaultRowHeight($height); - } - - /** - * @param int ...$columns One or more columns with this width - */ - public function setColumnWidth(float $width, ...$columns) - { - $this->styleManager->setColumnWidth($width, ...$columns); - } - - /** - * @param float $width The width to set - * @param int $start First column index of the range - * @param int $end Last column index of the range - */ - public function setColumnWidthForRange(float $width, int $start, int $end) - { - $this->styleManager->setColumnWidthForRange($width, $start, $end); - } - - /** - * Checks if the sheet has been sucessfully created. Throws an exception if not. - * - * @param bool|resource $sheetFilePointer Pointer to the sheet data file or FALSE if unable to open the file - * - * @throws IOException If the sheet data file cannot be opened for writing - */ - private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer) - { - if (!$sheetFilePointer) { - throw new IOException('Unable to open sheet for writing.'); - } - } - - /** - * Applies styles to the given style, merging the cell's style with its row's style. - * - * @throws InvalidArgumentException If a cell value's type is not supported - */ - private function applyStyleAndRegister(Cell $cell, Style $rowStyle): RegisteredStyle - { - $isMatchingRowStyle = false; - if ($cell->getStyle()->isEmpty()) { - $cell->setStyle($rowStyle); - - $possiblyUpdatedStyle = $this->styleManager->applyExtraStylesIfNeeded($cell); - - if ($possiblyUpdatedStyle->isUpdated()) { - $registeredStyle = $this->styleManager->registerStyle($possiblyUpdatedStyle->getStyle()); - } else { - $registeredStyle = $this->styleManager->registerStyle($rowStyle); - $isMatchingRowStyle = true; - } - } else { - $mergedCellAndRowStyle = $this->styleMerger->merge($cell->getStyle(), $rowStyle); - $cell->setStyle($mergedCellAndRowStyle); - - $possiblyUpdatedStyle = $this->styleManager->applyExtraStylesIfNeeded($cell); - if ($possiblyUpdatedStyle->isUpdated()) { - $newCellStyle = $possiblyUpdatedStyle->getStyle(); - } else { - $newCellStyle = $mergedCellAndRowStyle; - } - - $registeredStyle = $this->styleManager->registerStyle($newCellStyle); - } - - return new RegisteredStyle($registeredStyle, $isMatchingRowStyle); - } - - private function getCellXMLWithStyle(Cell $cell, Style $style, int $currentCellIndex, int $nextCellIndex): string - { - $styleIndex = $style->getId() + 1; // 1-based - - $numTimesValueRepeated = ($nextCellIndex - $currentCellIndex); - - return $this->getCellXML($cell, $styleIndex, $numTimesValueRepeated); - } - - /** - * Returns the cell XML content, given its value. - * - * @param Cell $cell The cell to be written - * @param int $styleIndex Index of the used style - * @param int $numTimesValueRepeated Number of times the value is consecutively repeated - * - * @throws InvalidArgumentException If a cell value's type is not supported - * - * @return string The cell XML content - */ - private function getCellXML(Cell $cell, $styleIndex, $numTimesValueRepeated) - { - $data = 'isString()) { - $data .= ' office:value-type="string" calcext:value-type="string">'; - - $cellValueLines = explode("\n", $cell->getValue()); - foreach ($cellValueLines as $cellValueLine) { - $data .= ''.$this->stringsEscaper->escape($cellValueLine).''; - } - - $data .= ''; - } elseif ($cell->isBoolean()) { - $value = $cell->getValue() ? 'true' : 'false'; // boolean-value spec: http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#datatype-boolean - $data .= ' office:value-type="boolean" calcext:value-type="boolean" office:boolean-value="'.$value.'">'; - $data .= ''.$cell->getValue().''; - $data .= ''; - } elseif ($cell->isNumeric()) { - $cellValue = $this->stringHelper->formatNumericValue($cell->getValue()); - $data .= ' office:value-type="float" calcext:value-type="float" office:value="'.$cellValue.'">'; - $data .= ''.$cellValue.''; - $data .= ''; - } elseif ($cell->isDate()) { - $value = $cell->getValue(); - if ($value instanceof \DateTimeInterface) { - $datevalue = substr((new \DateTimeImmutable('@'.$value->getTimestamp()))->format(\DateTimeInterface::W3C), 0, -6); - $data .= ' office:value-type="date" calcext:value-type="date" office:date-value="'.$datevalue.'Z">'; - $data .= ''.$datevalue.'Z'; - } elseif ($value instanceof \DateInterval) { - // workaround for missing DateInterval::format('c'), see https://stackoverflow.com/a/61088115/53538 - static $f = ['M0S', 'H0M', 'DT0H', 'M0D', 'Y0M', 'P0Y', 'Y0M', 'P0M']; - static $r = ['M', 'H', 'DT', 'M', 'Y0M', 'P', 'Y', 'P']; - $value = rtrim(str_replace($f, $r, $value->format('P%yY%mM%dDT%hH%iM%sS')), 'PT') ?: 'PT0S'; - $data .= ' office:value-type="time" office:time-value="'.$value.'">'; - $data .= ''.$value.''; - } else { - throw new InvalidArgumentException('Trying to add a date value with an unsupported type: '.\gettype($cell->getValue())); - } - $data .= ''; - } elseif ($cell->isError() && \is_string($cell->getValueEvenIfError())) { - // only writes the error value if it's a string - $data .= ' office:value-type="string" calcext:value-type="error" office:value="">'; - $data .= ''.$cell->getValueEvenIfError().''; - $data .= ''; - } elseif ($cell->isEmpty()) { - $data .= '/>'; - } else { - throw new InvalidArgumentException('Trying to add a value with an unsupported type: '.\gettype($cell->getValue())); - } - - return $data; - } -} diff --git a/upstream-3.x/src/Writer/ODS/Writer.php b/upstream-3.x/src/Writer/ODS/Writer.php deleted file mode 100644 index 5f510e6..0000000 --- a/upstream-3.x/src/Writer/ODS/Writer.php +++ /dev/null @@ -1,34 +0,0 @@ -throwIfWriterAlreadyOpened('Writer must be configured before opening it.'); - - $this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder); - - return $this; - } -} diff --git a/upstream-3.x/src/Writer/WriterAbstract.php b/upstream-3.x/src/Writer/WriterAbstract.php deleted file mode 100644 index a2a5e37..0000000 --- a/upstream-3.x/src/Writer/WriterAbstract.php +++ /dev/null @@ -1,270 +0,0 @@ -optionsManager = $optionsManager; - $this->globalFunctionsHelper = $globalFunctionsHelper; - $this->helperFactory = $helperFactory; - } - - /** - * {@inheritdoc} - */ - public function setDefaultRowStyle(Style $defaultStyle) - { - $this->optionsManager->setOption(Options::DEFAULT_ROW_STYLE, $defaultStyle); - - return $this; - } - - public function writeToStream($filePointer) - { - $this->outputFilePath = null; - - $this->filePointer = $filePointer; - $this->dontCloseFilePointer = true; - $this->throwIfFilePointerIsNotAvailable(); - - $this->openWriter(); - $this->isWriterOpened = true; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function openToFile($outputFilePath) - { - $this->outputFilePath = $outputFilePath; - - $this->filePointer = $this->globalFunctionsHelper->fopen($this->outputFilePath, 'w'); - $this->throwIfFilePointerIsNotAvailable(); - - $this->openWriter(); - $this->isWriterOpened = true; - - return $this; - } - - /** - * @codeCoverageIgnore - * {@inheritdoc} - */ - public function openToBrowser($outputFileName) - { - $this->outputFilePath = $this->globalFunctionsHelper->basename($outputFileName); - - $this->filePointer = $this->globalFunctionsHelper->fopen('php://output', 'w'); - $this->throwIfFilePointerIsNotAvailable(); - - // Clear any previous output (otherwise the generated file will be corrupted) - // @see https://github.com/box/spout/issues/241 - $this->globalFunctionsHelper->ob_end_clean(); - - /* - * Set headers - * - * For newer browsers such as Firefox, Chrome, Opera, Safari, etc., they all support and use `filename*` - * specified by the new standard, even if they do not automatically decode filename; it does not matter; - * and for older versions of Internet Explorer, they are not recognized `filename*`, will automatically - * ignore it and use the old `filename` (the only minor flaw is that there must be an English suffix name). - * In this way, the multi-browser multi-language compatibility problem is perfectly solved, which does not - * require UA judgment and is more in line with the standard. - * - * @see https://github.com/box/spout/issues/745 - * @see https://tools.ietf.org/html/rfc6266 - * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition - */ - $this->globalFunctionsHelper->header('Content-Type: '.static::$headerContentType); - $this->globalFunctionsHelper->header( - 'Content-Disposition: attachment; '. - 'filename="'.rawurlencode($this->outputFilePath).'"; '. - 'filename*=UTF-8\'\''.rawurlencode($this->outputFilePath) - ); - - /* - * When forcing the download of a file over SSL,IE8 and lower browsers fail - * if the Cache-Control and Pragma headers are not set. - * - * @see http://support.microsoft.com/KB/323308 - * @see https://github.com/liuggio/ExcelBundle/issues/45 - */ - $this->globalFunctionsHelper->header('Cache-Control: max-age=0'); - $this->globalFunctionsHelper->header('Pragma: public'); - - $this->openWriter(); - $this->isWriterOpened = true; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function addRow(Row $row) - { - if ($this->isWriterOpened) { - try { - $this->addRowToWriter($row); - } catch (SpoutException $e) { - // if an exception occurs while writing data, - // close the writer and remove all files created so far. - $this->closeAndAttemptToCleanupAllFiles(); - - // re-throw the exception to alert developers of the error - throw $e; - } - } else { - throw new WriterNotOpenedException('The writer needs to be opened before adding row.'); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function addRows(array $rows) - { - foreach ($rows as $row) { - if (!$row instanceof Row) { - $this->closeAndAttemptToCleanupAllFiles(); - - throw new InvalidArgumentException('The input should be an array of Row'); - } - - $this->addRow($row); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function close() - { - if (!$this->isWriterOpened) { - return; - } - - $this->closeWriter(); - - if (!$this->dontCloseFilePointer && \is_resource($this->filePointer)) { - $this->globalFunctionsHelper->fclose($this->filePointer); - } - - $this->isWriterOpened = false; - } - - /** - * Opens the streamer and makes it ready to accept data. - * - * @throws IOException If the writer cannot be opened - */ - abstract protected function openWriter(); - - /** - * Adds a row to the currently opened writer. - * - * @param Row $row The row containing cells and styles - * - * @throws WriterNotOpenedException If the workbook is not created yet - * @throws IOException If unable to write data - */ - abstract protected function addRowToWriter(Row $row); - - /** - * Closes the streamer, preventing any additional writing. - */ - abstract protected function closeWriter(); - - /** - * Checks if the pointer to the file/stream to write to is available. - * Will throw an exception if not available. - * - * @throws IOException If the pointer is not available - */ - protected function throwIfFilePointerIsNotAvailable() - { - if (!\is_resource($this->filePointer)) { - throw new IOException('File pointer has not be opened'); - } - } - - /** - * Checks if the writer has already been opened, since some actions must be done before it gets opened. - * Throws an exception if already opened. - * - * @param string $message Error message - * - * @throws WriterAlreadyOpenedException if the writer was already opened and must not be - */ - protected function throwIfWriterAlreadyOpened($message) - { - if ($this->isWriterOpened) { - throw new WriterAlreadyOpenedException($message); - } - } - - /** - * Closes the writer and attempts to cleanup all files that were - * created during the writing process (temp files & final file). - */ - private function closeAndAttemptToCleanupAllFiles() - { - // close the writer, which should remove all temp files - $this->close(); - - // remove output file if it was created - if ($this->globalFunctionsHelper->file_exists($this->outputFilePath)) { - $outputFolderPath = \dirname($this->outputFilePath); - $fileSystemHelper = $this->helperFactory->createFileSystemHelper($outputFolderPath); - $fileSystemHelper->deleteFile($this->outputFilePath); - } - } -} diff --git a/upstream-3.x/src/Writer/WriterInterface.php b/upstream-3.x/src/Writer/WriterInterface.php deleted file mode 100644 index 71a298e..0000000 --- a/upstream-3.x/src/Writer/WriterInterface.php +++ /dev/null @@ -1,77 +0,0 @@ -managerFactory = $managerFactory; - } - - /** - * Sets whether new sheets should be automatically created when the max rows limit per sheet is reached. - * This must be set before opening the writer. - * - * @param bool $shouldCreateNewSheetsAutomatically Whether new sheets should be automatically created when the max rows limit per sheet is reached - * - * @throws WriterAlreadyOpenedException If the writer was already opened - * - * @return WriterMultiSheetsAbstract - */ - public function setShouldCreateNewSheetsAutomatically($shouldCreateNewSheetsAutomatically) - { - $this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.'); - - $this->optionsManager->setOption( - Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, - $shouldCreateNewSheetsAutomatically - ); - - return $this; - } - - /** - * Returns all the workbook's sheets. - * - * @throws WriterNotOpenedException If the writer has not been opened yet - * - * @return Sheet[] All the workbook's sheets - */ - public function getSheets() - { - $this->throwIfWorkbookIsNotAvailable(); - - $externalSheets = []; - $worksheets = $this->workbookManager->getWorksheets(); - - foreach ($worksheets as $worksheet) { - $externalSheets[] = $worksheet->getExternalSheet(); - } - - return $externalSheets; - } - - /** - * Creates a new sheet and make it the current sheet. The data will now be written to this sheet. - * - * @throws IOException - * @throws WriterNotOpenedException If the writer has not been opened yet - * - * @return Sheet The created sheet - */ - public function addNewSheetAndMakeItCurrent() - { - $this->throwIfWorkbookIsNotAvailable(); - $worksheet = $this->workbookManager->addNewSheetAndMakeItCurrent(); - - return $worksheet->getExternalSheet(); - } - - /** - * Returns the current sheet. - * - * @throws WriterNotOpenedException If the writer has not been opened yet - * - * @return Sheet The current sheet - */ - public function getCurrentSheet() - { - $this->throwIfWorkbookIsNotAvailable(); - - return $this->workbookManager->getCurrentWorksheet()->getExternalSheet(); - } - - /** - * Sets the given sheet as the current one. New data will be written to this sheet. - * The writing will resume where it stopped (i.e. data won't be truncated). - * - * @param Sheet $sheet The sheet to set as current - * - * @throws SheetNotFoundException If the given sheet does not exist in the workbook - * @throws WriterNotOpenedException If the writer has not been opened yet - */ - public function setCurrentSheet($sheet) - { - $this->throwIfWorkbookIsNotAvailable(); - $this->workbookManager->setCurrentSheet($sheet); - } - - /** - * @throws WriterAlreadyOpenedException - */ - public function setDefaultColumnWidth(float $width) - { - $this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.'); - $this->optionsManager->setOption( - Options::DEFAULT_COLUMN_WIDTH, - $width - ); - } - - /** - * @throws WriterAlreadyOpenedException - */ - public function setDefaultRowHeight(float $height) - { - $this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.'); - $this->optionsManager->setOption( - Options::DEFAULT_ROW_HEIGHT, - $height - ); - } - - /** - * @param null|float $width - * @param int ...$columns One or more columns with this width - * - * @throws WriterNotOpenedException - */ - public function setColumnWidth($width, ...$columns) - { - $this->throwIfWorkbookIsNotAvailable(); - $this->workbookManager->setColumnWidth($width, ...$columns); - } - - /** - * @param float $width The width to set - * @param int $start First column index of the range - * @param int $end Last column index of the range - * - * @throws WriterNotOpenedException - */ - public function setColumnWidthForRange(float $width, int $start, int $end) - { - $this->throwIfWorkbookIsNotAvailable(); - $this->workbookManager->setColumnWidthForRange($width, $start, $end); - } - - /** - * {@inheritdoc} - */ - protected function openWriter() - { - if (null === $this->workbookManager) { - $this->workbookManager = $this->managerFactory->createWorkbookManager($this->optionsManager); - $this->workbookManager->addNewSheetAndMakeItCurrent(); - } - } - - /** - * Checks if the workbook has been created. Throws an exception if not created yet. - * - * @throws WriterNotOpenedException If the workbook is not created yet - */ - protected function throwIfWorkbookIsNotAvailable() - { - if (!$this->workbookManager->getWorkbook()) { - throw new WriterNotOpenedException('The writer must be opened before performing this action.'); - } - } - - /** - * {@inheritdoc} - * - * @throws Exception\WriterException - */ - protected function addRowToWriter(Row $row) - { - $this->throwIfWorkbookIsNotAvailable(); - $this->workbookManager->addRowToCurrentWorksheet($row); - } - - /** - * {@inheritdoc} - */ - protected function closeWriter() - { - if (null !== $this->workbookManager) { - $this->workbookManager->close($this->filePointer); - } - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Creator/HelperFactory.php b/upstream-3.x/src/Writer/XLSX/Creator/HelperFactory.php deleted file mode 100644 index 38a7f0d..0000000 --- a/upstream-3.x/src/Writer/XLSX/Creator/HelperFactory.php +++ /dev/null @@ -1,53 +0,0 @@ -getOption(Options::TEMP_FOLDER); - $zipHelper = $this->createZipHelper($entityFactory); - $escaper = $this->createStringsEscaper(); - - return new FileSystemHelper($tempFolder, $zipHelper, $escaper); - } - - /** - * @return Escaper\XLSX - */ - public function createStringsEscaper() - { - return new Escaper\XLSX(); - } - - /** - * @return StringHelper - */ - public function createStringHelper() - { - return new StringHelper(); - } - - /** - * @return ZipHelper - */ - private function createZipHelper(InternalEntityFactory $entityFactory) - { - return new ZipHelper($entityFactory); - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Creator/ManagerFactory.php b/upstream-3.x/src/Writer/XLSX/Creator/ManagerFactory.php deleted file mode 100644 index 47e60ce..0000000 --- a/upstream-3.x/src/Writer/XLSX/Creator/ManagerFactory.php +++ /dev/null @@ -1,145 +0,0 @@ -entityFactory = $entityFactory; - $this->helperFactory = $helperFactory; - } - - /** - * @return WorkbookManager - */ - public function createWorkbookManager(OptionsManagerInterface $optionsManager) - { - $workbook = $this->entityFactory->createWorkbook(); - - $fileSystemHelper = $this->helperFactory->createSpecificFileSystemHelper($optionsManager, $this->entityFactory); - $fileSystemHelper->createBaseFilesAndFolders(); - - $xlFolder = $fileSystemHelper->getXlFolder(); - $sharedStringsManager = $this->createSharedStringsManager($xlFolder); - - $styleMerger = $this->createStyleMerger(); - $styleManager = $this->createStyleManager($optionsManager); - $worksheetManager = $this->createWorksheetManager($optionsManager, $styleManager, $styleMerger, $sharedStringsManager); - - return new WorkbookManager( - $workbook, - $optionsManager, - $worksheetManager, - $styleManager, - $styleMerger, - $fileSystemHelper, - $this->entityFactory, - $this - ); - } - - /** - * @return SheetManager - */ - public function createSheetManager() - { - $stringHelper = $this->helperFactory->createStringHelper(); - - return new SheetManager($stringHelper); - } - - /** - * @return RowManager - */ - public function createRowManager() - { - return new RowManager(); - } - - /** - * @return WorksheetManager - */ - private function createWorksheetManager( - OptionsManagerInterface $optionsManager, - StyleManager $styleManager, - StyleMerger $styleMerger, - SharedStringsManager $sharedStringsManager - ) { - $rowManager = $this->createRowManager(); - $stringsEscaper = $this->helperFactory->createStringsEscaper(); - $stringsHelper = $this->helperFactory->createStringHelper(); - - return new WorksheetManager( - $optionsManager, - $rowManager, - $styleManager, - $styleMerger, - $sharedStringsManager, - $stringsEscaper, - $stringsHelper - ); - } - - /** - * @return StyleManager - */ - private function createStyleManager(OptionsManagerInterface $optionsManager) - { - $styleRegistry = $this->createStyleRegistry($optionsManager); - - return new StyleManager($styleRegistry); - } - - /** - * @return StyleRegistry - */ - private function createStyleRegistry(OptionsManagerInterface $optionsManager) - { - $defaultRowStyle = $optionsManager->getOption(Options::DEFAULT_ROW_STYLE); - - return new StyleRegistry($defaultRowStyle); - } - - /** - * @return StyleMerger - */ - private function createStyleMerger() - { - return new StyleMerger(); - } - - /** - * @param string $xlFolder Path to the "xl" folder - * - * @return SharedStringsManager - */ - private function createSharedStringsManager($xlFolder) - { - $stringEscaper = $this->helperFactory->createStringsEscaper(); - - return new SharedStringsManager($xlFolder, $stringEscaper); - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Entity/SheetView.php b/upstream-3.x/src/Writer/XLSX/Entity/SheetView.php deleted file mode 100644 index 7141615..0000000 --- a/upstream-3.x/src/Writer/XLSX/Entity/SheetView.php +++ /dev/null @@ -1,289 +0,0 @@ -showFormulas = $showFormulas; - - return $this; - } - - /** - * @return $this - */ - public function setShowGridLines(bool $showGridLines): self - { - $this->showGridLines = $showGridLines; - - return $this; - } - - /** - * @return $this - */ - public function setShowRowColHeaders(bool $showRowColHeaders): self - { - $this->showRowColHeaders = $showRowColHeaders; - - return $this; - } - - /** - * @return $this - */ - public function setShowZeroes(bool $showZeroes): self - { - $this->showZeroes = $showZeroes; - - return $this; - } - - /** - * @return $this - */ - public function setRightToLeft(bool $rightToLeft): self - { - $this->rightToLeft = $rightToLeft; - - return $this; - } - - /** - * @return $this - */ - public function setTabSelected(bool $tabSelected): self - { - $this->tabSelected = $tabSelected; - - return $this; - } - - /** - * @return $this - */ - public function setShowOutlineSymbols(bool $showOutlineSymbols): self - { - $this->showOutlineSymbols = $showOutlineSymbols; - - return $this; - } - - /** - * @return $this - */ - public function setDefaultGridColor(bool $defaultGridColor): self - { - $this->defaultGridColor = $defaultGridColor; - - return $this; - } - - /** - * @return $this - */ - public function setView(string $view): self - { - $this->view = $view; - - return $this; - } - - /** - * @return $this - */ - public function setTopLeftCell(string $topLeftCell): self - { - $this->topLeftCell = $topLeftCell; - - return $this; - } - - /** - * @return $this - */ - public function setColorId(int $colorId): self - { - $this->colorId = $colorId; - - return $this; - } - - /** - * @return $this - */ - public function setZoomScale(int $zoomScale): self - { - $this->zoomScale = $zoomScale; - - return $this; - } - - /** - * @return $this - */ - public function setZoomScaleNormal(int $zoomScaleNormal): self - { - $this->zoomScaleNormal = $zoomScaleNormal; - - return $this; - } - - /** - * @return $this - */ - public function setZoomScalePageLayoutView(int $zoomScalePageLayoutView): self - { - $this->zoomScalePageLayoutView = $zoomScalePageLayoutView; - - return $this; - } - - /** - * @return $this - */ - public function setWorkbookViewId(int $workbookViewId): self - { - $this->workbookViewId = $workbookViewId; - - return $this; - } - - /** - * @param int $freezeRow Set to 2 to fix the first row - * - * @return $this - */ - public function setFreezeRow(int $freezeRow): self - { - if ($freezeRow < 1) { - throw new InvalidArgumentException('Freeze row must be a positive integer', 1589543073); - } - - $this->freezeRow = $freezeRow; - - return $this; - } - - /** - * @param string $freezeColumn Set to B to fix the first column - * - * @return $this - */ - public function setFreezeColumn(string $freezeColumn): self - { - $this->freezeColumn = strtoupper($freezeColumn); - - return $this; - } - - public function getXml(): string - { - return 'getSheetViewAttributes().'>'. - $this->getFreezeCellPaneXml(). - ''; - } - - protected function getSheetViewAttributes(): string - { - // Get class properties - $propertyValues = get_object_vars($this); - unset($propertyValues['freezeRow'], $propertyValues['freezeColumn']); - - return $this->generateAttributes($propertyValues); - } - - protected function getFreezeCellPaneXml(): string - { - if ($this->freezeRow < 2 && 'A' === $this->freezeColumn) { - return ''; - } - - $columnIndex = CellHelper::getColumnIndexFromCellIndex($this->freezeColumn.'1'); - - return 'generateAttributes([ - 'xSplit' => $columnIndex, - 'ySplit' => $this->freezeRow - 1, - 'topLeftCell' => $this->freezeColumn.$this->freezeRow, - 'activePane' => 'bottomRight', - 'state' => 'frozen', - ]).'/>'; - } - - /** - * @param array $data with key containing the attribute name and value containing the attribute value - */ - protected function generateAttributes(array $data): string - { - // Create attribute for each key - $attributes = array_map(function ($key, $value) { - if (\is_bool($value)) { - $value = $value ? 'true' : 'false'; - } - - return $key.'="'.$value.'"'; - }, array_keys($data), $data); - - // Append all attributes - return ' '.implode(' ', $attributes); - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Helper/BorderHelper.php b/upstream-3.x/src/Writer/XLSX/Helper/BorderHelper.php deleted file mode 100644 index 1df43b9..0000000 --- a/upstream-3.x/src/Writer/XLSX/Helper/BorderHelper.php +++ /dev/null @@ -1,66 +0,0 @@ - [ - Border::WIDTH_THIN => 'thin', - Border::WIDTH_MEDIUM => 'medium', - Border::WIDTH_THICK => 'thick', - ], - Border::STYLE_DOTTED => [ - Border::WIDTH_THIN => 'dotted', - Border::WIDTH_MEDIUM => 'dotted', - Border::WIDTH_THICK => 'dotted', - ], - Border::STYLE_DASHED => [ - Border::WIDTH_THIN => 'dashed', - Border::WIDTH_MEDIUM => 'mediumDashed', - Border::WIDTH_THICK => 'mediumDashed', - ], - Border::STYLE_DOUBLE => [ - Border::WIDTH_THIN => 'double', - Border::WIDTH_MEDIUM => 'double', - Border::WIDTH_THICK => 'double', - ], - Border::STYLE_NONE => [ - Border::WIDTH_THIN => 'none', - Border::WIDTH_MEDIUM => 'none', - Border::WIDTH_THICK => 'none', - ], - ]; - - /** - * @return string - */ - public static function serializeBorderPart(BorderPart $borderPart) - { - $borderStyle = self::getBorderStyle($borderPart); - - $colorEl = $borderPart->getColor() ? sprintf('', $borderPart->getColor()) : ''; - $partEl = sprintf( - '<%s style="%s">%s', - $borderPart->getName(), - $borderStyle, - $colorEl, - $borderPart->getName() - ); - - return $partEl.PHP_EOL; - } - - /** - * Get the style definition from the style map. - * - * @return string - */ - protected static function getBorderStyle(BorderPart $borderPart) - { - return self::$xlsxStyleMap[$borderPart->getStyle()][$borderPart->getWidth()]; - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Helper/DateHelper.php b/upstream-3.x/src/Writer/XLSX/Helper/DateHelper.php deleted file mode 100644 index fb72df5..0000000 --- a/upstream-3.x/src/Writer/XLSX/Helper/DateHelper.php +++ /dev/null @@ -1,45 +0,0 @@ -format('Y'); - $month = (int) $dateTime->format('m'); - $day = (int) $dateTime->format('d'); - $hours = (int) $dateTime->format('H'); - $minutes = (int) $dateTime->format('i'); - $seconds = (int) $dateTime->format('s'); - // Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel - // This affects every date following 28th February 1900 - $excel1900isLeapYear = true; - if ((1900 === $year) && ($month <= 2)) { - $excel1900isLeapYear = false; - } - $myexcelBaseDate = 2415020; - - // Julian base date Adjustment - if ($month > 2) { - $month -= 3; - } else { - $month += 9; - --$year; - } - - // Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0) - $century = (int) substr((string) $year, 0, 2); - $decade = (int) substr((string) $year, 2, 2); - $excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myexcelBaseDate + $excel1900isLeapYear; - - $excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400; - - return (float) $excelDate + $excelTime; - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Helper/FileSystemHelper.php b/upstream-3.x/src/Writer/XLSX/Helper/FileSystemHelper.php deleted file mode 100644 index 12e4d79..0000000 --- a/upstream-3.x/src/Writer/XLSX/Helper/FileSystemHelper.php +++ /dev/null @@ -1,403 +0,0 @@ -zipHelper = $zipHelper; - $this->escaper = $escaper; - } - - /** - * @return string - */ - public function getRootFolder() - { - return $this->rootFolder; - } - - /** - * @return string - */ - public function getXlFolder() - { - return $this->xlFolder; - } - - /** - * @return string - */ - public function getXlWorksheetsFolder() - { - return $this->xlWorksheetsFolder; - } - - /** - * Creates all the folders needed to create a XLSX file, as well as the files that won't change. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create at least one of the base folders - */ - public function createBaseFilesAndFolders() - { - $this - ->createRootFolder() - ->createRelsFolderAndFile() - ->createDocPropsFolderAndFiles() - ->createXlFolderAndSubFolders() - ; - } - - /** - * Creates the "[Content_Types].xml" file under the root folder. - * - * @param Worksheet[] $worksheets - * - * @return FileSystemHelper - */ - public function createContentTypesFile($worksheets) - { - $contentTypesXmlFileContents = <<<'EOD' - - - - - - EOD; - - /** @var Worksheet $worksheet */ - foreach ($worksheets as $worksheet) { - $contentTypesXmlFileContents .= ''; - } - - $contentTypesXmlFileContents .= <<<'EOD' - - - - - - EOD; - - $this->createFileWithContents($this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME, $contentTypesXmlFileContents); - - return $this; - } - - /** - * Creates the "workbook.xml" file under the "xl" folder. - * - * @param Worksheet[] $worksheets - * - * @return FileSystemHelper - */ - public function createWorkbookFile($worksheets) - { - $workbookXmlFileContents = <<<'EOD' - - - - EOD; - - /** @var Worksheet $worksheet */ - foreach ($worksheets as $worksheet) { - $worksheetName = $worksheet->getExternalSheet()->getName(); - $worksheetVisibility = $worksheet->getExternalSheet()->isVisible() ? 'visible' : 'hidden'; - $worksheetId = $worksheet->getId(); - $workbookXmlFileContents .= ''; - } - - $workbookXmlFileContents .= <<<'EOD' - - - EOD; - - $this->createFileWithContents($this->xlFolder, self::WORKBOOK_XML_FILE_NAME, $workbookXmlFileContents); - - return $this; - } - - /** - * Creates the "workbook.xml.res" file under the "xl/_res" folder. - * - * @param Worksheet[] $worksheets - * - * @return FileSystemHelper - */ - public function createWorkbookRelsFile($worksheets) - { - $workbookRelsXmlFileContents = <<<'EOD' - - - - - EOD; - - /** @var Worksheet $worksheet */ - foreach ($worksheets as $worksheet) { - $worksheetId = $worksheet->getId(); - $workbookRelsXmlFileContents .= ''; - } - - $workbookRelsXmlFileContents .= ''; - - $this->createFileWithContents($this->xlRelsFolder, self::WORKBOOK_RELS_XML_FILE_NAME, $workbookRelsXmlFileContents); - - return $this; - } - - /** - * Creates the "styles.xml" file under the "xl" folder. - * - * @param StyleManager $styleManager - * - * @return FileSystemHelper - */ - public function createStylesFile($styleManager) - { - $stylesXmlFileContents = $styleManager->getStylesXMLFileContent(); - $this->createFileWithContents($this->xlFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents); - - return $this; - } - - /** - * Zips the root folder and streams the contents of the zip into the given stream. - * - * @param resource $streamPointer Pointer to the stream to copy the zip - */ - public function zipRootFolderAndCopyToStream($streamPointer) - { - $zip = $this->zipHelper->createZip($this->rootFolder); - - $zipFilePath = $this->zipHelper->getZipFilePath($zip); - - // In order to have the file's mime type detected properly, files need to be added - // to the zip file in a particular order. - // "[Content_Types].xml" then at least 2 files located in "xl" folder should be zipped first. - $this->zipHelper->addFileToArchive($zip, $this->rootFolder, self::CONTENT_TYPES_XML_FILE_NAME); - $this->zipHelper->addFileToArchive($zip, $this->rootFolder, self::XL_FOLDER_NAME.'/'.self::WORKBOOK_XML_FILE_NAME); - $this->zipHelper->addFileToArchive($zip, $this->rootFolder, self::XL_FOLDER_NAME.'/'.self::STYLES_XML_FILE_NAME); - - $this->zipHelper->addFolderToArchive($zip, $this->rootFolder, ZipHelper::EXISTING_FILES_SKIP); - $this->zipHelper->closeArchiveAndCopyToStream($zip, $streamPointer); - - // once the zip is copied, remove it - $this->deleteFile($zipFilePath); - } - - /** - * Creates the folder that will be used as root. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder - * - * @return FileSystemHelper - */ - private function createRootFolder() - { - $this->rootFolder = $this->createFolder($this->baseFolderRealPath, uniqid('xlsx', true)); - - return $this; - } - - /** - * Creates the "_rels" folder under the root folder as well as the ".rels" file in it. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or the ".rels" file - * - * @return FileSystemHelper - */ - private function createRelsFolderAndFile() - { - $this->relsFolder = $this->createFolder($this->rootFolder, self::RELS_FOLDER_NAME); - - $this->createRelsFile(); - - return $this; - } - - /** - * Creates the ".rels" file under the "_rels" folder (under root). - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the file - * - * @return FileSystemHelper - */ - private function createRelsFile() - { - $relsFileContents = <<<'EOD' - - - - - - - EOD; - - $this->createFileWithContents($this->relsFolder, self::RELS_FILE_NAME, $relsFileContents); - - return $this; - } - - /** - * Creates the "docProps" folder under the root folder as well as the "app.xml" and "core.xml" files in it. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder or one of the files - * - * @return FileSystemHelper - */ - private function createDocPropsFolderAndFiles() - { - $this->docPropsFolder = $this->createFolder($this->rootFolder, self::DOC_PROPS_FOLDER_NAME); - - $this->createAppXmlFile(); - $this->createCoreXmlFile(); - - return $this; - } - - /** - * Creates the "app.xml" file under the "docProps" folder. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the file - * - * @return FileSystemHelper - */ - private function createAppXmlFile() - { - $appName = self::APP_NAME; - $appXmlFileContents = << - - {$appName} - 0 - - EOD; - - $this->createFileWithContents($this->docPropsFolder, self::APP_XML_FILE_NAME, $appXmlFileContents); - - return $this; - } - - /** - * Creates the "core.xml" file under the "docProps" folder. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the file - * - * @return FileSystemHelper - */ - private function createCoreXmlFile() - { - $createdDate = (new \DateTime())->format(\DateTime::W3C); - $coreXmlFileContents = << - - {$createdDate} - {$createdDate} - 0 - - EOD; - - $this->createFileWithContents($this->docPropsFolder, self::CORE_XML_FILE_NAME, $coreXmlFileContents); - - return $this; - } - - /** - * Creates the "xl" folder under the root folder as well as its subfolders. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create at least one of the folders - * - * @return FileSystemHelper - */ - private function createXlFolderAndSubFolders() - { - $this->xlFolder = $this->createFolder($this->rootFolder, self::XL_FOLDER_NAME); - $this->createXlRelsFolder(); - $this->createXlWorksheetsFolder(); - - return $this; - } - - /** - * Creates the "_rels" folder under the "xl" folder. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder - * - * @return FileSystemHelper - */ - private function createXlRelsFolder() - { - $this->xlRelsFolder = $this->createFolder($this->xlFolder, self::RELS_FOLDER_NAME); - - return $this; - } - - /** - * Creates the "worksheets" folder under the "xl" folder. - * - * @throws \OpenSpout\Common\Exception\IOException If unable to create the folder - * - * @return FileSystemHelper - */ - private function createXlWorksheetsFolder() - { - $this->xlWorksheetsFolder = $this->createFolder($this->xlFolder, self::WORKSHEETS_FOLDER_NAME); - - return $this; - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Manager/OptionsManager.php b/upstream-3.x/src/Writer/XLSX/Manager/OptionsManager.php deleted file mode 100644 index b7e7eae..0000000 --- a/upstream-3.x/src/Writer/XLSX/Manager/OptionsManager.php +++ /dev/null @@ -1,64 +0,0 @@ -styleBuilder = $styleBuilder; - parent::__construct(); - } - - /** - * {@inheritdoc} - */ - protected function getSupportedOptions() - { - return [ - Options::TEMP_FOLDER, - Options::DEFAULT_ROW_STYLE, - Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, - Options::SHOULD_USE_INLINE_STRINGS, - Options::DEFAULT_COLUMN_WIDTH, - Options::DEFAULT_ROW_HEIGHT, - Options::COLUMN_WIDTHS, - Options::MERGE_CELLS, - ]; - } - - /** - * {@inheritdoc} - */ - protected function setDefaultOptions() - { - $defaultRowStyle = $this->styleBuilder - ->setFontSize(self::DEFAULT_FONT_SIZE) - ->setFontName(self::DEFAULT_FONT_NAME) - ->build() - ; - - $this->setOption(Options::TEMP_FOLDER, sys_get_temp_dir()); - $this->setOption(Options::DEFAULT_ROW_STYLE, $defaultRowStyle); - $this->setOption(Options::SHOULD_CREATE_NEW_SHEETS_AUTOMATICALLY, true); - $this->setOption(Options::SHOULD_USE_INLINE_STRINGS, true); - $this->setOption(Options::MERGE_CELLS, []); - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Manager/SharedStringsManager.php b/upstream-3.x/src/Writer/XLSX/Manager/SharedStringsManager.php deleted file mode 100644 index 9739556..0000000 --- a/upstream-3.x/src/Writer/XLSX/Manager/SharedStringsManager.php +++ /dev/null @@ -1,103 +0,0 @@ - - sharedStringsFilePointer = fopen($sharedStringsFilePath, 'w'); - - $this->throwIfSharedStringsFilePointerIsNotAvailable(); - - // the headers is split into different parts so that we can fseek and put in the correct count and uniqueCount later - $header = self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER.' '.self::DEFAULT_STRINGS_COUNT_PART.'>'; - fwrite($this->sharedStringsFilePointer, $header); - - $this->stringsEscaper = $stringsEscaper; - } - - /** - * Writes the given string into the sharedStrings.xml file. - * Starting and ending whitespaces are preserved. - * - * @param string $string - * - * @return int ID of the written shared string - */ - public function writeString($string) - { - fwrite($this->sharedStringsFilePointer, ''.$this->stringsEscaper->escape($string).''); - ++$this->numSharedStrings; - - // Shared string ID is zero-based - return $this->numSharedStrings - 1; - } - - /** - * Finishes writing the data in the sharedStrings.xml file and closes the file. - */ - public function close() - { - if (!\is_resource($this->sharedStringsFilePointer)) { - return; - } - - fwrite($this->sharedStringsFilePointer, ''); - - // Replace the default strings count with the actual number of shared strings in the file header - $firstPartHeaderLength = \strlen(self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER); - $defaultStringsCountPartLength = \strlen(self::DEFAULT_STRINGS_COUNT_PART); - - // Adding 1 to take into account the space between the last xml attribute and "count" - fseek($this->sharedStringsFilePointer, $firstPartHeaderLength + 1); - fwrite($this->sharedStringsFilePointer, sprintf("%-{$defaultStringsCountPartLength}s", 'count="'.$this->numSharedStrings.'" uniqueCount="'.$this->numSharedStrings.'"')); - - fclose($this->sharedStringsFilePointer); - } - - /** - * Checks if the book has been created. Throws an exception if not created yet. - * - * @throws \OpenSpout\Common\Exception\IOException If the sheet data file cannot be opened for writing - */ - protected function throwIfSharedStringsFilePointerIsNotAvailable() - { - if (!\is_resource($this->sharedStringsFilePointer)) { - throw new IOException('Unable to open shared strings file for writing.'); - } - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Manager/Style/StyleManager.php b/upstream-3.x/src/Writer/XLSX/Manager/Style/StyleManager.php deleted file mode 100644 index 5f284b9..0000000 --- a/upstream-3.x/src/Writer/XLSX/Manager/Style/StyleManager.php +++ /dev/null @@ -1,342 +0,0 @@ -styleRegistry->getFillIdForStyleId($styleId); - $hasStyleCustomFill = (null !== $associatedFillId && 0 !== $associatedFillId); - - $associatedBorderId = $this->styleRegistry->getBorderIdForStyleId($styleId); - $hasStyleCustomBorders = (null !== $associatedBorderId && 0 !== $associatedBorderId); - - $associatedFormatId = $this->styleRegistry->getFormatIdForStyleId($styleId); - $hasStyleCustomFormats = (null !== $associatedFormatId && 0 !== $associatedFormatId); - - return $hasStyleCustomFill || $hasStyleCustomBorders || $hasStyleCustomFormats; - } - - /** - * Returns the content of the "styles.xml" file, given a list of styles. - * - * @return string - */ - public function getStylesXMLFileContent() - { - $content = <<<'EOD' - - - EOD; - - $content .= $this->getFormatsSectionContent(); - $content .= $this->getFontsSectionContent(); - $content .= $this->getFillsSectionContent(); - $content .= $this->getBordersSectionContent(); - $content .= $this->getCellStyleXfsSectionContent(); - $content .= $this->getCellXfsSectionContent(); - $content .= $this->getCellStylesSectionContent(); - - $content .= <<<'EOD' - - EOD; - - return $content; - } - - /** - * Returns the content of the "" section. - * - * @return string - */ - protected function getFormatsSectionContent() - { - $tags = []; - $registeredFormats = $this->styleRegistry->getRegisteredFormats(); - foreach ($registeredFormats as $styleId) { - $numFmtId = $this->styleRegistry->getFormatIdForStyleId($styleId); - - //Built-in formats do not need to be declared, skip them - if ($numFmtId < 164) { - continue; - } - - /** @var Style $style */ - $style = $this->styleRegistry->getStyleFromStyleId($styleId); - $format = $style->getFormat(); - $tags[] = ''; - } - $content = ''; - $content .= implode('', $tags); - $content .= ''; - - return $content; - } - - /** - * Returns the content of the "" section. - * - * @return string - */ - protected function getFontsSectionContent() - { - $registeredStyles = $this->styleRegistry->getRegisteredStyles(); - - $content = ''; - - /** @var Style $style */ - foreach ($registeredStyles as $style) { - $content .= ''; - - $content .= ''; - $content .= ''; - $content .= ''; - - if ($style->isFontBold()) { - $content .= ''; - } - if ($style->isFontItalic()) { - $content .= ''; - } - if ($style->isFontUnderline()) { - $content .= ''; - } - if ($style->isFontStrikethrough()) { - $content .= ''; - } - - $content .= ''; - } - - $content .= ''; - - return $content; - } - - /** - * Returns the content of the "" section. - * - * @return string - */ - protected function getFillsSectionContent() - { - $registeredFills = $this->styleRegistry->getRegisteredFills(); - - // Excel reserves two default fills - $fillsCount = \count($registeredFills) + 2; - $content = sprintf('', $fillsCount); - - $content .= ''; - $content .= ''; - - // The other fills are actually registered by setting a background color - foreach ($registeredFills as $styleId) { - /** @var Style $style */ - $style = $this->styleRegistry->getStyleFromStyleId($styleId); - - $backgroundColor = $style->getBackgroundColor(); - $content .= sprintf( - '', - $backgroundColor - ); - } - - $content .= ''; - - return $content; - } - - /** - * Returns the content of the "" section. - * - * @return string - */ - protected function getBordersSectionContent() - { - $registeredBorders = $this->styleRegistry->getRegisteredBorders(); - - // There is one default border with index 0 - $borderCount = \count($registeredBorders) + 1; - - $content = ''; - - // Default border starting at index 0 - $content .= ''; - - foreach ($registeredBorders as $styleId) { - /** @var Style $style */ - $style = $this->styleRegistry->getStyleFromStyleId($styleId); - $border = $style->getBorder(); - $content .= ''; - - /** @see https://github.com/box/spout/issues/271 */ - $sortOrder = ['left', 'right', 'top', 'bottom']; - - foreach ($sortOrder as $partName) { - if ($border->hasPart($partName)) { - /** @var BorderPart $part */ - $part = $border->getPart($partName); - $content .= BorderHelper::serializeBorderPart($part); - } - } - - $content .= ''; - } - - $content .= ''; - - return $content; - } - - /** - * Returns the content of the "" section. - * - * @return string - */ - protected function getCellStyleXfsSectionContent() - { - return <<<'EOD' - - - - EOD; - } - - /** - * Returns the content of the "" section. - * - * @return string - */ - protected function getCellXfsSectionContent() - { - $registeredStyles = $this->styleRegistry->getRegisteredStyles(); - - $content = ''; - - foreach ($registeredStyles as $style) { - $styleId = $style->getId(); - $fillId = $this->getFillIdForStyleId($styleId); - $borderId = $this->getBorderIdForStyleId($styleId); - $numFmtId = $this->getFormatIdForStyleId($styleId); - - $content .= 'shouldApplyFont()) { - $content .= ' applyFont="1"'; - } - - $content .= sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0); - - if ($style->shouldApplyCellAlignment() || $style->shouldWrapText() || $style->shouldShrinkToFit()) { - $content .= ' applyAlignment="1">'; - $content .= 'shouldApplyCellAlignment()) { - $content .= sprintf(' horizontal="%s"', $style->getCellAlignment()); - } - if ($style->shouldWrapText()) { - $content .= ' wrapText="1"'; - } - if ($style->shouldShrinkToFit()) { - $content .= ' shrinkToFit="true"'; - } - - $content .= '/>'; - $content .= ''; - } else { - $content .= '/>'; - } - } - - $content .= ''; - - return $content; - } - - /** - * Returns the content of the "" section. - * - * @return string - */ - protected function getCellStylesSectionContent() - { - return <<<'EOD' - - - - EOD; - } - - /** - * Returns the fill ID associated to the given style ID. - * For the default style, we don't a fill. - * - * @param int $styleId - * - * @return int - */ - private function getFillIdForStyleId($styleId) - { - // For the default style (ID = 0), we don't want to override the fill. - // Otherwise all cells of the spreadsheet will have a background color. - $isDefaultStyle = (0 === $styleId); - - return $isDefaultStyle ? 0 : ($this->styleRegistry->getFillIdForStyleId($styleId) ?: 0); - } - - /** - * Returns the fill ID associated to the given style ID. - * For the default style, we don't a border. - * - * @param int $styleId - * - * @return int - */ - private function getBorderIdForStyleId($styleId) - { - // For the default style (ID = 0), we don't want to override the border. - // Otherwise all cells of the spreadsheet will have a border. - $isDefaultStyle = (0 === $styleId); - - return $isDefaultStyle ? 0 : ($this->styleRegistry->getBorderIdForStyleId($styleId) ?: 0); - } - - /** - * Returns the format ID associated to the given style ID. - * For the default style use general format. - * - * @param int $styleId - * - * @return int - */ - private function getFormatIdForStyleId($styleId) - { - // For the default style (ID = 0), we don't want to override the format. - // Otherwise all cells of the spreadsheet will have a format. - $isDefaultStyle = (0 === $styleId); - - return $isDefaultStyle ? 0 : ($this->styleRegistry->getFormatIdForStyleId($styleId) ?: 0); - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Manager/Style/StyleRegistry.php b/upstream-3.x/src/Writer/XLSX/Manager/Style/StyleRegistry.php deleted file mode 100644 index 259d4e8..0000000 --- a/upstream-3.x/src/Writer/XLSX/Manager/Style/StyleRegistry.php +++ /dev/null @@ -1,276 +0,0 @@ - 0, - '0' => 1, - '0.00' => 2, - '#,##0' => 3, - '#,##0.00' => 4, - '$#,##0,\-$#,##0' => 5, - '$#,##0,[Red]\-$#,##0' => 6, - '$#,##0.00,\-$#,##0.00' => 7, - '$#,##0.00,[Red]\-$#,##0.00' => 8, - '0%' => 9, - '0.00%' => 10, - '0.00E+00' => 11, - '# ?/?' => 12, - '# ??/??' => 13, - 'mm-dd-yy' => 14, - 'd-mmm-yy' => 15, - 'd-mmm' => 16, - 'mmm-yy' => 17, - 'h:mm AM/PM' => 18, - 'h:mm:ss AM/PM' => 19, - 'h:mm' => 20, - 'h:mm:ss' => 21, - 'm/d/yy h:mm' => 22, - - '#,##0 ,(#,##0)' => 37, - '#,##0 ,[Red](#,##0)' => 38, - '#,##0.00,(#,##0.00)' => 39, - '#,##0.00,[Red](#,##0.00)' => 40, - - '_("$"* #,##0.00_),_("$"* \(#,##0.00\),_("$"* "-"??_),_(@_)' => 44, - 'mm:ss' => 45, - '[h]:mm:ss' => 46, - 'mm:ss.0' => 47, - - '##0.0E+0' => 48, - '@' => 49, - - '[$-404]e/m/d' => 27, - 'm/d/yy' => 30, - 't0' => 59, - 't0.00' => 60, - 't#,##0' => 61, - 't#,##0.00' => 62, - 't0%' => 67, - 't0.00%' => 68, - 't# ?/?' => 69, - 't# ??/??' => 70, - ]; - - /** - * @var array - */ - protected $registeredFormats = []; - - /** - * @var array [STYLE_ID] => [FORMAT_ID] maps a style to a format declaration - */ - protected $styleIdToFormatsMappingTable = []; - - /** - * If the numFmtId is lower than 0xA4 (164 in decimal) - * then it's a built-in number format. - * Since Excel is the dominant vendor - we play along here. - * - * @var int the fill index counter for custom fills - */ - protected $formatIndex = 164; - - /** - * @var array - */ - protected $registeredFills = []; - - /** - * @var array [STYLE_ID] => [FILL_ID] maps a style to a fill declaration - */ - protected $styleIdToFillMappingTable = []; - - /** - * Excel preserves two default fills with index 0 and 1 - * Since Excel is the dominant vendor - we play along here. - * - * @var int the fill index counter for custom fills - */ - protected $fillIndex = 2; - - /** - * @var array - */ - protected $registeredBorders = []; - - /** - * @var array [STYLE_ID] => [BORDER_ID] maps a style to a border declaration - */ - protected $styleIdToBorderMappingTable = []; - - /** - * XLSX specific operations on the registered styles. - * - * @return Style - */ - public function registerStyle(Style $style) - { - if ($style->isRegistered()) { - return $style; - } - - $registeredStyle = parent::registerStyle($style); - $this->registerFill($registeredStyle); - $this->registerFormat($registeredStyle); - $this->registerBorder($registeredStyle); - - return $registeredStyle; - } - - /** - * @param int $styleId - * - * @return null|int Format ID associated to the given style ID - */ - public function getFormatIdForStyleId($styleId) - { - return $this->styleIdToFormatsMappingTable[$styleId] ?? null; - } - - /** - * @param int $styleId - * - * @return null|int Fill ID associated to the given style ID - */ - public function getFillIdForStyleId($styleId) - { - return (isset($this->styleIdToFillMappingTable[$styleId])) ? - $this->styleIdToFillMappingTable[$styleId] : - null; - } - - /** - * @param int $styleId - * - * @return null|int Fill ID associated to the given style ID - */ - public function getBorderIdForStyleId($styleId) - { - return (isset($this->styleIdToBorderMappingTable[$styleId])) ? - $this->styleIdToBorderMappingTable[$styleId] : - null; - } - - /** - * @return array - */ - public function getRegisteredFills() - { - return $this->registeredFills; - } - - /** - * @return array - */ - public function getRegisteredBorders() - { - return $this->registeredBorders; - } - - /** - * @return array - */ - public function getRegisteredFormats() - { - return $this->registeredFormats; - } - - /** - * Register a format definition. - */ - protected function registerFormat(Style $style) - { - $styleId = $style->getId(); - - $format = $style->getFormat(); - if ($format) { - $isFormatRegistered = isset($this->registeredFormats[$format]); - - // We need to track the already registered format definitions - if ($isFormatRegistered) { - $registeredStyleId = $this->registeredFormats[$format]; - $registeredFormatId = $this->styleIdToFormatsMappingTable[$registeredStyleId]; - $this->styleIdToFormatsMappingTable[$styleId] = $registeredFormatId; - } else { - $this->registeredFormats[$format] = $styleId; - - $id = self::$builtinNumFormatToIdMapping[$format] ?? $this->formatIndex++; - $this->styleIdToFormatsMappingTable[$styleId] = $id; - } - } else { - // The formatId maps a style to a format declaration - // When there is no format definition - we default to 0 ( General ) - $this->styleIdToFormatsMappingTable[$styleId] = 0; - } - } - - /** - * Register a fill definition. - */ - private function registerFill(Style $style) - { - $styleId = $style->getId(); - - // Currently - only solid backgrounds are supported - // so $backgroundColor is a scalar value (RGB Color) - $backgroundColor = $style->getBackgroundColor(); - - if ($backgroundColor) { - $isBackgroundColorRegistered = isset($this->registeredFills[$backgroundColor]); - - // We need to track the already registered background definitions - if ($isBackgroundColorRegistered) { - $registeredStyleId = $this->registeredFills[$backgroundColor]; - $registeredFillId = $this->styleIdToFillMappingTable[$registeredStyleId]; - $this->styleIdToFillMappingTable[$styleId] = $registeredFillId; - } else { - $this->registeredFills[$backgroundColor] = $styleId; - $this->styleIdToFillMappingTable[$styleId] = $this->fillIndex++; - } - } else { - // The fillId maps a style to a fill declaration - // When there is no background color definition - we default to 0 - $this->styleIdToFillMappingTable[$styleId] = 0; - } - } - - /** - * Register a border definition. - */ - private function registerBorder(Style $style) - { - $styleId = $style->getId(); - - if ($style->shouldApplyBorder()) { - $border = $style->getBorder(); - $serializedBorder = serialize($border); - - $isBorderAlreadyRegistered = isset($this->registeredBorders[$serializedBorder]); - - if ($isBorderAlreadyRegistered) { - $registeredStyleId = $this->registeredBorders[$serializedBorder]; - $registeredBorderId = $this->styleIdToBorderMappingTable[$registeredStyleId]; - $this->styleIdToBorderMappingTable[$styleId] = $registeredBorderId; - } else { - $this->registeredBorders[$serializedBorder] = $styleId; - $this->styleIdToBorderMappingTable[$styleId] = \count($this->registeredBorders); - } - } else { - // If no border should be applied - the mapping is the default border: 0 - $this->styleIdToBorderMappingTable[$styleId] = 0; - } - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Manager/WorkbookManager.php b/upstream-3.x/src/Writer/XLSX/Manager/WorkbookManager.php deleted file mode 100644 index 2feb331..0000000 --- a/upstream-3.x/src/Writer/XLSX/Manager/WorkbookManager.php +++ /dev/null @@ -1,74 +0,0 @@ -fileSystemHelper->getXlWorksheetsFolder(); - - return $worksheetFilesFolder.'/'.strtolower($sheet->getName()).'.xml'; - } - - /** - * @return int Maximum number of rows/columns a sheet can contain - */ - protected function getMaxRowsPerWorksheet() - { - return self::$maxRowsPerWorksheet; - } - - /** - * Closes custom objects that are still opened. - */ - protected function closeRemainingObjects() - { - $this->worksheetManager->getSharedStringsManager()->close(); - } - - /** - * Writes all the necessary files to disk and zip them together to create the final file. - * - * @param resource $finalFilePointer Pointer to the spreadsheet that will be created - */ - protected function writeAllFilesToDiskAndZipThem($finalFilePointer) - { - $worksheets = $this->getWorksheets(); - - $this->fileSystemHelper - ->createContentTypesFile($worksheets) - ->createWorkbookFile($worksheets) - ->createWorkbookRelsFile($worksheets) - ->createStylesFile($this->styleManager) - ->zipRootFolderAndCopyToStream($finalFilePointer) - ; - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Manager/WorksheetManager.php b/upstream-3.x/src/Writer/XLSX/Manager/WorksheetManager.php deleted file mode 100644 index c84be5a..0000000 --- a/upstream-3.x/src/Writer/XLSX/Manager/WorksheetManager.php +++ /dev/null @@ -1,376 +0,0 @@ - - - EOD; - - /** @var bool Whether inline or shared strings should be used */ - protected $shouldUseInlineStrings; - - /** @var OptionsManagerInterface */ - private $optionsManager; - - /** @var RowManager Manages rows */ - private $rowManager; - - /** @var StyleManager Manages styles */ - private $styleManager; - - /** @var StyleMerger Helper to merge styles together */ - private $styleMerger; - - /** @var SharedStringsManager Helper to write shared strings */ - private $sharedStringsManager; - - /** @var XLSXEscaper Strings escaper */ - private $stringsEscaper; - - /** @var StringHelper String helper */ - private $stringHelper; - - /** - * WorksheetManager constructor. - */ - public function __construct( - OptionsManagerInterface $optionsManager, - RowManager $rowManager, - StyleManager $styleManager, - StyleMerger $styleMerger, - SharedStringsManager $sharedStringsManager, - XLSXEscaper $stringsEscaper, - StringHelper $stringHelper - ) { - $this->optionsManager = $optionsManager; - $this->shouldUseInlineStrings = $optionsManager->getOption(Options::SHOULD_USE_INLINE_STRINGS); - $this->setDefaultColumnWidth($optionsManager->getOption(Options::DEFAULT_COLUMN_WIDTH)); - $this->setDefaultRowHeight($optionsManager->getOption(Options::DEFAULT_ROW_HEIGHT)); - $this->columnWidths = $optionsManager->getOption(Options::COLUMN_WIDTHS) ?? []; - $this->rowManager = $rowManager; - $this->styleManager = $styleManager; - $this->styleMerger = $styleMerger; - $this->sharedStringsManager = $sharedStringsManager; - $this->stringsEscaper = $stringsEscaper; - $this->stringHelper = $stringHelper; - } - - /** - * @return SharedStringsManager - */ - public function getSharedStringsManager() - { - return $this->sharedStringsManager; - } - - /** - * {@inheritdoc} - */ - public function startSheet(Worksheet $worksheet) - { - $sheetFilePointer = fopen($worksheet->getFilePath(), 'w'); - $this->throwIfSheetFilePointerIsNotAvailable($sheetFilePointer); - - $worksheet->setFilePointer($sheetFilePointer); - - fwrite($sheetFilePointer, self::SHEET_XML_FILE_HEADER); - } - - /** - * {@inheritdoc} - */ - public function addRow(Worksheet $worksheet, Row $row) - { - if (!$this->rowManager->isEmpty($row)) { - $this->addNonEmptyRow($worksheet, $row); - } - - $worksheet->setLastWrittenRowIndex($worksheet->getLastWrittenRowIndex() + 1); - } - - /** - * Construct column width references xml to inject into worksheet xml file. - * - * @return string - */ - public function getXMLFragmentForColumnWidths() - { - if (empty($this->columnWidths)) { - return ''; - } - $xml = ''; - foreach ($this->columnWidths as $entry) { - $xml .= ''; - } - $xml .= ''; - - return $xml; - } - - /** - * Constructs default row height and width xml to inject into worksheet xml file. - * - * @return string - */ - public function getXMLFragmentForDefaultCellSizing() - { - $rowHeightXml = empty($this->defaultRowHeight) ? '' : " defaultRowHeight=\"{$this->defaultRowHeight}\""; - $colWidthXml = empty($this->defaultColumnWidth) ? '' : " defaultColWidth=\"{$this->defaultColumnWidth}\""; - if (empty($colWidthXml) && empty($rowHeightXml)) { - return ''; - } - // Ensure that the required defaultRowHeight is set - $rowHeightXml = empty($rowHeightXml) ? ' defaultRowHeight="0"' : $rowHeightXml; - - return ""; - } - - /** - * {@inheritdoc} - */ - public function close(Worksheet $worksheet) - { - $worksheetFilePointer = $worksheet->getFilePointer(); - - if (!\is_resource($worksheetFilePointer)) { - return; - } - $this->ensureSheetDataStated($worksheet); - fwrite($worksheetFilePointer, ''); - - // create nodes for merge cells - if ($this->optionsManager->getOption(Options::MERGE_CELLS)) { - $mergeCellString = ''; - foreach ($this->optionsManager->getOption(Options::MERGE_CELLS) as $values) { - $output = array_map(function ($value) { - return CellHelper::getColumnLettersFromColumnIndex($value[0]).$value[1]; - }, $values); - $mergeCellString .= ''; - } - $mergeCellString .= ''; - fwrite($worksheet->getFilePointer(), $mergeCellString); - } - - fwrite($worksheetFilePointer, ''); - fclose($worksheetFilePointer); - } - - /** - * Writes the sheet data header. - * - * @param Worksheet $worksheet The worksheet to add the row to - */ - private function ensureSheetDataStated(Worksheet $worksheet) - { - if (!$worksheet->getSheetDataStarted()) { - $worksheetFilePointer = $worksheet->getFilePointer(); - $sheet = $worksheet->getExternalSheet(); - if ($sheet->hasSheetView()) { - fwrite($worksheetFilePointer, ''.$sheet->getSheetView()->getXml().''); - } - fwrite($worksheetFilePointer, $this->getXMLFragmentForDefaultCellSizing()); - fwrite($worksheetFilePointer, $this->getXMLFragmentForColumnWidths()); - fwrite($worksheetFilePointer, ''); - $worksheet->setSheetDataStarted(true); - } - } - - /** - * Checks if the sheet has been sucessfully created. Throws an exception if not. - * - * @param bool|resource $sheetFilePointer Pointer to the sheet data file or FALSE if unable to open the file - * - * @throws IOException If the sheet data file cannot be opened for writing - */ - private function throwIfSheetFilePointerIsNotAvailable($sheetFilePointer) - { - if (!$sheetFilePointer) { - throw new IOException('Unable to open sheet for writing.'); - } - } - - /** - * Adds non empty row to the worksheet. - * - * @param Worksheet $worksheet The worksheet to add the row to - * @param Row $row The row to be written - * - * @throws InvalidArgumentException If a cell value's type is not supported - * @throws IOException If the data cannot be written - */ - private function addNonEmptyRow(Worksheet $worksheet, Row $row) - { - $this->ensureSheetDataStated($worksheet); - $sheetFilePointer = $worksheet->getFilePointer(); - $rowStyle = $row->getStyle(); - $rowIndexOneBased = $worksheet->getLastWrittenRowIndex() + 1; - $numCells = $row->getNumCells(); - - $hasCustomHeight = $this->defaultRowHeight > 0 ? '1' : '0'; - $rowXML = ""; - - foreach ($row->getCells() as $columnIndexZeroBased => $cell) { - $registeredStyle = $this->applyStyleAndRegister($cell, $rowStyle); - $cellStyle = $registeredStyle->getStyle(); - if ($registeredStyle->isMatchingRowStyle()) { - $rowStyle = $cellStyle; // Replace actual rowStyle (possibly with null id) by registered style (with id) - } - $rowXML .= $this->getCellXML($rowIndexOneBased, $columnIndexZeroBased, $cell, $cellStyle->getId()); - } - - $rowXML .= ''; - - $wasWriteSuccessful = fwrite($sheetFilePointer, $rowXML); - if (false === $wasWriteSuccessful) { - throw new IOException("Unable to write data in {$worksheet->getFilePath()}"); - } - } - - /** - * Applies styles to the given style, merging the cell's style with its row's style. - * - * @throws InvalidArgumentException If the given value cannot be processed - */ - private function applyStyleAndRegister(Cell $cell, Style $rowStyle): RegisteredStyle - { - $isMatchingRowStyle = false; - if ($cell->getStyle()->isEmpty()) { - $cell->setStyle($rowStyle); - - $possiblyUpdatedStyle = $this->styleManager->applyExtraStylesIfNeeded($cell); - - if ($possiblyUpdatedStyle->isUpdated()) { - $registeredStyle = $this->styleManager->registerStyle($possiblyUpdatedStyle->getStyle()); - } else { - $registeredStyle = $this->styleManager->registerStyle($rowStyle); - $isMatchingRowStyle = true; - } - } else { - $mergedCellAndRowStyle = $this->styleMerger->merge($cell->getStyle(), $rowStyle); - $cell->setStyle($mergedCellAndRowStyle); - - $possiblyUpdatedStyle = $this->styleManager->applyExtraStylesIfNeeded($cell); - - if ($possiblyUpdatedStyle->isUpdated()) { - $newCellStyle = $possiblyUpdatedStyle->getStyle(); - } else { - $newCellStyle = $mergedCellAndRowStyle; - } - - $registeredStyle = $this->styleManager->registerStyle($newCellStyle); - } - - return new RegisteredStyle($registeredStyle, $isMatchingRowStyle); - } - - /** - * Builds and returns xml for a single cell. - * - * @param int $rowIndexOneBased - * @param int $columnIndexZeroBased - * @param int $styleId - * - * @throws InvalidArgumentException If the given value cannot be processed - * - * @return string - */ - private function getCellXML($rowIndexOneBased, $columnIndexZeroBased, Cell $cell, $styleId) - { - $columnLetters = CellHelper::getColumnLettersFromColumnIndex($columnIndexZeroBased); - $cellXML = 'isString()) { - $cellXML .= $this->getCellXMLFragmentForNonEmptyString($cell->getValue()); - } elseif ($cell->isBoolean()) { - $cellXML .= ' t="b">'.(int) ($cell->getValue()).''; - } elseif ($cell->isNumeric()) { - $cellXML .= '>'.$this->stringHelper->formatNumericValue($cell->getValue()).''; - } elseif ($cell->isFormula()) { - $cellXML .= '>'.substr($cell->getValue(), 1).''; - } elseif ($cell->isDate()) { - $value = $cell->getValue(); - if ($value instanceof \DateTimeInterface) { - $cellXML .= '>'.(string) DateHelper::toExcel($value).''; - } else { - throw new InvalidArgumentException('Trying to add a date value with an unsupported type: '.\gettype($value)); - } - } elseif ($cell->isError() && \is_string($cell->getValueEvenIfError())) { - // only writes the error value if it's a string - $cellXML .= ' t="e">'.$cell->getValueEvenIfError().''; - } elseif ($cell->isEmpty()) { - if ($this->styleManager->shouldApplyStyleOnEmptyCell($styleId)) { - $cellXML .= '/>'; - } else { - // don't write empty cells that do no need styling - // NOTE: not appending to $cellXML is the right behavior!! - $cellXML = ''; - } - } else { - throw new InvalidArgumentException('Trying to add a value with an unsupported type: '.\gettype($cell->getValue())); - } - - return $cellXML; - } - - /** - * Returns the XML fragment for a cell containing a non empty string. - * - * @param string $cellValue The cell value - * - * @throws InvalidArgumentException If the string exceeds the maximum number of characters allowed per cell - * - * @return string The XML fragment representing the cell - */ - private function getCellXMLFragmentForNonEmptyString($cellValue) - { - if ($this->stringHelper->getStringLength($cellValue) > self::MAX_CHARACTERS_PER_CELL) { - throw new InvalidArgumentException('Trying to add a value that exceeds the maximum number of characters allowed in a cell (32,767)'); - } - - if ($this->shouldUseInlineStrings) { - $cellXMLFragment = ' t="inlineStr">'.$this->stringsEscaper->escape($cellValue).''; - } else { - $sharedStringId = $this->sharedStringsManager->writeString($cellValue); - $cellXMLFragment = ' t="s">'.$sharedStringId.''; - } - - return $cellXMLFragment; - } -} diff --git a/upstream-3.x/src/Writer/XLSX/Writer.php b/upstream-3.x/src/Writer/XLSX/Writer.php deleted file mode 100644 index fa3a054..0000000 --- a/upstream-3.x/src/Writer/XLSX/Writer.php +++ /dev/null @@ -1,72 +0,0 @@ -throwIfWriterAlreadyOpened('Writer must be configured before opening it.'); - - $this->optionsManager->setOption(Options::TEMP_FOLDER, $tempFolder); - - return $this; - } - - /** - * Use inline string to be more memory efficient. If set to false, it will use shared strings. - * This must be set before opening the writer. - * - * @param bool $shouldUseInlineStrings Whether inline or shared strings should be used - * - * @throws \OpenSpout\Writer\Exception\WriterAlreadyOpenedException If the writer was already opened - * - * @return Writer - */ - public function setShouldUseInlineStrings($shouldUseInlineStrings) - { - $this->throwIfWriterAlreadyOpened('Writer must be configured before opening it.'); - - $this->optionsManager->setOption(Options::SHOULD_USE_INLINE_STRINGS, $shouldUseInlineStrings); - - return $this; - } - - /** - * Merge cells. - * Row coordinates are indexed from 1, columns from 0 (A = 0), - * so a merge B2:G2 looks like $writer->mergeCells([1,2], [6, 2]);. - * - * You may use CellHelper::getColumnLettersFromColumnIndex() to convert from "B2" to "[1,2]" - * - * @param int[] $range1 - top left cell's coordinate [column, row] - * @param int[] $range2 - bottom right cell's coordinate [column, row] - * - * @return $this - */ - public function mergeCells(array $range1, array $range2) - { - $this->optionsManager->addOption(Options::MERGE_CELLS, [$range1, $range2]); - - return $this; - } -}